From 4673dbf502155a1c25a324f9054322f116d0f0d2 Mon Sep 17 00:00:00 2001 From: Claude-51124 Date: Fri, 13 Mar 2026 18:29:55 +0900 Subject: [PATCH] Document Pydantic-first IR deck validation rollout --- journey/README.md | 4 + ...ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md | 109 +++++++ ...๋ถ€๋ถ„๋„์ž…_llm_์ถœ๋ ฅ์•ˆ์ •ํ™”_๊ณ„ํš.md | 179 ++++++++++++ ...˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md | 265 ++++++++++++++++++ ...only_์ถœ๋ ฅ๊ฒ€์ฆ_๊ตฌํ˜„๋ฐ๋ฐฐํฌ๊ฒ€์ฆ.md | 46 +++ 5 files changed, 603 insertions(+) create mode 100644 journey/ideas/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md create mode 100644 journey/plans/260313_pydantic_ai_๋ถ€๋ถ„๋„์ž…_llm_์ถœ๋ ฅ์•ˆ์ •ํ™”_๊ณ„ํš.md create mode 100644 journey/research/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md create mode 100644 journey/worklog/260313_ir_deck_pydantic_only_์ถœ๋ ฅ๊ฒ€์ฆ_๊ตฌํ˜„๋ฐ๋ฐฐํฌ๊ฒ€์ฆ.md diff --git a/journey/README.md b/journey/README.md index 700020f..7a59028 100644 --- a/journey/README.md +++ b/journey/README.md @@ -64,6 +64,10 @@ - ๋กœ๋น™ ์—์ด์ „ํŠธ ๋ฃจํ”„ยท์Šคํ‚ฌ ํ›…ยทLLM ์‹คํ–‰ ๊ตฌ์กฐ ๋ฆฌ์„œ์น˜ โ€“ `research/orchestration_tools/260312_๋กœ๋น™_์—์ด์ „ํŠธ๋ฃจํ”„_์Šคํ‚ฌํ›…_LLM์‹คํ–‰๊ตฌ์กฐ_๋ฆฌ์„œ์น˜.md` - ๋กœ๋น™ LLM APIยทAgent APIยท๋ชจ๋ธ ์„ ์ •ยท๋น„์šฉ ๋น„๊ต ๋ฆฌ์„œ์น˜ โ€“ `research/orchestration_tools/260312_๋กœ๋น™_LLM_API_Agent_API_๋ชจ๋ธ์„ ์ •_๋น„์šฉ๋น„๊ต_๋ฆฌ์„œ์น˜.md` +### LLM ์ถœ๋ ฅ ๊ณ„์•ฝ / ์•ˆ์ •ํ™” + +- Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์ข…๋ฃŒ ๋ฆฌ์„œ์น˜ โ€“ `research/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md` + ### HITL / ๋ฆฌ๋ทฐ ํ - Human-in-the-Loop Intent Learning ์•„ํ‚คํ…์ฒ˜ โ€“ `../300_architecture/390_human_in_the_loop_intent_learning.md` diff --git a/journey/ideas/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md b/journey/ideas/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md new file mode 100644 index 0000000..bed14e6 --- /dev/null +++ b/journey/ideas/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md @@ -0,0 +1,109 @@ +tags: [ideas, pydantic-ai, langgraph, llm, structured-output, ir-analysis] + +# Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์•„์ด๋””์–ด + +## ์•„์ด๋””์–ด ํ•œ ์ค„ +- `LangGraph`๋Š” ์œ ์ง€ํ•˜๊ณ , ํ•ต์‹ฌ ๊ตฌ์กฐํ™” ์ถœ๋ ฅ ๊ฒฝ๋กœ์—๋Š” ์šฐ์„  `Pydantic` ๊ธฐ๋ฐ˜ typed validation์„ ๋„์ž…ํ•˜๊ณ , ํ•„์š” ์‹œ ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ `Pydantic AI`๊นŒ์ง€ ํ™•์žฅํ•˜์ž๋Š” ์•„์ด๋””์–ด์ž…๋‹ˆ๋‹ค. + +## ์™œ ์ด ์•„์ด๋””์–ด๋ฅผ ๋– ์˜ฌ๋ ธ๋Š”๊ฐ€ +- ํ˜„์žฌ ์ผ๋ถ€ ํ‰๊ฐ€ ๊ฒฝ๋กœ๋Š” LLM์ด ๋ฐ˜ํ™˜ํ•œ ๊ตฌ์กฐํ™” ๊ฒฐ๊ณผ๋ฅผ ๊ฑฐ์˜ ๊ทธ๋Œ€๋กœ ์‹ ๋ขฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. +- ์˜ค๋Š˜ IR Deck ๋ถ„์„์—์„œ๋Š” ํšŒ์‚ฌ์†Œ๊ฐœ์„œ ์ž์ฒด๋Š” ์ œ๋Œ€๋กœ ์„ ํƒ๋์ง€๋งŒ, LLM์ด `total_score=100`, `grade=B`์ฒ˜๋Ÿผ ์„œ๋กœ ๋งž์ง€ ์•Š๋Š” ๊ฐ’์„ ํ•จ๊ป˜ ๋ฐ˜ํ™˜ํ–ˆ๊ณ , ์ด ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋์Šต๋‹ˆ๋‹ค. +- ๊ทธ๋ž˜์„œ ์ง€๊ธˆ ํ•„์š”ํ•œ ๊ฒƒ์€ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๊ฐˆ์•„์—Ž๋Š” ๊ฒƒ๋ณด๋‹ค, `Pydantic` ๊ณ„์—ด typed output ๊ณ„์ธต์„ ๋ถ™์—ฌ ์ถœ๋ ฅ ๊ณ„์•ฝ์„ ๋” ๊ฐ•ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉํ–ฅ์ด๋ผ๊ณ  ๋ดค์Šต๋‹ˆ๋‹ค. + +## ์ด ์•„์ด๋””์–ด๊ฐ€ ๊ฒจ๋ƒฅํ•˜๋Š” ํ˜„์žฌ ์ƒํƒœ +- ์ง€๊ธˆ ๊ตฌ์กฐ์—์„œ๋Š” LLM์ด JSON ๋น„์Šทํ•œ ์ถœ๋ ฅ์„ ํ•˜๋ฉด ํŒŒ์‹ฑ ํ›„ ์ผ๋ถ€ ํ•„๋“œ๋งŒ ๋ฒ”์œ„ ์ œํ•œํ•˜๊ณ  ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. +- ๊ทธ ๊ฒฐ๊ณผ: + - ์ ์ˆ˜์™€ ๋“ฑ๊ธ‰์˜ ์ •ํ•ฉ์„ฑ์ด ๊นจ์ ธ๋„ ํ†ต๊ณผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + - `story_scores` ํ‰๊ท ๊ณผ `total_score`๊ฐ€ ๋งž์ง€ ์•Š์•„๋„ ๊ทธ๋Œ€๋กœ ์ €์žฅ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + - ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€, DB ์ €์žฅ, ํ›„์† ์˜์‚ฌ๊ฒฐ์ •์ด ๋ชจ๋‘ ํ”๋“ค๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +- ์ฆ‰ ํ˜„์žฌ ๋ฌธ์ œ๋Š” "LLM์ด ๋“ค์ญ‰๋‚ ์ญ‰ํ•˜๋‹ค"๋ณด๋‹ค, "๋“ค์ญ‰๋‚ ์ญ‰ํ•œ ์ถœ๋ ฅ์„ ๋ง‰๋Š” typed validation ๊ณ„์ธต์ด ์•ฝํ•˜๋‹ค"์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. + +## ํ•ต์‹ฌ ์•„์ด๋””์–ด +- `LangGraph`๋Š” ์œ ์ง€ํ•˜๋˜, LLM ์ถœ๋ ฅ์ด ์ค‘์š”ํ•œ ๊ตฌ๊ฐ„์—๋Š” `Pydantic` ๊ธฐ๋ฐ˜ typed validation ๊ณ„์ธต์„ ๋จผ์ € ๋ถ€๋ถ„ ๋„์ž…ํ•ฉ๋‹ˆ๋‹ค. +- ์ดํ›„ retry, output validator, agentํ™”๊ฐ€ ๋” ํ•„์š”ํ•ด์ง€๋ฉด `Pydantic AI`๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ๊ฒฝ๊ณ„๋ฅผ ์žก์Šต๋‹ˆ๋‹ค. +- ๋ชฉํ‘œ๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๊ฐˆ์•„์—Ž๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, **LLM์ด ๋ฐ˜ํ™˜ํ•˜๋Š” ํ‰๊ฐ€/๋ถ„๋ฅ˜/ํŒ์ • ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆ ๊ฐ€๋Šฅํ•œ ๋ชจ๋ธ๋กœ ๊ฐ•์ œ**ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. +- ์ฆ‰ ์ด ์•„์ด๋””์–ด์˜ ์ค‘์‹ฌ ๋ฌธ์žฅ์€ `Pydantic ๊ณ„์—ด typed validation์„ ์•ˆ์ •ํ™” ๊ณ„์ธต์œผ๋กœ ๋„์ž…ํ•˜์ž`์ž…๋‹ˆ๋‹ค. + +## ์™œ ํ•˜ํ•„ Pydantic ๊ณ„์—ด์ธ๊ฐ€ + +### 1. ์ง€๊ธˆ ๋ฌธ์ œ๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๋ณด๋‹ค ์ถœ๋ ฅ ๊ณ„์•ฝ ๋ฌธ์ œ๋‹ค +- ์ฝœ๋“œ๋ฉ”์ผ์ฒ˜๋Ÿผ `์ค‘๋‹จ/์žฌ๊ฐœ`, `์ฒดํฌํฌ์ธํŠธ`, `์‚ฌ๋žŒ ํ™•์ธ ํ›„ ๊ณ„์† ์ง„ํ–‰`์ด ํ•„์š”ํ•œ ๊ฒฝ๋กœ๋Š” ์—ฌ์ „ํžˆ LangGraph๊ฐ€ ์ž˜ ๋งž์Šต๋‹ˆ๋‹ค. +- ํ•˜์ง€๋งŒ IR ํ‰๊ฐ€์ฒ˜๋Ÿผ "์ ์ˆ˜/๋“ฑ๊ธ‰/์š”์•ฝ/์ถ”์ฒœ"์„ ๊ตฌ์กฐํ™”ํ•ด์„œ ๋‚ด์•ผ ํ•˜๋Š” ๊ฒฝ๋กœ๋Š” LangGraph๋ณด๋‹ค ์ถœ๋ ฅ ๋ชจ๋ธ ๊ฒ€์ฆ์ด ๋” ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. + +### 2. LangGraph๋Š” ๊ดœ์ฐฎ์ง€๋งŒ, ๋ณต์žกํ•œ ์ง€์ ์ด ๋ถ„๋ช…ํžˆ ์žˆ๋‹ค +- ์žฅ์ : + - ์ƒํƒœ ์ €์žฅ + - interrupt / resume + - ์žฅ๊ธฐ ์‹คํ–‰ ํ๋ฆ„ + - ์‚ฌ๋žŒ ํ™•์ธ ํ›„ ์žฌ๊ฐœ ๊ฐ™์€ ์šด์˜ ํ๋ฆ„ +- ๋ณต์žกํ•œ ์ : + - ๋‹จ์ˆœ ๊ตฌ์กฐํ™” ์‘๋‹ต ๋ฌธ์ œ๊นŒ์ง€ ์›Œํฌํ”Œ๋กœ์šฐ ๋ฌธ์ œ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + - ์ถœ๋ ฅ ๊ฒ€์ฆ์ด ์•ฝํ•˜๋ฉด, ๊ทธ๋ž˜ํ”„๋Š” ์ž˜ ๋Œ์•„๋„ ์ž˜๋ชป๋œ ๊ฐ’์ด ์˜ค๋ž˜ ์‚ด์•„๋‚จ์Šต๋‹ˆ๋‹ค. +- ๋”ฐ๋ผ์„œ ์ง€๊ธˆ ๋ฌธ์ œ๋ฅผ ๋ณด๊ณ  `LangGraph๊ฐ€ ๋‚˜์˜๋‹ค`๊ณ  ๊ฒฐ๋ก ๋‚ด๋ฆฌ๋Š” ๊ฒƒ์€ ๊ณผ๋„ํ•ฉ๋‹ˆ๋‹ค. +- ๋” ์ •ํ™•ํ•œ ํ•ด์„์€ `LangGraph๋Š” ์œ ์ง€ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์ง€๋งŒ, typed output ์•ˆ์ •ํ™” ๊ณ„์ธต์ด ๋ณ„๋„๋กœ ํ•„์š”ํ•˜๋‹ค`์ž…๋‹ˆ๋‹ค. + +## Pydantic ๊ณ„์—ด์„ ์“ฐ๋ฉด ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ + +### 1. ์ถœ๋ ฅ ๋ชจ๋ธ ๊ฐ•์ œ +- ์˜ˆ: `IRDeckEvaluationResult` ๊ฐ™์€ ๋ชจ๋ธ๋กœ ์•„๋ž˜๋ฅผ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + - `total_score: int` + - `grade: Literal["S", "A", "B", "C"]` + - `story_scores: list[...]` + - `summary: str` + - `investment_opinion: ...` +- ๊ตฌ์กฐ๊ฐ€ ๋งž์ง€ ์•Š์œผ๋ฉด ๋ฐ”๋กœ ์‹คํŒจ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### 2. ํ›„์ฒ˜๋ฆฌ ๊ฒ€์ฆ์„ ๋ช…์‹œ์ ์œผ๋กœ ๋‘˜ ์ˆ˜ ์žˆ์Œ +- ์˜ˆ: + - `total_score`์™€ `grade` ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ + - `story_scores` ํ‰๊ท ๊ณผ `total_score` ์ฐจ์ด ์ œํ•œ + - ํ•„์ˆ˜ ํ•„๋“œ ๊ธธ์ด/๊ฐœ์ˆ˜ ๊ฒ€์ฆ +- ์ฆ‰ "ํŒŒ์‹ฑ๋งŒ ์„ฑ๊ณตํ•˜๋ฉด ํ†ต๊ณผ"๊ฐ€ ์•„๋‹ˆ๋ผ "์˜๋ฏธ์ ์œผ๋กœ๋„ ๋งž์•„์•ผ ํ†ต๊ณผ"๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +### 3. ์‹คํŒจ๋ฅผ ์„ฑ๊ณต์ฒ˜๋Ÿผ ํฌ์žฅํ•˜์ง€ ์•Š๊ธฐ ์‰ฌ์›€ +- ์ง€๊ธˆ์ฒ˜๋Ÿผ `100์ ์ธ๋ฐ B๋“ฑ๊ธ‰` ๊ฐ™์€ ๋ชจ์ˆœ์„ ์กฐ์šฉํžˆ ์ €์žฅํ•˜๋Š” ๋Œ€์‹ , + - ์žฌ์งˆ์˜ + - ์žฌ๊ณ„์‚ฐ + - ์‹คํŒจ ๊ฐ€์‹œํ™” + ์ค‘ ํ•˜๋‚˜๋กœ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ์ด ์•„์ด๋””์–ด์˜ ๊ถŒ์žฅ ๋ฐฉํ–ฅ + +### 1. LangGraph ์ „๋ฉด ๊ต์ฒด๋Š” ํ•˜์ง€ ์•Š๋Š”๋‹ค +- ์ฝœ๋“œ๋ฉ”์ผ, ๋ธŒ๋ฆฌํ•‘, ์ธํ„ฐ๋ŸฝํŠธ ์›Œํฌํ”Œ๋กœ์šฐ๋Š” LangGraph๊ฐ€ ๊ณ„์† ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. +- ์žฅ๊ธฐ ์ƒํƒœ/์ฒดํฌํฌ์ธํŠธ/์‚ฌ๋žŒ ํ™•์ธ ํ๋ฆ„์€ LangGraph์˜ ์‹ค์ œ ๊ฐ•์ ์ž…๋‹ˆ๋‹ค. + +### 2. 1์ฐจ๋Š” `Pydantic-only`, 2์ฐจ ํ›„๋ณด๋Š” `Pydantic AI`๋กœ ๋‘”๋‹ค +- ์šฐ์„  ํ›„๋ณด: + - IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€ ์ถœ๋ ฅ + - ๋ธŒ๋ฆฌํ•‘ ์ธ์‚ฌ์ดํŠธ ํ•œ ์ค„ ์ถœ๋ ฅ + - ์ฝœ๋“œ๋ฉ”์ผ ํ›„๋ณด ํŒ์ • ์ด์œ  ์š”์•ฝ + - ํˆฌ์ž ์˜๊ฒฌ ๊ตฌ์กฐํ™” ์ถœ๋ ฅ +- ์ฆ‰ LLM์ด ๋ง๋งŒ ์ž˜ํ•˜๋ฉด ๋˜๋Š” ๊ฒฝ๋กœ๋ณด๋‹ค, **์ถœ๋ ฅ๊ฐ’์ด ์ œํ’ˆ ๋ฐ์ดํ„ฐ๊ฐ€ ๋˜๋Š” ๊ฒฝ๋กœ**์— ๋จผ์ € ๋ถ™์ž…๋‹ˆ๋‹ค. +- ์ฒซ ๋‹จ๊ณ„๋Š” ๊ธฐ์กด `call_llm()` ๋’ค์— `Pydantic model_validate()`์™€ semantic validator๋ฅผ ๋ถ™์ด๋Š” ๋ฐฉ์‹์ด ๊ฐ€์žฅ ํ˜„์‹ค์ ์ž…๋‹ˆ๋‹ค. +- ์ดํ›„ ์žฌ์งˆ์˜, output validator, retry๋ฅผ ๊ฒฝ๋กœ ๋‚ด๋ถ€์— ๋” ๊ฐ•ํ•˜๊ฒŒ ๋ฌถ๊ณ  ์‹ถ์„ ๋•Œ `Pydantic AI`๋กœ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค. + +### 3. ์ ์ˆ˜๋Š” LLM์—๊ฒŒ ์ „๋ถ€ ๋งก๊ธฐ์ง€ ์•Š๋Š”๋‹ค +- ์ด์ƒ์ ์ธ ๋ฐฉํ–ฅ์€: + - LLM์€ `story_scores`, ์š”์•ฝ, ๊ฐ•์ /์•ฝ์  ๊ฐ™์€ ํ•ด์„์„ ๋ฐ˜ํ™˜ + - `total_score`, `grade`, `recommendation` ์ผ๋ถ€๋Š” ์„œ๋ฒ„๊ฐ€ ์žฌ๊ณ„์‚ฐ ๋˜๋Š” ์žฌ๊ฒ€์ฆ +- ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด LLM์˜ ์ž์œ ๋„๋Š” ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ์ตœ์ข… ์‚ฌ์šฉ์ž ๊ฐ’์€ ๋” ์•ˆ์ •ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ์ด ์•„์ด๋””์–ด๊ฐ€ ๋‹ตํ•˜๋ ค๋Š” ์งˆ๋ฌธ +1. `Pydantic-only`๋งŒ์œผ๋กœ๋„ ํ˜„์žฌ ๋ชจ์ˆœ๊ฐ’ ์ฐจ๋‹จ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€? +2. `LangGraph`๋Š” ์œ ์ง€ํ•˜๋ฉด์„œ `Pydantic` ๊ณ„์—ด๋กœ ์ถœ๋ ฅ ์•ˆ์ •ํ™” ๊ณ„์ธต๋งŒ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€? +3. `Pydantic-only`๋กœ ์‹œ์ž‘ํ•œ ๋’ค ํ•„์š”ํ•˜๋ฉด `Pydantic AI`๋กœ ํ™•์žฅํ•˜๋Š” ์ˆœ์„œ๊ฐ€ ๋งž๋Š”๊ฐ€? + +## ์•„์ง ์•„์ด๋””์–ด ๋‹จ๊ณ„์ธ ์ด์œ  +- ์‹ค์ œ๋กœ `Pydantic-only`์™€ `Pydantic AI`์˜ ๊ฒฝ๊ณ„๋ฅผ ์–ด๋””๊นŒ์ง€ ๋‘˜์ง€ ๋ฒ”์œ„๊ฐ€ ์ •ํ•ด์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. +- ๊ธฐ์กด FastAPI/Pydantic ๋ชจ๋ธ๊ณผ ์–ด๋–ค ๊ฒฝ๊ณ„๋กœ ๊ฒน์น ์ง€ ์„ค๊ณ„๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. +- ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์žฌ์งˆ์˜, ์„œ๋ฒ„ ์žฌ๊ณ„์‚ฐ, ์‹คํŒจ ๋…ธ์ถœ ์ค‘ ์–ด๋–ค ์ •์ฑ…์„ ๊ธฐ๋ณธ์œผ๋กœ ํ• ์ง€ ์•„์ง ์ •ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. +- ๋น„์šฉ๊ณผ ์ง€์—ฐ ์‹œ๊ฐ„๋„ ์‹ค์ œ ๊ฒฝ๋กœ์—์„œ ์ธก์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. + +## ๋ฐ”๋กœ ์‹คํ–‰ ๊ฒฐ๋ก ์œผ๋กœ ๋„˜๊ธฐ์ง€ ์•Š์€ ์ด์œ  +- ์ด๋ฒˆ ๋ฌธ์„œ๋Š” `Pydantic ๊ณ„์—ด typed validation์„ ๋ถ€๋ถ„ ๋„์ž…ํ•˜์ž`๋Š” ๋ฐฉํ–ฅ์„ ์—ฌ๋Š” ์•„์ด๋””์–ด ๋ฌธ์„œ์ž…๋‹ˆ๋‹ค. +- ๋‹ค๋งŒ ์•„์ด๋””์–ด ๋‹จ๊ณ„์—์„œ๋Š” `๋„์ž… ๋ฒ”์œ„`, `๋„์ž… ๊ฒฝ๊ณ„`, `์‹คํŒจ ์ •์ฑ…`์ด ์•„์ง ์—ด๋ ค ์žˆ์œผ๋ฏ€๋กœ ๋ฐ”๋กœ ์‹คํ–‰ ๊ฒฐ๋ก ์œผ๋กœ ๋„˜๊ธฐ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. +- ์‹ค์ œ ๊ณ„ํš์—์„œ๋Š” `์–ด๋А ๊ฒฝ๋กœ๋ถ€ํ„ฐ`, `์–ด๋–ค ํ•„๋“œ๋ถ€ํ„ฐ`, `์–ด๋–ค ์‹คํŒจ ์ •์ฑ…์œผ๋กœ` ๋„์ž…ํ• ์ง€๋ฅผ ์ขํ˜€์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ํ•œ ์ค„ ๊ฒฐ๋ก  +- ๋ฐฉํ–ฅ์€ `LangGraph ์œ ์ง€ + Pydantic-only ๋จผ์ €`์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์ถœ๋ ฅ ๊ฒ€์ฆ์ด ์•ฝํ•œ ๊ฒฝ๋กœ์— typed validation ๊ณ„์ธต์„ ๋จผ์ € ๋ถ™์ด๊ณ , ํ•„์š”ํ•˜๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ `Pydantic AI`๋กœ ํ™•์žฅํ•˜๋Š” ๊ฒƒ์ด ์ด ์•„์ด๋””์–ด์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. diff --git a/journey/plans/260313_pydantic_ai_๋ถ€๋ถ„๋„์ž…_llm_์ถœ๋ ฅ์•ˆ์ •ํ™”_๊ณ„ํš.md b/journey/plans/260313_pydantic_ai_๋ถ€๋ถ„๋„์ž…_llm_์ถœ๋ ฅ์•ˆ์ •ํ™”_๊ณ„ํš.md new file mode 100644 index 0000000..73e17ee --- /dev/null +++ b/journey/plans/260313_pydantic_ai_๋ถ€๋ถ„๋„์ž…_llm_์ถœ๋ ฅ์•ˆ์ •ํ™”_๊ณ„ํš.md @@ -0,0 +1,179 @@ +--- +tags: [plans, pydantic-ai, langgraph, llm, structured-output, ir-analysis] +--- + +# Pydantic AI ๋ถ€๋ถ„ ๋„์ž… LLM ์ถœ๋ ฅ์•ˆ์ •ํ™” ๊ณ„ํš + +**์ž‘์„ฑ์ผ**: 2026-03-13 +**์ƒํƒœ**: planned +**๋ชฉํ‘œ**: `LangGraph`๋Š” ์œ ์ง€ํ•˜๋ฉด์„œ `IR Deck` ์ข…ํ•ฉ ํ‰๊ฐ€ ๊ฒฝ๋กœ์— ์šฐ์„  `Pydantic-only` ๊ธฐ๋ฐ˜ typed validation ๊ณ„์ธต์„ ๋„์ž…ํ•ด, ๋ชจ์ˆœ๋œ ์ ์ˆ˜/๋“ฑ๊ธ‰/์ถ”์ฒœ์ด ์ €์žฅยท๋…ธ์ถœ๋˜์ง€ ์•Š๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. + +## ๊ด€๋ จ ๋ฌธ์„œ +- [Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์•„์ด๋””์–ด](../ideas/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md) +- [Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์ข…๋ฃŒ ๋ฆฌ์„œ์น˜](../research/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md) +- [์ฝœ๋“œ๋ฉ”์ผ IR ๋ถ„์„์ด ์ฒซ ๋ฒˆ์งธ PDF๋ฅผ ์ž˜๋ชป ์„ ํƒํ•˜๋Š” ์ƒํƒœ](../debug/260313_coldmail_ir_๋ถ„์„๋Œ€์ƒ_์˜ค์„ ํƒ_๋””๋ฒ„๊ทธ.md) + +## 1. ์ด๋ฒˆ ๊ณ„ํš์˜ ๊ฒฐ์ • +- 1์ฐจ๋Š” `Pydantic-only`๋กœ ๊ฐ‘๋‹ˆ๋‹ค. +- `Pydantic AI`๋Š” ๋ฒ„๋ฆฌ์ง€ ์•Š๋˜, ํ›„์† 2์ฐจ ํ›„๋ณด๋กœ ๋‘ก๋‹ˆ๋‹ค. +- ์ „๋ฉด ๊ต์ฒด๋Š” ํ•˜์ง€ ์•Š๊ณ , `IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€` ๊ฒฝ๋กœ์—๋งŒ ๋ถ€๋ถ„ ๋„์ž…ํ•ฉ๋‹ˆ๋‹ค. +- `LangGraph` ์›Œํฌํ”Œ๋กœ๋Š” ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. +- ์ฒซ ๋‹จ๊ณ„์—์„œ๋Š” `raw output -> Pydantic model_validate -> semantic validation -> final output -> save` ๊ฒฝ๊ณ„๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. + +## 2. ์™œ ์ง€๊ธˆ ์ด ๊ณ„ํš์œผ๋กœ ๊ฐ€๋Š”๊ฐ€ +- ์‹ค์ธก ๊ธฐ์ค€์œผ๋กœ ํ˜„์žฌ `_evaluate_comprehensive()`๋Š” `total_score=100`, `grade=B` ๊ฐ™์€ ๋ชจ์ˆœ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผ์‹œํ‚ต๋‹ˆ๋‹ค. +- ์‹ค์ธก ๊ธฐ์ค€์œผ๋กœ `Pydantic` validator๋งŒ์œผ๋กœ๋„ ๊ฐ™์€ ์ž…๋ ฅ์„ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. +- ๋”ฐ๋ผ์„œ ์ง€๊ธˆ ํ•„์š”ํ•œ ๊ฒƒ์€ `Pydantic AI`๊นŒ์ง€ ๋ฐ”๋กœ ์˜ฌ๋ฆฌ๋Š” ๊ฒƒ๋ณด๋‹ค, ๋จผ์ € ๊ฐ€์žฅ ์ž‘์€ ๋ณ€๊ฒฝ์œผ๋กœ `IR Deck` ์ข…ํ•ฉ ํ‰๊ฐ€ ์ถœ๋ ฅ์— ๊ฒ€์ฆ ๊ฒฝ๊ณ„๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## 3. ๋ฒ”์œ„ +- ํฌํ•จ: + - `rb8001/app/services/ir_deck_analyzer.py` ์ข…ํ•ฉ ํ‰๊ฐ€ ์ถœ๋ ฅ ๊ฒฝ๋กœ + - ํ•„์š” ์‹œ `rb8001/app/services/ir_deck_output_models.py` ๋˜๋Š” ๋™๋“ฑํ•œ ์ „์šฉ ๋ชจ๋ธ ํŒŒ์ผ ์ถ”๊ฐ€ + - `rb8001/tests/test_ir_deck_json_parsing.py` ๋ณด๊ฐ• + - `rb8001/tests/test_ir_deck_workflow.py` ๋ณด๊ฐ• + - `Pydantic` typed output ๋ชจ๋ธ ๋ฐ validator ์ถ”๊ฐ€ + - ์ €์žฅ ์ „ `final_evaluation_result` ์ƒ์„ฑ + - ๋ชจ์ˆœ ์ถœ๋ ฅ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ +- ์ œ์™ธ: + - `LangGraph` ์ œ๊ฑฐ ๋˜๋Š” ์žฌ์„ค๊ณ„ + - `Pydantic AI` ์˜์กด์„ฑ ์ฆ‰์‹œ ์ถ”๊ฐ€ + - ๋ธŒ๋ฆฌํ•‘, ์ฝœ๋“œ๋ฉ”์ผ, ํˆฌ์ž์˜๊ฒฌ ์ „ ๊ฒฝ๋กœ ๋™์‹œ ์ ์šฉ + - ์šด์˜์šฉ ๊ณตํ†ต ์ถœ๋ ฅ ์ •์ฑ… ํ”„๋ ˆ์ž„์›Œํฌ ์ „๋ฉดํ™” + +## 4. ๊ตฌํ˜„ ์›์น™ +- `LangGraph`๋Š” ์ƒํƒœ ๊ด€๋ฆฌ ๊ณ„์ธต์œผ๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. +- `Pydantic-only`๋Š” ์ข…ํ•ฉ ํ‰๊ฐ€ ๊ฒฐ๊ณผ ๊ฒ€์ฆ ๊ณ„์ธต์œผ๋กœ๋งŒ ๋ถ™์ž…๋‹ˆ๋‹ค. +- `raw`์™€ `final` ๊ฒฐ๊ณผ๋ฅผ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. +- `grade`, `recommendation`์€ ์„œ๋ฒ„๊ฐ€ ์ตœ์ข… ์†Œ์œ ํ•ฉ๋‹ˆ๋‹ค. +- ๊ฒ€์ฆ ์‹คํŒจ๋Š” ์„ฑ๊ณต์ฒ˜๋Ÿผ ์ €์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +## 5. ๊ตฌํ˜„ ๋‹จ๊ณ„ + +### A. ์˜์กด์„ฑ ์ถ”๊ฐ€ +- ์ถ”๊ฐ€ ์˜์กด์„ฑ ์—†์ด ํ˜„์žฌ `pydantic` ์ž์‚ฐ์„ ๋จผ์ € ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. +- ์ฒซ ์ ์šฉ ๋ฒ”์œ„๋Š” `rb8001` ํ•œ ๊ฒฝ๋กœ๋กœ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. + +### B. ์ถœ๋ ฅ ๋ชจ๋ธ ์ถ”๊ฐ€ +- ๊ถŒ์žฅ ์œ„์น˜: + - 1์ˆœ์œ„: `rb8001/app/services/ir_deck_output_models.py` + - ๋Œ€์•ˆ: `rb8001/app/services/ir_deck_analyzer.py` ๋‚ด๋ถ€ ๋ณด์กฐ ๋ชจ๋ธ +- ์ด์œ : + - validator ๋กœ์ง๊ณผ LLM ํ˜ธ์ถœ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•ด ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. +- `IRDeckEvaluationResult`์— ํ•ด๋‹นํ•˜๋Š” typed output ๋ชจ๋ธ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. +- ์ตœ์†Œ ํ•„๋“œ: + - `total_score` + - `grade` + - `story_scores` + - `summary` + - `strengths` + - `weaknesses` + - `risks` + - `investment_opinion` +- ํ•จ๊ป˜ ๋‘˜ ๋ณด์กฐ ๋ชจ๋ธ: + - `StoryScore` + - `InvestmentOpinion` +- validator ๊ถŒ์žฅ ํ•ญ๋ชฉ: + - `grade == assign_grade(total_score)` + - `summary` ์ตœ์†Œ ๊ธธ์ด + - `story_scores` ๊ฐœ์ˆ˜/๋ฒ”์œ„ + - `recommendation` ํ—ˆ์šฉ๊ฐ’ ๋˜๋Š” ์„œ๋ฒ„ ์žฌ๊ณ„์‚ฐ ๋Œ€์ƒ ํ‘œ์‹œ + +### C. `Pydantic-only` ๊ฒ€์ฆ ๊ฒฝ๋กœ ์ถ”๊ฐ€ +- `_evaluate_comprehensive()` ๋‚ด๋ถ€์—์„œ ๊ธฐ์กด ์ž์œ ํ˜• JSON ํŒŒ์‹ฑ ๋’ค `Pydantic model_validate()`๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. +- model validator์—์„œ ์˜๋ฏธ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. +- ํ•„์š” ์‹œ ๊ฒ€์ฆ ํ•จ์ˆ˜๋Š” `parse_raw_evaluation_result()`์™€ `finalize_evaluation_result()`๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. +- ๊ถŒ์žฅ ํ•จ์ˆ˜ ๋ถ„๋ฆฌ: + - `_extract_raw_evaluation_dict(response: str) -> dict` + - `_validate_evaluation_result(raw: dict) -> IRDeckEvaluationResult` + - `_finalize_evaluation_result(validated: IRDeckEvaluationResult) -> dict` +- ์ด์œ : + - ์žฌ์งˆ์˜ ์ •์ฑ…๊ณผ validator ํ…Œ์ŠคํŠธ๋ฅผ ๊ฐ๊ฐ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. + +### D. ์„œ๋ฒ„ ์ตœ์ข…๊ฐ’ ์žฌ๊ณ„์‚ฐ +- `grade`๋Š” `total_score` ๊ธฐ๋ฐ˜์œผ๋กœ ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. +- `recommendation`๋„ ์„œ๋ฒ„ ์ •์ฑ…์œผ๋กœ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. +- ํ•„์š” ์‹œ `story_scores` ๊ธฐ๋ฐ˜ ์ด์  ์žฌ๊ณ„์‚ฐ ๋˜๋Š” ํ—ˆ์šฉ ์˜ค์ฐจ ๊ฒ€์ฆ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. +- ๊ถŒ์žฅ ๊ณ ์ •์•ˆ: + - 1์ฐจ์—์„œ๋Š” `grade`, `recommendation`๋งŒ ์„œ๋ฒ„ ์ตœ์ข…๊ฐ’์œผ๋กœ ๊ณ ์ • + - `total_score`๋Š” ์šฐ์„  validator ํ†ต๊ณผ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋˜, ํ›„์† ๋‹จ๊ณ„์—์„œ ์žฌ๊ณ„์‚ฐ ์—ฌ๋ถ€ ๊ฒ€ํ†  +- ์ด์œ : + - ๋ณ€๊ฒฝ ๋ฒ”์œ„๋ฅผ ์ค„์ด๋ฉด์„œ๋„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์ด๋Š” ํ•ต์‹ฌ ๋ชจ์ˆœ์„ ๋จผ์ € ๋‹ซ๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. + +### E. ์ €์žฅ ๊ฒฝ๊ณ„ ๋ถ„๋ฆฌ +- ์ €์žฅ์€ `raw_evaluation_result`๊ฐ€ ์•„๋‹ˆ๋ผ `final_evaluation_result`๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. +- ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์ €์žฅํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, ์‹คํŒจ ์ƒํƒœ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. +- ๊ถŒ์žฅ ๊ตฌํ˜„: + - raw dict๋Š” ์ง€์—ญ ๋ณ€์ˆ˜๋กœ๋งŒ ์œ ์ง€ + - repository์—๋Š” `final_evaluation_result`์—์„œ ๊บผ๋‚ธ ๊ฐ’๋งŒ ์ „๋‹ฌ + - ์‹คํŒจ ์‹œ `ValueError` ๋˜๋Š” ๋ช…์‹œ์  ์ปค์Šคํ…€ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ์ƒ์œ„ ๊ฒฝ๋กœ์—์„œ ์‹คํŒจ ์ฒ˜๋ฆฌ + +### E-1. ๋ผ์šฐํ„ฐ/์›Œํฌํ”Œ๋กœ ์˜ํ–ฅ ๋ฒ”์œ„ ํ™•์ธ +- ํ™•์ธ ๋Œ€์ƒ: + - `rb8001/app/services/workflows/ir_deck_workflow.py` + - `rb8001/app/router/ir_deck.py` +- ํ™•์ธ ํ•ญ๋ชฉ: + - `_evaluate_comprehensive()` ๋ฐ˜ํ™˜ ๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ๊ธฐ์กด `evaluate_node`, `save_node`, `EvaluationResponse`๊ฐ€ ๊นจ์ง€์ง€ ์•Š๋Š”์ง€ + - ์‹คํŒจ ์‹œ ๋น„๋™๊ธฐ ํ‰๊ฐ€ ์žก์ด ์นจ๋ฌต ์„ฑ๊ณต์œผ๋กœ ๋๋‚˜์ง€ ์•Š๋Š”์ง€ +- ์ด์œ : + - ์ด๋ฒˆ ์ˆ˜์ •์€ analyzer ๋‚ด๋ถ€๊ฐ€ ์ค‘์‹ฌ์ด์ง€๋งŒ, ์‹ค์ œ ์ €์žฅ/์‘๋‹ต ๊ฒฝ๋กœ๋Š” workflow/router๊นŒ์ง€ ์ด์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +### F. ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ +- ์ตœ์†Œ ํ…Œ์ŠคํŠธ: + - `100์  + B๋“ฑ๊ธ‰` ์ž…๋ ฅ ์ฐจ๋‹จ + - ์ž˜๋ชป๋œ enum ์ฐจ๋‹จ + - `story_scores`์™€ ์ด์  ๋ถˆ์ผ์น˜ ์ฐจ๋‹จ ๋˜๋Š” ๋ณด์ • + - `summary` ์ตœ์†Œ ๊ธธ์ด ๋ฏธ๋‹ฌ ์ฒ˜๋ฆฌ +- ๊ถŒ์žฅ ๋ฐ˜์˜ ์œ„์น˜: + - `rb8001/tests/test_ir_deck_json_parsing.py` + - validator ๋‹จ์œ„ ํ…Œ์ŠคํŠธ + - raw dict -> model_validate ์‹คํŒจ ํ…Œ์ŠคํŠธ + - ์žฌ์งˆ์˜ ํ•„์š” ํŒ์ • ํ…Œ์ŠคํŠธ + - `rb8001/tests/test_ir_deck_workflow.py` + - validator ํ†ต๊ณผ ๊ฒฐ๊ณผ๊ฐ€ workflow save๊นŒ์ง€ ์ •์ƒ ์ „๋‹ฌ๋˜๋Š”์ง€ + - validator ์‹คํŒจ ์‹œ workflow๊ฐ€ ์‹คํŒจ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋Š”์ง€ +- ๊ฐ€๋Šฅํ•˜๋ฉด `_evaluate_comprehensive()` ํŒŒ์‹ฑ ๊ฒฝ๋กœ์™€ validator๋ฅผ ๋ถ„๋ฆฌํ•ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์šฐ์„  ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. + +## 6. ์‹คํŒจ ์ •์ฑ… +- 1์ฐจ ์›์น™: + - validator ์‹คํŒจ ์‹œ ์กฐ์šฉํžˆ ์ €์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. +- ๊ธฐ๋ณธ์•ˆ: + - ๊ธฐ์กด `call_llm()` ์‘๋‹ต ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์ง์ ‘ ์žฌ์งˆ์˜ 1ํšŒ + - 1ํšŒ ์žฌ์‹œ๋„ ํ›„์—๋„ ์‹คํŒจํ•˜๋ฉด ๋ช…์‹œ์  ์‹คํŒจ ์ฒ˜๋ฆฌ +- ๋ณด์กฐ์•ˆ: + - `grade`, `recommendation`์€ ์„œ๋ฒ„ ์žฌ๊ณ„์‚ฐ์œผ๋กœ ๋ณด์ • ๊ฐ€๋Šฅ + - ๊ตฌ์กฐ ์ž์ฒด๊ฐ€ ๊นจ์ง„ ๊ฒฝ์šฐ๋Š” ์‹คํŒจ ์ฒ˜๋ฆฌ +- ๊ถŒ์žฅ ์„ธ๋ถ€ ์ˆœ์„œ: + 1. ์ฒซ ์‘๋‹ต JSON ์ถ”์ถœ + 2. `Pydantic model_validate()` + 3. ์‹คํŒจ ์‹œ ๊ฐ™์€ ํ”„๋กฌํ”„ํŠธ๋กœ 1ํšŒ ์žฌํ˜ธ์ถœ + 4. 2์ฐจ๋„ ์‹คํŒจํ•˜๋ฉด ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ์‹คํŒจ ๋กœ๊ทธ ๋‚จ๊น€ + 5. ๋‹จ, `grade/recommendation` ๊ฐ™์€ ์„œ๋ฒ„ ์†Œ์œ  ํ•„๋“œ๋Š” validator ํ†ต๊ณผ ํ›„์—๋„ ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ๊ณ„์‚ฐ +- ์ด๋ ‡๊ฒŒ ๋‘๋Š” ์ด์œ : + - ์žฌ์งˆ์˜๋Š” ์ตœ์†Œ 1ํšŒ๋งŒ ํ—ˆ์šฉํ•ด ๋น„์šฉ๊ณผ ์ง€์—ฐ์„ ์ œํ•œํ•˜๊ณ  + - ์„œ๋ฒ„ ์†Œ์œ  ํ•„๋“œ๋Š” ๋ณด์ • ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ตฌ์กฐ ์‹คํŒจ๋Š” ์ˆจ๊ธฐ์ง€ ์•Š๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. + +## 7. ์™„๋ฃŒ ํŒ์ • ๊ธฐ์ค€ +1. `IR Deck` ์ข…ํ•ฉ ํ‰๊ฐ€ ๊ฒฝ๋กœ๊ฐ€ `Pydantic` typed validation์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. +2. `total_score=100`, `grade=B` ๊ฐ™์€ ๋ชจ์ˆœ ์ž…๋ ฅ์€ ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. +3. ์ €์žฅ ์ „ `final_evaluation_result` ๊ฒฝ๊ณ„๊ฐ€ ์ฝ”๋“œ์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. +4. `grade`, `recommendation`์€ ์„œ๋ฒ„ ์ •์ฑ…์„ ๊ฑฐ์นœ ๊ฐ’๋งŒ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. +5. ๊ด€๋ จ ํ…Œ์ŠคํŠธ๊ฐ€ ์ถ”๊ฐ€๋˜๊ณ  ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. + +## 7-1. ๊ฒ€์ฆ ์‹คํ–‰ ๊ธฐ์ค€ +- ์ฝ”๋“œ ์ˆ˜์ • ํ›„ ์ตœ์†Œ ๊ฒ€์ฆ: + - `pytest rb8001/tests/test_ir_deck_json_parsing.py` + - `pytest rb8001/tests/test_ir_deck_workflow.py` +- ๊ฐ€๋Šฅํ•˜๋ฉด ์ถ”๊ฐ€ ๊ฒ€์ฆ: + - ๊ธฐ์กด IR Deck ๊ด€๋ จ ๋Œ€ํ‘œ ํ…Œ์ŠคํŠธ 1ํšŒ +- ๋ฐฐํฌ ํ›„ ํ™•์ธ ๊ธฐ์ค€: + - ์‹ค์ œ ํ‰๊ฐ€ 1๊ฑด์—์„œ `100์  + B๋“ฑ๊ธ‰` ๊ฐ™์€ ๋ชจ์ˆœ ์ €์žฅ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”์ง€ + - ์‹คํŒจ ์ผ€์ด์Šค๊ฐ€ ์žˆ์œผ๋ฉด ์„ฑ๊ณต์ฒ˜๋Ÿผ `completed`๋กœ๋งŒ ๋‚จ์ง€ ์•Š๋Š”์ง€ ๋กœ๊ทธ ํ™•์ธ +- ์ด์œ : + - ์ด๋ฒˆ ์ˆ˜์ •์˜ ํ•ต์‹ฌ์€ parser/validator/workflow ๊ฒฝ๊ณ„์ด๋ฏ€๋กœ ์ด ์„ธ ์ถ•์„ ์ง์ ‘ ๊ฒ€์ฆํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## 8. ํ›„์† ๊ฒฝ๊ณ„ +- ์ด๋ฒˆ ๊ณ„ํš์ด ๋‹ซํ˜€๋„ ๋ธŒ๋ฆฌํ•‘, ์ฝœ๋“œ๋ฉ”์ผ, ํˆฌ์ž ์˜๊ฒฌ ๊ฒฝ๋กœ๋Š” ๋ณ„๋„ ํ™•๋Œ€ ๊ณ„ํš์œผ๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค. +- 2์ฐจ ๋‹จ๊ณ„์—์„œ retry, output validator, ์ƒ์„ฑ-๊ฒ€์ฆ ์ผ์ฒดํ™”๋ฅผ ๋” ๊ฐ•ํ•˜๊ฒŒ ์›ํ•˜๋ฉด `Pydantic AI` ํ™•์žฅ ๊ณ„ํš์„ ๋ณ„๋„๋กœ ๊ฒ€ํ† ํ•ฉ๋‹ˆ๋‹ค. + +## 9. ์ด๋ฒˆ ๊ณ„ํš์˜ ํ•œ ์ค„ ๊ฒฐ๋ก  +- ์ง€๊ธˆ์€ `LangGraph๋ฅผ ๋ฐ”๊พธ๋Š” ๋‹จ๊ณ„`๊ฐ€ ์•„๋‹ˆ๋ผ, `IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€์— Pydantic-only๋ฅผ ๋จผ์ € ๋ถ™์—ฌ typed validation ๊ณ„์•ฝ์„ ์‹ค์ œ ์ฝ”๋“œ์— ๊ณ ์ •ํ•˜๋Š” ๋‹จ๊ณ„`์ž…๋‹ˆ๋‹ค. diff --git a/journey/research/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md b/journey/research/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md new file mode 100644 index 0000000..cde7952 --- /dev/null +++ b/journey/research/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md @@ -0,0 +1,265 @@ +--- +tags: [research, pydantic-ai, langgraph, llm, structured-output, ir-analysis, closure] +--- + +# Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์ข…๋ฃŒ ๋ฆฌ์„œ์น˜ + +## ๊ด€๋ จ ๋ฌธ์„œ +- [Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์•„์ด๋””์–ด](../ideas/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md) +- [์ž๊ฐ€์ˆ˜์ • ์—์ด์ „ํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ๋ฐ Workspace CLI ๊ฒ€์ฆ ๋ฆฌ์„œ์น˜](./260311_์ž๊ฐ€์ˆ˜์ •_์—์ด์ „ํŠธ_ํ”„๋ ˆ์ž„์›Œํฌ_๋ฐ_workspace_cli_๊ฒ€์ฆ_๋ฆฌ์„œ์น˜.md) +- [๋กœ๋น™ ์—์ด์ „ํŠธ ๋ฃจํ”„, ์Šคํ‚ฌ ํ›…, LLM ์‹คํ–‰ ๊ตฌ์กฐ ๋ฆฌ์„œ์น˜](./orchestration_tools/260312_๋กœ๋น™_์—์ด์ „ํŠธ๋ฃจํ”„_์Šคํ‚ฌํ›…_LLM์‹คํ–‰๊ตฌ์กฐ_๋ฆฌ์„œ์น˜.md) +- [NAVER WORKS ๋ธŒ๋ฆฌํ•‘ ์ธ์‚ฌ์ดํŠธ ์„œ๋‘ ๋ˆ„์ถœ ์ข…๋ฃŒ ๋ฆฌ์„œ์น˜](./260311_naverworks_briefing_insight_preamble_leak_closure_research.md) +- [์ฝœ๋“œ๋ฉ”์ผ IR ๋ถ„์„์ด ์ฒซ ๋ฒˆ์งธ PDF๋ฅผ ์ž˜๋ชป ์„ ํƒํ•˜๋Š” ์ƒํƒœ](../debug/260313_coldmail_ir_๋ถ„์„๋Œ€์ƒ_์˜ค์„ ํƒ_๋””๋ฒ„๊ทธ.md) + +## ๋ชฉ์  +- `Pydantic` ๊ณ„์—ด typed validation ๋„์ž… ์•„์ด๋””์–ด๊ฐ€ ์‹ค์ œ ๊ณ„ํš์œผ๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. +- ์ด๋ฒˆ ๋ฌธ์„œ๋Š” `Pydantic-only ๋จผ์ €, ํ•„์š” ์‹œ Pydantic AI ํ™•์žฅ`์ด๋ผ๋Š” ๋ฐฉํ–ฅ์„ ๊ธฐ์ค€์œผ๋กœ, ์–ด๋””์— ๋ถ™์ด๋Š” ๊ฒƒ์ด ๋งž๋Š”์ง€์™€ ์™œ ์ด ์ˆœ์„œ๊ฐ€ ํƒ€๋‹นํ•œ์ง€๋ฅผ ์ขํžˆ๋Š” ๋ฐ ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค. +- ๊ตฌํ˜„ ์ˆœ์„œ, ๋ฐฐํฌ, ์„ธ๋ถ€ ์ˆ˜์ • ํ•ญ๋ชฉ์€ ๋‹ค์Œ `plans` ๋ฌธ์„œ์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค. + +## ์กฐ์‚ฌ ์งˆ๋ฌธ +1. `Pydantic-only`๋ฅผ ์ง€๊ธˆ ๋กœ๋น™์— ๋ถ™์ธ๋‹ค๋ฉด ์–ด๋””๊ฐ€ ์ฒซ ๋„์ž… ์ง€์ ์œผ๋กœ ๊ฐ€์žฅ ์ ์ ˆํ•œ๊ฐ€ +2. ํ˜„์žฌ IR Deck ํ‰๊ฐ€ ๊ฒฝ๋กœ์—์„œ `Pydantic-only`๊ฐ€ ๋ฐ”๋กœ ๋ฉ”์šธ ์ˆ˜ ์žˆ๋Š” ๊ณต๋ฐฑ์€ ์ •ํ™•ํžˆ ๋ฌด์—‡์ธ๊ฐ€ +3. `LangGraph`, ๊ธฐ์กด `Pydantic`, `Pydantic AI`์˜ ์—ญํ•  ๊ฒฝ๊ณ„๋Š” ์–ด๋–ป๊ฒŒ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด ๋งž๋Š”๊ฐ€ +4. ๋‹ค์Œ `plans` ๋ฌธ์„œ๊ฐ€ ๋ฐ”๋กœ ๋ฐ›์•„์•ผ ํ•  `Pydantic-only ์šฐ์„  ๋„์ž…` ๊ฒฐ์ • ํฌ์ธํŠธ๋Š” ๋ฌด์—‡์ธ๊ฐ€ + +## ํ™•์ธ๋œ ์‚ฌ์‹ค +1. ํ˜„์žฌ ์•„์ด๋””์–ด ๋ฌธ์„œ๊ฐ€ ๊ฒจ๋ƒฅํ•˜๋Š” ์ง์ ‘ ๋ฌธ์ œ๋Š” `LLM์ด total_score=100, grade=B ๊ฐ™์€ ๋ชจ์ˆœ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด๋„ ์ €์žฅยท๋…ธ์ถœ๋  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ`์ž…๋‹ˆ๋‹ค. +2. ํ˜„์žฌ IR Deck ํ‰๊ฐ€๋Š” `LangGraph` ์›Œํฌํ”Œ๋กœ๋ฅผ ํ†ตํ•ด `extract -> evaluate -> analyze_pages -> save` ์ˆœ์„œ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค. + - [ir_deck_workflow.py](../../../rb8001/app/services/workflows/ir_deck_workflow.py) +3. ์ข…ํ•ฉ ํ‰๊ฐ€ ์ถœ๋ ฅ์€ `IRDeckAnalyzer._evaluate_comprehensive()`์—์„œ LLM JSON ์‘๋‹ต์„ ํŒŒ์‹ฑํ•œ ๋’ค ์ผ๋ถ€ ํ•„๋“œ๋งŒ ๋ฒ”์œ„ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. + - [ir_deck_analyzer.py](../../../rb8001/app/services/ir_deck_analyzer.py#L591) +4. ํ˜„์žฌ ํŒŒ์‹ฑ ๋‹จ๊ณ„๋Š” ์•„๋ž˜๋งŒ ์ง์ ‘ ๋ณด์ •ํ•ฉ๋‹ˆ๋‹ค. + - `total_score`๋ฅผ `0~100`์œผ๋กœ clamp + - `grade`๊ฐ€ `S/A/B/C`๊ฐ€ ์•„๋‹ˆ๋ฉด ์ ์ˆ˜ ๊ธฐ๋ฐ˜์œผ๋กœ ์žฌ๊ณ„์‚ฐ + - `story_scores`๋Š” ๋ฆฌ์ŠคํŠธ/์ ์ˆ˜ ๋ฒ”์œ„๋งŒ ์ œํ•œ + - `investment_opinion.recommendation`์€ ๊ฐ’์ด ์—†์„ ๋•Œ๋งŒ ์ ์ˆ˜ ๊ธฐ๋ฐ˜ ๊ธฐ๋ณธ๊ฐ’ ์ฑ„์›€ +5. ๋ฐ˜๋Œ€๋กœ ํ˜„์žฌ ํŒŒ์‹ฑ ๋‹จ๊ณ„์—๋Š” ์•„๋ž˜ ์˜๋ฏธ ๊ฒ€์ฆ์ด ์—†์Šต๋‹ˆ๋‹ค. + - `total_score`์™€ `grade`์˜ ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ + - `story_scores` ํ‰๊ท /๋ถ„ํฌ์™€ `total_score`์˜ ํ—ˆ์šฉ ์˜ค์ฐจ ๊ฒ€์ฆ + - `recommendation`๊ณผ `grade/score` ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ + - ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์ €์žฅ ์ฐจ๋‹จ +6. ์ €์žฅ ๋ ˆ์ด์–ด๋Š” ์ „๋‹ฌ๋ฐ›์€ `total_score`, `grade`, `story_scores`, `investment_opinion`์„ ๊ทธ๋Œ€๋กœ DB์— ๋„ฃ์Šต๋‹ˆ๋‹ค. + - [ir_valuation_repository.py](../../../rb8001/app/state/ir_valuation_repository.py#L130) +7. ํ˜„์žฌ `EvaluationResponse`๋„ ์ €์žฅ๊ฐ’์„ ๊ทธ๋Œ€๋กœ API ์‘๋‹ต์œผ๋กœ ๋…ธ์ถœํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. + - [ir_deck.py](../../../rb8001/app/router/ir_deck.py#L44) +8. ํ˜„์žฌ `rb8001` ์˜์กด์„ฑ์—๋Š” `pydantic`๊ณผ `pydantic-settings`๋Š” ์žˆ์ง€๋งŒ `pydantic-ai`๋Š” ์—†์Šต๋‹ˆ๋‹ค. + - [requirements.txt](../../../rb8001/requirements.txt) +9. ๊ธฐ์กด ๋กœ๋น™ ๋ฆฌ์„œ์น˜ ๋ฌธ์„œ๋„ `LangGraph ์ „๋ฉด ๊ต์ฒด`๋ณด๋‹ค `typed output validation ์š”๊ตฌ๊ฐ€ ํฐ ๊ฒฝ๋กœ๋งŒ ๋ถ€๋ถ„ ๋„์ž…`์ด ๋งž๋‹ค๊ณ  ์ด๋ฏธ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. + - [260311_์ž๊ฐ€์ˆ˜์ •_์—์ด์ „ํŠธ_ํ”„๋ ˆ์ž„์›Œํฌ_๋ฐ_workspace_cli_๊ฒ€์ฆ_๋ฆฌ์„œ์น˜.md](./260311_์ž๊ฐ€์ˆ˜์ •_์—์ด์ „ํŠธ_ํ”„๋ ˆ์ž„์›Œํฌ_๋ฐ_workspace_cli_๊ฒ€์ฆ_๋ฆฌ์„œ์น˜.md) +10. NAVER WORKS ๋ธŒ๋ฆฌํ•‘ ์ข…๋ฃŒ ๋ฆฌ์„œ์น˜์—์„œ๋„ ๊ฐ™์€ ํŒจํ„ด์ด ํ™•์ธ๋์Šต๋‹ˆ๋‹ค. + - ํ”„๋กฌํ”„ํŠธ๋งŒ์œผ๋กœ ๋‹ซ์ง€ ๋ชปํ–ˆ๊ณ  + - `generate -> validate -> regenerate/fail-visible` ๊ณ„์•ฝ์ด ๋‹ซํž˜ ์กฐ๊ฑด์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +## 2026-03-13 ์‹ค์ธก ํ…Œ์ŠคํŠธ + +### ํ…Œ์ŠคํŠธ 1. ํ˜„์žฌ `_evaluate_comprehensive()`๋Š” ๋ชจ์ˆœ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผ์‹œํ‚ต๋‹ˆ๋‹ค +- ๋ฐฉ๋ฒ•: + - `ir_deck_analyzer.py`๋ฅผ ์ตœ์†Œ stub ์˜์กด์„ฑ์œผ๋กœ importํ–ˆ์Šต๋‹ˆ๋‹ค. + - `call_llm()`์ด ์•„๋ž˜ JSON์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ณ ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. + - `total_score=100` + - `grade=B` + - `investment_opinion.recommendation=ํˆฌ์ž ๋ณด๋ฅ˜` + - ์‹ค์ œ `_evaluate_comprehensive()`๋ฅผ ํ˜ธ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. +- ๊ฒฐ๊ณผ: + - ๋ฐ˜ํ™˜๊ฐ’์€ ์•„๋ž˜์ฒ˜๋Ÿผ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค. + - `{'total_score': 100, 'grade': 'B', ..., 'investment_opinion': {'recommendation': 'ํˆฌ์ž ๋ณด๋ฅ˜', ...}}` +- ํ•ด์„: + - ํ˜„์žฌ ๊ตฌํ˜„์—๋Š” `total_score`์™€ `grade`, `recommendation`์˜ ์˜๋ฏธ ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ์ด ์—†๋‹ค๋Š” ๊ฒƒ์ด ์‹ค์ธก์œผ๋กœ ํ™•์ธ๋์Šต๋‹ˆ๋‹ค. + +### ํ…Œ์ŠคํŠธ 2. ์ˆœ์ˆ˜ `Pydantic` ๋ชจ๋ธ validator๋Š” ๊ฐ™์€ ์ž…๋ ฅ์„ ์ฆ‰์‹œ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค +- ๋ฐฉ๋ฒ•: + - ์ž„์‹œ ๊ฒฝ๋กœ์— `pydantic==2.12.5`๋ฅผ ์„ค์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค. + - `Evaluation` ๋ชจ๋ธ์— `grade == assign_grade(total_score)` validator๋ฅผ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค. + - ๊ฐ™์€ ์ž…๋ ฅ(`100์  + B๋“ฑ๊ธ‰`)์„ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค. +- ๊ฒฐ๊ณผ: + - `VALIDATION_FAILED_AS_EXPECTED` + - `Value error, grade mismatch: total_score=100, grade=B, expected=S` +- ํ•ด์„: + - ์ตœ์†Œํ•œ์˜ typed validator๋งŒ ์žˆ์–ด๋„ ํ˜„์žฌ ํ†ต๊ณผ ์ค‘์ธ ๋ชจ์ˆœ๊ฐ’์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ํ…Œ์ŠคํŠธ 3. `Pydantic AI` Agent + output validator๋„ ๊ฐ™์€ ์ž…๋ ฅ์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค +- ๋ฐฉ๋ฒ•: + - `pydantic-ai==1.68.0`๋ฅผ ์ž„์‹œ ๊ฒฝ๋กœ์— ์„ค์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค. + - `TestModel(custom_output_args=...)`๋กœ ๋™์ผํ•œ ๋ชจ์ˆœ ์ถœ๋ ฅ(`100์  + B๋“ฑ๊ธ‰`)์„ ์ฃผ์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. + - `Agent(output_type=Evaluation)`์— output validator๋ฅผ ๋ถ™์—ฌ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. +- ๊ฒฐ๊ณผ: + - `UnexpectedModelBehavior` + - `Exceeded maximum retries (1) for output validation` +- ํ•ด์„: + - `Pydantic AI`๋Š” ๋กœ๋น™ ๊ฐ™์€ ๊ฒฝ๋กœ์—์„œ `typed output + validator + retry/fail-visible` ๊ณ„์•ฝ์„ ๊ตฌํ˜„ํ•˜๋Š” ์‹ค์ œ ํ›„๋ณด๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ํ…Œ์ŠคํŠธ 4. `Pydantic AI` ๋„์ž…์€ ๊ฒฝ๋Ÿ‰ ์ถ”๊ฐ€๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค +- ๋ฐฉ๋ฒ•: + - `python3 -m pip install --target /tmp/pydantic_ai_pkg pydantic-ai`๋ฅผ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. +- ๊ฒฐ๊ณผ: + - ์„ค์น˜๋Š” ์„ฑ๊ณตํ–ˆ์ง€๋งŒ `openai`, `anthropic`, `google-genai`, `mcp`, `fastmcp`, `logfire`, `temporalio` ๋“ฑ ๋งค์šฐ ๋งŽ์€ ์˜์กด์„ฑ์ด ํ•จ๊ป˜ ๋‚ด๋ ค์™”์Šต๋‹ˆ๋‹ค. +- ํ•ด์„: + - `Pydantic AI`๋Š” ๊ธฐ๋Šฅ์ ์œผ๋กœ๋Š” ์œ ํšจํ•˜์ง€๋งŒ, ์˜์กด์„ฑ/์ด๋ฏธ์ง€ ํฌ๊ธฐ/๋นŒ๋“œ ์‹œ๊ฐ„ ์˜ํ–ฅ๊นŒ์ง€ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + - ๋”ฐ๋ผ์„œ ์ฒซ ๋„์ž… ๊ฒฐ์ •์—๋Š” `๊ธฐ๋Šฅ ์ ํ•ฉ์„ฑ`๋ฟ ์•„๋‹ˆ๋ผ `์˜์กด์„ฑ ๋น„์šฉ`๋„ ํ•จ๊ป˜ ํฌํ•จ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ํ•ด์„ +- ํ˜„์žฌ ๋กœ๋น™์—๋Š” `Pydantic` ๊ณ„์—ด typed validation์„ ๋ถ™์ผ ๋ช…ํ™•ํ•œ ๋„์ž… ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. +- ๊ทธ ์ด์œ ๋Š” `LangGraph`๋ฅผ ๋ฒ„๋ ค์•ผ ํ•ด์„œ๊ฐ€ ์•„๋‹ˆ๋ผ, ํ˜„์žฌ ์ฝ”๋“œ๊ฐ€ `ํ˜•์‹ ํŒŒ์‹ฑ`๊ณผ `๋ฒ”์œ„ ์ œํ•œ`๊นŒ์ง€๋งŒ ํ•˜๊ณ  `์ œํ’ˆ๊ฐ’ ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ`์€ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. +- ๋”ฐ๋ผ์„œ 1์ฐจ ํ•ด๋ฒ•์€ `ํ˜„์žฌ call_llm() ์œ ์ง€ + Pydantic model_validate() + semantic validator ์ถ”๊ฐ€`๊ฐ€ ๊ฐ€์žฅ ํ˜„์‹ค์ ์ž…๋‹ˆ๋‹ค. +- `IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€`๋Š” ์ด ๊ฒฝ๊ณ„๋ฅผ ๊ฐ€์žฅ ๋จผ์ € ๋ถ™์—ฌ๋ณผ ๊ฐ€์น˜๊ฐ€ ๋†’์€ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค. +- `Pydantic AI`๋Š” ์œ ํšจํ•œ ํ›„๋ณด์ง€๋งŒ, ์‹ค์ธก ๊ฒฐ๊ณผ ๊ธฐ์ค€์œผ๋กœ ์˜์กด์„ฑ ๋ฌด๊ฒŒ๊ฐ€ ์ž‘์ง€ ์•Š์œผ๋ฏ€๋กœ ์ฒซ ๋‹จ๊ณ„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋‘๊ธฐ๋ณด๋‹ค 2์ฐจ ํ™•์žฅ ํ›„๋ณด๋กœ ๋‘๋Š” ํŽธ์ด ๋” ํƒ€๋‹นํ•ฉ๋‹ˆ๋‹ค. + +## `Pydantic-only` vs `Pydantic AI` ๋น„๊ต + +### 1. `Pydantic-only` +- ๊ตฌ์กฐ: + - ๊ธฐ์กด `call_llm()` ์œ ์ง€ + - ์‘๋‹ต JSON ์ถ”์ถœ ํ›„ `Pydantic model_validate()` ์ˆ˜ํ–‰ + - model validator์—์„œ ์ ์ˆ˜/๋“ฑ๊ธ‰ ์ •ํ•ฉ์„ฑ ๊ฒ€์‚ฌ +- ์žฅ์ : + - ํ˜„์žฌ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์ด ๊ฐ€์žฅ ์ž‘์Šต๋‹ˆ๋‹ค. + - `LangGraph`, `call_llm()`, ์ €์žฅ ํ๋ฆ„์„ ๊ฑฐ์˜ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + - ์‹ค์ธก์ƒ ํ˜„์žฌ ๋ฌธ์ œ(`100์  + B๋“ฑ๊ธ‰`)๋ฅผ ๋ง‰๋Š” ๋ฐ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. +- ๋‹จ์ : + - ์žฌ์งˆ์˜, retry, output validator orchestration์€ ์ง์ ‘ ๋ถ™์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. + - ์ƒ์„ฑ๊ณผ ๊ฒ€์ฆ์ด ๋ถ„๋ฆฌ๋ผ ์žˆ์–ด ํ˜ธ์ถœ ๊ฒฝ๊ณ„๊ฐ€ ๋А์Šจํ•ฉ๋‹ˆ๋‹ค. + +### 2. `Pydantic AI` +- ๊ตฌ์กฐ: + - `Agent(output_type=...)` + - output validator + - retry / fail-visible +- ์žฅ์ : + - ์ƒ์„ฑ๊ณผ ๊ฒ€์ฆ์„ ํ•œ ๊ณ„์ธต์œผ๋กœ ๋ฌถ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + - retry ์ •์ฑ…์„ ๋” ์ผ๊ด€๋˜๊ฒŒ ๋„ฃ๊ธฐ ์ข‹์Šต๋‹ˆ๋‹ค. + - ํ–ฅํ›„ tool calling, structured output, agent ๊ฒฝ๊ณ„ ํ™•์žฅ์— ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. +- ๋‹จ์ : + - ์˜์กด์„ฑ ์ฆ๊ฐ€๊ฐ€ ํฝ๋‹ˆ๋‹ค. + - ํ˜„์žฌ ๋กœ๋น™ ๊ฒฝ๋กœ์— ๋ฐ”๋กœ ๋ถ™์ด๊ธฐ์—” ๋ณ€๊ฒฝ ๋ฒ”์œ„๊ฐ€ ๋” ํฝ๋‹ˆ๋‹ค. + - ์ง€๊ธˆ ๋‹น์žฅ ํ•„์š”ํ•œ `๋ชจ์ˆœ๊ฐ’ ์ฐจ๋‹จ`์—๋Š” ๊ณผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### 3. ํ˜„์žฌ ๊ธฐ์ค€ ํŒ๋‹จ +- ํ˜„์žฌ ๋‹จ๊ณ„์˜ ๊ธฐ๋ณธ๊ฐ’์€ `Pydantic-only`๊ฐ€ ๋” ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค. +- ์ด์œ : + 1. ๋ฌธ์ œ๋ฅผ ๋ฐ”๋กœ ๋ง‰๋Š” ๋ฐ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. + 2. ๊ธฐ์กด ๊ฒฝ๊ณ„๋ฅผ ๊ฑฐ์˜ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + 3. ์‹ค์ธก์ƒ `Pydantic` validator๋งŒ์œผ๋กœ๋„ ํ˜„์žฌ ๋ชจ์ˆœ๊ฐ’์„ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. +- ๋”ฐ๋ผ์„œ ์ง€๊ธˆ ๋ฆฌ์„œ์น˜์˜ ๊ฒฐ๋ก ์€ `Pydantic AI๋ฅผ ๋ฒ„๋ฆฌ์ž`๊ฐ€ ์•„๋‹ˆ๋ผ `1์ฐจ๋Š” Pydantic-only, 2์ฐจ๋Š” Pydantic AI`์ž…๋‹ˆ๋‹ค. + +## ์„ ํƒ์ง€ ๋น„๊ต + +### ์„ ํƒ์ง€ A. ํ˜„์žฌ ๊ตฌ์กฐ ์œ ์ง€ + ํ”„๋กฌํ”„ํŠธ ๋ณด๊ฐ•๋งŒ ์ถ”๊ฐ€ +- ์žฅ์ : + - ๊ฐ€์žฅ ๋น ๋ฆ…๋‹ˆ๋‹ค. + - ์˜์กด์„ฑ ์ถ”๊ฐ€๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. +- ๋‹จ์ : + - ์ด๋ฏธ ์ถœ๋ ฅ ์ผํƒˆ์ด ํ•œ ๋ฒˆ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. + - ์ €์žฅ ์ „ ๊ณ„์•ฝ ๊ฒ€์ฆ์ด ์—†์–ด ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๋‹ค์‹œ ๋ง‰์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. +- ํŒ๋‹จ: + - `Pydantic AI ๋„์ž… ์•„์ด๋””์–ด`๋ฅผ ๊ฒ€์ฆํ•œ ๊ฒฐ๊ณผ, ์ด ์„ ํƒ์ง€๋Š” ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +### ์„ ํƒ์ง€ B. `LangGraph`๋ฅผ ๋ฒ„๋ฆฌ๊ณ  `Pydantic AI` ์ค‘์‹ฌ์œผ๋กœ ์žฌ๊ตฌ์„ฑ +- ์žฅ์ : + - ์ถœ๋ ฅ ๊ณ„์•ฝ์„ ์ƒˆ๋กœ ์„ค๊ณ„ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + - `Agent` + `result_type` + validator ์กฐํ•ฉ์„ ์ผ๊ด€๋˜๊ฒŒ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +- ๋‹จ์ : + - ํ˜„์žฌ `interrupt`, checkpoint, ๋น„๋™๊ธฐ ํ‰๊ฐ€ ํ๋ฆ„๊ณผ ๋ถ„๋ฆฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + - ์ด๋ฒˆ ๋ฌธ์ œ๋ณด๋‹ค ๋ฒ”์œ„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ํฝ๋‹ˆ๋‹ค. + - ๊ธฐ์กด ๋ฆฌ์„œ์น˜ ๊ฒฐ๋ก ๊ณผ๋„ ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค. +- ํŒ๋‹จ: + - `Pydantic AI ๋„์ž…` ์•„์ด๋””์–ด์™€๋Š” ์—ฐ๊ฒฐ๋˜์ง€๋งŒ, ๋ถ€๋ถ„ ๋„์ž… ์•„์ด๋””์–ด์˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚ฉ๋‹ˆ๋‹ค. + +### ์„ ํƒ์ง€ C. `LangGraph`๋Š” ์œ ์ง€ํ•˜๊ณ , ์ข…ํ•ฉ ํ‰๊ฐ€ ์ถœ๋ ฅ ์งํ›„์— `Pydantic-only` typed contract ๊ณ„์ธต์„ ์ถ”๊ฐ€ +- ์žฅ์ : + - ํ˜„์žฌ ๋ฌธ์ œ ์ง€์ ์„ ์ง์ ‘ ๋‹ซ์Šต๋‹ˆ๋‹ค. + - `LangGraph`์˜ ์ƒํƒœ ๊ด€๋ฆฌ ๊ฐ€์น˜๋Š” ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. + - `Pydantic-only`๋งŒ์œผ๋กœ ๋ฐ”๋กœ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +- ๋‹จ์ : + - retry์™€ fail-visible ์ผ๋ถ€๋Š” ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + - ์„œ๋ฒ„ ์žฌ๊ณ„์‚ฐ ์ •์ฑ…์„ ๋”ฐ๋กœ ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +- ํŒ๋‹จ: + - ์ง€๊ธˆ ๋ฐ”๋กœ ์‹คํ–‰ํ•  ์ฒซ ๋‹จ๊ณ„๋กœ ๊ฐ€์žฅ ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค. + +### ์„ ํƒ์ง€ D. `LangGraph`๋Š” ์œ ์ง€ํ•˜๊ณ , ๋ฐ”๋กœ `Pydantic AI`๋ฅผ ๋ถ™์ธ๋‹ค +- ์žฅ์ : + - output validator, retry, typed output์„ ํ•œ ๊ณ„์ธต์œผ๋กœ ๋ฌถ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + - ๋‹ค์Œ ๋‹จ๊ณ„ ํ™•์žฅ์„ฑ์€ ์ข‹์Šต๋‹ˆ๋‹ค. +- ๋‹จ์ : + - ํ˜„์žฌ ํ•„์š”ํ•œ ๋ฌธ์ œ ํฌ๊ธฐ๋ณด๋‹ค ๋ณ€๊ฒฝ ๋ฒ”์œ„๊ฐ€ ํฝ๋‹ˆ๋‹ค. + - ์˜์กด์„ฑ ์ฆ๊ฐ€๊ฐ€ ์ฆ‰์‹œ ๋“ค์–ด์˜ต๋‹ˆ๋‹ค. +- ํŒ๋‹จ: + - ํ›„์† 2์ฐจ ํ›„๋ณด๋กœ๋Š” ์ข‹์ง€๋งŒ, ์ฒซ ๋‹จ๊ณ„ ๊ธฐ๋ณธ์•ˆ์œผ๋กœ ๋‘๊ธฐ์—” ๋ฌด๊ฒ์Šต๋‹ˆ๋‹ค. + +## ๊ฒฐ๋ก  +- ์ด๋ฒˆ ๋ฆฌ์„œ์น˜ ๊ธฐ์ค€์œผ๋กœ ๊ฐ€์žฅ ๋งž๋Š” ๊ฒฐ๋ก ์€ `์„ ํƒ์ง€ C`, ์ฆ‰ `LangGraph ์œ ์ง€ + Pydantic-only ์šฐ์„  ๋„์ž…`์ž…๋‹ˆ๋‹ค. +- ์ •ํ™•ํ•œ ์‹คํ–‰ ๋ฌธ์žฅ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. + - `LangGraph`๋Š” ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. + - IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€ ๊ฒฝ๋กœ๋ฅผ `Pydantic-only` ์ฒซ ๋„์ž… ํ›„๋ณด๋กœ ์žก์Šต๋‹ˆ๋‹ค. + - ์ด ๊ฒฝ๋กœ์— `LLM raw output -> Pydantic model_validate -> semantic validation -> server-derived final fields -> save` ๊ณ„์ธต์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉํ–ฅ์ด ๋งž์Šต๋‹ˆ๋‹ค. + - `Pydantic AI`๋Š” 2์ฐจ ํ™•์žฅ ํ›„๋ณด๋กœ ๋‚จ๊น๋‹ˆ๋‹ค. + - ์‹ค์ธก ํ…Œ์ŠคํŠธ ๊ธฐ์ค€์œผ๋กœ๋„ ํ˜„์žฌ ๊ตฌํ˜„์˜ ๊ณต๋ฐฑ์€ ์žฌํ˜„๋๊ณ , ์ˆœ์ˆ˜ `Pydantic` validator๋งŒ์œผ๋กœ๋„ ์ด ๊ณต๋ฐฑ์„ ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ํ™•์ธ๋์Šต๋‹ˆ๋‹ค. + +## ์ด๋ฒˆ ๋„์ž… ์•„์ด๋””์–ด๋ฅผ ๋‹ซ๋Š” ๊ถŒ์žฅ ๊ณ„์•ฝ + +### 1. Raw Output๊ณผ Final Output์„ ๋ถ„๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค +- LLM์ด ์ฒ˜์Œ ๋ฐ˜ํ™˜ํ•œ ๊ฐ’์€ `raw_evaluation_result`๋กœ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค. +- ์‚ฌ์šฉ์ž์™€ DB์— ์ €์žฅ๋˜๋Š” ๊ฐ’์€ `final_evaluation_result`๋กœ ๋ถ„๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +- ์ด ๋‘˜์„ ๊ฐ™์€ dict๋กœ ๋‹ค๋ฃจ๋Š” ํ˜„์žฌ ๊ตฌ์กฐ๊ฐ€ ๋ฌธ์ œ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. + +### 2. Typed Contract๋Š” ๊ตฌ์กฐ ๊ฒ€์ฆ๊ณผ ์˜๋ฏธ ๊ฒ€์ฆ์„ ๋‘˜ ๋‹ค ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค +- ๊ตฌ์กฐ ๊ฒ€์ฆ: + - ํ•„์ˆ˜ ํ•„๋“œ ์กด์žฌ + - ํƒ€์ž… ์ผ์น˜ + - ๋ฆฌ์ŠคํŠธ ๊ธธ์ด, ๋ฌธ์ž์—ด ๊ธธ์ด, enum ๋ฒ”์œ„ +- ์˜๋ฏธ ๊ฒ€์ฆ: + - `grade == assign_grade(total_score)` ์ด์–ด์•ผ ํ•จ + - `recommendation`์€ `total_score/grade` ์ •์ฑ…๊ณผ ์ถฉ๋Œํ•˜๋ฉด ์•ˆ ๋จ + - `story_scores`๊ฐ€ ์žˆ์œผ๋ฉด `total_score`๋Š” ํ—ˆ์šฉ ์˜ค์ฐจ ๋‚ด์—์„œ ์žฌ๊ณ„์‚ฐ ๊ฐ€๋Šฅํ•ด์•ผ ํ•จ + - ํ•„์ˆ˜ ์„ค๋ช… ํ•„๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋น„์–ด ์žˆ์œผ๋ฉด ํ†ต๊ณผ์‹œํ‚ค์ง€ ์•Š์•„์•ผ ํ•จ + +### 3. ํ•ต์‹ฌ ์ œํ’ˆ๊ฐ’์€ ์„œ๋ฒ„๊ฐ€ ์ตœ์ข… ์†Œ์œ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค +- `grade`๋Š” ์„œ๋ฒ„๊ฐ€ `total_score`๋กœ ์žฌ๊ณ„์‚ฐํ•˜๋Š” ์ชฝ์ด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. +- `recommendation`๋„ ์„œ๋ฒ„ ์ •์ฑ…์—์„œ ํŒŒ์ƒ์‹œํ‚ค๋Š” ์ชฝ์ด ๋” ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. +- `total_score`๋Š” ์ด์ƒ์ ์œผ๋กœ `story_scores` ๋˜๋Š” ์„œ๋ฒ„ ์Šค์ฝ”์–ด๋ง ๊ทœ์น™์œผ๋กœ ์žฌ์‚ฐ์ถœ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋กœ ์˜ฎ๊ฒจ์•ผ ํ•ฉ๋‹ˆ๋‹ค. +- ์ฆ‰ LLM์€ `ํ•ด์„๊ฐ’`์„ ๋‚ด๊ณ , ์„œ๋ฒ„๋Š” `์ตœ์ข… ๋…ธ์ถœ๊ฐ’`์„ ์†Œ์œ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +### 4. ๊ฒ€์ฆ ์‹คํŒจ๋Š” ์ €์žฅ ๊ธˆ์ง€ ๋˜๋Š” ๋ช…์‹œ์  ์‹คํŒจ๋กœ ๋‹ค๋ค„์•ผ ํ•ฉ๋‹ˆ๋‹ค +- ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์กฐ์šฉํžˆ clamp ํ›„ ์ €์žฅํ•˜๋Š” ํ˜„์žฌ ์ ‘๊ทผ์€ ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. +- ์ตœ์†Œํ•œ ์•„๋ž˜ ์ค‘ ํ•˜๋‚˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. + - ์žฌ์งˆ์˜ 1ํšŒ ํ›„ ์‹คํŒจ ๊ฐ€์‹œํ™” + - ์„œ๋ฒ„ ์žฌ๊ณ„์‚ฐ ํ›„ `raw`์™€ ์ฐจ์ด๋ฅผ ๋กœ๊ทธ๋กœ ๋‚จ๊ธฐ๊ณ  ์ €์žฅ + - ๊ธฐ์ค€ ๋ฏธ๋‹ฌ์ด๋ฉด ์ €์žฅ ์ž์ฒด๋ฅผ ์‹คํŒจ ์ฒ˜๋ฆฌ +- ์–ด๋–ค ์ •์ฑ…์„ ํƒํ•˜๋“  `์„ฑ๊ณต์ฒ˜๋Ÿผ ์ €์žฅ`์€ ๊ธˆ์ง€๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ๋‹ค์Œ `plans` ๋ฌธ์„œ๊ฐ€ ๋ฐ”๋กœ ๊ฒฐ์ •ํ•ด์•ผ ํ•  ํฌ์ธํŠธ +1. typed contract ๊ตฌํ˜„์„ ์–ด๋–ค `Pydantic` ๋ชจ๋ธ ๊ตฌ์กฐ๋กœ ๋‘˜์ง€ +2. `final`๋กœ ์„œ๋ฒ„๊ฐ€ ์†Œ์œ ํ•  ํ•„๋“œ๋ฅผ ์–ด๋””๊นŒ์ง€๋กœ ํ• ์ง€ + - ์ตœ์†Œ ํ›„๋ณด: `grade`, `recommendation` + - ํ™•์žฅ ํ›„๋ณด: `total_score` +3. ๊ฒ€์ฆ ์‹คํŒจ ์ •์ฑ…์„ ๋ฌด์—‡์œผ๋กœ ํ• ์ง€ + - ์žฌ์งˆ์˜ ์ง์ ‘ ๊ตฌํ˜„ 1ํšŒ + - ์„œ๋ฒ„ ์žฌ๊ณ„์‚ฐ ์šฐ์„  + - ์ €์žฅ ์ฐจ๋‹จ +4. ์ฒซ ์ ์šฉ ๋ฒ”์œ„๋ฅผ ์–ด๋””๋กœ ์ž๋ฅผ์ง€ + - 1์ˆœ์œ„: IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€ + - ์ดํ›„: ๋ธŒ๋ฆฌํ•‘ ์ธ์‚ฌ์ดํŠธ, ์ฝœ๋“œ๋ฉ”์ผ ํ›„๋ณด ํŒ์ •, ํˆฌ์ž ์˜๊ฒฌ ์ถœ๋ ฅ + - 2์ฐจ: ๊ฐ™์€ ๊ฒฝ๊ณ„๋ฅผ `Pydantic AI`๋กœ ์˜ฌ๋ฆด์ง€ ๊ฒ€ํ†  +5. ์–ด๋–ค ๋กœ๊ทธ์™€ ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ซํž˜ ๊ธฐ์ค€์œผ๋กœ ์‚ผ์„์ง€ + +## ๋‹ซํž˜ ๊ธฐ์ค€ +1. IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€ ๊ฒฝ๋กœ์— `raw -> validate -> finalize -> save` ๊ฒฝ๊ณ„๊ฐ€ ์ฝ”๋“œ๋กœ ์กด์žฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +2. `total_score=100, grade=B` ๊ฐ™์€ ๋ชจ์ˆœ ์ž…๋ ฅ์€ ๋” ์ด์ƒ ๊ทธ๋Œ€๋กœ ์ €์žฅ๋˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. +3. ์ €์žฅ๋˜๋Š” `grade`, `recommendation`์€ LLM ์›๋ฌธ์ด ์•„๋‹ˆ๋ผ ์„œ๋ฒ„ ์ •์ฑ…์„ ํ†ต๊ณผํ•œ ์ตœ์ข…๊ฐ’์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +4. ๊ฒ€์ฆ ์‹คํŒจ ์ผ€์ด์Šค๋Š” ์„ฑ๊ณต์ฒ˜๋Ÿผ ์‘๋‹ตํ•˜๊ฑฐ๋‚˜ DB์— ๋‚จ์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. +5. ์•„๋ž˜ ํ…Œ์ŠคํŠธ๊ฐ€ ์ตœ์†Œ ํฌํ•จ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. + - ์ ์ˆ˜/๋“ฑ๊ธ‰ ๋ชจ์ˆœ + - `story_scores` ํ‰๊ท ๊ณผ ์ด์  ๋ถˆ์ผ์น˜ + - ์ž˜๋ชป๋œ enum/๋ˆ„๋ฝ ํ•„๋“œ + - summary/strengths/risks ์ตœ์†Œ ํ’ˆ์งˆ ๋ฏธ๋‹ฌ + +## ๋ฏธํ™•์ • ํ•ญ๋ชฉ +- `Pydantic-only` 1์ฐจ ์ ์šฉ ํ›„ ์–ด๋А ์‹œ์ ์— `Pydantic AI`๋กœ ์˜ฌ๋ฆด์ง€๋Š” ์•„์ง ๋ฏธ์ •์ž…๋‹ˆ๋‹ค. +- `total_score`๋ฅผ ์™„์ „ํžˆ ์„œ๋ฒ„ ์žฌ๊ณ„์‚ฐ์œผ๋กœ ๊ฐ€์ ธ๊ฐˆ์ง€, LLM ์ ์ˆ˜๋ฅผ ํ—ˆ์šฉ ์˜ค์ฐจ ๋‚ด์—์„œ๋งŒ ์ˆ˜์šฉํ• ์ง€๋Š” ์•„์ง ๋ฏธ์ •์ž…๋‹ˆ๋‹ค. +- ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์‚ฌ์šฉ์ž์—๊ฒŒ `ํ‰๊ฐ€ ์‹คํŒจ`๋ฅผ ์ง์ ‘ ๋…ธ์ถœํ• ์ง€, ๋‚ด๋ถ€ ์žฌ์‹œ๋„ ํ›„๋งŒ ๋…ธ์ถœํ• ์ง€๋Š” ์•„์ง ๋ฏธ์ •์ž…๋‹ˆ๋‹ค. +- `raw output`์„ DB๋‚˜ ๋กœ๊ทธ์— ๋ณ„๋„ ์ €์žฅํ• ์ง€๋Š” ์•„์ง ๋ฏธ์ •์ž…๋‹ˆ๋‹ค. +- `Pydantic AI`์˜ ์˜์กด์„ฑ ์ฆ๊ฐ€๋ฅผ ๋‚˜์ค‘์— ์šด์˜ ์ด๋ฏธ์ง€์—์„œ ์ˆ˜์šฉํ• ์ง€, ๋ณ„๋„ ์‹คํ—˜ ๋ธŒ๋žœ์น˜๋กœ ๋จผ์ € ๊ฒ€์ฆํ• ์ง€๋„ ์•„์ง ๋ฏธ์ •์ž…๋‹ˆ๋‹ค. + +## ์ด๋ฒˆ ๋ฆฌ์„œ์น˜์˜ ๊ฒฐ๋ก  ๋ฌธ์žฅ +- ์ด ๋ฆฌ์„œ์น˜๋Š” `Pydantic` ๊ณ„์—ด typed validation ๋„์ž… ์•„์ด๋””์–ด๋ฅผ ๋ฐ˜๋ฐ•ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. +- ์˜คํžˆ๋ ค `IR Deck ์ข…ํ•ฉ ํ‰๊ฐ€`์— `Pydantic-only`๋ฅผ ๋จผ์ € ๋ถ™์ด๋Š” ์ชฝ์ด ํ˜„์žฌ ๊ตฌ์กฐ์™€ ๊ฐ€์žฅ ์ž˜ ๋งž๋Š”๋‹ค๋Š” ์ชฝ์œผ๋กœ ๊ฒฐ๋ก ์ด ๋ชจ์ž…๋‹ˆ๋‹ค. +- ๋”ฐ๋ผ์„œ ๋‹ค์Œ ๋‹จ๊ณ„ `plans`๋Š” `Pydantic-only๋ฅผ ์–ด๋–ค ๋ชจ๋ธ๊ณผ ์ •์ฑ…์œผ๋กœ ๋ถ™์ผ์ง€`๋ฅผ ๊ณ ์ •ํ•˜๋Š” ๋ฌธ์„œ๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. diff --git a/journey/worklog/260313_ir_deck_pydantic_only_์ถœ๋ ฅ๊ฒ€์ฆ_๊ตฌํ˜„๋ฐ๋ฐฐํฌ๊ฒ€์ฆ.md b/journey/worklog/260313_ir_deck_pydantic_only_์ถœ๋ ฅ๊ฒ€์ฆ_๊ตฌํ˜„๋ฐ๋ฐฐํฌ๊ฒ€์ฆ.md new file mode 100644 index 0000000..1bb33f4 --- /dev/null +++ b/journey/worklog/260313_ir_deck_pydantic_only_์ถœ๋ ฅ๊ฒ€์ฆ_๊ตฌํ˜„๋ฐ๋ฐฐํฌ๊ฒ€์ฆ.md @@ -0,0 +1,46 @@ +--- +tags: [worklog, ir-deck, pydantic, validation, rb8001, deployment] +--- + +# IR Deck Pydantic-only ์ถœ๋ ฅ๊ฒ€์ฆ ๊ตฌํ˜„ ๋ฐ ๋ฐฐํฌ๊ฒ€์ฆ + +## ๊ด€๋ จ ๋ฌธ์„œ +- [Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์•„์ด๋””์–ด](../ideas/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์•„์ด๋””์–ด.md) +- [Pydantic AI ๋„์ž… ๊ธฐ๋ฐ˜ LLM ์ถœ๋ ฅ ์•ˆ์ •ํ™” ์ข…๋ฃŒ ๋ฆฌ์„œ์น˜](../research/260313_pydantic_ai_๋„์ž…_๊ธฐ๋ฐ˜_llm_์ถœ๋ ฅ_์•ˆ์ •ํ™”_์ข…๋ฃŒ_๋ฆฌ์„œ์น˜.md) +- [Pydantic AI ๋ถ€๋ถ„ ๋„์ž… LLM ์ถœ๋ ฅ์•ˆ์ •ํ™” ๊ณ„ํš](../plans/260313_pydantic_ai_๋ถ€๋ถ„๋„์ž…_llm_์ถœ๋ ฅ์•ˆ์ •ํ™”_๊ณ„ํš.md) + +## ์™„๋ฃŒ ์š”์•ฝ +- `rb8001`์˜ `IR Deck` ์ข…ํ•ฉ ํ‰๊ฐ€ ๊ฒฝ๋กœ์— `Pydantic-only` typed validation์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. +- `100์  + B๋“ฑ๊ธ‰` ๊ฐ™์€ ๋ชจ์ˆœ ์ถœ๋ ฅ์€ 1ํšŒ ์žฌ์งˆ์˜ ํ›„์—๋„ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์œผ๋ฉด ์ €์žฅํ•˜์ง€ ์•Š๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. +- `grade`, `recommendation`์€ ์„œ๋ฒ„ ์ตœ์ข…๊ฐ’์œผ๋กœ ์žฌ๊ณ„์‚ฐํ•˜๋„๋ก ๊ณ ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. + +## ๊ตฌํ˜„ ๋‚ด์šฉ +- ์ถ”๊ฐ€: + - `rb8001/app/services/ir_deck_output_models.py` +- ์ˆ˜์ •: + - `rb8001/app/services/ir_deck_analyzer.py` + - `rb8001/tests/test_ir_deck_json_parsing.py` +- ํ•ต์‹ฌ ๋ณ€๊ฒฝ: + - `raw -> Pydantic model_validate -> finalize -> save` ๊ฒฝ๊ณ„ ์ถ”๊ฐ€ + - `story_scores` ์ •๊ทœํ™” ํ•จ์ˆ˜ ์ถ”๊ฐ€ + - `summary` fallback ์ƒ์„ฑ ํ›„ ์ตœ์†Œ ๊ธธ์ด ๊ฒ€์ฆ ์ถ”๊ฐ€ + - validator ์‹คํŒจ ์‹œ ๊ฐ™์€ ํ”„๋กฌํ”„ํŠธ ์žฌํ˜ธ์ถœ 1ํšŒ ์ถ”๊ฐ€ + +## ๊ฒ€์ฆ ๊ฒฐ๊ณผ +- ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ํ…Œ์ŠคํŠธ: + - `pytest /code/tests/test_ir_deck_json_parsing.py` โ†’ `15 passed` + - `pytest /code/tests/test_ir_deck_workflow.py` โ†’ `5 passed` +- ๋ฐฐํฌ ๊ฒ€์ฆ: + - `git push origin main` ์™„๋ฃŒ + - `rb8001` ์ปจํ…Œ์ด๋„ˆ ์žฌ์‹œ์ž‘ ํ™•์ธ + - ์žฌ์‹œ์ž‘ ํ›„ `curl localhost:8001/health` ์ •์ƒ ์‘๋‹ต ํ™•์ธ + +## ๋ฐฐํฌ ํ™•์ธ ๊ฐ’ +- ํ‘ธ์‹œ ์ปค๋ฐ‹: `cc8f281` +- ๋ฐฐํฌ ํ›„ `rb8001` ์‹œ์ž‘ ์‹œ๊ฐ: + - `2026-03-13T09:28:41Z` +- ๋ฐฐํฌ ํ›„ ์ƒํƒœ: + - `rb8001 Up 22 seconds (healthy)` ํ™•์ธ ํ›„ ํ—ฌ์Šค ์‘๋‹ต ์ •์ƒ + +## ํ•œ ์ค„ ๊ฒฐ๋ก  +- ์ด๋ฒˆ ์ž‘์—…์œผ๋กœ `IR Deck` ์ข…ํ•ฉ ํ‰๊ฐ€๋Š” ๊ธฐ์กด `LangGraph`๋ฅผ ์œ ์ง€ํ•œ ์ฑ„, `Pydantic-only` ๊ฒ€์ฆ ๊ณ„์ธต์„ ํ†ตํ•ด ๋ชจ์ˆœ๋œ ๊ตฌ์กฐํ™” ์ถœ๋ ฅ ์ €์žฅ์„ ์ฐจ๋‹จํ•˜๋Š” ์ƒํƒœ๋กœ ์ „์ง„ํ–ˆ์Šต๋‹ˆ๋‹ค.