์ถ์ฒ์ ๊ธฐ๋ฐํด ์์ ํ๊ฒ ๋ตํ๋, ํต์ ๋ LLM ์์ด์ ํธ ํ๊ฒฝ.
์๋น์ค์ LLM์ ๋ถ์ผ ๋, ๊ฐ์ฅ ์ค์ํ ๊ฑด ๋ญ๊น์? ์ ์ฐฝํ ์ค๋ต๋ณด๋ค๋ ๋ฏฟ์ ์ ์๋ ๋ต๋ณ์ด ํ์ํ ๋๊ฐ ์์ต๋๋ค. ๊ทผ๊ฑฐ ์์ด ๊ทธ๋ด๋ฏํ๊ฒ ์ง์ด๋ธ ํ ๋ฌธ์ฅ์ด, ๊ทธ๋๋ก ์ฌ๊ณ ๊ฐ ๋๊ธฐ๋ ํ๋๊น์.
๋ณดํต์ ๋จ์ผ ์์ด์ ํธ๋ ๋ชจ๋ธ ํ๋๊ฐ ์์์ ํ๋จํ๊ณ , ๋๊ตฌ๋ฅผ ๋ถ๋ฅด๊ณ , ๋ต๊น์ง ์๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ง์ด๋ด์ง ์์๊ธฐ๋ฅผ ๋ฐ๋ ์๋ฐ์ ์์ฃ . Orchestrion์ ์ด ๊ตฌ์กฐ๋ฅผ ๋ค์ง์์ต๋๋ค. ์์๋ ์ฝ๋๊ฐ ์ ํ๊ณ , ๋ชจ๋ธ์ ๊ฐ ๋จ๊ณ์์ ์๊ธฐ ํ๋จ๋ง ๋ด๋ฆฝ๋๋ค. ๊ทธ๋์ ๋ชจ๋ ๋ต๋ณ์ ์ ๋ ฅ ๊ฒ์ดํธ๋ฅผ ์ง๋๊ณ , ๋ฌธ์ฅ๋ง๋ค ๊ทผ๊ฑฐ๋ฅผ ๋ฌ๊ณ , ์ถ๋ ฅ ๊ฒ์ฆ๊น์ง ํต๊ณผํ ๋ค์์ผ ํ๋ฉด์ ๋๋ฌํฉ๋๋ค.
Claude Code์ ๋ช ๊ฐ์ง ์์กด์ฑ๋ง ์์ผ๋ฉด ๋ฐ๋ก ๋๋ ค๋ณผ ์ ์๋ ์ํ ๊ตฌ์กฐ์ ๋๋ค. ํต์ ๋ ์์ด์ ํธ ๋ผ์ฐํ , ๊ฐ๋๋ ์ผ, ์ง์๋ฒ ์ด์ค(KB) ๊ธฐ๋ฐ ์๋ต์ด ํ๋์ ์๋๋ฆฌ์ค๋ก ๋ฌถ์ฌ ์์ต๋๋ค. ํ๋ฉด์ ํ๊ตญ์ด์ด๊ณ , ์์๋ก ํํ ์์ ๋ฌธ์๋ฅผ ๋ฃ์ด ๋์ง๋ง ์์ง์ ๋๋ฉ์ธ์ ๊ฐ๋ฆฌ์ง ์์ ์ด๋ค ๋งํฌ๋ค์ด ๋ฌธ์ ๋ฌถ์์๋ ๊ทธ๋๋ก ๋ถ์ต๋๋ค.
์ง๋ฌธ ํ๋๊ฐ ์ ๋ ฅ ๊ฒ์ดํธ โ ๊ณํ โ ๊ทผ๊ฑฐ ์์ง โ ๊ฒ์ โ ์ถ๋ ฅ ๊ฒ์ฆ 6๋จ๊ณ๋ฅผ ์ง๋๋ฉฐ, ๋ต๋ณ์ ๋ชจ๋ ์ฌ์ค ๋ฌธ์ฅ์ด ๋ฌธ์ ์ฒญํฌ(
[S#])์ ์ ํ ํฉํธ([DB#])์ ๊ฑธ๋ ค ์์ต๋๋ค. ์ค๋ฅธ์ชฝ ํจ๋์๋ ์คํ๋ SQL, ์กฐํ๋ ํฉํธ ํ, ๋๊ตฌ ํธ์ถ, ๊ฐ๋๋ ์ผ ํ์ ์ด ์ค์๊ฐ์ผ๋ก ์์ ๋๋ค.
- ์งํ๋ ๋ชจ๋ธ์ด ์๋๋ผ ๋ธ๋ฆฌ์ง๊ฐ ํ๋ค. ๊ฐ ๋ ธ๋๋ ๋ฑ ํ ๊ฐ์ง ์ผ๋ง ํ๋ LLM ํธ์ถ ํ๋์ ๋๋ค(๊ฒ์ดํธ โ ๋ผ์ฐํ โ ๊ณํ โ ์์ โ ๊ฒ์ โ ๊ฒ์ฆ). ๋ค์์ ์ด๋ ๋ ธ๋๊ฐ ๋์ง๋ ๋ชจ๋ธ์ด ์๋๋ผ ํ์ด์ฌ ๋ธ๋ฆฌ์ง๊ฐ ๊ท์น๋๋ก ์ ํฉ๋๋ค. ์ ์ด ํ๋ฆ์ด ๊ธด ๋ํ ์์์ ์ฐ์ฐํ ๊ฒฐ์ ๋๋ ๊ฒ ์๋๋ผ, ์ธ์ ๋ ๋ฐ๋ผ ์ฝ๊ณ ์ฌํํ ์ ์๋ ์ฝ๋๋ก ๋จ์ต๋๋ค.
- ๊ทผ๊ฑฐ๊ฐ ๊ฒ์ฆ๋ผ์ผ ๋๊ฐ๋ค. ์์
๋
ธ๋๋ ์ฌ์ค์ ๋ด์ ๋ฌธ์ฅ๋ง๋ค ๊ทผ๊ฑฐ๋ฅผ ๋ต๋๋ค โ
๋ฌธ์์์ ์์ผ๋ฉด
[S#], ์ ํ ๋ฐ์ดํฐ์์ ์์ผ๋ฉด[DB#]. ๋ฐ์ณ ์ค ๊ทผ๊ฑฐ๊ฐ ์์ผ๋ฉด ๊ทธ๋ด๋ฏํ๊ฒ ์ง์ด๋ด๋ ๋์ "์ ๊ณต ์๋ฃ์ ์์" ์ด๋ผ๊ณ ๋ถ๋ช ํ ๋ฐํ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ณ๋์ ๊ฒ์ฆ ๋ ธ๋๊ฐ ์ด์์ ๊ทผ๊ฑฐ์ ํ ๋ฒ ๋ ๋ง์ถฐ ๋ด ๋๋ค. - ๋๊ตฌ ๊ฒ์ดํธ๋ ๋จ ํ๋, ๋น ์ง์์ด ์ง๋๋ค. ๋ชจ๋ ๋๊ตฌ ํธ์ถ์ ์์ธ ์์ด ํ๋์ ์ ์ฑ ํ ์ ์ง๋ฉ๋๋ค. ์ด ํ ์ ๊ท์น ์ ์ฅ์๋ฅผ ํธ์ถํ ๋๋ง๋ค ์๋ก ์ฝ๊ธฐ ๋๋ฌธ์, ํ๋ฉด์ด๋ ์์ฐ์ด๋ก ๊ท์น์ ์ผ๊ณ ๋๋ฉด ๋ฐ๋ก ๋ค์ ํธ์ถ๋ถํฐ ์ ์ฉ๋ฉ๋๋ค.
- ๋ฌด์์ด ๋น ์ ธ๋ ๋ฉ์ถ์ง ์๊ณ ๋ฌผ๋ฌ์ ๋ค. ์๋ฒ ๋ฉ ๋ชจ๋ธ์ด ์์ผ๋ฉด ๊ฒ์์ BM25๋ง์ผ๋ก ๋์๊ฐ๊ณ , ์ง์์ด ์์ ์์ด๋ ๊ทธ๋ํ๋ ๊ทธ๋๋ก ๋๋ฉด์ ์์งํ๊ฒ ๋ชจ๋ฅธ๋ค๊ณ ๋ตํฉ๋๋ค. ์ด๋ ์ง์ ์์๋ ์๋ฆฌ ์์ด ๋ฌด๋์ง์ง ์์ต๋๋ค.
- ์ ์ฒด์ฑ์ ์ฝ๋๊ฐ ์๋๋ผ ์ฝํ
์ธ ๋ค. ํ๋์ ์์ด์ ํธ๋
scenario.md+knowledge/raw/+data/demo.json+ ๊ฐ๋๋ ์ผ ์ ์ฑ ์ผ๋ก ์ ์๋๊ณ , ์คํํ ๋๋ง๋ค ์ด ํ์ผ๋ค์ ์๋ก ์ฝ์ต๋๋ค. ๊ทธ๋์ ์ ์ ๋ฌด๋ฅผ ๋ง๋๋ ์ผ์ ์์ง์ ๋ค์ ์ง๋ ๊ฒ ์๋๋ผ ์ฝํ ์ธ ๋ฅผ ๊ฐ์ ๋ผ์ฐ๋ ์ผ์ ๋๋ค.
์ง๋ฌธ ํ๋๋ ์ด ๋ ธ๋ ๊ทธ๋ํ๋ฅผ ๋ฐ๋ผ ํ๋ฆ ๋๋ค. ๊ฐ๋ฆผ๊ธธ๊ณผ ์์๋ ๋ธ๋ฆฌ์ง๊ฐ ๊ท์น๋๋ก ์ ํ๊ณ , ๊ฐ ๋ ธ๋๋ ๋ชจ๋ธ์ ํ ๋ฒ ๋ถ๋ฌ ์๊ธฐ ๋ชซ์ ํ๋จ๋ง ๋ด๋ฆฝ๋๋ค.
flowchart LR
Q([์ง๋ฌธ]) --> RMI["์
๋ ฅ ๊ฒ์ดํธ<br/>rm_input"]
RMI -. REJECT .-> DECL([์ ์คํ ๊ฑฐ์ ])
RMI -- ALLOW --> RT{"๋ผ์ฐํ
<br/>route"}
RT -- ๋จ์ --> SA["์ฆ๋ต<br/>simple_answer"]
RT -- ์
๋ฌด --> PL["๊ณํ<br/>plan"]
PL --> WK["๊ทผ๊ฑฐ ์์งยท์ด์<br/>worker"]
WK --> RV["๊ฒ์<br/>review"]
RV -. "REDO โค 2" .-> WK
RV --> RMO["์ถ๋ ฅ ๊ฒ์ฆ<br/>rm_output"]
SA --> RMO
RMO --> ANS([๊ฒ์ฆ ํต๊ณผ ๋ต๋ณ])
classDef rm fill:#fde2e2,stroke:#c0392b,color:#7b241c;
classDef hyp fill:#e2ecfd,stroke:#2c5aa0,color:#1a3c6e;
classDef wrk fill:#e6f6ea,stroke:#27ae60,color:#186a3b;
class RMI,RMO rm;
class RT,SA,PL,RV hyp;
class WK wrk;
| ๋ ธ๋ | ๋ ์ธ | ์ญํ | ๋ชจ๋ธ |
|---|---|---|---|
rm_input |
์ํ๊ด๋ฆฌ | ์ง๋ฌธ์ด ์ ๋ฌด ๋ฒ์ ์์ธ์ง ํ์ โ ๋ฒ์ด๋๋ฉด ์ ์คํ ๊ฑฐ์ | haiku |
route |
์ด๊ด | ๋จ์ ์ง๋ฌธ(์ฆ๋ต)์ธ์ง ์ค์ ์ ๋ฌด์ธ์ง ๋ถ๊ธฐ | haiku |
simple_answer |
์ด๊ด | ์ธ์ฌยท์๋ด ๊ฐ์ ๋จ์ ์ง๋ฌธ์ ์งง๊ฒ ์ฆ๋ต | sonnet |
plan |
์ด๊ด | ์
๋ฌด ๋ฐฉ์ ์ ์ฐจ์ ๋ฐ๋ผ ๊ฒ์ ๊ณํ ์๋ฆฝ |
sonnet |
worker |
์ค๋ฌด | ์ง์ ๋๊ตฌ๋ก ๊ทผ๊ฑฐ๋ฅผ ๋ชจ์ [S#]/[DB#] ์ธ์ฉ ์ด์ ์์ฑ |
sonnet |
review |
์ด๊ด | ์ด์์ด ์ง๋ฌธ์ ๋ตํ๊ณ ์ธ์ฉ์ ๊ฐ์ท๋์ง ๊ฒ์ โ ๋ฏธํกํ๋ฉด REDO(์ต๋ 2ํ) | haiku |
rm_output |
์ํ๊ด๋ฆฌ | ์ด์์ ๊ทผ๊ฑฐ์ ๋์กฐํด ์ฌ์คยทํค ๊ฒ์ฆ, JSON ํ์ ์ฐ์ถ | haiku |
์ธ ๋ ์ธ(์ํ๊ด๋ฆฌ / ์ด๊ด / ์ค๋ฌด)์ ๋์๋ณด๋์์ ์์ผ๋ก ๊ตฌ๋ถ๋์ด,
์ง๊ธ ์ด๋ค ์ฑ๊ฒฉ์ ๋
ธ๋๊ฐ ๋๊ณ ์๋์ง ํ๋์ ๋ค์ด์ต๋๋ค.
์ด ํ๋ก์ ํธ์์ ๊ฐ์ฅ ๊ณต๋ค์ธ ๋ถ๋ถ์ ๋๋ค. ์์ ๋ ธ๋๊ฐ ์ธ์ฉํ ์ ์๋ ๊ทผ๊ฑฐ๋ ๋ฑ ๋ ์ข ๋ฅ์ ๋๋ค.
[S#]โ ๋ฌธ์์์ ์๋ผ๋ธ ์ฒญํฌ. ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์์ผ๋ก ์ฐพ์ ์๋ฌธ์ด๊ณ , ํ๋ฉด์๋ ์ฃผํฉ์์ผ๋ก ๋์ต๋๋ค.[DB#]โ ์ ํ ํฉํธ ํ ์ด๋ธ์ ํ ํ. ์ ํํ ์์น๋ ์์ฑ์ SQL๋ก ์กฐํํ ๊ฒ์ด๊ณ , ๋ ธ๋์์ผ๋ก ํ์ํ๋ฉด์ ์คํํ ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ ํ๊น์ง ํจ๊ป ๋ณด์ฌ ์ค๋๋ค.
๊ทผ๊ฑฐ๊ฐ ์๋ ๋ฌธ์ฅ์ ์ ์ด์ ์ธ ์ ์๊ณ , ๊ทธ๋ด ๋๋ "์ ๊ณต ์๋ฃ์ ์์"์ด๋ผ๊ณ ๋ตํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก rm_output ๋
ธ๋๊ฐ ์ด์์ ์ฌ์ค ๋ฌธ์ฅ์ ํ๋ํ๋ ๊ทผ๊ฑฐ์ ๋ค์ ๋ง์ถฐ ๋ณด๊ณ ,
์ฌ๊ธฐ๊น์ง ํต๊ณผํ ๋ต๋ณ๋ง ์ฌ์ฉ์์๊ฒ ์ ๋ฌ๋ฉ๋๋ค.
knowledge/raw/*.md
โ ingest (make ingest)
โผ
์ฒญํฌ ๋ถํ โโ FTS5 ์์ธ(BM25) + ์๋ฒ ๋ฉ(bge-m3) + LLM ํฉํธ ์ถ์ถ
โผ
SQLite (docs ยท chunks ยท chunks_fts ยท embeddings ยท facts)
โฒ
โ worker์ 4๊ฐ ์ฝ๊ธฐ ์ ์ฉ ๋๊ตฌ๋ง ์ ๊ทผ (MCP)
โ search_knowledge ยท read_doc ยท query_facts ยท list_sources
๊ฒ์์ BM25์ ์ฝ์ฌ์ธ ์ ์ฌ๋๋ฅผ ์ํธ ์์ ์ตํฉ(RRF)์ผ๋ก ์์ด ์๋๋ค. ์๋ฒ ๋ฉ ๋ชจ๋ธ์ด ์์ผ๋ฉด ์๋์ผ๋ก BM25๋ง ์ฐ๊ธฐ ๋๋ฌธ์, ์ด๋ค ํ๊ฒฝ์์๋ ๋ฐ๋ชจ๊ฐ ๋ฉ์ถ์ง ์์ต๋๋ค.
| ํ์ผ | ์ญํ |
|---|---|
bridge/graph.py |
๋ ธ๋ ๊ทธ๋ํ + ๋ชจ๋ธ ํฐ์ด๋ณ ์ ์ธ์ ํ(๋ฐ ๊ฐ ์ ์ง, ๋ ธ๋๋ง๋ค ์ ์ธ์ id๋ก ํ์คํ ๋ฆฌ ๊ฒฉ๋ฆฌ) |
bridge/hooks.py ยท bridge/policy.py |
์ ์ผํ ๋๊ตฌ ๊ฒ์ดํธ + ํธ์ง ๊ฐ๋ฅํ first-match ๊ท์น ์ ์ฅ์ |
bridge/api.py ยท bridge/app.py |
REST API + WebSocket ์ด๋ฒคํธ ํผ๋, ์ ์ ํ๋ฐํธ ์๋น |
bridge/scenario.py |
scenario.md๋ฅผ ํ์ฑํด ๊ทธ๋ํ ํ๋กฌํํธ์ ๋ฐํ์ ์ฃผ์
|
core/db.py ยท core/search.py ยท core/embed.py |
SQLite ์คํค๋ง ยท ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์ ยท ์๋ฒ ๋ฉ(๊ฐ๋ฑ ์ง์) |
ingest/ |
๋งํฌ๋ค์ด โ ์ฒญํฌ โ FTS โ ์๋ฒ ๋ฉ โ ํฉํธ ์ ์ฌ |
mcpserver/server.py |
FastMCP โ worker์๊ฒ ๋ ธ์ถ๋๋ ์ง์ ๋๊ตฌ 4์ข |
frontend/ |
๊ทธ๋ํ๊ฐ ์ค์๊ฐ์ผ๋ก ์ผ์ง๋ ๊ณผ์ ์ ๋ณด์ฌ ์ฃผ๋ React ๋์๋ณด๋ |
๊ตฌ์ฑ ์์๋ณ ์์ธํ ์ค๋ช
๊ณผ ์ง์ผ์ผ ํ ๋ถ๋ณ์(invariant)์ CLAUDE.md์
์ ๋ฆฌํด ๋์ต๋๋ค.
์ค๋น๋ฌผ: uv(Python 3.11), Node 18+, Claude ๊ตฌ๋
๋๋
API ํค, ๊ทธ๋ฆฌ๊ณ ์๋ฒ ๋ฉ์ ์ฐ๋ ค๋ฉด ollama + bge-m3(์ ํ).
cp .env.example .env # ์ ํ: ANTHROPIC_API_KEY ์ค์ , ์์ผ๋ฉด ๊ตฌ๋
์ธ์ฆ
make setup # uv sync
make ingest # knowledge/raw/ โ kb.sqlite
./start.sh # FastMCP + ๋ธ๋ฆฌ์ง, ํ๋ฐํธ ๋น๋, ๋ธ๋ผ์ฐ์ ์๋ ์ด๋ฆผ./start.sh๋ ํ๋ฐํธ์๋๋ฅผ ๋น๋ํด์ ๋ธ๋ฆฌ์ง๊ฐ ๊ฐ์ด ๋์ ์ค๋๋ค. ๊ทธ๋์ ๋ธ๋ผ์ฐ์ ์์
http://localhost:8787 ํ ๊ณณ์ผ๋ก ๋์๋ณด๋์ ๋ค์ด๊ฐ๋๋ค(์๋์ผ๋ก ์ด๋ฆฌ๊ณ , ์์น
์์ผ๋ฉด ./start.sh --no-open). ์์ชฝ ํญ์ผ๋ก ์ธ ํ๋ฉด์ ์ค๊ฐ๋๋ค.
| ํ๋ฉด | ๋ฌด์์ ๋ณด๋ |
|---|---|
| ํ๋ก์ธ์ค | ์ง๋ฌธ์ ๋ฃ์ผ๋ฉด ๋ ธ๋ ๊ทธ๋ํ๊ฐ ์ค์๊ฐ์ผ๋ก ์ผ์ง๊ณ , ์ธ์ฉ๋ ๊ทผ๊ฑฐยท๊ฒ์ฆ ํ์ ์ด ๋ํ๋๋ ๋ฉ์ธ ํ๋ฉด |
| ์ง์์ฒด๊ณ | ์ ์ฌ๋ ๋ฌธ์ยท์ฒญํฌยทํฉํธ๋ฅผ ํ์ด๋ณด๊ณ ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์์ ์ง์ ๋์ ธ ๋ณด๋ ํ๋ฉด |
| ๊ฐ๋๋ ์ผ | ๋๊ตฌ ์ ์ฑ ๊ท์น์ ์ผ๊ณ ๋๊ณ , ์ง๊ธ๊น์ง์ ํ์ฉยท์ฐจ๋จ ๋ก๊ทธ๋ฅผ ๋ณด๋ ํ๋ฉด |
ํ๋ฐํธ์๋๋ง ๋ฐ๋ก ๋ง์ง ๋๋ HMR์ด ๋ถ์ ๊ฐ๋ฐ ์๋ฒ๊ฐ ํธํฉ๋๋ค. ./start.sh --dev๋ฅผ
์ฐ๋ฉด Vite๊ฐ http://localhost:5173 ์ ๋จ๊ณ , /ws๋ ๋ธ๋ฆฌ์ง๋ก ํ๋ก์๋ฉ๋๋ค.
๋์๋ณด๋์์ ์ง๋ฌธ์ ๋ฃ์ผ๋ฉด ๊ทธ๋ํ๊ฐ ๋๋ ๋ชจ์ต์ ๋ณผ ์ ์์ต๋๋ค. ํ๋ฉด ์์ด ํ์ธํ๊ณ ์ถ๋ค๋ฉด:
python scripts/graph_probe.py "๋ฌผ์ง A์ 1์ผ ์ต๋ ์ทจ๊ธ๋๊ณผ ์ธํ์ ์?"
curl -s localhost:8787/api/kb/statsknowledge/raw/์ ์ ์ ๋ฆฌ๋.mdํ์ผ์ ๋ฃ์ต๋๋ค. (PDF ํ์๋ ์ผ๋ถ๋ฌ ๋ฃ์ง ์์์ผ๋, ๋จผ์ ๋งํฌ๋ค์ด์ผ๋ก ๋ฐ๊ฟ ์ฃผ์ธ์.)scenario.md๋ฅผ ๋ค์ ์๋๋ค(์์ฝ/๋์/๊ธ์ง +์ ๋ฌด ๋ฐฉ์:๋ถ๋ฆฟ). ์ด ๋ด์ฉ์ด ์คํํ ๋ ๊ทธ๋ํ ํ๋กฌํํธ๋ก ๋ค์ด๊ฐ๋๋ค.make ingestํ ๋ค์ ์์ํฉ๋๋ค.
์ด๋ ๊ฒ ๋ง๋ ๋ฒ๋ค์ scenarios/<์ด๋ฆ>/์ ์ค๋
์ท์ผ๋ก ์ ์ฅํด ๋๊ณ ํ์ํ ๋ ๊ฐ์ ๋ผ์ธ
์ ์์ต๋๋ค. ๊ธฐ๋ณธ์ผ๋ก ๋ค์ด ์๋ scenarios/chemical/์ด ๊ทธ ์์์
๋๋ค.
MIT โ LICENSE ์ฐธ๊ณ .