Spaces:
Sleeping
CLAUDE.md โ Interactive Novel Engine Backend
ํ๋ก์ ํธ ๊ฐ์
์์ ๊ณ ์ ์์ค(์นด๋ฅด๋ฐ๋ผ)์ ์จํจ๋ก์ง ๊ธฐ๋ฐ์ผ๋ก ํ์ํํ์ฌ "์ฑ ์ ์ฝ๋ ๋๋ + ํต์ฌ ์ฌ์์์ ์ํธ์์ฉ"์ ์ ๊ณตํ๋ ์ธํฐ๋ํฐ๋ธ ๋ ธ๋ฒจ ์์ง. Google ADK ๊ธฐ๋ฐ ํ์ดํ๋ผ์ธ์ด ์ ์ ์ ๋ ฅ์ ๋ฐ์ ๊ฐ๋ โ ์บ๋ฆญํฐ ์๋ต โ ๋๋ ํฐ(์๋ ด ํ๋จ) ์์ผ๋ก ์ฒ๋ฆฌํ๊ณ , ์์ยท์จํจ๋ก์ง ๊ฒ์์ GraphRAG ๊ณ์ธต์ด ๋ด๋น.
์ต์์ ๊ฐ์น: ์์ ์ถฉ์ค. ์์์ ํค๊ณผ ์ธ๊ณ๊ด์ด ๊นจ์ง๋ ์๊ฐ ์คํจ๋ค. LLM์ ์์ ๋๋ ๊ธฐ๋ฅ์ด ์๋๋ผ ๋ฆฌ์คํฌ๋ก ์ทจ๊ธํ๋ค.
์ํคํ ์ฒ 3์ธต ๊ตฌ์กฐ
[๋ฐ๋จ] ์จํจ๋ก์ง/์ํ ์ค๊ณ ๋ฌธ์ โ YAML v1.0 (๋ํ๋ ์ฐ์ถ๋ฌผ) โ ์์ง์ด ํ์ฑ
[์ค๊ฐ] GraphRAG: ์จํจ๋ก์ง YAML์ Neo4j์ ์ ์ฌ + ์๋ฌธ ๋ฐ์ท ๋ฒกํฐ ๊ฒ์
[์๋จ] ADK ์์ด์ ํธ ํ์ดํ๋ผ์ธ (guard โ character โ director)
- ์จํจ๋ก์ง์ ๋ด์ฉ์ ๋ํ๋ ์์ , YAML **์คํค๋ง(ํ์)**๋ ๊ฐ๋ฐํ ์์ . ์คํค๋ง ๋ณ๊ฒฝ์ ์์ชฝ ํฉ์๋ก๋ง.
- ๊ทธ๋ํ๋ ์๋ฌธ์์ LLM์ด ์๋ ์ถ์ถํ์ง ์๋๋ค. ํ๋ ์ด์ ๋ YAML์ ์ ์ฌํ๋ ๊ฒ์ด ์ ์ผํ ๊ฒฝ๋ก.
์์ ์์น
1. ๋ ผ์ โ ์ค๊ณ โ ๊ณํ โ ๊ตฌํ โ ๋ฆฌ๋ทฐ
์ฝ๋๋ฅผ ๋จผ์ ์์ฑํ์ง ์๋๋ค. ๋ฐ๋์ ์ด ์์๋ฅผ ๋ฐ๋ฅธ๋ค:
- ๋ ผ์: ์๊ตฌ์ฌํญ์ ์ ํํ ์ดํดํ๊ณ ์ง๋ฌธํ๋ค
- ์ค๊ณ: 2-3๊ฐ ์ ๊ทผ๋ฒ์ ๋น๊ตํ๊ณ ์ถ์ฒํ๋ค
- ๊ณํ: ๊ตฌ์ฒด์ ์ธ ํ์ผ ๋ชฉ๋ก๊ณผ ๋ณ๊ฒฝ ์ฌํญ์ ๋ฌธ์ํํ๋ค
- ๊ตฌํ: ๊ณํ๋๋ก๋ง ๊ตฌํํ๋ค. ๋ฒ์๋ฅผ ๋ฒ์ด๋์ง ์๋๋ค
- ๋ฆฌ๋ทฐ: ๊ตฌํ ๊ฒฐ๊ณผ๋ฅผ spec๊ณผ ๋์กฐํ์ฌ ๊ฒ์ฆํ๋ค
- ๊ฐฑ์ : ์ปค๋ฐ ๋ฐ ์ฝ๋ ๊ตฌํ์ด ์๋ฃ๋๋ฉด ํด๋น ๋ถ๋ถ์ ๊ผผ๊ผผํ๊ฒ ๋จ๊ณ๋ณ๋ก ๋ฉ๋ชจ๋ฆฌ์ ๋ฐ์ ๋ฐ ๊ฐฑ์ ํ์ธ์
์ด ์ฌ์ดํด์ ์ถฉ๋ถํ ๋ฐ๋ณตํ๋ค. ๊ธํ๋ค๊ณ ๊ฑด๋๋ฐ์ง ์๋๋ค.
2. ๊ธฐ์กด ์ฝ๋์ ์ผ๊ด์ฑ์ ์งํจ๋ค
- ์ ์ฝ๋๋ ๊ธฐ์กด ํจํด์ ๋ฐ๋ฅธ๋ค. ๋ ๋์ ํจํด์ด ์์ด๋ ํผ์ ๋ฐ๊พธ์ง ์๋๋ค
- ํด๋ ๊ตฌ์กฐ, ๋ค์ด๋ฐ, import ์คํ์ผ์ ๊ธฐ์กด ์ฝ๋์์ ๋จผ์ ํ์ธํ๋ค
- "๊ฐ์ "์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ๊ธฐ์กด ์ปจ๋ฒค์ ์ ๋ฌด์ํ์ง ์๋๋ค
3. Google ADK ๊ณต์ ํจํด์ ๋ฐ๋ฅธ๋ค
- ADK๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ(LlmAgent, ParallelAgent, SequentialAgent, LoopAgent, AgentTool, FunctionTool)์ผ๋ก ํด๊ฒฐํ๋ค
- Python์ผ๋ก ADK๋ฅผ ์ฐํํ๊ฑฐ๋ ๋์ฒดํ๋ ์ฝ๋๋ฅผ ์์ฑํ์ง ์๋๋ค
- ํ๋กฌํํธ๋ก ํด๊ฒฐํ๋ ค ํ๊ธฐ ์ ์, ์์ด์ ํธ ๊ตฌ์กฐ(๊ณ์ธต, ํ์ , ์กฐํฉ)๋ก ํด๊ฒฐํ ์ ์๋์ง ๋จผ์ ๊ฒํ ํ๋ค
- ์กฐ๊ธ์ด๋ผ๋ ์์ฌ๋๋ฉด ๋ฐ๋ก ADK ๊ณต์๋ฌธ์๋ฅผ ํ์ธํ๋ค: https://google.github.io/adk-docs/
4. ํ์ธ ์์ด ์ถ์ธกํ์ง ์๋๋ค
- ํ์ผ์ ์์ ํ๊ธฐ ์ ์ ๋ฐ๋์ ์ฝ๋๋ค
- ADK API๋ฅผ ์ฌ์ฉํ ๋ ๊ธฐ์ต์ ์์กดํ์ง ์๊ณ ๋ฌธ์๋ ์์ค๋ฅผ ํ์ธํ๋ค
- Docker ๋ก๊ทธ, grep ๊ฒฐ๊ณผ ๋ฑ ์ค์ ์ฆ๊ฑฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋จํ๋ค
- ์์ ์๋ฌธยท์จํจ๋ก์ง ๋ด์ฉ์ ๊ธฐ์ต์ผ๋ก ์ธ์ฉํ์ง ์๋๋ค. ๋ฐ๋์ content/ ๋๋ ๊ทธ๋ํ์์ ์กฐํํ๋ค
5. ์์ฌ ์งํ์ ์ฝ๋๊ฐ, ๋์ฌ๋ LLM์ด
- ์ฑํฐ/์ฌ ์ ํ, ๋ถ๊ธฐ ์กฐ๊ฑด, ์๋ ด ํ์ ์ ์ต์ข ๊ฒฐ์ ์ ๊ฒฐ์ ์ ์ฝ๋(์ํ๋จธ์ + director ์ถ๋ ฅ์ ๊ตฌ์กฐํ๋ ๊ฒ์ฆ)๊ฐ ๋ด๋ฆฐ๋ค
- LLM์ "์ง๊ธ ์ด ์ฌ ์์์ ์ด ์บ๋ฆญํฐ๋ก ์๋ต"ํ๋ ์ข์ ์ญํ ๋ง ๋งก๋๋ค
- LLM ์ถ๋ ฅ๋ง ๋ฏฟ๊ณ state๋ฅผ ์ ์ด์ํค์ง ์๋๋ค. ์ ์ด ์กฐ๊ฑด์ ๋ฐ๋์ ์ฝ๋์์ ์ฌ๊ฒ์ฆํ๋ค
ํต์ฌ ๋๋ฉ์ธ ๊ท์น
์์ ์ถฉ์ค (Canon Fidelity)
- ์บ๋ฆญํฐ๊ฐ ์์์ ์๋ ์ฌ์ค์ ๋จ์ ํ๋ ์๋ต์ ๊ฒฐํจ์ด๋ค. ํ๋กฌํํธ์ ์ฃผ์ ๋ ์จํจ๋ก์งยท์๋ฌธ ๋ฐ์ท ๋ฒ์ ์์์๋ง ๋งํ๊ฒ ํ๋ค
- ์ ์ ๊ฐ ์ธ๊ณ๊ด์ ๋ฒ์ด๋ ํ๋์ ํ๋ฉด director๊ฐ ์ธ๊ณ๊ด ๋ด ํ๋ฒ์ผ๋ก ๋๋๋ฆฐ๋ค. ๋ฉํ ๋ฐํ("์ ๋ AI๋ผ์...")๋ ์ ๋ ๊ธ์ง
- ๋ฌธ์ฒด๋
content/style/๋ฌธ์ฒด์ฐ์ถ ๊ฐ์ด๋๊ฐ ๊ธฐ์ค. style_critic ๋ฃจํ๊ฐ ๊ฒ์ํ๋ค
์คํฌ์ผ๋ฌ ์ค์ฝํ (๋งค์ฐ ์ค์)
- ๋ชจ๋ ์๋ฌธ ์ฒญํฌ, ๊ทธ๋ํ ๋
ธ๋/์ฃ์ง, ์จํจ๋ก์ง ํญ๋ชฉ์๋
state_id(์์ฌ ์งํ ๋จ์)๊ฐ ๋ถ๋๋ค - ๋ชจ๋ ๊ฒ์์
state_id <= ํ์ฌ stateํํฐ๋ฅผ ๊ฐ์ ํ๋ค. ํํฐ ์๋ ๊ฒ์ ์ฝ๋๋ ๋ฆฌ๋ทฐ์์ ๋ฐ๋ ค - ์ง์ ์ํ: "state N ์์ ์ ์ด ์บ๋ฆญํฐ๊ฐ ์๋ ๊ฒ"์ ์จํจ๋ก์ง์ ์ง์ ์ํ ํญ๋ชฉ์ด ๊ถ์. ์บ๋ฆญํฐ ํ๋กฌํํธ์๋ ํด๋น ์์ ์ง์๋ง ์ฃผ์ ํ๋ค
์๋ ด (Convergence)
- ์ฌ๋ง๋ค YAML์ ์ ์๋ ๋ชฉํ ๋นํธ(goal beats)์ ์๋ ด ์กฐ๊ฑด(exit condition)์ด ์๋ค
- director๋ ๋งค ํด "๋ชฉํ ๋นํธ ๋ฌ์ฑ ์ฌ๋ถ / ์ดํ ์ ๋ / ์๋ ด ์์ ๋๋ฌ ์ฌ๋ถ"๋ฅผ ๊ตฌ์กฐํ ์ถ๋ ฅ์ผ๋ก ํ๋จํ๋ค
- ์ต๋ ํด ์๋ฅผ ์ด๊ณผํ๋ฉด director๊ฐ ๊ฐ์ ์๋ ด ๋ด๋ ์ด์ ์ผ๋ก ์ฌ์ ๋ซ๋๋ค (๋ฌดํ ์ฒด๋ฅ ๋ฐฉ์ง)
์์ด์ ํธ ํด๋ ๊ท์น
์์ด์ ํธ = ํด๋ + agent.py โ ํ์ ์์ด์ ํธ๊ฐ ์๋ ๊ณ์ธตํ
ํ์ ์์ด์ ํธ = sub_agents/ ํด๋ โ ๋ถ๋ชจ-์์ ๊ณ์ธต
๊ฐ์ ๋์ค ๋ณ๋ ฌ ์์ด์ ํธ = agents/ ํ์ผ โ ์์ฒด ํ์ ์๋ ๋จ์ ์์ด์ ํธ (๊ฐ๋ ๋ฑ)
๋๊ตฌ = tools/ ์์ 1ํ์ผ = 1ํจ์
instruction 10์ค ์ด๊ณผ = instructions/ ํด๋๋ก ๋ถ๋ฆฌ
ํฉํ ๋ฆฌ ํจ์ = ์กฐ๋ฆฝ๋ง ๋ด๋น (ํด๋์ค ์ ์/DB ํฌํผ ํฌํจ ๊ธ์ง)
sub_agents/ vs agents/ ๊ตฌ๋ถ
sub_agents/X/agent.pyํจํด์ X๊ฐ ์ถ๊ฐ๋ก ํ์ ๊ณ์ธต์ ๊ฐ์ง ๋ ์ฌ์ฉ. ์:sub_agents/director/sub_agents/style_critic/...agents/X.pyํจํด์ X๊ฐ ์์ฒด ํ์ ์์ด ๋จ์ผ BaseAgent/LlmAgent๋ก ๋๋ ๋ ์ฌ์ฉ. ํฉํ ๋ฆฌ ํจ์(create_X())์ ํด๋์ค๋ฅผ ๊ฐ์ ํ์ผ์ ๋ชจ์๋ . ์:engine/agents/input_guard.py,spoiler_guard.py,scene_context_loader.py
ํฉํ ๋ฆฌ ํ์ผ ์ฑ ์ ๋ถ๋ฆฌ
factory.py๋ ์กฐ๋ฆฝ๋ง ๋ด๋น. ์์ด์ ํธ ํด๋์ค ์ ์ยทDB ํฌํผ ํจ์๋
agents/*.py๋ sub_agents/*/agent.py์ ์์น. ํฉํ ๋ฆฌ๋ ๊ทธ๊ฒ๋ค์ importํด์
SequentialAgent(sub_agents=[create_guard(scene_id), create_character(scene_id), ...])
ํํ๋ก ์ฎ๋ ์ญํ ๋ง.
์์ง ๊ตฌ์กฐ
engine/
โโโ agent.py โ ๋ฃจํธ ์์ด์ ํธ (ํด ํ์ดํ๋ผ์ธ)
โโโ app.py โ ADK App + Runner + Plugin ๋ฑ๋ก
โโโ model.py โ LLM ๋ชจ๋ธ ์ค์ (env์์ ์ฝ์)
โโโ router.py โ FastAPI ์๋ํฌ์ธํธ (HTTP๋ง ๋ด๋น)
โโโ instructions/ โ instruction ํ
์คํธ (์บ๋ฆญํฐ/๋๋ ํฐ/๊ฐ๋)
โโโ callbacks/ โ ์์ด์ ํธ๋ณ ์ฝ๋ฐฑ
โโโ plugins/ โ ADK Plugin (ํก์ข
๋จ ๊ด์ฌ์ฌ)
โ โโโ logging.py โ ํด ๋ก๊น
(์
๋ ฅ/์ปจํ
์คํธ/์๋ต/๊ฐ๋๋ฐ๋/์๋ ด) โ ์ฐ๊ตฌ ๋ฐ์ดํฐ ๊ฒธ์ฉ
โโโ services/ โ ๋น์ฆ๋์ค ๋ก์ง
โ โโโ story.py โ ์นด๋ ์งํ ์ํ๋จธ์ (canon ์ฝ๊ธฐ/next ์ ์ด/์๋ฉ ํ์ )
โ โโโ pocket.py โ ๊ฐ์
ํฌ์ผ: ADK ์ธ์
๊ด๋ฆฌ + run_turn() + ์๋ ด ์ฝ๋ ์ฌ๊ฒ์ฆ
โ โโโ state.py โ ์จ์ ์ํ (์ ๋ขฐ์ถ/์ ์ฝ๋/์) ์ปค๋ฐ
โ โโโ retrieval.py โ ๊ฒ์ ์ค์ผ์คํธ๋ ์ด์
(์คํฌ์ผ๋ฌ ํํฐ ๊ฐ์ ์ง์ , GraphRAG๋ 2๋จ๊ณ)
โโโ repositories/ โ ๋ฐ์ดํฐ ์ ๊ทผ
โ โโโ ontology.py โ ์จํจ๋ก์ง ์กฐํ ์ธํฐํ์ด์ค (๊ตฌํ: yaml_direct / neo4j)
โ โโโ manuscript.py โ ์๋ฌธ ์ฒญํฌ + ๋ฒกํฐ ๊ฒ์
โ โโโ playlog.py โ ํด ๋ก๊ทธ ์ ์ฅ
โโโ tools/ โ 1ํ์ผ = 1๋๊ตฌํจ์
โ โโโ retrieval/ โ query_ontology, search_excerpt
โ โโโ state/ โ set_flag, update_affinity, mark_beat
โโโ sub_agents/ โ ์์ด์ ํธ ํธ๋ฆฌ
โ โโโ character/ โ ์บ๋ฆญํฐ ์๋ต (์ธ๋ฌผ ์ํธ + ์ฌ ๋ธ๋ฆฌํ + ๋ฐ์ท ์ฃผ์
)
โ โโโ director/ โ ๋นํธ ๋ฌ์ฑ/์ดํ/์๋ ด ํ๋จ (๊ตฌ์กฐํ ์ถ๋ ฅ)
โ โโโ style_critic/ โ LoopAgent (generator + critic) ๋ฌธ์ฒด ๊ฒ์
โโโ schemas/ โ Pydantic: ์ฝํ
์ธ 3๋ ์ด์ด ์คํค๋ง + validate CLI
โโโ content/ โ (์ฝ๊ธฐ ์ ์ฉ) ์์ง์ ์ ์ผํ ์์ฌ ์
๋ ฅ โ ์์ฒ์ ๋นํธ์นด๋ MD(๋ํ๋ ์์ )
โโโ beatcards/ โ ๋นํธ์นด๋ YAML (scripts/beatcard_convert.py ์ฐ์ถ๋ฌผ, E01~)
โโโ chapters/ โ ์ฑํฐ ๋ฉํ (entry/exit beats, ending_rules, R์นด๋ ๋ฑ๋ก)
โโโ characters.yaml โ ์ธ๋ฌผ ์ ์ฒด์ฑ ์ํธ (ENOS, ์ง์๊ฒฝ๊ณ, kb_query ํ
)
โโโ style/ โ ๋ฌธ์ฒด์ฐ์ถ ๊ฐ์ด๋
โโโ narrative/ โ ์์ฌ/ํธ๋์คํ ๋ฌธ์
ADK App + Plugin ๊ท์น
app.py์์App๊ฐ์ฒด +Runner๋ฅผ ์ ์ํ๋ค- ํก์ข
๋จ ๊ด์ฌ์ฌ(๋ก๊น
, ๋ชจ๋ํฐ๋ง ๋ฑ)๋ Plugin์ผ๋ก ๋ถ๋ฆฌํ๋ค (
plugins/) - Runner๋
App๊ธฐ๋ฐ์ผ๋ก ์ด๊ธฐํํ๋ค (Runner(app=app, ...)) - agent๋ฅผ Runner์ ์ง์ ์ ๋ฌํ์ง ์๋๋ค (๋ ๊ฑฐ์ ํจํด)
- ์ค์บํด๋ ํ์ผ(๋ฏธ๊ตฌํ ๋๊ตฌ, ๋น ์ฝ๋ฐฑ, ๋น instruction)์ ์ญ์ ํ์ง ์๋๋ค
- Claude Code๊ฐ ADK ํจํด์ ๋ฒ์ด๋์ง ์๋๋ก ํ๋ ๊ฐ๋๋ ์ผ ์ญํ
- ํธ์ถ ํ๋ฆ: Agent โ Tool(FunctionTool) โ Service โ Repository โ ๋ฐ์ดํฐ(YAML/Neo4j/DB)
์ฝํ ์ธ ํ์ดํ๋ผ์ธ (A์ ์ํคํ ์ฒ โ 2026-07-03 ํ์ )
์ฝํ ์ธ 3๋ ์ด์ด
| ๋ ์ด์ด | ํ์ผ | ๋ด์ฉ | ๋์ ์คํค๋ง |
|---|---|---|---|
| ์ ์ฒด์ฑ (๋ถ๋ณ) | content/characters.yaml |
์ธ๋ฌผ ์ํธ(ENOS), player=laura, ์ง์๊ฒฝ๊ณ, kb_query ํ | schemas/character.py |
| ์ฑํฐ ๋ฉํ | content/chapters/*.yaml |
entry/exit beats, beat_range, ์๋ฉ ๊ท์น, R์นด๋ ๋ฑ๋ก | schemas/chapter.py |
| ๋ด๋ฌํฐ๋ธ | content/beatcards/E##/*.yaml |
์นด๋: narration ๋ธ๋ก/choices/next/canon/๋ณต์ ํ๊ทธ/์ฐ์ถ | schemas/beatcard.py |
- ์ฝํ
์ธ ์์ฒ(๊ถ์)์ ๋นํธ์นด๋ MD(
์นด๋ฅด๋ฐ๋ผ/.../๋นํธ์นด๋/, ๋ํ๋ ์์ ). ๋ณํ YAML์ ํ์๋ฌผ์ด๋ฉฐscripts/beatcard_convert.py(๊ฐ๋ฐํ ์์ )๋ก๋ง ์์ฑ - ์ฝํ ์ธ ๋ด์ฉ ์์ ์ ๊ฐ๋ฐํ์ด ํ์ง ์๋๋ค. ์คํค๋ง ์๋ฐ ๋ฐ๊ฒฌ ์ ๊ฒ์ฆ ๋ฆฌํฌํธ๋ก ๋ํ๋๊ป ์ ๋ฌ
python -m engine.schemas.validate content/๊ฐ ํต๊ณผํ์ง ์๋ YAML์ ์ ์ฌํ์ง ์๋๋ค- MVP์ ์์ฌ ์งํ ๋จ์๋ ๋นํธ์นด๋ ์์: ์คํฌ์ผ๋ฌ ํํฐ์
state_id๋beat_id โค ํ์ฌ๋ก ํด์ํ๋ค
์จ์ ์ํ ๋ชจ๋ธ (P0 4์ข ์์ง ๋ช ์ธ ์ค๊ฑฐ)
- ์ ๋ขฐ์ถ(์์ฌโ์ ๋ขฐ ์์) ยท ์ ์ฝ๋(๋จ์กฐ ์ฆ๊ฐ, ์ ๋ขฐ์ ๋ ๋ฆฝ โ ํต์ฌ ์์ด๋ฌ๋) ยท ์(์ ์ , ๋์ 1ํ ํ๋ฆฝ ํ ๋ถ๋ณ)
- ์ซ์ยท๊ฒ์ด์งยท์์คํ ๋ฉ์์ง๋ฅผ ์ ์ ์๊ฒ ์ ๋ ๋ ธ์ถํ์ง ์๋๋ค. ์ํ๋ ๋ด๋ ์ด์ ํคยท์นด๋ฅด๋ฐ๋ผ์ ๊ฒฐยทํด๊ธ์ผ๋ก๋ง ๋๋ฌ๋ธ๋ค
- ์๋ฉ ํ์ ์ ์ฑํฐ ๋ฉํ์
ending_rules์กฐ๊ฑด์์ ์ฝ๋๋ก ํ๊ฐํ๋ค (LLM ํ์ ๊ธ์ง)
GraphRAG ๊ณ์ธต (2๋จ๊ณ โ MVP ์ ์ธ)
- MVP๋ pre_kb ์ํ:
content/์kb_queryํ (source: kb_query, status: pending)์ ์ฑ์ฐ์ง ์๊ณ ๊ทธ๋๋ก ๋๋ค - MVP ์ฝํ
์ธ ๋ก๋๋
repositories/content.py(YAML ๋ฉ๋ชจ๋ฆฌ ๋ก๋). ์ดํ GraphRAG ๋์ ์kb_queryํ ์ ์ฑ์ฐ๋ ๊ตฌํ์ผ๋ก ๊ต์ฒด. ์์ ๊ณ์ธต์ ๊ตฌํ์ ๋ชฐ๋ผ์ผ ํ๋ค (ablation ์คํ ์๊ฑด) - Neo4j ์ ์ฌ ์ LLM ์๋ ์ถ์ถ KG ๋น๋(SimpleKGPipeline ๋ฅ)๋ ์ฌ์ฉ ๊ธ์ง โ ํ๋ ์ด์ ์ฝํ ์ธ ์ ์ถฉ๋
- ๋
์ ์ํธ์์ฉ ๋ฐ์ดํฐ์ ๊ทธ๋ํํ๋ 2๋จ๊ณ. MVP์์๋
playlog์คํค๋ง ์ค๊ณ๊น์ง๋ง
ADK ํต์ฌ ๊ท์น
Instruction
| ์ข ๋ฅ | ์ฉ๋ | ์ฃผ์ |
|---|---|---|
instruction |
๋์ . {var}๋ก state ๋ณ์ ์นํ |
{}๋ state ๋ณ์๋ก ํ์ฑ๋จ. ์์ ํ
์คํธ์ {} ์ฐ์ง ๋ง ๊ฒ |
global_instruction |
์ ์ฒด sub_agent์ ์ ํ | ์๋์ ์ผ๋ก ์ฌ์ฉํ ๋๋ง ์ฑ์ธ ๊ฒ |
static_instruction |
๊ณ ์ . ๋ฉํฐ๋ชจ๋ฌ ์ง์ | ์บ๋ฆญํฐ ์ํธ์ฒ๋ผ ์ฌ ๋ด ๋ถ๋ณ ์ปจํ ์คํธ์ ๊ฒํ |
- ์์ ๋ฐ์ทยท์จํจ๋ก์ง ์ปจํ
์คํธ๋ฅผ instruction์ ๋ฃ์ ๋ ์๋ฌธ์
{,}๊ฐ ํฌํจ๋ ์ ์์ผ๋ฏ๋ก ์ฃผ์ ์ ์ด์ค์ผ์ดํ ์ฒ๋ฆฌ๋ฅผ ๊ฑฐ์น๋ค (์ ์ฒ๋ฆฌ ์ ํธ ํ์)
Callback (8์ข )
before_agent, after_agent, before_model, after_model, before_tool, after_tool, on_model_error, on_tool_error
before_model: ์คํฌ์ผ๋ฌ ํํฐ ์ต์ข ๋ฐฉ์ด์ (์ฃผ์ ์ปจํ ์คํธ์ state_id ๊ฒ์ฌ)after_model: ํด ๋ก๊น ํ
์์ด์ ํธ ํ์
| ํ์ | ์ฉ๋ | ์์ |
|---|---|---|
LlmAgent |
LLM์ด ํ๋จ/๋๊ตฌ ํธ์ถ | character, director |
ParallelAgent |
ํ์ ์์ด์ ํธ ๋ณ๋ ฌ ์คํ | (ํ์ ์) ๋ค์ค ๊ฒ์ |
SequentialAgent |
ํ์ ์์ด์ ํธ ์์ฐจ ์คํ | turn_pipeline (guard โ character โ director) |
LoopAgent |
ํ์ ์์ด์ ํธ ๋ฐ๋ณต (escalate๋ก ํ์ถ) | style_critic (generator + critic), ์ฌ ๋ด ํด ๋ฃจํ |
BaseAgent |
LLM ์์ด ์ฝ๋๋ก ์ง์ ์ฒ๋ฆฌ | input_guard(๊ท์น ๊ธฐ๋ฐ 1์ฐจ), scene_context_loader, ์ํ ์ ์ด ๊ฒ์ฆ |
CustomAgent |
BaseAgent ํ์ฅ. ์กฐ๊ฑด ๋ถ๊ธฐ, ๋์ ์์ด์ ํธ ์ ํ | ์ฌ ํ์ ๋ณ ํ์ดํ๋ผ์ธ ์ ํ |
์ ๋ ํ์ง ๋ง ๊ฒ
output_schema์tools๋์ ์ฌ์ฉ โ Gemini 3.0๋ง ์ง์, ๋ค๋ฅธ ๋ชจ๋ธ์์๋ ์ ๋จ. ํ์ํ๋ฉด sub-agent๋ก ์ถ๋ ฅ ํฌ๋งทํ ์ ๋ถ๋ฆฌํ ๊ฒmode="ANY"์ฌ์ฉ (๋ฌดํ ๋ฃจํ ๋ฐ์)- instruction ์์ ์์ ํ
์คํธ๋ก
{ํ๊ตญ์ด}์ฌ์ฉ (state ๋ณ์๋ก ํ์ฑ๋จ โ ์๋ฌ) - Python ์ฝ๋๋ก ADK ์ํฌํ๋ก์ฐ๋ฅผ ๋์ฒด (keyword matching, hardcoded fallback ๋ฑ)
LlmAgent์output_key๋ก ๊ตฌ์กฐํ๋ ๊ฐ์ ๊ธฐ๋ โ LLM ํ ์คํธ๋ง ์ ์ฅ๋จ. director์ ์๋ ด ํ์ ์ฒ๋ผ ๊ตฌ์กฐํ ๊ฐ์ด ํ์ํ๋ฉดFunctionTool์ดtool_context.state์ ์ง์ ์ฐ๊ฑฐ๋ BaseAgent๋ก ๊ตฌํ- ๊ฒ์/์ปจํ
์คํธ ์ฃผ์
๊ฒฝ๋ก์์
state_idํํฐ๋ฅผ ์๋ต (์คํฌ์ผ๋ฌ ์ฌ๊ณ ) - LLM ํ๋จ๋ง์ผ๋ก ์ฌ/์ฑํฐ ์ ์ด (๋ฐ๋์
services/scene.py์ ์ฝ๋ ๊ฒ์ฆ์ ๊ฑฐ์น ๊ฒ) - ์์ ์๋ฌธ์ ํ๋กฌํํธ์ ๊ธฐ์ต์ผ๋ก ์ฌํ (๋ฐ๋์ repository ์กฐํ)
Escalate scope ๊ฒฉ๋ฆฌ (๋งค์ฐ ์ค์)
ADK LoopAgent๋ sub_agent์ escalate=True ์ด๋ฒคํธ๋ฅผ ์์๋ก yieldํ๋ค. ์ฆ
์ค์ฒฉ LoopAgent๊ฐ ์์ผ๋ฉด ๋ฐ๊นฅ ๋ฃจํ๊น์ง ํจ๊ป ํ์ถํ๋ค(์ ํ๋ก์ ํธ ์ค์ธก ํ์ธ). ์ด๋ก ์ธํด:
- SequentialAgent๋ escalate๋ฅผ ์ธ์ํ์ง ์์. escalate๊ฐ ํ์ํ ์๋ฆฌ์ ๋์ผ๋ ค๋ฉด
ํด๋น ๋ฒ์๋ง
LoopAgent(max_iterations=1)๋ก ๊ฐ์ ๊ฒ - ๋ด๋ถ LoopAgent์ ์ฑ๊ณต-escalate๊ฐ ๋ฐ๊นฅ์ ์ค์ผ์ํค๋ฉด ์ ๋๋ ๊ฒฝ์ฐ์ ๋ฐ๋์ ๋ฐ๊นฅ์ SequentialAgent๋ก ๋๊ณ , escalate๋ฅผ ์ํํ scope๋ง LoopAgent๋ก
- ์ฌ ํด ํ์ดํ๋ผ์ธ ์ค๊ณ ์ ์ด ๊ท์น์ ์ ์ฉํ ๊ธฐ์ค ๊ตฌ์กฐ:
scene_session (SequentialAgent) โโโ scene_context_loader โ BaseAgent, YAML/๊ทธ๋ํ์์ ์ฌ ๋ธ๋ฆฌํ ๋ก๋ โโโ turn_loop (LoopAgent max=์ฌ๋ณ max_turns) โ director์ ์๋ ด-escalate๊ฐ ์ฌ๊ธฐ์ ๊ทธ์นจ โ โโโ input_guard โ โโโ character โ โโโ director โ ์๋ ด ํ์ ์ escalate โโโ scene_closer (LoopAgent max=1) โ ๊ฐ์ ์๋ ด ๋ด๋ ์ด์ + ์ํ ์ ์ด ์ปค๋ฐ
๊ถ์ ์์ค ์์น
"ํ ์ ๋ณด์ ์ง์ค์ ํ ๊ณณ์๋ง ์์ด์ผ ํ๋ค."
- ์์ฌ ๋ด์ฉ(์ธ๋ฌผ/๊ด๊ณ/์ฌ๊ฑด/์ง์์ํ) ๊ถ์:
content/yaml/(์ ์ฌ๋ Neo4j๋ ๊ทธ ์ฌ๋ณธ). ์ฝ๋์ ์์ฌ ๋ด์ฉ ํ๋์ฝ๋ฉ ๊ธ์ง - ์ฌ ์งํ ์ํ ๊ถ์:
session.state(์ฌ ๋ด) โ ์ฌ ์ข ๋ฃ ์scene.py๊ฐ DB์ ์ปค๋ฐ (์์). ๋ ๊ณณ์ ๋์์ ๊ฐฑ์ ํ๋ ์ฝ๋ ๊ธ์ง - ํ์ฌ ์ ์ ์ ์์ฌ ์์น ๊ถ์: DB
play_session.current_state_id. ๊ฒ์ ํํฐ๋ ์ด ๊ฐ์ผ๋ก๋ง - ๋ฌธ์ฒด ๊ธฐ์ค ๊ถ์:
content/style/. ํ๋กฌํํธ๋ง๋ค ๋ฌธ์ฒด ์ง์๋ฅผ ์ฆํฅ ์์ฑํ์ง ์๋๋ค - ์๋ ด ํ์ ๊ถ์: director์ ๊ตฌ์กฐํ ์ถ๋ ฅ +
scene.py์ ์ฝ๋ ๊ฒ์ฆ. LLM ํ ์คํธ ํ์ฑ์ผ๋ก ํ์ ๊ธ์ง
LLM ๋ชจ๋ธ
# engine/model.py
from google.adk.models.lite_llm import LiteLlm
_MODEL_ID = os.environ.get("LLM_MODEL", "openai/gpt-5.4-nano")
LLM_MODEL = LiteLlm(model=_MODEL_ID)
- ๋ก์ปฌ ํ ์คํธ: ์ ๋น์ฉ ๋ชจ๋ธ๋ก ํ์ดํ๋ผ์ธ ๊ฒ์ฆ
- ์บ๋ฆญํฐ/๋ฌธ์ฒด ํ์ง์ด ์ค์ํ character, style_critic์ ์์ ๋ชจ๋ธ์ ๋ณ๋ env๋ก ๋ถ๋ฆฌ ๊ฐ๋ฅํ๊ฒ (
CHARACTER_MODEL) - ๋ชจ๋ธ ๊ต์ฒด๊ฐ ์ฆ์ผ๋ฏ๋ก ํ๋กฌํํธ์ ํน์ ๋ชจ๋ธ ์์กด ๋ฌธ๋ฒ์ ๋ฃ์ง ์๋๋ค
ํ ์คํธ & ๊ฒ์ฆ
# ๋จ์ ํ
์คํธ
python3 -m pytest tests/ -v
# ์ฝํ
์ธ ์คํค๋ง ๊ฒ์ฆ
python3 -m engine.schemas.validate content/
# ์คํฌ์ผ๋ฌ ํํฐ ํ๊ท ํ
์คํธ (state N์์ N+1 ์ ๋ณด ๋์ถ ์ฌ๋ถ)
python3 -m pytest tests/test_spoiler_scope.py -v
# ํ๊ฐ ํ๋ค์ค (LLM judge: ์์ ์ถฉ์ค๋ / ๋ฌธ์ฒด / ์๋ ด ์ฑ๊ณต๋ฅ )
python3 -m eval.run --scene <scene_id> --n 10
# Docker ๋น๋ ํ์ธ
docker compose up -d --build backend
docker compose logs --tail=20 backend
- ํ๋กฌํํธ ๋ณ๊ฒฝ์ ํ๊ฐ ํ๋ค์ค ์ ์์ ํจ๊ป PR์ ์ฒจ๋ถํ๋ค (๊ฐ์ด ์๋๋ผ ์ซ์๋ก)
- ํด ๋ก๊ทธ๋ ์ญ์ ํ์ง ์๋๋ค (์ฐ๊ตฌ ๋ฐ์ดํฐ)
์ปค๋ฐ ๊ท์น
- main์ ์ง์ ํธ์ ๊ธ์ง. ์์ ์ ๋ฌด์กฐ๊ฑด feature ๋ธ๋์น์์ ํ๊ณ PR๋ก ์ฌ๋ฆฐ๋ค
- ํ์ผ ์์ ์ ๋ฐ๋์ Read๋ก ํ์ฌ ๋ด์ฉ ํ์ธ
- ๊ตฌ์กฐ ๋ณ๊ฒฝ ์ "๋ณต์ฌ โ ์ ํ โ ์ญ์ " ์์ (๊ธฐ์กด ๊ธฐ๋ฅ ์ ์ง)
- Docker ๋น๋๋ก ๊ฒ์ฆ ํ ์ปค๋ฐ
- ๋นํธ์นด๋ MD ์๋ณธ๊ณผ
content/ํ์ ์ฝํ ์ธ ๋ด์ฉ์ ๊ฐ๋ฐ ์ปค๋ฐ์์ ์์ ํ์ง ์๋๋ค (์ฝํ ์ธ ๋ณ๊ฒฝ์ ๋ณ๋ PR + ๋ํ๋ ์น์ธ. ๋ณํ ์คํฌ๋ฆฝํธ ์ฌ์คํ ์ฐ์ถ๋ฌผ ๊ฐฑ์ ์ ์์ธ)
์ฐธ๊ณ ๋งํฌ
- ADK ๊ณต์๋ฌธ์: https://google.github.io/adk-docs/
- ADK GitHub: https://github.com/google/adk-python
- Neo4j GraphRAG Python: https://github.com/neo4j/neo4j-graphrag-python
- LiteLLM ๋ฌธ์: https://docs.litellm.ai/docs/providers