LLM μμ΄μ νΈμ λΆνμ€ν μλ΅ νμ§μ μλνλ νμ΄νλΌμΈμΌλ‘ κ²μ¦νκ³ κ΄λ¦¬ν©λλ€.
LLM μμ΄μ νΈ κ°λ°μ μΌλ°μ μΈ λ°±μλ κ°λ°κ³Ό λ€λ¦ λλ€. ν둬ννΈ ν μ€, μμ€μ½λ ν μ€μ μμ μ΄ μλ°± κ°μ ν μ€νΈ μΌμ΄μ€μ μ΄λ€ μν₯μ μ€μ§ μμΈ‘νκΈ° μ΄λ ΅μ΅λλ€. μ΄ νλ‘μ νΈλ "λ± λ¨μ΄μ§μ§ μλ LLM μλ΅"μ ν¨μ¨μ μΌλ‘ κ²μ¦νκΈ° μν΄ μμλμμ΅λλ€. λ§€μΌ λ°λ³΅λλ μλ ν μ€νΈμ λ²μ μμ μ μλννκ³ , λμκ° ν μ€νΈ β ν둬ννΈ μμ β λ°μ β μ¬μΈ‘μ μ κ°μ 루ν μ 체λ₯Ό μλνν©λλ€.
- κ²μ¦ 리μμ€ 95.8% μ κ°: κΈ°μ‘΄ 8μκ° μμλλ μ μ κ²μ¬λ₯Ό 20λΆ λ΄μΈ(κ²ν μκ° κΈ°μ€)λ‘ λ¨μΆ.
- νμ§ μμ μ± ν보: μλ°± κ°μ μΌμ΄μ€λ₯Ό μμ κ²μ¦νμ¬ κ³΅μ μμ° λ° UAT 리μ€ν¬ μ΅μν.
ββββββββββββββββββββββββββββββββββββββββββββββββ
β μμ΄μ νΈ μλ² (μΈλΆ) β
β agentChat / agentChatStream β Redis μ°Έμ‘° β
βββββββββ²βββββββββββββββββββββββββββ²ββββββββββββ
β API νΈμΆ β llm_prompt:* / config:llm:*
βββββββββββββββββββββββββββββββ΄ββββ βββββββββ΄βββββββββββββββββββββββββ
β e2e_regression β β Redis β
β YAML μΌμ΄μ€ β API νΈμΆ β κ²μ¦ β β β² νμΌ sync β² DB sync β
β (entity golden, judge) β β prompt_update db_to_redis β
β β μνΈ/μ¬λ/ν°λ―Έλ/JSON 리ν¬νΈ β β (prompts/*.md) (PostgreSQL) β
βββββββββ²ββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββ
β μ€ν¨ μμ§ / μ¬μΈ‘μ
βββββββββ΄ββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββ
β prompt_optimizer β β model_payload_test β
β μ€ν¨ λΆμ β LLM μμ μ β β LLM μ§μ payload μ€νΈλ¦Ό μλΉ β
β β prompt_updateλ‘ λ°μ β μ¬μΈ‘μ β β β λ©ν°λͺ¨λΈ/μ¨λ λΉκ΅ + GPT judge β
βββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββ
β cache_probe: κ²μ΄νΈμ¨μ΄ μΊμ μ ν©μ± νλ‘λΈ β
ββββββββββββββββββββββββββββββββββββββββββ
| ν¨ν€μ§ | μν | μμΈ |
|---|---|---|
packages/e2e_regression |
E2E νκ· λ¬λ β YAML μΌμ΄μ€λ‘ μμ΄μ νΈ API κ²μ¦, μνΈ/μ¬λ 리ν¬νΈ | μ΄ λ ν¬μ μ€μ¬ |
packages/prompt_update |
prompts/*.md νμΌ β Redis ν둬ννΈ μ€μκ° λ°μ μλ² (8083) |
νμΌμ΄ μμ€ μ€λΈ νΈλ£¨μ€ |
packages/prompt_optimizer |
ν둬ννΈ μλ κ°μ 루ν (μ€ν¨ μμ§ β LLM μμ μ β λ°μ β μ¬μΈ‘μ β μ±ν/λ‘€λ°±) | flaky λΆλ¦¬, μ¨λ νλ‘λΈ ν¬ν¨ |
packages/db_to_redis |
PostgreSQL(CMS) β Redis λκΈ°ν μλ² (8082) | μΊμ/ν둬ννΈ/LLM μ€μ |
packages/model_payload_test |
LLM μ§μ payloadλ₯Ό μ€νΈλ¦ΌμΌλ‘ λ°μ λ©ν°λͺ¨λΈ/μ¨λ λΉκ΅ + judge | μμ watcher |
packages/cache_probe |
api-gateway λ μ¨ μΊμ μ ν©μ± μ¦κ±° μμ§ νλ‘λΈ | ν¬λ μ λꡬ |
ν둬ννΈ μλ³Έμ λ ν¬ λ£¨νΈ prompts/*.md + λ§€ν μ μΈ prompts/manifest.yaml,
μμ΄μ νΈ μ μ±
λ¬Έμλ spec/weather_agent_policy.md,
ν둬ννΈ λ³κ²½ μ΄λ ₯(μ/μ΄λ»κ²)μ prompts/CHANGELOG.mdμ μλ€.
npm install # λ£¨νΈ (e2e_regression, optimizer, watcher, probe 곡μ©)
cd packages/prompt_update && npm install # μλ² ν¨ν€μ§λ κ°λ³ μ€μΉ
cd packages/db_to_redis && npm installλ£¨νΈ .env(μμλ μλ "νκ²½λ³μ μ±μ°λ λ²" μ°Έκ³ )μ κ° μλ² ν¨ν€μ§μ .env(.env.example μ°Έκ³ )λ₯Ό μμ±νλ€.
.envλ μ λ μ»€λ° κΈμ§.
νμ μ΅μ μ :
# λ£¨νΈ .env
CONTROL_BASE_URL="http://localhost:8080/aia-control/v1/agentChat" # νλ‘ν λ―Έμ¬μ© μ ν΄λ°±
X_API_KEY=""
AI_API_KEY="" # Gemini (judge, prompt_optimizer)
REDIS_URL="redis://127.0.0.1:6379"
# μνΈ λ¦¬ν¬νΈλ₯Ό μ°λ €λ©΄: GOOGLE_SHEET_ID / GOOGLE_SERVICE_ACCOUNT_EMAIL / GOOGLE_PRIVATE_KEY
# μ¬λ μλ¦Όμ μ°λ €λ©΄: SLACK_WEBHOOK_URLprofiles:
local:
baseUrl: http://localhost:8080/aia-control/v1/agentChat
sheetName: local_260714
stg:
baseUrl: https://β¦-stagingβ¦/aia-control/v1/agentChat
sheetName: stg_260627μ νκ²½μ μ¬κΈ°μ μΆκ°λ§ νλ©΄ μ½λ μμ μμ΄ test:*:<νλ‘ν>λ‘ λ°λ‘ μΈ μ μλ€.
μ νμ: terminal[:ai][:νλ‘ν] λλ sheet:<judge>[:νλ‘ν] (judge = none|internal|api|gpt|local)
npm run test:terminal:local # ν°λ―Έλ 리ν¬νΈ
npm run test:sheet:none:local # μνΈ κΈ°λ‘, judge μμ
npm run test:profile -- sheet:gpt:local --mode sync # GPT judge
npm run test:profile -- sheet:api:stg --mode stream # Gemini judge + μ€νΈλ¦¬λ°(TTFT μΈ‘μ )
npm run test:profile -- terminal:local -- -t "ν¨ν΄" # jest -t νν°- νμ± μΌμ΄μ€ νμΌ λͺ©λ‘:
packages/e2e_regression/data/testcase_groups.tsμCASE_GROUPS - μ΄μ΄ λ리기: κ°μ λ μ¬μ€ννλ©΄
.checkpoint.jsonκΈ°μ€μΌλ‘ μ±κ³΅ μΌμ΄μ€ skip, μ€ν¨λ§ μ¬μλ
cd packages/prompt_update && npm run dev # μλ² κΈ°λ (8083)
# prompts/weather_answer.md μμ ν:
curl -X POST localhost:8083/promptUpdate -H 'Content-Type: application/json' -d '{}'
curl localhost:8083/status # νμΌβRedis μΌμΉ/llm_id νμΈμ μ : μμ΄μ νΈ μλ² + prompt_update μλ²(8083) μ€ν μ€. μλ ν μ€νΈ λ°κ³Ό λμ μ€ν κΈμ§.
npm run optimize:prompt -- --analyze-only # μ€ν¨ μμ§ + flaky/μ§μ§ μ€ν¨ λΆλ₯λ§
npm run optimize:prompt # κ°μ 루ν (κΈ°λ³Έ weather_entity.md, 3ν)
npm run optimize:prompt -- --prompt weather_answer.md --iterations 5 --repeat 3 --temps 0,0.7
# λλλ©΄: git diff prompts/ λ‘ λ¦¬λ·° ν 컀λ°κ°μ μμ΄ μ±νλλ©΄ λ³κ²½ μ΄μ (LLM λΆμ)κ° prompts/CHANGELOG.mdμ μλ κΈ°λ‘λλ€.
μ»€λ° μ ν¨κ» λ΄μ κ² (μ»€λ° λ©μμ§ κ·μΉμ CLAUDE.md μ°Έκ³ ).
cd packages/db_to_redis && npm run dev # μλ² κΈ°λ (8082)
curl -X POST localhost:8082/dbToRedis -H 'Content-Type: application/json' -d '{"type": "prompt"}'μ£Όμ: μ 체 λκΈ°ν({})λ ν둬ννΈ/llm_idλ₯Ό DB κ°μΌλ‘ λλλ¦°λ€ β μ΄ν POST localhost:8083/promptUpdateλ‘ λ³΅κ΅¬.
npm run watch:weather:answer-compare # ν°λ―Έλ
REPORT_TO=sheet EVALUATE_WITH_GPT=1 npm run watch:weather:answer-compare # μνΈ + judgeend-to-end κ°μ¬: νκ· λ¬λμ PUBLISH_AGENT_RESPONSE_STREAM=1, watcherμ JOIN_AGENT_RESPONSE_STREAM=1.
npm run probe:cache
CACHE_PROBE_ROUNDS=0 CACHE_PROBE_STOP_ON_DIVERGENCE=1 npm run probe:cache # 무ν, λ°κ²¬ μ μ€λ¨npm run llm:list # Redis config:llm:* λͺ©λ‘ (llm_id β λͺ¨λΈ νμΈ)
npm run llm:list -- --verbose # μ 체 JSON ν¬ν¨
tsx scripts/generate-entity-goldens.ts # entity golden μΌκ΄ μμ±
# β testcase YAMLμ μ§μ μμ ν¨. ENTITY_PARSER_PROMPT_FILE μ§μ νμ
npm run typecheck # μ 체 νμ
체ν¬-
Google Sheets API
https://console.cloud.google.com/welcome/new μμ νλ‘μ νΈ μμ± β Google Sheets API Enable β Credentials β Create Credentials β Service Account β Keys β Create private key (JSON)
λ€μ΄λ‘λλ JSONμμ
client_emailβGOOGLE_SERVICE_ACCOUNT_EMAIL,private_keyβGOOGLE_PRIVATE_KEY. μ€νλ λμνΈλ₯Ό λ§λ€κ³ URLμhttps://docs.google.com/spreadsheets/d/<μ΄ λΆλΆ>/editμGOOGLE_SHEET_IDμ. ν΄λΉ μνΈλ₯Ό μλΉμ€ κ³μ μ΄λ©μΌμ νΈμ§μλ‘ κ³΅μ ν΄μΌ νλ€. κ²°κ³Ό ν μ΄λ¦μ νλ‘νμsheetNameμ΄ μ°μ νλ©°, νμ΄ μμΌλ©΄ μλ μμ±λλ€. -
Gemini: https://ai.google.dev/gemini-api/docs/models μμ λͺ¨λΈ μ ν β
AI_MODEL, API ν€ βAI_API_KEY -
Ollama (JUDGE_MODE=localμ©): https://ollama.com μ€μΉ β
ollama pull gemma2:27bβLOCAL_AI_MODEL='gemma2:27b' -
μνΈ wrap μ€μ :
packages/e2e_regression/config/settings/sheet.yamlμwrapColumns(κΈ°λ³Έ E,F,G,H)
κ° ν¨ν€μ§κ° μ½λ envμ μ 체 λͺ©λ‘μ κ° ν¨ν€μ§ READMEμ νκ²½λ³μ ν μ°Έκ³ .
μ΄ λ ν¬λ CLI μμ΄μ νΈκ° "ν μ€νΈ λλ¦¬κ³ ν둬ννΈ κ°μ ν΄μ€" ν λ§λλ‘ μ 체 루νλ₯Ό μμ¨ μνν μ μλλ‘ μΈν λμ΄ μλ€:
| νμΌ | μν |
|---|---|
CLAUDE.md (= AGENTS.md μ¬λ§ν¬) |
μμ΄μ νΈκ° μΈμ μμ μ μλμΌλ‘ μ½λ μ΄μ κ°μ΄λ. ν΅μ¬ μ¬μ€, λͺ λ Ήμ΄, κΈμ§ κ·μΉ(λμ μ€ν κΈμ§, promptUpdate νμ, λ°±μλ μ€ν¨ ꡬλΆ, flaky 무μ λ±), μ»€λ° λ©μμ§ κ·μΉ |
.claude/skills/improve-prompts/ |
ν둬ννΈ κ°μ νλ μ΄λΆ μ€ν¬. Claude Codeμμ /improve-promptsλ‘ νΈμΆνκ±°λ "ν둬ννΈ κ°μ ν΄μ€"λΌκ³ νλ©΄ μλ λ°λ. μ¬μ νμΈ β μ§λ¨ β κ°μ β κΈ°λ‘ β λ³΄κ³ μμ κ°μ |
prompts/CHANGELOG.md |
ν둬ννΈ λ³κ²½μ "μ/μ΄λ»κ²" μꡬ κΈ°λ‘ (μ΅μ μ΄ μ). prompt_optimizerκ° κ°μ μμ μ±ννλ©΄ μλ append, μλ μμ μμ κ°μ νμμΌλ‘ μ§μ μΆκ° |
.claude/settings.json |
μν¬νλ‘ λͺ
λ Ή κΆν allowlist (npm run test/optimize/typecheck/llm:list, curl localhost:8083/8082, μ½κΈ° μ μ© redis-cli). μ°κΈ°μ± λͺ
λ Ήμ μλμ μΌλ‘ μ μΈ β λ¬Όμ΄λ³΄κ³ μ€ν |
ν둬ννΈ μ»€λ°μλ λ°λμ "μ"κ° λ¨μμΌ νλ€:
prompt(weather_entity): tuαΊ§n nΓ y ννμ weekPart μ€μΆμΆ μμ
- fail 5 β 2 (Weather_Week_Matrix κΈ°μ€)
- κ·Όκ±°/λΆμ: prompts/CHANGELOG.md λ° packages/prompt_optimizer/runs/<runλλ ν 리>
ν둬ννΈ λ³κ²½κ³Ό μ½λ λ³κ²½μ 컀λ°μ λΆλ¦¬νλ€. μμΈ κ·μΉμ CLAUDE.md μ°Έκ³ .
ν
μ€νΈκ° μλ£λλ©΄ μ¬λμ λ©μμ§κ° μ μ‘λ©λλ€.

κ΅¬κΈ μνΈμλ μΌμ΄μ€λ³ κ²°κ³Όκ° μ 리λμ΄ FailμΈ κ²λ§ κ²ν νλ©΄ λ©λλ€.

μ΄ νλ‘μ νΈλ λ ν¨μ¨μ μΈ AI μμ΄μ νΈ κ°λ° λ¬Ένλ₯Ό μ§ν₯ν©λλ€. λ²κ·Έ 리ν¬νΈλ κΈ°λ₯ μ μμ μΈμ λ νμν©λλ€!