- AG-UI Serialization์ ์์ด์ ํธโUI ์ธ์ ์ ์ด๋ฒคํธ ์คํธ๋ฆผ์ ์์/๋ณต์ํ๊ธฐ ์ํ ํ์ค ๋ชจ๋ธ
- BaseEvent ์ํ์ค๋ฅผ JSON ๋ฑ portable ํฌ๋งท์ผ๋ก ์ ์ฅยท์ฌ์ํ๋ ์ด๋ฒคํธ ์์ฑ(Event Sourcing) ๊ธฐ๋ฐ ์์ํ ๊ณ์ฝ
- ์๋ก๊ณ ์นจยท์ฌ์ ์ยท๊ณต์ ์์๋ ๋ํยท์ํยทUI๋ฅผ ๊ทธ๋๋ก ๋ณต์ํ๋ ์ธ์ ์ฌํ ๋ฉ์ปค๋์ฆ
parentRunId๋ก git-like ๊ณ๋ณด๋ฅผ ๋ง๋๋ append-only branching ๋ก๊ทธ- ์๋ฏธ๋ฅผ ๋ณด์กดํ๋ฉด์ ์คํธ๋ฆผ ๊ธธ์ด๋ฅผ ์ค์ด๋ ์ด๋ฒคํธ ์์ถ(Compaction) ๊ท์ฝ
ํด๋น ๊ฐ๋ ์ด ํ์ํ ์ด์
- ์๋ก๊ณ ์นจ/์ฌ์ ์ ์ ๋ํยทUI ์ํ๊ฐ ์ฌ๋ผ์ง๋ ๋ฌธ์ โ ํด๋ผ์ด์ธํธ/์๋ฒ ์ด๋์๋ ๊ฐ์ ์คํธ๋ฆผ์ ๋ณต์ ๊ฐ๋ฅํด์ผ ํจ
- ๊ฐ์ thread ์์์ โ๋ค๋ฅธ ๋ต์ ์๋ํด๋ณด๊ณ ๋น๊ตโํ๋ ์ํฌํ๋ก์ฐ โ
parentRunId๊ธฐ๋ฐ ๋ถ๊ธฐ + ์๊ฐ ์ฌํ ํ์ - streaming delta(
TEXT_MESSAGE_CONTENT,STATE_DELTA,TOOL_CALL_ARGS) ๋์ ์ผ๋ก ์ธํ ์ ์ฅ/๋คํธ์ํฌ ๋น์ฉ ํญ์ฆ โ ์๋ฏธ๋ฅผ ๋ณด์กดํ ์์ถ ํ์ - run ๋จ์๋ก โ์์ด์ ํธ์๊ฒ ์ ํํ ๋ฌด์์ด ์ ๋ ฅ๋์๋๊ฐโ๋ฅผ ๋ช ์ ๊ธฐ๋ก โ ์ฌํ ๊ฐ๋ฅ์ฑ(reproducibility) ๋ณด์ฅ
- ์คํ ์ค ์์ด์ ํธ์ **์ฌ์ ์(attach)**ํ์ฌ ๋๊น ์์ด ์ด๋ฒคํธ๋ฅผ ์ด์ด ๋ฐ๊ธฐ ์ํ ํ์คํ๋ ๋ฐฑํ(replay) ๊ฒฝ๋ก ํ์
AS-IS โ ํ๋ฐ์ฑ in-memory ์คํธ๋ฆผ
sequenceDiagram autonumber participant UI participant Agent as AG-UI Agent participant Mem as In-Memory Stream UI->>Agent: runAgent(input) Agent-->>Mem: BaseEvent[] (ํ๋ฐ์ฑ) Note over Mem: ์๋ก๊ณ ์นจ / ํฌ๋์ / ํญ ์ ํ<br/>= ์ ์ฒด ์คํธ๋ฆผ ์์ค UI--xMem: reload (ํ์คํ ๋ฆฌ ๋ณต๊ตฌ ๋ถ๊ฐ) UI--xAgent: ์ฌ์ ์ ์ mid-run ์ด๋ฒคํธ ๋ฐ์ ๋ฐฉ๋ฒ ์์
TO-BE โ ์ง๋ ฌํ๋ append-only ์คํธ๋ฆผ
sequenceDiagram autonumber participant UI participant Agent as AG-UI Agent participant Store as Append-Only Storage UI->>Agent: runAgent(input, parentRunId?) Agent-->>Store: BaseEvent append (JSON ์ง๋ ฌํ) UI->>Store: load(threadId) Store-->>UI: serialized events UI->>UI: compactEvents() โ snapshot ๋ณต์ UI->>UI: ์์ runId ์ ํ โ ์๊ฐ ์ฌํ / ๋ถ๊ธฐ
ํต์ฌ 3๊ฐ์ง ๋ฉ์ปค๋์ฆ
1. Stream Serialization โ ์ด๋ฒคํธ ํ์คํ ๋ฆฌ ์์ํ
- BaseEvent ๋ฐฐ์ด ์ ์ฒด๋ฅผ JSON ๋ฑ portable ํฌ๋งท์ผ๋ก ์ ์ฅ โ DBยทํ์ผยท๋ก๊ทธ ์ด๋๋ ๋ณด๊ด
- ํต์ฌ์ **โ์ด๋ฒคํธ ์์ฒด๊ฐ ์ง์ค(source of truth)โ**์ด๋ผ๋ ์ด๋ฒคํธ ์์ฑ ๋ชจ๋ธ
threadId,runId, timestamp ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํด ๋น ๋ฅธ ์กฐํ ๊ฐ๋ฅ- ํด๋ผ์ด์ธํธ ์ฌ๋ก๋ / ๋ค๋ฅธ ๋๋ฐ์ด์ค ์ ์ / ๊ณต์ ๋งํฌ ๋ชจ๋ ๋์ผ ๋ฉ์ปค๋์ฆ์ผ๋ก ์ฒ๋ฆฌ
2. Event Compaction โ ์๋ฏธ ๋ณด์กด ์์ถ
streaming delta๊ฐ ๊ทธ๋๋ก ์์ด๋ฉด ๋น์ฉ์ด ํญ์ฆํ๋ค. Compaction์ ๊ด์ฐฐ ๊ฐ๋ฅํ ๊ฒฐ๊ณผ(observable outcome)๋ ์ ์งํ๋ฉด์ ์ด๋ฒคํธ ์๋ฅผ ์ค์ธ๋ค.
| ๋์ | Before | After |
|---|---|---|
| ๋ฉ์์ง | TEXT_MESSAGE_START + N๊ฐ CONTENT(delta) + END | ๋จ์ผ MESSAGES_SNAPSHOT |
| ๋๊ตฌ ํธ์ถ | TOOL_CALL_START + ARGS(delta)* + END + RESULT | ์์ถ๋ ๋จ์ผ tool record (TOOL_CALL_RESULT ํฌํจ ํํ) |
| ์ํ | ์ฐ์ STATE_DELTA (JSON Patch) | ์ต์ข STATE_SNAPSHOT |
| run ์ ๋ ฅ | RunStarted.input.messages์ ์ค๋ณต๋ ๋ฉ์์ง | ์ด์ ์คํธ๋ฆผ์ ์ด๋ฏธ ์๋ ๋ฉ์์ง๋ ์ ๊ฑฐ |
์์ธํ Tool ์ด๋ฒคํธ ํ๋ฆ๊ณผ Message ์ด๋ฒคํธ ํ๋ฆ์ ๊ฐ ๊ฐ๋ ๋ฌธ์๋ฅผ ์ฐธ๊ณ .
3. Run Lineage โ parentRunId ๊ธฐ๋ฐ git-like ๊ณ๋ณด
- ๊ฐ
RUN_STARTED๊ฐ ์ด๋ค run์์ ๋ถ๊ธฐํ๋์งparentRunId๋ก ๋ช ์ - ํ thread ์์ ์ฌ๋ฌ ๋ถ๊ธฐ๋ฅผ ๋์์ ๋ณด๊ด ๊ฐ๋ฅ (ํธ๋ฆฌ/DAG)
- ๋ก๊ทธ๋ immutable & append-only โ ๊ฒฐ๊ณผ์ ์ผ๋ก ๊ฒฐ์ ์ ์๊ฐ ์ฌํ๊ณผ ์ฌํ ๊ฐ๋ฅํ ์ธ์ ๊ณต์ ๊ฐ ๋์์ ์ฑ๋ฆฝ
RunStarted ํ์ฅ ํ๋
AG-UI Event์ lifecycle ์ด๋ฒคํธ ์ค RUN_STARTED๊ฐ ์ง๋ ฌํ/๋ถ๊ธฐ๋ฅผ ์ํด ๋ค์ ํ๋๋ฅผ ์ถ๊ฐ๋ก ๊ฐ๋๋ค.
type RunStartedEvent = BaseEvent & {
type: EventType.RUN_STARTED
threadId: string
runId: string
/** ๊ฐ์ thread ์์์ ์ด๋ค run์ ๋ถ๋ชจ๋ก ๊ฐ์ง์น๊ธฐ ํ๋์ง */
parentRunId?: string
/** ์ด๋ฒ run์ Agent์๊ฒ ์ ํํ ์ ๋ฌ๋ ์
๋ ฅ
* (์ด๋ฏธ ํ์คํ ๋ฆฌ์ ์๋ ๋ฉ์์ง๋ ์๋ต ๊ฐ๋ฅ โ Normalized Input) */
input?: AgentInput
}ํต์ฌ์ **โrun ์ ๋ ฅ์ ๋ช ์ ๊ธฐ๋กโ**์ด๋ค. ๋ฉ์์ง๊ฐ ๋์ ๋๋ ํ๊ฒฝ์์, โ์ด๋ฒ run์ ์ ํํ ๋ฌด์์ด ๋ค์ด๊ฐ๋๊ฐโ๋ฅผ ์ฌํ์ ์๊ธฐ ์ด๋ ต๊ธฐ ๋๋ฌธ.
์ด๋ฒคํธ ์์ถ(Compaction) ์์
Before โ streaming raw events
[
{ type: "TEXT_MESSAGE_START", messageId: "msg1", role: "user" },
{ type: "TEXT_MESSAGE_CONTENT", messageId: "msg1", delta: "Hello " },
{ type: "TEXT_MESSAGE_CONTENT", messageId: "msg1", delta: "world" },
{ type: "TEXT_MESSAGE_END", messageId: "msg1" },
{ type: "STATE_DELTA", patch: { op: "add", path: "/foo", value: 1 } },
{ type: "STATE_DELTA", patch: { op: "replace", path: "/foo", value: 2 } },
]After โ compacted snapshots
[
{
type: "MESSAGES_SNAPSHOT",
messages: [{ id: "msg1", role: "user", content: "Hello world" }],
},
{
type: "STATE_SNAPSHOT",
state: { foo: 2 },
},
]์์ถ ํ์๋ ๊ด์ฐฐ์๊ฐ ๋ณด๋ ์ต์ข ๊ฒฐ๊ณผ๋ ๋์ผํ๋ค. ๋ค๋ง ์ค๊ฐ streaming์ ์๊ฐ์ ํจ๊ณผ(ํ์ดํ ์ ๋๋ฉ์ด์ ๋ฑ)๋ ์ฌ๋ผ์ง๋ฏ๋ก, โlive ์ฌ์โ ์ฉ๋์๋ raw, โ์ฅ๊ธฐ ๋ณด๊ดโ์ฉ์๋ compacted๋ฅผ ์ ํ์ ์ผ๋ก ์ฌ์ฉํ๋ค.
๋ถ๊ธฐ(Branching)์ ์๊ฐ ์ฌํ
parentRunId๋ง์ผ๋ก git-like ๊ทธ๋ํ๊ฐ ์์ฐ ๋ฐ์ํ๋ค.
gitGraph commit id: "run1" commit id: "run2" branch alternative checkout alternative commit id: "run3 (parent run2)" commit id: "run4" checkout main commit id: "run5 (parent run2)" commit id: "run6"
๋ถ๊ธฐ ์ฌ์ฉ ์ฌ๋ก
- ๊ฐ์ ์ง๋ฌธ์ ๋ํ ๋์ ์๋ต ๋น๊ต โ A/B ๋น๊ต, โRegenerateโ UX์ ๊ทผ๊ฐ
- Human-in-the-Loop ์ํฌํ๋ก์ฐ์์ ์ฌ์ฉ์๊ฐ ๋ค๋ฅธ ๊ฒฐ์ ์ ๋ด๋ ธ์ ๋์ what-if ํ์
- ๋๋ฒ๊น ์ โ๋ฒ๊ทธ ๋ฐ์ ์ง์ run์ผ๋ก ๋๋์๊ฐ์ ๋ค์ ์คํโ โ ๊ฒฐ์ ์ ์ฌํ
์๊ฐ ์ฌํ์ด ๊ฐ๋ฅํ ์ด์
- ๋ก๊ทธ๊ฐ append-only์ด๋ฏ๋ก ์์ ์์ ์ ์ค๋ ์ท์ ๊ฒฐ์ ์ ์ผ๋ก ์ฌ๊ตฌ์ฑ ๊ฐ๋ฅ
- run ์
๋ ฅ์ด
RunStarted.input์ ๋ช ์๋์ด ์์ด, ๊ทธ ์์ ์ ์ปจํ ์คํธ๋ฅผ ์ ํํ ๋ณต์
// ๋ถ๊ธฐ ์์
{ type: "RUN_STARTED", threadId: "t1", runId: "run1",
input: { messages: ["Tell me about Paris"] } }
// run1์์ ๋ถ๊ธฐ
{ type: "RUN_STARTED", threadId: "t1", runId: "run2",
parentRunId: "run1",
input: { messages: ["Actually, tell me about London instead"] } }์ ๊ทํ๋ ์ ๋ ฅ (Normalized Input)
๊ฐ์ thread์์ run์ ๊ฑฐ๋ญํ๋ฉด ๋ฉ์์ง๊ฐ ๋์ ๋๋ค. ๋ ๋ฒ์งธ run์์ ์ฒซ run์ ๋ฉ์์ง๋ฅผ ๋ input.messages์ ๋ด์ผ๋ฉด ์ค๋ณต ์ ์ฅ์ด ๋๋ค. ๋ฐ๋ผ์ ์ด๋ฏธ ์คํธ๋ฆผ์ ์กด์ฌํ๋ ๋ฉ์์ง๋ ์๋ตํ๋ค.
// run1: ์ฒซ ๋ฉ์์ง๋ฅผ ํ๋ก ๊ธฐ๋ก
{ type: "RUN_STARTED", runId: "run1",
input: { messages: [{ id: "msg1", role: "user", content: "Hello" }] } }
// run2: msg1์ ์ด๋ฏธ history์ ์์ผ๋ฏ๋ก input์์ ์ ์ธ
{ type: "RUN_STARTED", runId: "run2",
input: { messages: [{ id: "msg2", role: "user", content: "How are you?" }] } }์ด ๊ท์น์ ๋ฉ์์ง ๋ชจ๋ธ์ โ์ด๋ฒคํธ(ํ๋ฆ) โ ๋ฉ์์ง(์ํ) ๋ถ๋ฆฌโ ์ฒ ํ๊ณผ ๋ง๋ฌผ๋ฆฐ๋ค โ ๋ฉ์์ง๋ ๋ ๋ฆฝ๋ ์์ ๋จ์์ด๋ฏ๋ก ๋์ผ ๋ฉ์์ง๊ฐ ์ฌ๋ฌ run์ input์ ์ค๋ณต๋ ํ์๊ฐ ์๋ค.
๋ชจ๋ธ ๊ต์ฒด์ ์ปจํ ์คํธ ๋ณด์กด
๋ฉ์์ง๊ฐ ๋ฒค๋ ์ค๋ฆฝ ์์ ๋จ์๋ผ๋ ์ฌ์ค์ ์ง์ ์ ๊ท๊ฒฐ: ๊ฐ์ thread ์์์ LLM ๋ชจ๋ธ์ ๋ฐ๊ฟ๋ ํ์คํ ๋ฆฌ๋ ๋ ์๊ฐ์ง ์๋๋ค. AG-UI ๊ณต์ ๋ฌธ์๊ฐ ์ด ์๋๋ฆฌ์ค๋ฅผ ๋ช ์์ ์ค๊ณ ๋ชฉํ๋ก ์ผ๋๋ค.
โAG-UI messages are designed to be vendor-neutralโฆ a common interface regardless of the underlying AI service.โ โ messages.mdx
์๋๋ฆฌ์ค โ ๊ฐ์ thread์์ ๋ชจ๋ธ A โ B ์ ํ
ํ์ ์๋ด thread์์ ์ฒซ 5ํด์ OpenAI GPT-4o๋ก ์ฒ๋ฆฌํ๋ค๊ฐ, 6ํด๋ถํฐ ๋น์ฉ ๋ฌธ์ ๋ก Anthropic Claude๋ก ๋ผ์ฐํ ์ ๋ฐ๊พธ๋ ์ํฉ. 6ํด์งธ์ ์ฌ์ฉ์๊ฐ โ์ง๊ธ๊น์ง ๋ํ ์์ฝํด์คโ๋ผ๊ณ ๋ฌป๋๋ค.
flowchart TD subgraph Persist["์์ ๊ณ์ธต โ ๋ฒค๋ ๋ฌด๊ด"] Thread["threadId t-1<br/>MESSAGES_SNAPSHOT (1~5ํด)<br/>STATE_SNAPSHOT"] end subgraph Run5["Run 5 โ ๋ชจ๋ธ A"] IN5[RunAgentInput] --> ADP_A[OpenAI ์ด๋ํฐ<br/>aguiToOpenAI] ADP_A --> LLM_A[GPT-4o] end subgraph Run6["Run 6 โ ๋ชจ๋ธ B ๊ฐ์ thread"] IN6[RunAgentInput<br/>๊ฐ์ messages ๋ฐฐ์ด] --> ADP_B[Anthropic ์ด๋ํฐ<br/>aguiToAnthropic] ADP_B --> LLM_B[Claude] end Thread --> IN5 Thread --> IN6 LLM_A -- "RUN_FINISHED ํ ๋์ " --> Thread LLM_B -- "์ด์ 5ํด์ ์ปจํ ์คํธ๋ก ๋ฐ์ ์์ฝ ๊ฐ๋ฅ" --> Thread
ํต์ฌ์ RunAgentInput.messages๊ฐ ๋ชจ๋ธ ๋
๋ฆฝ์ ์ธ ๋์ผ ๋ฐฐ์ด์ด๋ผ๋ ์ ์ด๋ค. ๋ชจ๋ธ๋ณ ๋ณํ์ ์ด๋ํฐ๊ฐ ๋งค๋ฒ ๋ค๋ฅด๊ฒ ์ ์ฉํ ๋ฟ์ด๋ค.
# ํต์ฌ ์๋ ์ฝ๋ โ AbstractAgent ๋ด๋ถ ๋ผ์ฐํ
def run(input: RunAgentInput):
messages = input.messages # ๋ฒค๋ ์ค๋ฆฝ AG-UI ๋ฉ์์ง
if route_to_b(input):
vendor_msgs = agui_to_anthropic(messages)
stream = anthropic.stream(vendor_msgs)
else:
vendor_msgs = agui_to_openai(messages)
stream = openai.stream(vendor_msgs)
yield from to_agui_events(stream) # ๊ฒฐ๊ณผ๋ ๋ค์ AG-UI ์ด๋ฒคํธ๋ก ํ์๋ณด์กด๋๋ ๊ฒ vs ์ ์ค๋๋ ๊ฒ
| ํญ๋ชฉ | ๋ชจ๋ธ ๊ต์ฒด ์ | ์ด์ |
|---|---|---|
user / assistant / tool ๋ฉ์์ง ๋ณธ๋ฌธ | ๋ณด์กด | ๋ฒค๋ ์ค๋ฆฝ ํฌ๋งท |
STATE_SNAPSHOT / STATE_DELTA | ๋ณด์กด | JSON ๊ฐ์ฒด, ๋ชจ๋ธ ๋ฌด๊ด |
threadId / runId / parentRunId lineage | ๋ณด์กด | AG-UI ๋ ์ด์ด๊ฐ ๊ด๋ฆฌ |
| Reasoning ๋ฉ์์ง | ๋ถ๋ถ ๋ณด์กด | role: "reasoning"์ ํ์คํ๋๋, ๋ชจ๋ธ๋ณ reasoning ์๋ช
ยท์ธ๋ถ ๊ตฌ์กฐ๋ ์์ค ๊ฐ๋ฅ |
| Prompt cache key | ๋ฌดํจํ | ์บ์๋ ๋ชจ๋ธ๋ณ โ ์ ๋ชจ๋ธ์์ hit ๋ถ๊ฐ |
| ๋ฒค๋ ์ ์ฉ tool ๋ฉํ | ๋ณํ ํ์ | AG-UI Tools๋ JSON Schema ๊ธฐ๋ฐ์ด๋ผ ๋๋ถ๋ถ ํธํ |
๋ฐ๋ผ์ ๋ชจ๋ธ ์ ํ์ โ์ปจํ ์คํธ ์ ์คโ๋ณด๋ค๋ โ์บ์ยท๋ฏธ์ธ ๋ฉํ ๋ฆฌ์ โ ๊ด์ ์ผ๋ก ์ดํดํด์ผ ํ๋ค.
์ฅ๊ธฐ ๊ธฐ์ต๊ณผ RAG ํตํฉ
thread ๋ด๋ถ ํ์คํ ๋ฆฌ๋ ์ ๋ฉ์ปค๋์ฆ์ผ๋ก ๋ณด์กด๋์ง๋ง, ์๊ฐ์ยท์๋
์น ๋ํ๋ฅผ ๋ชจ๋ ๋งค run์ messages์ ๋ฃ๋ ๊ฑด ๋นํ์ค์ ์ด๋ค. AG-UI๋ thread ์ธ๋ถ์ long-term memory๋ฅผ capability flag๋ก ์ธ์งํ๋ค.
interface StateCapabilities {
/** Set `true` if the agent has long-term memory beyond the current thread
* (e.g., vector store, knowledge base, or cross-session recall). */
memory?: boolean
persistentState?: boolean
}โ docs/concepts/capabilities.mdx
์ฆ vector DB๋ AG-UI ํ๋กํ ์ฝ์ด ์ง์ ์ ๊ณตํ์ง๋ ์์ง๋ง, โ์์ด์ ํธ๊ฐ ์ด๋ฐ ๋ฅ๋ ฅ์ ๊ฐ์งโ์ ํด๋ผ์ด์ธํธ์ ์ ์ธํ๋ ํ์ค์ด ์๋ค. ์ง๋ ฌํ๋ ์ด๋ฒคํธ ์คํธ๋ฆผ์ RAG ๋ฐฑ๋ณธ์ผ๋ก ์ฌํ์ฉํ๋ ๊ฒ์ ์ค๊ณ ์๋์ ์ ํํ ๋ถํฉํ๋ค.
RAG ํ์ดํ๋ผ์ธ
flowchart LR Stream[์ง๋ ฌํ๋ BaseEvent ๋ก๊ทธ] -->|compactEvents| Snap[MESSAGES_SNAPSHOT] Snap -->|user/assistant ์ถ์ถ + ํ๋ ์ด์ | Chunk[ํ ์คํธ ์ฒญํฌ] Chunk -->|embedding model| Emb[๋ฒกํฐ] Emb --> VDB[(Vector DB<br/>pgvector / Pinecone /<br/>Weaviate / Qdrant)] NewQ[์ user ๋ฉ์์ง] -->|query embedding| VDB VDB -->|top-K ์ ์ฌ ์ฒญํฌ| Inject[RunAgentInput.context] Inject --> Agent[AG-UI Agent]
ํต์ฌ์ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ messages์ ์ง์ prependํ์ง ์๊ณ Context ์ฌ๋กฏ์ ์ฃผ์
ํ๋ค๋ ๊ฒ์ด๋ค. AG-UI์ ์ด๋ฏธ ํ์ค ์ฌ๋กฏ์ด ์๋ค.
# ํต์ฌ ์๋ ์ฝ๋ โ RAG ์ปจํ
์คํธ ์ฃผ์
hits = vector_db.search(embed(latest_user_msg), top_k=5)
input = RunAgentInput(
thread_id="t-1",
run_id="r-42",
messages=current_thread_messages, # ์ ๊ทํ๋ ์งง์ ํ์คํ ๋ฆฌ
context=[
Context(description=f"recall_{i}", value=h.text)
for i, h in enumerate(hits) # โ ์ฅ๊ธฐ ๊ธฐ์ต ์ฃผ์
],
state={}, tools=[], forwarded_props={},
)์๋๋ฆฌ์ค โ 1๋ ์น ๊ณ ๊ฐ ์๋ด RAG
๊ฐ์ ์ฌ์ฉ์๊ฐ 6๊ฐ์ ์ ํ๋ถ ๋ฌธ์๋ฅผ ํ๊ณ , ์ค๋ ๋ค์ ๋น์ทํ ์ผ์ด์ค๋ก ์ ์. compaction๋ ๊ณผ๊ฑฐ ์ด๋ฒคํธ ๋ก๊ทธ์์ user/assistant ๋ฉ์์ง๋ง ์ถ์ถํด ์๋ฒ ๋ฉํ ์ํ๋ผ๋ฉด:
- ์ค๋ ์ ๋ฉ์์ง์ ์๋ฒ ๋ฉ์ผ๋ก vector DB ์ง์
- ์ ์ฌ๋ top-3 ์ฒญํฌ๊ฐ
Context๋ก ์ฃผ์ - ์์ด์ ํธ๋ 6๊ฐ์ ์ ๊ฒฐ์ ์ฌ์ ๊น์ง ํ์ ํ ์ฑ๋ก ์๋ต ์์ฑ
ํ๋ ์ด์ ๊ฐ์ด๋
| ๋ฉ์์ง role | ์๋ฒ ๋ฉ ๋์ |
|---|---|
user, assistant | ๊ถ์ฅ โ ์๋/๊ฒฐ์ ์ ํต์ฌ |
tool | ์ ํ์ โ ์งง์ ๊ฒฐ๊ณผ๋ง, ๊ธด raw payload๋ ๋ ธ์ด์ฆ |
reasoning | ์ ํ์ โ ์์ฌ๊ฒฐ์ ์ถ์ ์ฉ์ผ๋ก๋ง |
activity | ๋น๊ถ์ฅ โ UI ์งํ ์ํฉ์ ๊ฒ์ ๊ฐ์น ๋ฎ์ |
system, developer | ๋น๊ถ์ฅ โ ์ ์ ํ๋กฌํํธ, ๋งค๋ฒ ๋ณ๋ ์ฃผ์ |
Storage ๋ฐฑ์๋ โ ํ๋กํ ์ฝ์ ๊ฒฝ๊ณ
์ ๋ ๋ฉ์ปค๋์ฆ(์ปจํ ์คํธ ๋ณด์กด + RAG)์ ๋ชจ๋ โ์ด๋๊ฐ์ ์ง๋ ฌํ๋ ์ด๋ฒคํธ๊ฐ ์ ์ฅ๋ผ ์๋คโ๋ฅผ ์ ์ ํ๋ค. ๊ทธ โ์ด๋๊ฐโ๋ AG-UI๊ฐ ์ ๊ณตํ์ง ์๋๋ค.
โConversation history is stored only in memory. If the application restarts, all history is lost. For persistence, you must implement your own storage solution by retrieving conversations using
getHistory()and saving them externally.โ โ docs/sdk/kotlin/client/stateful-agui-agent.mdx
์ฆ SQLiteยทPostgresยทRedis ๊ฐ์ ํน์ DB๊ฐ SDK์ ๋ฐํ ์์ง ์๋ค. ํ๋กํ ์ฝ์ ์ง๋ ฌํ ํ์๊ณผ ๊ถ์ฅ์ฌํญ๋ง ์ ๊ณตํ๊ณ , storage ์ด๋ํฐ๋ ์ฌ์ฉ์/integration์ด ๋ถ์ธ๋ค.
์ด๋ํฐ ํจํด
flowchart LR Bus[Event Bus] -->|"save(threadId, events)"| Adapter[StorageAdapter<br/>์ธํฐํ์ด์ค] Adapter --> A[(Postgres<br/>JSONB events ์ปฌ๋ผ)] Adapter --> B[(S3<br/>append-only ํ์ผ)] Adapter --> C[(Redis Streams<br/>์ค์๊ฐ + ์งง์ ๋ณด๊ด)] Adapter --> D[(ClickHouse<br/>๋ถ์/์ง๊ณ)] Adapter --> E[(Vector DB<br/>RAG์ฉ ์ฌ์ด๋์นด)]
# ํต์ฌ ์๋ ์ฝ๋ โ ์ฌ์ฉ์๊ฐ ๊ตฌํํ๋ ์ด๋ํฐ ๊ณ์ฝ
class StorageAdapter(Protocol):
async def append(self, thread_id: str, event: BaseEvent) -> None: ...
async def load(self, thread_id: str) -> list[BaseEvent]: ...
async def compact(self, thread_id: str) -> None: ...๋ฐฑ์๋ ์ ํ ๊ฐ์ด๋
| ๋ฐฑ์๋ | ์ ํฉํ ๊ฒฝ์ฐ | ํธ๋ ์ด๋์คํ |
|---|---|---|
| Postgres + JSONB | ํธ๋์ญ์ ยท๊ด๊ณํ ์กฐ์ธ ํ์, ์ค๊ท๋ชจ | ์ธ๋ฑ์ค ์ค๊ณ ํ์, ๋งค์ฐ ๊ธด ์คํธ๋ฆผ์ row ๋น๋ |
| S3 / ํ์ผ append | ์ฅ๊ธฐ ๋ณด๊ดยท์ ๋น์ฉ cold storage | ๋ถ๋ถ ์กฐํ ์ด๋ ค์ โ compaction ํ snapshot์ผ๋ก ๋ณ๋ ๋ณด๊ด |
| Redis Streams | ์ค์๊ฐ attachยท์ฌ์ ์, ์งง์ ๋ณด๊ด | ์์์ฑ์ ๋ณด์กฐ์ฉ, ์ฅ๊ธฐ ๋ณด๊ด์๋ ๋ถ์ ํฉ |
| ClickHouse / BigQuery | ๋ถ์ยท์ง๊ณยท์๊ณ์ด ์ง์ | ๋จ๊ฑด ํธ๋์ญ์ ๋ถ์ ํฉ |
| Vector DB (์ฌ์ด๋์นด) | RAG ์ ์ฉ โ ์ RAG ์น์ ํจํด | ์๋ณธ ์ด๋ฒคํธ ์ ์ฅ์ ๋ชป ํจ, ๋ค๋ฅธ backend์ ๋ณํ ํ์ |
์ค์ ์์๋ 2-tier ์กฐํฉ์ด ํํ๋ค โ hot tier(Redis/Postgres)์ raw events, cold tier(S3/ClickHouse)์ compacted snapshots, ์ฌ์ด๋์นด๋ก vector DB.
๊ณต์ integration ์ฌ๋ก
- ADK middleware โ
POST /agents/state์๋ํฌ์ธํธ๋ก thread๋ณ messages/state ์กฐํ. ๋ด๋ถ storage๋ ADK ์ธก ์์ ์ ํ. - ADK + VertexAIMemoryService โ ์ธ์
๋ง๋ฃ ์
memory_service.add_session_to_memory()์๋ ํธ์ถ โ ์ฌ์ค์ ์๋ RAG ๋ฐฑํ - Kotlin
getHistory(threadId)โ ๋ฉ๋ชจ๋ฆฌ ์ ์ฉ. ์์ํ ์ฑ ์์ ํธ์ถ์.
์ง๋ ฌํ ํ๋ฆ โ End-to-End
sequenceDiagram autonumber participant UI participant Store as Storage (append-only) participant Bus as Event Bus participant Agent as AbstractAgent Agent-->>Bus: RUN_STARTED (threadId, runId, parentRunId?, input?) Agent-->>Bus: TEXT_MESSAGE_START / CONTENT(delta)* / END Agent-->>Bus: STATE_DELTA (JSON Patch) ... Agent-->>Bus: TOOL_CALL_START / ARGS / END / RESULT Agent-->>Bus: RUN_FINISHED Bus->>Store: append events (JSON.stringify) Note over Store: ์ ์ฑ ์ ๋ฐ๋ผ ์ฃผ๊ธฐ์ ์ผ๋ก compactEvents() ์ ์ฉ UI->>Store: load(threadId) Store-->>UI: serialized events UI->>UI: compactEvents โ MESSAGES_SNAPSHOT / STATE_SNAPSHOT ๋ณต์ UI->>UI: ์์ runId ์ ํ โ ์๊ฐ ์ฌํ
๊ธฐ๋ณธ ์ฝ๋ ์์
// ์ง๋ ฌํ (์ ์ฅ)
const events: BaseEvent[] = [...];
const serialized = JSON.stringify(events);
await storage.save(threadId, serialized);
// ๋ณต์ + ์์ถ
const restored = JSON.parse(await storage.load(threadId));
const compacted = compactEvents(restored);
// compacted ์คํธ๋ฆผ์ผ๋ก UI ๋ค์ ๊ทธ๋ฆฌ๊ธฐ
ui.render(compacted);๋ค๋ฅธ AG-UI ๊ฐ๋ ๊ณผ์ ๊ด๊ณ
- AG-UI Event โ Serialization์ ๊ฒฐ๊ตญ ์ด๋ฒคํธ ์ํ์ค์ ์์ํ๋ค. 28๊ฐ ์ด๋ฒคํธ ํ์ ์์ฒด๊ฐ ์ง๋ ฌํ์ ๋จ์.
- AG-UI Messages โ
TEXT_MESSAGE_*์คํธ๋ฆผ์ด ์์ถ๋์ดMESSAGES_SNAPSHOT์ด ๋๋ค. ๋ฉ์์ง๋ ์์ ์ํ, ์ด๋ฒคํธ๋ ํ๋ฆ. - AG-UI State Management โ
STATE_DELTA(JSON Patch RFC 6902)๊ฐ ๋์ ๋์ด ์ต์ขSTATE_SNAPSHOT์ผ๋ก ์์ถ๋๋ค. - AG-UI Tools โ
TOOL_CALL_START/ARGS/END/RESULTํ๋ฆ์ด ๋จ์ผ tool record๋ก ์์ถ ๊ฐ๋ฅ. - AG-UI Agents / AbstractAgent โ ์ง๋ ฌํ ๋์์
runAgent(input)๊ฒฐ๊ณผ ์ด๋ฒคํธ ์คํธ๋ฆผ.RunStarted.inputํ๋๊ฐ ์ ๋ ฅ์ ์ ํํ ๊ธฐ๋ก์ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
๊ตฌํ ์ ๊ณ ๋ ค์ฌํญ
- SDK ํฌํผ ์ ๊ณต:
(de)serialize,compactEvents๊ฐ์ ์ ํธ์ SDK ์ฐจ์์์ ์ ๊ณตํด์ผ ์ผ๊ด์ฑ ํ๋ณด - append-only ์ ์ฅ: ๊ฐ๋ฅํ๋ฉด incremental write โ ํ run์ด ๋๋ ๋๋ง๋ค append, ๋์ค ์์ ๊ธ์ง
- ์์ถ ์ ์ฑ : ์ค์๊ฐ ๋ณด๊ธฐ = raw, ์ฅ๊ธฐ ๋ณด๊ด = compacted, ๋๋ [hot=raw, cold=compacted] 2-tier ์ ์ฑ
- ์ธ๋ฑ์ฑ:
threadId,runId,parentRunId, timestamp์ ์ธ๋ฑ์ค โ ๋ถ๊ธฐ ๊ทธ๋ํ ํ์๊ณผ ๋ถ๋ถ ๋ก๋ ์ฑ๋ฅ ํ๋ณด - ์์ถ ์๊ณ ๋ฆฌ์ฆ: ์ฅ๊ธฐ ๋ณด๊ด ์ gzip/zstd ๋ฑ ํ์ด๋ก๋ ์์ถ ์ถ๊ฐ ๊ณ ๋ ค
- PII / ZDR: ๋ฉ์์ง์ ๋ฏผ๊ฐ ์ ๋ณด๊ฐ ํฌํจ๋ ์ ์์ผ๋ฏ๋ก ์ ์ฅ ์ redaction ๋๋ ํด๋ผ์ด์ธํธ ๋ณด๊ด ์ ์ฑ ๊ณ ๋ ค