• AG-UI Messages๋Š” ์—์ด์ „ํŠธ์™€ ์‚ฌ์šฉ์ž ๊ฐ„ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์˜์† ๋‹จ์œ„ ๊ฐ์ฒด
  • role ํ•„๋“œ๋ฅผ discriminator๋กœ ์‚ผ๋Š” tagged union ๊ตฌ์กฐ (user/assistant/system/developer/tool/activity/reasoning)
  • LLM ํ”„๋กœ๋ฐ”์ด๋”(OpenAI/Anthropic/Bedrock ๋“ฑ)์— ๋…๋ฆฝ์ ์ธ ๋ฒค๋” ์ค‘๋ฆฝ ํฌ๋งท
  • ์ŠคํŠธ๋ฆฌ๋ฐ ์ด๋ฒคํŠธ(TEXT_MESSAGE_*, TOOL_CALL_*, REASONING_*)๋ฅผ ๋ˆ„์ ํ•˜์—ฌ materialize๋˜๋Š” ์ตœ์ข… ์ƒํƒœ ์Šค๋ƒ…์ƒท
  • Generative UI ์ŠคํŽ™(A2UI/Open-JSON-UI/MCP-UI)์„ ์‹ค์–ด ๋‚˜๋ฅด๋Š” ์ˆ˜์†ก ๊ฒฝ๋กœ

ํ•ด๋‹น ๊ฐœ๋…์ด ํ•„์š”ํ•œ ์ด์œ 

  • LLM ํ”„๋กœ๋ฐ”์ด๋”๋งˆ๋‹ค ๋ฉ”์‹œ์ง€ ํฌ๋งท์ด ์ œ๊ฐ๊ฐ โ†’ ํด๋ผ์ด์–ธํŠธ ๋ณ€๊ฒฝ ์—†์ด ๋ฐฑ์—”๋“œ ๋ชจ๋ธ ๊ต์ฒด ๊ฐ€๋Šฅํ•ด์•ผ ํ•จ
  • ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต์„ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ๋กœ ๋ˆ„์ ํ•ด์•ผ ํ•จ โ†’ ์ด๋ฒคํŠธ(ํ๋ฆ„)์™€ ๋ฉ”์‹œ์ง€(์ƒํƒœ)์˜ ๋ถ„๋ฆฌ ํ•„์š”
  • ํˆด ํ˜ธ์ถœ / ๋‚ด๋ถ€ ์ถ”๋ก  / UI ์ƒํƒœ / ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ž…๋ ฅ ๋“ฑ ์ด์งˆ์  ์š”์†Œ๋ฅผ ํ•˜๋‚˜์˜ ๋Œ€ํ™” ํƒ€์ž„๋ผ์ธ์— ํ†ตํ•ฉ โ†’ 7๊ฐ€์ง€ role๋กœ ์˜๋ฏธ ๋ถ„๋ฆฌ
  • ZDR(Zero Data Retention) ๊ฐ™์€ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์ปดํ”Œ๋ผ์ด์–ธ์Šค ํ™˜๊ฒฝ์—์„œ ์„œ๋ฒ„๊ฐ€ ์ƒํƒœ ์ €์žฅ ๋ถˆ๊ฐ€ โ†’ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์•”ํ˜ธํ™”๋œ ์ƒํƒœ๋ฅผ ์บ๋ฆฌ์–ด๋กœ ์šด๋ฐ˜

AS-IS: ๋ฒค๋” ์ข…์†์ ์ธ ๋‹จ์ผ ํฌ๋งท

sequenceDiagram
    autonumber
    participant UI as Frontend
    participant API as OpenAI API

    UI->>API: OpenAI ์ „์šฉ ํฌ๋งท ์š”์ฒญ
    API-->>UI: OpenAI ์ „์šฉ ์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต
    Note over UI,API: Anthropic์œผ๋กœ ๊ฐˆ์•„ํƒ€๋ ค๋ฉด<br/>ํด๋ผ์ด์–ธํŠธ ์ „์ฒด ์ˆ˜์ •

TO-BE: AG-UI ์ถ”์ƒํ™” ๋ ˆ์ด์–ด (3๊ณ„์ธต ๋ถ„๋ฆฌ)

sequenceDiagram
    autonumber
    participant UI as Frontend
    participant AD as AG-UI Adapter
    participant FW as Framework
    participant LLM as Vendor LLM

    Note over UI,AD: Layer 1 - AG-UI Protocol ๊ตฌ๊ฐ„
    UI->>AD: AG-UI Message (๋ฒค๋” ์ค‘๋ฆฝ)

    Note over AD: ๋ณ€ํ™˜ 1 - Adapter ์ฑ…์ž„<br/>aguiMessagesToLangChain
    AD->>FW: Framework Format<br/>LangChain HumanMessage ๋“ฑ

    Note over FW: ๋ณ€ํ™˜ 2 - Framework ์ฑ…์ž„<br/>AG-UI ๋ฌด๊ด€
    FW->>LLM: Vendor API JSON<br/>OpenAI chat.completions ๋“ฑ

    LLM-->>FW: streaming chunks
    FW-->>AD: framework events
    AD-->>UI: AG-UI Events (ํ‘œ์ค€ ์ŠคํŠธ๋ฆผ)
    Note over UI,AD: ๋ชจ๋ธ ๋ณ€๊ฒฝ ์‹œ ํด๋ผ์ด์–ธํŠธ ๋ฌด๋ณ€๊ฒฝ

ํ•ต์‹ฌ: ํ”„๋ก ํŠธ์—”๋“œ๋Š” Layer 1(AG-UI Protocol) ๋งŒ ์•Œ๋ฉด ๋˜๊ณ , ๋‚ด๋ถ€์˜ ํ”„๋ ˆ์ž„์›Œํฌ/๋ฒค๋” ๋ณ€๊ฒฝ์€ Adapter ๊ต์ฒด๋งŒ์œผ๋กœ ํก์ˆ˜๋œ๋‹ค.

Message ํƒ€์ž… 7๊ฐ€์ง€ โ€” ์—ญํ• ๊ณผ ๊ฐ€์‹œ์„ฑ

RoleUI ํ‘œ์‹œLLM ์ „๋‹ฌ์šฉ๋„
userํ‘œ์‹œ์ „๋‹ฌ์‚ฌ์šฉ์ž ์ž…๋ ฅ (ํ…์ŠคํŠธ or ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ)
assistantํ‘œ์‹œ์ „๋‹ฌ (๋‹ค์Œ ํ„ด ์ปจํ…์ŠคํŠธ)AI ์‘๋‹ต + ํˆด ํ˜ธ์ถœ
system์ˆจ๊น€์ „๋‹ฌํ”Œ๋žซํผ ๋ ˆ๋ฒจ ์ง€์‹œ (๋ณด์•ˆ, ํ•ญ์ƒ ์ ์šฉ)
developer์ˆจ๊น€์ „๋‹ฌ์•ฑ ๋ ˆ๋ฒจ ์ง€์‹œ (system๋ณด๋‹ค ์•ฝํ•จ, user๋ณด๋‹ค ๊ฐ•ํ•จ)
tool์ปค์Šคํ…€์ „๋‹ฌํˆด ์‹คํ–‰ ๊ฒฐ๊ณผ (toolCallId๋กœ ๋งํฌ)
reasoning์„ ํƒ์  ์š”์•ฝ์ „๋‹ฌ (encryptedValue๋กœ)๋‚ด๋ถ€ chain-of-thought
activity์ปค์Šคํ…€ UI์ „๋‹ฌ ์•ˆ ํ•จํ”„๋ก ํŠธ์—”๋“œ ์ „์šฉ ์ƒํƒœ

types.ts:161-169 ์—์„œ z.discriminatedUnion("role", [...])๋กœ ๊ตฌํ˜„๋จ.

โ€Message ํƒ€์ž…โ€์ด ์˜๋ฏธํ•˜๋Š” ๊ฒƒ

AG-UI์—์„œ Message๋Š” **โ€œ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ์— ๋ˆ„์ ๋˜๋Š” ์˜์† ๊ฐ์ฒดโ€**๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ์‚ฌ์šฉ์ž์™€ ๋ฐฑ์—”๋“œ ์‚ฌ์ด๋ฅผ ์˜ค๊ฐ€๋Š” ํŠธ๋ž˜ํ”ฝ ์ „๋ถ€๋ฅผ ๋œปํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

AG-UI ๋ฐ์ดํ„ฐ ๋ถ„๋ฅ˜
โ”‚
โ”œโ”€ Message (์ƒํƒœ/๋ˆ„์ ) โ€” RunAgentInput.messages ๋ฐฐ์—ด์— ์Œ“์ž„
โ”‚   โ”œโ”€ LLM ์ „๋‹ฌ: user, assistant, system, developer, tool, reasoning
โ”‚   โ””โ”€ ํ”„๋ก ํŠธ์—”๋“œ ์ „์šฉ: activity
โ”‚
โ”œโ”€ Event (ํ๋ฆ„/์ผํšŒ์„ฑ) โ€” ํ†ต์‹  ์ค‘ ํ˜๋Ÿฌ๊ฐ€๋Š” ์‹ ํ˜ธ
โ”‚   โ”œโ”€ TEXT_MESSAGE_START/CONTENT/END
โ”‚   โ”œโ”€ TOOL_CALL_START/ARGS/END
โ”‚   โ”œโ”€ STATE_SNAPSHOT/DELTA
โ”‚   โ””โ”€ RUN_STARTED/FINISHED
โ”‚
โ””โ”€ ๊ธฐํƒ€
    โ”œโ”€ Tool (LLM์ด ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•œ ํ•จ์ˆ˜ ์Šคํ‚ค๋งˆ)
    โ””โ”€ Context (์ถ”๊ฐ€ ์ปจํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ)

Event๋Š” Message๋ฅผ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•œ ์ŠคํŠธ๋ฆผ ์กฐ๊ฐ์ด๊ณ , ์ด๋ฒคํŠธ ์‹œํ€€์Šค๊ฐ€ ๋ˆ„์ ๋˜์–ด ๊ฒฐ๊ตญ ํ•˜๋‚˜์˜ Message ๊ฐ์ฒด๋กœ materialize๋œ๋‹ค.

system / developer์˜ ์˜๋ฏธ

๋‘ role ๋ชจ๋‘ ์‚ฌ์šฉ์ž UI์— ๋…ธ์ถœ๋˜์ง€ ์•Š์œผ๋ฉฐ, LLM ์ž…๋ ฅ์šฉ ์ง€์‹œ์‚ฌํ•ญ์ด๋‹ค. OpenAI๊ฐ€ 2024๋…„๋ง ๋„์ž…ํ•œ instruction hierarchy ๊ฐœ๋…์ด ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜๋œ ์„ค๊ณ„๋กœ, ์šฐ์„ ์ˆœ์œ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์šฐ์„ ์ˆœ์œ„ (๋†’์Œ โ†’ ๋‚ฎ์Œ)
1. system    โ€” ํ”Œ๋žซํผ/OS ๋ ˆ๋ฒจ ๊ทœ์น™ (ํ•ญ์ƒ ์ ์šฉ๋˜๋Š” ์•ˆ์ „์žฅ์น˜)
2. developer โ€” ์•ฑ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ถ”๊ฐ€ํ•˜๋Š” ์ง€์‹œ (์„ธ์…˜/์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„)
3. user      โ€” ์ตœ์ข… ์‚ฌ์šฉ์ž ์ž…๋ ฅ

utils.ts:207์—์„œ ๋‘ role์„ ๋™์ผํ•˜๊ฒŒ instruction ์˜์—ญ์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

โ€œ๋””๋ฒ„๊ทธ ๋กœ๊ทธโ€๋‚˜ โ€œํˆด์ด ๋๋‚ฌ์Œ์„ UI์— ํ‘œ์‹œโ€ ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” developer๊ฐ€ ์•„๋‹ˆ๋ผ activity ๋ฉ”์‹œ์ง€ ๋˜๋Š” STEP_FINISHED ์ด๋ฒคํŠธ์˜ ์˜์—ญ์ด๋‹ค.

UserMessage์˜ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๊ตฌ์กฐ

types.ts:133-136:

export const UserMessageSchema = BaseMessageSchema.extend({
  role: z.literal("user"),
  content: z.union([z.string(), z.array(InputContentSchema)]),
});

content๊ฐ€ string | InputContent[] ์œ ๋‹ˆ์˜จ์ธ ๊ฒƒ์ด ํ•ต์‹ฌ. ํ…์ŠคํŠธ๋งŒ ๋ณด๋‚ด๋Š” ๋ ˆ๊ฑฐ์‹œ ๊ฒฝ๋กœ์™€ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๊ฒฝ๋กœ๋ฅผ ํ•˜๋‚˜์˜ ๋ฉ”์‹œ์ง€ ๊ตฌ์กฐ๋กœ ํ†ตํ•ฉํ•œ๋‹ค.

InputContent 2๋‹จ ๊ณ„์ธต

InputContent (discriminatedUnion by "type")
โ”œโ”€โ”€ TextInputContent      { type: "text",  text }
โ”œโ”€โ”€ ImageInputContent     { type: "image", source, metadata? }
โ”œโ”€โ”€ AudioInputContent     { type: "audio", source, metadata? }
โ”œโ”€โ”€ VideoInputContent     { type: "video", source, metadata? }
โ””โ”€โ”€ DocumentInputContent  { type: "document", source, metadata? }

InputContentSource (discriminatedUnion by "type")
โ”œโ”€โ”€ { type: "data", value: base64, mimeType }   โ€” ์ธ๋ผ์ธ ๋ฐ์ดํ„ฐ
โ””โ”€โ”€ { type: "url",  value: url,    mimeType? }  โ€” ์™ธ๋ถ€ ์ฐธ์กฐ

๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ์€ URL ์ฐธ์กฐ, ์ž‘์€ ํŒŒ์ผ์€ ์ธ๋ผ์ธ base64๋กœ ๋ณด๋‚ด๋Š” ์ตœ์ ํ™” ๋ถ„๊ธฐ๋ฅผ ํ”„๋กœํ† ์ฝœ ๋ ˆ๋ฒจ์—์„œ ์—ด์–ด๋‘”๋‹ค.

๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์š”์ฒญ์ด ๋ชจ๋ธ๊นŒ์ง€ ๋„๋‹ฌํ•˜๋Š” ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ

์‚ฌ์šฉ์ž๊ฐ€ [TextInputContent, VideoInputContent]๋ฅผ ์—…๋กœ๋“œํ•œ ๊ฒฝ์šฐ:

sequenceDiagram
    autonumber
    participant U as User
    participant FE as Frontend
    participant AD as AG-UI Adapter
    participant LC as LangChain Runtime
    participant API as OpenAI API

    U->>FE: ํ…์ŠคํŠธ์™€ ๋น„๋””์˜ค ์—…๋กœ๋“œ
    FE->>AD: AG-UI UserMessage<br/>content - text + video ๋ฐฐ์—ด

    Note over AD: convertAguiMultimodalToLangchain<br/>mediaSourceToUrl
    AD->>LC: LangChain HumanMessage<br/>content - text + image_url ๋ฐฐ์—ด

    Note over LC: LangChain ๋‚ด๋ถ€์—์„œ<br/>OpenAI API ํฌ๋งท์œผ๋กœ ์žฌ๋ณ€ํ™˜
    LC->>API: OpenAI chat.completions<br/>content - text + image_url ๋ฐฐ์—ด

    API-->>LC: streaming response
    LC-->>AD: LangChain events
    AD-->>FE: AG-UI Events ํ‘œ์ค€ํ™”
    FE-->>U: ๋ Œ๋”๋ง

utils.ts:119-151 ์˜ convertAguiMultimodalToLangchain:

function convertAguiMultimodalToLangchain(content: InputContent[]) {
  for (const item of content) {
    if (item.type === "text") {
      langchainContent.push({ type: "text", text: item.text });
    }
    else if (MEDIA_CONTENT_TYPES.has(item.type)) {
      const url = mediaSourceToUrl(mediaItem.source);
      langchainContent.push({
        type: "image_url",  // LangChain์€ ์ „๋ถ€ image_url๋กœ ํ†ตํ•ฉ
        image_url: { url },
      });
    }
  }
}

ํ”„๋กœํ† ์ฝœ ๋ ˆ๋ฒจ๊ณผ ์–ด๋Œ‘ํ„ฐ ๋ ˆ๋ฒจ์˜ ํƒ€์ž… ๋ณด์กด ์ฐจ์ด

๋ ˆ๋ฒจํƒ€์ž… ๊ตฌ๋ถ„๋น„๊ณ 
AG-UI ํ”„๋กœํ† ์ฝœimage/audio/video/document ๊ตฌ๋ถ„ ๋ณด์กด๋ฏธ๋ž˜ ๋Œ€๋น„ ์„ค๊ณ„
LangChain ์–ด๋Œ‘ํ„ฐ๋ชจ๋‘ image_url๋กœ ๋ญ‰๊ฐฌutils.ts:71-73 ์ฃผ์„์ด ๋ช…์‹œ
์ตœ์ข… ๋ชจ๋ธ API๋ชจ๋ธ ์ง€์› ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‹ค๋ฆ„GPT-4o๋Š” ์ด๋ฏธ์ง€, ๋น„๋””์˜ค๋Š” ์ง€์› ์ œํ•œ์ 

AG-UI๊ฐ€ VideoInputContent, AudioInputContent๋ฅผ ๋ฏธ๋ฆฌ ์ •์˜ํ•œ ๊ฒƒ์€ ๋ชจ๋ธ/ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋”ฐ๋ผ์˜ฌ ๋•Œ๋ฅผ ๋Œ€๋น„ํ•œ ํ™•์žฅ์„ฑ ํฌ์„์ด๋‹ค.

Adapter ๋ ˆ์ด์–ด โ€” ํ”„๋ ˆ์ž„์›Œํฌ ํฌ๋งท ๋ณ€ํ™˜์˜ ์ฑ…์ž„ ์ฃผ์ฒด

AG-UI๋Š” ๋ฒค๋” ์ค‘๋ฆฝ ํฌ๋งท๊ณผ ์‹ค์ œ ํ”„๋ ˆ์ž„์›Œํฌ ์‚ฌ์ด์˜ ๋ณ€ํ™˜์„ ๊ณต์‹์ ์œผ๋กœ ์ œ๊ณตํ•˜๋ฉฐ, ๊ทธ ๊ตฌํ˜„์ฒด๊ฐ€ *Agent ํด๋ž˜์Šค๋“ค์ด๋‹ค.

ํ•ต์‹ฌ ์›์น™: ํ”„๋ก ํŠธ์—”๋“œ๋Š” ์˜ค์ง AG-UI ํ”„๋กœํ† ์ฝœ๋งŒ ์‚ฌ์šฉํ•ด ํ†ต์‹ ํ•˜๊ณ , Adapter๊ฐ€ ํ”„๋ ˆ์ž„์›Œํฌ ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ์ „๋‹ดํ•œ๋‹ค. ๋•๋ถ„์— ๋ชจ๋ธ/ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ต์ฒดํ•ด๋„ ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๋ฌด๋ณ€๊ฒฝ์ด๋‹ค.

โš ๏ธ ํ—ท๊ฐˆ๋ฆฌ๊ธฐ ์‰ฌ์šด ์ง€์  โ€” โ€œํ”„๋ ˆ์ž„์›Œํฌโ€์™€ โ€œ๋ฒค๋”โ€๋Š” ๋‹ค๋ฅด๋‹ค

์ด ์„น์…˜์„ ์ฝ์„ ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ํ˜ผ๋™๋˜๋Š” ์ง€์ ์€ **โ€œAG-UI๊ฐ€ ๋ฒค๋”(OpenAI)์— ์ง์ ‘ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ธ๋‹คโ€**๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์‹ค์ œ๋กœ๋Š” ์‚ฌ์ด์— **โ€œํ”„๋ ˆ์ž„์›Œํฌโ€**๋ผ๋Š” ์ค‘๊ฐ„ ๊ณ„์ธต์ด ํ•˜๋‚˜ ๋” ์žˆ๋‹ค.

์šฉ์–ด ๊ตฌ๋ถ„:

์šฉ์–ด์ •์ฒด์˜ˆ์‹œ
AG-UI Protocol๋ฒค๋”ยทํ”„๋ ˆ์ž„์›Œํฌ ์ค‘๋ฆฝ ํ‘œ์ค€ ํฌ๋งทtypes.ts์˜ MessageSchema
ํ”„๋ ˆ์ž„์›Œํฌ (Framework)AI ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (์ค‘๊ฐ„ ๊ณ„์ธต)LangChain, LangGraph, Mastra, Vercel AI SDK, LlamaIndex
๋ฒค๋” (Vendor / Provider)์‹ค์ œ LLM API ์ œ๊ณต์ž (์ตœ์ข… ๋ชฉ์ ์ง€)OpenAI, Anthropic, AWS Bedrock, Google

์™œ ํ˜ผ๋™๋˜๋Š”๊ฐ€? โ€œ๋ฒค๋”์‚ฌ๋งˆ๋‹ค ์ด๋ฒคํŠธ๊ฐ€ ๋‹ค๋ฅด๋‹คโ€๋Š” ๋ง์€ ๋งž์ง€๋งŒ, AG-UI๊ฐ€ ๊ทธ๊ฑธ ์ง์ ‘ ํ•ด๊ฒฐํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๋ฒค๋” ์ถ”์ƒํ™”๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ด๋ฏธ ํ•ด๋‘” ์ผ์ด๊ณ , AG-UI๋Š” ๊ทธ ์œ„์— ํ•œ ์ธต ๋” ์–นํžŒ ํ”„๋ก ํŠธ์—”๋“œ์šฉ ์ถ”์ƒํ™”๋‹ค.

3๊ณ„์ธต ๊ตฌ์กฐ โ€” ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฑฐ์น˜๋Š” ์‹ค์ œ ๊ฒฝ๋กœ

flowchart TB
    L1["Layer 1 - AG-UI Protocol<br/>๋ฒค๋”/ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘๋ฆฝ ํ‘œ์ค€<br/>์˜ˆ์‹œ - role assistant, toolCalls, camelCase"]
    L2["Layer 2 - Framework Format<br/>LangChain, ai-sdk, Mastra ๋“ฑ<br/>์˜ˆ์‹œ - type ai, tool_calls, snake_case"]
    L3["Layer 3 - Vendor API Format<br/>OpenAI, Anthropic, Bedrock ๋“ฑ<br/>์˜ˆ์‹œ - chat.completions JSON"]

    L1 <==>|"๋ณ€ํ™˜ 1<br/>AG-UI Adapter๊ฐ€ ๋‹ด๋‹น"| L2
    L2 <==>|"๋ณ€ํ™˜ 2<br/>Framework ๋‚ด๋ถ€ ๋‹ด๋‹น<br/>AG-UI ๋ฌด๊ด€"| L3

    classDef agui fill:#b3d9ff,stroke:#0066cc,color:#000
    classDef framework fill:#ffd9b3,stroke:#cc6600,color:#000
    classDef vendor fill:#b3ffb3,stroke:#009900,color:#000

    class L1 agui
    class L2 framework
    class L3 vendor

AG-UI Message๊ฐ€ LLM์— ๋„๋‹ฌํ•˜๊ธฐ๊นŒ์ง€ โ€” ๋ณ€ํ™˜์€ 2ํšŒ ์ผ์–ด๋‚œ๋‹ค

ํ•œ ๋ฒˆ์˜ ์‚ฌ์šฉ์ž ์š”์ฒญ์ด LLM๊นŒ์ง€ ๋„๋‹ฌํ•˜๋ ค๋ฉด ์ด 2๋ฒˆ์˜ ํฌ๋งท ๋ณ€ํ™˜์ด ํ•„์š”ํ•˜๋‹ค.

flowchart LR
    A["AG-UI Message<br/>role assistant<br/>toolCalls array"]
    B["LangChain Message<br/>type ai<br/>tool_calls array"]
    C["OpenAI API JSON<br/>role assistant<br/>tool_calls array"]
    LLM(("LLM"))

    A ==>|"๋ณ€ํ™˜ 1<br/>Adapter<br/>aguiMessagesToLangChain"| B
    B ==>|"๋ณ€ํ™˜ 2<br/>Framework<br/>ChatOpenAI ๋‚ด๋ถ€"| C
    C ==>|"HTTP ์ „์†ก<br/>Framework"| LLM

    classDef agui fill:#b3d9ff,stroke:#0066cc,color:#000
    classDef framework fill:#ffd9b3,stroke:#cc6600,color:#000
    classDef vendor fill:#b3ffb3,stroke:#009900,color:#000

    class A agui
    class B framework
    class C vendor

๋ณ€ํ™˜ ํšŸ์ˆ˜ ์š”์•ฝ:

๋ณ€ํ™˜๋‹ด๋‹น์žAG-UI ์ฑ…์ž„?
โ‘  AG-UI Message โ†’ ํ”„๋ ˆ์ž„์›Œํฌ MessageAdapter (LangGraphAgent ๋“ฑ)โœ… Yes
โ‘ก ํ”„๋ ˆ์ž„์›Œํฌ Message โ†’ ๋ฒค๋” API JSONFramework ๋‚ด๋ถ€ (ChatOpenAI ๋“ฑ)โŒ No

์‹ค์ œ ํฌ๋งท ์ฐจ์ด โ€” ์ฝ”๋“œ๋กœ ํ™•์ธ

๊ฐ™์€ โ€œassistant ๋ฉ”์‹œ์ง€โ€๊ฐ€ 3๊ณ„์ธต์—์„œ ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅด๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ๋น„๊ตํ•˜๋ฉด 3๊ณ„์ธต์˜ ์กด์žฌ๊ฐ€ ํ™•์‹คํžˆ ๋ณด์ธ๋‹ค.

Layer 1: AG-UI Protocol ํฌ๋งท โ€” types.ts:127-131

{
  id: "msg_1",
  role: "assistant",                    // ์†Œ๋ฌธ์ž role
  content: "๋‹ต๋ณ€",
  toolCalls: [                          // camelCase
    { id, type: "function",
      function: { name, arguments: "{\"city\":\"tokyo\"}" }  // arguments๋Š” JSON ๋ฌธ์ž์—ด
    }
  ]
}

Layer 2: Framework (LangChain) ํฌ๋งท โ€” utils.ts:241-253

{
  id,
  type: "ai",                           // "ai" (AG-UI๋Š” "assistant")
  role: "assistant",
  content: "๋‹ต๋ณ€",
  tool_calls: [                         // snake_case
    { id,
      name: "weather",
      args: { city: "tokyo" },          // args๋Š” ํŒŒ์‹ฑ๋œ ๊ฐ์ฒด
      type: "tool_call",
    }
  ]
}

Layer 3: Vendor (OpenAI) API ํฌ๋งท

{
  "role": "assistant",
  "content": "๋‹ต๋ณ€",
  "tool_calls": [
    { "id": "call_1",
      "type": "function",
      "function": {
        "name": "weather",
        "arguments": "{\"city\":\"tokyo\"}"
      }
    }
  ]
}

์ฃผ์š” ์ฐจ์ด์  ์š”์•ฝ:

ํ•„๋“œLayer 1 (AG-UI)Layer 2 (LangChain)Layer 3 (OpenAI)
role ํ‘œ๊ธฐrole: "assistant"type: "ai"role: "assistant"
ํˆด ๋ฐฐ์—ด ์ด๋ฆ„toolCallstool_callstool_calls
ํˆด ์ธ์žarguments: "{...}" (๋ฌธ์ž์—ด)args: {...} (๊ฐ์ฒด)arguments: "{...}" (๋ฌธ์ž์—ด)
๋„ค์ด๋ฐcamelCasesnake_casesnake_case

์žฌ๋ฏธ์žˆ๋Š” ์ : Layer 1(AG-UI)๊ณผ Layer 3(OpenAI)์€ ๋ฌ˜ํ•˜๊ฒŒ ๋‹ฎ์•˜์ง€๋งŒ Layer 2(LangChain)๋Š” ๋‹ค๋ฅด๋‹ค. LangChain์ด ์ž๊ธฐ๋งŒ์˜ ๋…์ž ํฌ๋งท์„ ์“ฐ๊ธฐ ๋•Œ๋ฌธ์ด๋ฉฐ, ์ด๊ฒƒ์ด โ€œํ”„๋ ˆ์ž„์›Œํฌ ํฌ๋งทโ€์ด๋ผ๋Š” ์ค‘๊ฐ„ ๊ณ„์ธต์ด ์‹ค์žฌํ•˜๋Š” ์ฆ๊ฑฐ๋‹ค.

โš ๏ธ ํ—ท๊ฐˆ๋ฆฌ๊ธฐ ์‰ฌ์šด ์ง€์  โ€” โ€œ์™œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๊ฑฐ์ณ ๊ฐ€๋‚˜? ๋ฐ”๋กœ ๋ฒค๋”๋กœ ๋ณด๋‚ด๋ฉด ์•ˆ ๋˜๋‚˜?โ€

์ด๋ก ์ ์œผ๋กœ๋Š” Adapter๊ฐ€ AG-UI โ†’ OpenAI JSON์œผ๋กœ ๋ฐ”๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋Ÿฌ์ง€ ์•Š๋Š” ์ด์œ :

๋ฌธ์ œํ”„๋ ˆ์ž„์›Œํฌ ๊ฒฝ์œ  ์‹œ
๋ฒค๋” ์ถ”์ƒํ™”๋ฅผ ๋ˆ„๊ฐ€ ํ•˜๋‚˜?LangChain์ด ์ด๋ฏธ 20+ ๋ฒค๋” ์ง€์› โ†’ ๊ณต์งœ๋กœ ์ƒ์†
์ƒˆ LLM ์ถ”๊ฐ€๋˜๋ฉด?LangChain์ด ์ง€์›ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด AG-UI Adapter ์ˆ˜์ • ๋ถˆํ•„์š”
๋ณต์žกํ•œ ๊ธฐ๋Šฅ(RAG, ๋ฉ€ํ‹ฐ์Šคํ…)?ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ด๋ฏธ ๊ตฌํ˜„ โ†’ AG-UI๊ฐ€ ์žฌ๋ฐœ๋ช… ์•ˆ ํ•ด๋„ ๋จ
์ฝ”๋“œ ๋‹จ์ˆœ์„ฑAdapter๋Š” โ€œLangChain๊ณผ๋งŒโ€ ๋Œ€ํ™”ํ•˜๋ฉด ๋จ

์ฆ‰ AG-UI๊ฐ€ โ€œํ”„๋ ˆ์ž„์›Œํฌ ๋ ˆ๋ฒจโ€์—์„œ ๋ฉˆ์ถ”๋Š” ๊ฑด ์„ค๊ณ„์  ํƒ€ํ˜‘์ด ์•„๋‹ˆ๋ผ ์ „๋žต์  ์„ ํƒ์ด๋‹ค. ์ด๋ฏธ LangChain/ai-sdk/Mastra๊ฐ€ ๋ฒค๋” ์ถ”์ƒํ™”๋ฅผ ์™„์„ฑํ•ด๋’€๊ธฐ ๋•Œ๋ฌธ์—, AG-UI๋Š” ๊ทธ ์ถ”์ƒํ™”๋ฅผ ํ”„๋ก ํŠธ์—”๋“œ๊นŒ์ง€ ์—ฐ์žฅํ•˜๋Š” ์—ญํ• ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

์ „์ฒด ํ†ต์‹  ํ๋ฆ„ (Adapter๊ฐ€ ํฌ๋งท ๋ณ€ํ™˜์„ ์ „๋‹ด)

sequenceDiagram
    autonumber
    participant FE as Frontend
    participant AD as AG-UI Adapter
    participant FW as Framework Runtime
    participant LLM as LLM API

    rect rgba(100, 180, 255, 0.15)
        Note over FE,AD: AG-UI ํ”„๋กœํ† ์ฝœ ๊ตฌ๊ฐ„<br/>ํ”„๋ก ํŠธ์—”๋“œ๋Š” ์ด ํฌ๋งท๋งŒ ์•Œ๋ฉด ๋จ
        FE->>AD: AG-UI Message - role, content, toolCalls
    end

    rect rgba(255, 180, 100, 0.15)
        Note over AD: Adapter๊ฐ€ ํฌ๋งท ๋ณ€ํ™˜ ์ „๋‹ด<br/>aguiMessagesToLangChain
        AD->>AD: AG-UI to Framework Native<br/>ํฌ๋งท ๋ณ€ํ™˜
    end

    rect rgba(180, 255, 180, 0.15)
        Note over AD,LLM: ํ”„๋ ˆ์ž„์›Œํฌ/๋ชจ๋ธ ๊ตฌ๊ฐ„<br/>ํ”„๋ก ํŠธ์—”๋“œ๋Š” ์•Œ ํ•„์š” ์—†์Œ
        AD->>FW: Framework-native Message<br/>LangChain HumanMessage ๋“ฑ
        FW->>LLM: Vendor-specific JSON<br/>OpenAI chat.completions ๋“ฑ
        LLM-->>FW: streaming chunks
        FW-->>AD: framework events
    end

    rect rgba(255, 180, 100, 0.15)
        Note over AD: Adapter๊ฐ€ ์—ญ๋ณ€ํ™˜ ์ „๋‹ด<br/>framework event to AG-UI Event
        AD->>AD: Framework Native to AG-UI<br/>ํฌ๋งท ์—ญ๋ณ€ํ™˜
    end

    rect rgba(100, 180, 255, 0.15)
        AD-->>FE: AG-UI Events<br/>TEXT_MESSAGE_*, TOOL_CALL_* ๋“ฑ
        Note over FE,AD: AG-UI ํ”„๋กœํ† ์ฝœ ๊ตฌ๊ฐ„
    end

๊ตฌ๊ฐ„๋ณ„ ์ฑ…์ž„ ๋ถ„๋ฆฌ:

๊ตฌ๊ฐ„ํฌ๋งท์ฑ…์ž„ ์ฃผ์ฒด
Frontend โ†” AdapterAG-UI Message / Events (ํ‘œ์ค€)AG-UI Protocol
Adapter ๋‚ด๋ถ€AG-UI โ†” Framework ๋ณ€ํ™˜Adapter (LangGraphAgent ๋“ฑ)
Adapter โ†’ Framework RuntimeLangChain/ai-sdk ๋„ค์ดํ‹ฐ๋ธŒ ํฌ๋งทFramework
Framework โ†’ LLM APIOpenAI/Anthropic JSONFramework ๋‚ด๋ถ€

์ฆ‰ LLM ๊ต์ฒด(OpenAI โ†’ Claude) ์‹œ Framework ๋‚ด๋ถ€์—์„œ๋งŒ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๊ณ , ํ”„๋ ˆ์ž„์›Œํฌ ๊ต์ฒด(LangChain โ†’ Mastra) ์‹œ์—๋„ Adapter ๊ตฌํ˜„์ฒด๋งŒ ๋ฐ”๊พธ๋ฉด ํ”„๋ก ํŠธ์—”๋“œ๋Š” ๋ฌด๋ณ€๊ฒฝ์ด๋‹ค.

Adapter์˜ ๋‘ ๊ฐ€์ง€ ๋ฒ ์ด์Šค ํด๋ž˜์Šค

โ‘  HttpAgent ์ƒ์† โ€” ๋‹จ์ˆœ ์ค‘๊ณ„ (๋ฐฑ์—”๋“œ๊ฐ€ ์ด๋ฏธ AG-UI ํฌ๋งท์œผ๋กœ ์‘๋‹ต):

// integrations/crew-ai/typescript/src/index.ts
export class CrewAIAgent extends HttpAgent {}
export class AgnoAgent extends HttpAgent { ... }
export class PydanticAIAgent extends HttpAgent { ... }

โ‘ก AbstractAgent ์ƒ์† โ€” ํ’€ ์ปค์Šคํ…€ (๋„ค์ดํ‹ฐ๋ธŒ ํฌ๋งท ์–‘๋ฐฉํ–ฅ ๋ณ€ํ™˜ ํ•„์š”):

// integrations/langgraph/typescript/src/agent.ts:124
export class LangGraphAgent extends AbstractAgent {
  run(input: RunAgentInput): Observable<ProcessedEvents> {
    // input.messages (AG-UI) โ†’ LangChain messages ๋ณ€ํ™˜
    // LangChain ์‹คํ–‰
    // LangChain ์ด๋ฒคํŠธ โ†’ AG-UI Events ์—ญ๋ณ€ํ™˜ ์ŠคํŠธ๋ฆฌ๋ฐ
  }
}

์ฑ…์ž„ ๋ฒ”์œ„์˜ ๊ฒฝ๊ณ„

๋ฐ์ดํ„ฐ๊ฐ€ 3๋ฒˆ ๋ชจ์–‘์„ ๋ฐ”๊พธ๋ฉฐ LLM๊นŒ์ง€ ๋„๋‹ฌํ•˜๋Š”๋ฐ, ๊ฐ ๋ณ€ํ™˜ ๋‹จ๊ณ„๋ฅผ ๋ˆ„๊ฐ€ ์ฑ…์ž„์ง€๋Š”์ง€๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค.

flowchart LR
    A["AG-UI Message"]
    B["LangChain Message"]
    C["OpenAI API JSON"]

    A -->|"๋ณ€ํ™˜ 1 - LangGraphAgent<br/>AG-UI Adapter"| B
    B -->|"๋ณ€ํ™˜ 2 - LangChain ChatOpenAI<br/>ํ”„๋ ˆ์ž„์›Œํฌ ๋‚ด๋ถ€"| C

    classDef format fill:#f5f5f5,stroke:#666,color:#000

    class A,B,C format

AG-UI๊ฐ€ ์ฑ…์ž„์ง€๋Š” ๊ฒƒ / ์•„๋‹Œ ๊ฒƒ:

flowchart LR
    subgraph AGUI_SCOPE["AG-UI Adapter ์ฑ…์ž„ ๋ฒ”์œ„"]
        direction LR
        A2["AG-UI Message"] ==>|"๋ณ€ํ™˜ 1"| B2["LangChain Message"]
    end

    subgraph FW_SCOPE["Framework ๋‚ด๋ถ€ ์ฑ…์ž„ - AG-UI ๋ฌด๊ด€"]
        direction LR
        B3["LangChain Message"] ==>|"๋ณ€ํ™˜ 2"| C2["OpenAI API JSON"]
    end

    AGUI_SCOPE -.๊ฒฝ๊ณ„.-> FW_SCOPE

    classDef adapter fill:#ffd9b3,stroke:#cc6600,color:#000
    classDef framework fill:#b3ffb3,stroke:#009900,color:#000

    class A2,B2 adapter
    class B3,C2 framework

ํ•œ ์ค„ ์š”์•ฝ: Adapter๋Š” โ€œAG-UI โ†” ํ”„๋ ˆ์ž„์›Œํฌ ํฌ๋งทโ€๊นŒ์ง€๋งŒ ์ฑ…์ž„์ง„๋‹ค. ๊ทธ ์ดํ›„ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์–ด๋–ค LLM API๋ฅผ ์“ฐ๋“ (OpenAI/Anthropic/Bedrock) ๊ทธ๊ฑด ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ชซ์ด์ง€ AG-UI๊ฐ€ ๊ด€์—ฌํ•  ์ผ์ด ์•„๋‹ˆ๋‹ค.

VercelAISDKAgent์ฒ˜๋Ÿผ ํ”„๋ ˆ์ž„์›Œํฌ ์ž์ฒด๊ฐ€ ๋ฉ€ํ‹ฐ ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ํ†ตํ•ฉ ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ, ๋ณ€ํ™˜ โ‘ก๊ฐ€ ํ”„๋ ˆ์ž„์›Œํฌ ๋‚ด๋ถ€์— ์ด๋ฏธ ๋…น์•„ ์žˆ์–ด Adapter๋Š” ๋ณ€ํ™˜ โ‘ ๋งŒ ์‹ ๊ฒฝ ์“ฐ๋ฉด ๋œ๋‹ค.

๋ณ‘๋ ฌ Tool Calls์˜ ์ˆœ์„œ ๋ณด์กด

N๊ฐœ์˜ ํˆด์ด ๋ณ‘๋ ฌ ์‹คํ–‰๋  ๋•Œ ์™„๋ฃŒ ์ˆœ์„œ์™€ ์ƒ๊ด€์—†์ด ์‹œ์ž‘(START) ์ˆœ์„œ๋กœ ๋ฐฐ์—ด ์œ„์น˜๊ฐ€ ๊ณ ์ •๋œ๋‹ค.

ํ•ต์‹ฌ ์›๋ฆฌ: TOOL_CALL_START๊ฐ€ ์Šฌ๋กฏ์„ ์„ ์ 

default.ts:293-315:

case EventType.TOOL_CALL_START: {
  const { toolCallId, toolCallName, parentMessageId } = event;
  const targetMessage = resolveOrCreateAssistantMessage(...);
  targetMessage.toolCalls ??= [];
 
  // ๋ฐฐ์—ด์— push โ€” ์ด ์‹œ์ ์— ์ˆœ์„œ๊ฐ€ ๊ฒฐ์ •๋จ
  targetMessage.toolCalls.push({
    id: toolCallId,
    type: "function",
    function: { name: toolCallName, arguments: "" },
  });
}

ํƒ€์ž„๋ผ์ธ ์˜ˆ์‹œ

์‹œ๊ฐ„  ์ด๋ฒคํŠธ                          toolCalls ๋ฐฐ์—ด ์ƒํƒœ
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
t=0   TOOL_CALL_START(id:1)           [1(๋น„์–ด์žˆ์Œ)]
t=1   TOOL_CALL_START(id:2)           [1, 2(๋น„์–ด์žˆ์Œ)]
t=2   TOOL_CALL_START(id:3)           [1, 2, 3(๋น„์–ด์žˆ์Œ)]
t=3   TOOL_CALL_START(id:4)           [1, 2, 3, 4(๋น„์–ด์žˆ์Œ)]
t=4   TOOL_CALL_ARGS(id:3, "...")     [1, 2, 3(์ฑ„์›Œ์ง), 4]
t=5   TOOL_CALL_END(id:3)             [1, 2, 3(์™„๋ฃŒ), 4]
t=10  TOOL_CALL_ARGS(id:1, "...")     [1(์ฑ„์›Œ์ง), 2, 3, 4]
t=11  TOOL_CALL_END(id:1)             [1(์™„๋ฃŒ), 2, 3, 4]

3๋ฒˆ์ด ๋จผ์ € ์™„๋ฃŒ๋˜์–ด๋„ ๋ฐฐ์—ด ์œ„์น˜๋Š” ๊ทธ๋Œ€๋กœ 3๋ฒˆ์งธ ์Šฌ๋กฏ์„ ์ง€ํ‚จ๋‹ค. TOOL_CALL_ARGS์™€ TOOL_CALL_END๋Š” toolCallId๋กœ ์ž๊ธฐ ์Šฌ๋กฏ์„ ์ฐพ์„ ๋ฟ ์œ„์น˜๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค(default.ts:336).

์„ค๊ณ„ ์˜๋„

  1. LLM ์‘๋‹ต ์ˆœ์„œ ๋ณด์กด: LLM์ด ์˜๋„ํ•œ ์ˆœ์„œ๊ฐ€ UI์—๋„ ๋ฐ˜์˜๋˜์–ด์•ผ ํ•จ
  2. ์žฌ์ •๋ ฌ ์‹œ UI ๊นœ๋นก์ž„ ๋ฐฉ์ง€: React ๋ฐฐ์—ด ํ‚ค ์ˆœ์„œ ๋ณ€๊ฒฝ์ด ๋ฆฌ๋ Œ๋” ํญํ’ ์œ ๋ฐœ
  3. toolCallId๋กœ ์ •ํ•ฉ์„ฑ ๋ณด์žฅ: ์ˆœ์„œ ์œ ์ง€ํ•ด๋„ ๊ฒฐ๊ณผ ๋งค์นญ(ToolMessage.toolCallId)์€ id ๊ธฐ๋ฐ˜์ด๋ฏ€๋กœ ๋ฌด๊ฒฐ

์˜ˆ์™ธ: ๋งŽ์€ ํ†ตํ•ฉ์ด ์ˆœ์ฐจ ์‹คํ–‰์„ ๊ฐ•์ œ

agent.ts:87 ๋“ฑ ๋Œ€๋ถ€๋ถ„์˜ LangGraph ์˜ˆ์ œ๋Š”:

parallel_tool_calls: false,  // ๋ ˆ์ด์Šค ์ปจ๋””์…˜ ํšŒํ”ผ

๋กœ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•ด ์‹ค์ œ๋กœ๋Š” ๋ณ‘๋ ฌ ์‹คํ–‰ ์ž์ฒด๊ฐ€ ์กฐ์‹ฌ์Šค๋Ÿฝ๊ฒŒ ์‚ฌ์šฉ๋œ๋‹ค.

Tool Call Linking ํŒจํ„ด

์„ธ ๊ฐœ์˜ ID๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ์—ญํ• ์„ ํ•˜๋ฉฐ ๋ณ‘๋ ฌ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ์ •ํ™•ํžˆ ๋งค์นญํ•œ๋‹ค.

flowchart LR
    subgraph AM["AssistantMessage"]
        AM_ID["id - msg_2<br/>๋ฉ”์‹œ์ง€ ์ž์ฒด์˜ ID"]
        TC_ID["toolCalls 0๋ฒˆ์งธ id - call_1<br/>ํ˜ธ์ถœ ์š”์ฒญ ID"]
    end

    subgraph TM["ToolMessage"]
        TM_ID["id - result_1<br/>๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€ ID"]
        TC_REF["toolCallId - call_1<br/>์ฐธ์กฐ"]
    end

    TC_ID -. "๋งํฌ - ID ๋งค์นญ" .-> TC_REF

    classDef request fill:#ffe6cc,stroke:#d79b00,color:#000
    classDef result fill:#d5e8d4,stroke:#82b366,color:#000
    class AM_ID,TC_ID request
    class TM_ID,TC_REF result

์ˆœ์„œ ๋…๋ฆฝ์ ์œผ๋กœ ๋งค์นญ๋˜๋ฏ€๋กœ assistant๊ฐ€ N๊ฐœ ํˆด์„ ๋ณ‘๋ ฌ ๋ฐœํ–‰ํ•ด๋„ ๊ฒฐ๊ณผ ์ถฉ๋Œ ์—†์ด ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•˜๋‹ค.

encryptedValue โ€” ZDR/store: false ํ™˜๊ฒฝ์˜ ์ƒํƒœ ์บ๋ฆฌ์–ด

encryptedValue๋Š” UI ๋ Œ๋”๋ง์šฉ์ด ์•„๋‹ˆ๋ผ LLM ์„œ๋ฒ„์— ๋‹ค์‹œ ์ž…๋ ฅํ•˜๊ธฐ ์œ„ํ•œ ๋ถˆํˆฌ๋ช… ํ† ํฐ์ด๋‹ค.

ํ‘œ์‹œ/์ €์žฅ/์ „๋‹ฌ์˜ 3์ถ• ๋ถ„๋ฆฌ

๊ฒฝ๋กœUI ๋…ธ์ถœํด๋ผ์ด์–ธํŠธ ์ €์žฅLLM ์žฌ์ „์†ก
content (ํ‰๋ฌธ ์š”์•ฝ)๋ณด์ž„๊ฐ€๋Šฅ๊ฐ€๋Šฅ
encryptedValue (์•”ํ˜ธ๋ฌธ)์•ˆ ๋ณด์ž„๊ทธ๋Œ€๋กœ ๋ณด๊ด€๊ทธ๋Œ€๋กœ ์™•๋ณต

reasoning.mdx:315:

// Client stores only:
// - The encrypted blob (cannot decrypt)
// - The summary text (no sensitive details)
// Full reasoning is never persisted in plaintext

์™œ ํ•„์š”ํ•œ๊ฐ€: ์ผ๋ฐ˜ ์ƒํƒœ vs ZDR ์ •์ฑ…

์ผ๋ฐ˜ (์„œ๋ฒ„๊ฐ€ reasoning ์ €์žฅ ๊ฐ€๋Šฅ):

[1ํ„ด] User: "๋ณต์žกํ•œ ๋ฌธ์ œ"
      LLM: reasoning 1000 ํ† ํฐ ์ƒ์„ฑ โ†’ Answer
      ์„œ๋ฒ„ ์ €์žฅ โœ…
[2ํ„ด] User: "์™œ?"
      LLM: ์ €์žฅ๋œ reasoning ์ฐธ์กฐ โ†’ ๋‹ต๋ณ€

ZDR / store: false (์„œ๋ฒ„ ์ €์žฅ ๊ธˆ์ง€):

[1ํ„ด] User: "๋ณต์žกํ•œ ๋ฌธ์ œ"
      LLM: reasoning โ†’ ์•”ํ˜ธํ™”(์ž๊ธฐ ํ‚ค) โ†’ encryptedValue
      ํด๋ผ์ด์–ธํŠธ์— ์ „์†ก (๋‚ด์šฉ ๋ชจ๋ฆ„, ์ €์žฅ๋งŒ)
      ์„œ๋ฒ„ ์ €์žฅ ์•ˆ ํ•จ โœ…
[2ํ„ด] User: "์™œ?"
      ํด๋ผ์ด์–ธํŠธ: encryptedValue ๊ทธ๋Œ€๋กœ ์žฌ์ „์†ก
      LLM: ๋ณตํ˜ธํ™” โ†’ ์›๋ณธ reasoning ๋ณต์› โ†’ ๋‹ต๋ณ€ โœ…

encryptedValue ์—†์ด 2ํ„ด์„ ๋ณด๋‚ด๋ฉด LLM์ด reasoning์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ์žฌ๊ณ„์‚ฐ โ†’ ๋น„์šฉ 2๋ฐฐ + ๊ฒฐ๊ณผ ๋ถˆ์ผ์น˜ ๊ฐ€๋Šฅ.

์„ธ ์ฃผ์ฒด์˜ ์—ญํ• 

  • LLM ์„œ๋ฒ„: ์•”ํ˜ธํ™”/๋ณตํ˜ธํ™” ํ‚ค ์†Œ์œ  (์ƒํƒœ๋Š” ์ €์žฅ ์•ˆ ํ•จ)
  • ํด๋ผ์ด์–ธํŠธ: ์•”ํ˜ธ๋ฌธ ๋ณด๊ด€๋งŒ (๋‚ด์šฉ ์ดํ•ด ๋ถˆ๊ฐ€)
  • AG-UI Protocol: ์–‘์ชฝ์„ ์ค‘๊ณ„

3๋‹จ๊ณ„ ๋ผ์ดํ”„์‚ฌ์ดํด

sequenceDiagram
    autonumber
    participant A as Agent
    participant C as Client

    Note over A,C: Turn 1
    A->>C: REASONING_MESSAGE_START - msg-456
    A->>C: REASONING_MESSAGE_CONTENT - ์š”์ฒญ ๋ถ„์„ ์ค‘ - UI ์š”์•ฝ
    A->>C: REASONING_MESSAGE_END
    A->>C: REASONING_ENCRYPTED_VALUE<br/>subtype message<br/>entityId msg-456<br/>encryptedValue eyJhbG...

    Note over C: UI์—๋Š” ์š”์•ฝ๋งŒ ๋ Œ๋”<br/>encryptedValue๋Š” ๊ฐ์ฒด ํ•„๋“œ์—๋งŒ ์ €์žฅ

    Note over A,C: Turn 2
    C->>A: RunAgentInput.messages์— reasoning ๋ฉ”์‹œ์ง€ ํฌํ•จ<br/>id msg-456, role reasoning,<br/>content ์š”์•ฝ, encryptedValue eyJhbG...
    Note over A: encryptedValue ๋ณตํ˜ธํ™”ํ•˜์—ฌ<br/>์›๋ณธ CoT ๋ณต์›
    A->>C: ๋งฅ๋ฝ ์ด์–ด์„œ ์‘๋‹ต

default.ts:1092-1100 ์˜ ํด๋ผ์ด์–ธํŠธ ์ธก ์ฒ˜๋ฆฌ:

// subtype is "message"
const message = messages.find((m) => m.id === entityId);
if (message?.role !== "activity" && message) {
  message.encryptedValue = encryptedValue;  // ๊ฐ์ฒด ํ•„๋“œ์—๋งŒ ์ €์žฅ, ๋ Œ๋” ์•ˆ ํ•จ
}

์–ธ์ œ ์‹ค์ œ๋กœ ์จ์•ผ ํ•˜๋Š”๊ฐ€

์ƒํ™ฉํ•„์š”?
์ผ๋ฐ˜ ์ฑ—๋ด‡๋ถˆํ•„์š”
OpenAI store: true (๊ธฐ๋ณธ๊ฐ’)๋ถˆํ•„์š”
OpenAI store: false + reasoning ๋ชจ๋ธ (o1/o3)ํ•„์š”
๊ธˆ์œต/์˜๋ฃŒ/๋ฒ•๋ฅ  ์—”ํ„ฐํ”„๋ผ์ด์ฆˆํ•„์š”
ZDR ๊ณ„์•ฝ ๊ณ ๊ฐํ•„์š”

์ผ๋ฐ˜ ๊ฐœ๋ฐœ์ž๋Š” ๋Œ€๋ถ€๋ถ„ ์‹ ๊ฒฝ ์“ธ ํ•„์š” ์—†๊ณ , ์ปดํ”Œ๋ผ์ด์–ธ์Šค ํ™˜๊ฒฝ ์ „์šฉ ๊ธฐ๋Šฅ์ด๋‹ค. ๋‹ค๋งŒ AG-UI ํ”„๋กœํ† ์ฝœ ์„ค๊ณ„์ƒ ํ•„๋“œ๊ฐ€ ์ƒ์‹œ ๋…ธ์ถœ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์กด์žฌ ์ด์œ ๋ฅผ ์ดํ•ดํ•ด๋‘๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

์œ ์‚ฌ ํŒจํ„ด: Tool Call์—๋„ encryptedValue

types.ts:8-13:

export const ToolCallSchema = z.object({
  id: z.string(),
  type: z.literal("function"),
  function: FunctionCallSchema,
  encryptedValue: z.string().optional(),  // ํˆด ์„ ํƒ ์ด์œ ๋ฅผ ์•”ํ˜ธํ™” ๋ณด์กด
});

โ€œ์™œ ์ด ํˆด์„ ์ด ์ธ์ž๋กœ ํ˜ธ์ถœํ–ˆ๋Š”์ง€โ€์˜ ๊ทผ๊ฑฐ๋ฅผ ZDR ํ™˜๊ฒฝ์—์„œ๋„ ๋ณด์กด ๊ฐ€๋Šฅ.

ActivityMessage โ€” ํ”„๋ก ํŠธ์—”๋“œ ์ „์šฉ Generative UI ์Šฌ๋กฏ

types.ts:147-152:

export const ActivityMessageSchema = z.object({
  id: z.string(),
  role: z.literal("activity"),
  activityType: z.string(),       // ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ์ปดํฌ๋„ŒํŠธ ์„ ํƒ
  content: z.record(z.any()),     // ์ž์œ  ์Šคํ‚ค๋งˆ
});

์™œ LLM์— ๋ณด๋‚ด์ง€ ์•Š๋Š”๊ฐ€

โ€œ๊ฒ€์ƒ‰ ์ค‘โ€ฆโ€, โ€œ3๋‹จ๊ณ„ ์ค‘ 2๋‹จ๊ณ„ ์ง„ํ–‰โ€ ๊ฐ™์€ UI ์ƒํƒœ๋ฅผ LLM ํžˆ์Šคํ† ๋ฆฌ์— ๋„ฃ์œผ๋ฉด:

  1. ํ† ํฐ ๋‚ญ๋น„ โ€” ๋‹ค์Œ ํ„ด์— ์žฌ์ž…๋ ฅ
  2. LLM ํ˜ผ๋ž€ โ€” โ€œ์ด JSON์€ ๋ญ์ง€?โ€ ํ•ด์„ ์‹œ๋„

default.ts:578-582 ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์„œ๋ฒ„ ์ „์†ก ์ œ์™ธ:

const isClientOnlyRole = (role: string) =>
  role === "activity" || role === "reasoning";

Custom UI Schema ์˜ˆ์‹œ 3๊ฐ€์ง€

ํŒจํ„ด A: ์ฒดํฌ๋ฆฌ์ŠคํŠธ (PLAN)

{
  id: "activity_plan_1",
  role: "activity",
  activityType: "PLAN",
  content: {
    title: "๋ฆฌ์„œ์น˜ ์ž‘์—…",
    tasks: [
      { id: "t1", label: "๋…ผ๋ฌธ ๊ฒ€์ƒ‰", status: "pending" },
      { id: "t2", label: "์š”์•ฝ ์ž‘์„ฑ", status: "pending" },
    ]
  }
}

์ง„ํ–‰ ์ค‘ ACTIVITY_DELTA๋กœ JSON Patch ์—…๋ฐ์ดํŠธ:

{
  type: EventType.ACTIVITY_DELTA,
  messageId: "activity_plan_1",
  activityType: "PLAN",
  patch: [
    { op: "replace", path: "/tasks/0/status", value: "done" }
  ]
}

ํŒจํ„ด B: ๊ฒ€์ƒ‰ ์ง„ํ–‰ ์ƒํƒœ (SEARCH)

{
  id: "activity_search_1",
  role: "activity",
  activityType: "SEARCH",
  content: {
    query: "AG-UI protocol spec",
    status: "running",
    results: [
      { title: "AG-UI Docs", url: "...", snippet: "..." },
    ],
    metrics: { totalFound: 2, searchTimeMs: 342 }
  }
}

ํŒจํ„ด C: ๋ฉ€ํ‹ฐ ์Šคํ… ์ง„ํ–‰ (SCRAPE)

{
  id: "activity_scrape_1",
  role: "activity",
  activityType: "SCRAPE",
  content: {
    url: "https://example.com",
    currentStep: 2,
    totalSteps: 4,
    steps: [
      { label: "ํŽ˜์ด์ง€ ๋กœ๋“œ", status: "done", duration: 120 },
      { label: "DOM ํŒŒ์‹ฑ", status: "done", duration: 45 },
      { label: "๋ฐ์ดํ„ฐ ์ถ”์ถœ", status: "running" },
      { label: "์ •์ œ ๋ฐ ์ €์žฅ", status: "pending" },
    ]
  }
}

ํ”„๋ก ํŠธ์—”๋“œ ๋””์ŠคํŒจ์น˜ ํŒจํ„ด

function ActivityRenderer({ message }: { message: ActivityMessage }) {
  switch (message.activityType) {
    case "PLAN":    return <PlanActivity content={message.content} />;
    case "SEARCH":  return <SearchActivity content={message.content} />;
    case "SCRAPE":  return <ScrapeActivity content={message.content} />;
    default:        return <GenericActivity content={message.content} />;
  }
}

Record<string, any>์˜ ์ž์œ ๋„๋Š” activityType ๋‹จ์œ„๋กœ ํŒ€์ด ์ž์œจ์ ์œผ๋กœ ์Šคํ‚ค๋งˆ๋ฅผ ์„ค๊ณ„ํ•˜๊ณ , JSON Patch๋กœ ์ฆ๋ถ„ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฐ›๋Š” ํ”„๋กœํ† ์ฝœ์— ๋ฌถ์ด์ง€ ์•Š์€ ํ™•์žฅ์ ์ด๋‹ค.

Generative UI โ€” JSON/HTML ์ง์ ‘ ์ „๋‹ฌ

AG-UI๋Š” โ€œ๋ Œ๋”๋ง ์ŠคํŽ™โ€์ด ์•„๋‹ˆ๋ผ **โ€œ์ˆ˜์†ก ๊ฒฝ๋กœโ€**๋‹ค. JSON์„ ๊ทธ๋Œ€๋กœ ๋‚˜๋ฅด๋Š” ๊ฒƒ์ด ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์ด๋ฉฐ, generative-ui-specs.mdx:18-20:

AG-UI is not a generative UI specification โ€” itโ€™s a User Interaction protocolโ€ฆ AG-UI natively supports all of the above generative UI specs and allows developers to define their own custom generative UI standards as well.

4๊ฐ€์ง€ ์ „๋‹ฌ ๋ฐฉ์‹ ๋น„๊ต

๋ฐฉ๋ฒ•๋ฌด์†์‹ค ์ „๋‹ฌ๋ณด์•ˆ์œ ์—ฐ์„ฑ์‚ฌ์šฉ ํŒจํ„ด
ActivityMessage + JSON๊ฐ€๋Šฅ์•ˆ์ „ (ํ”„๋ก ํŠธ๊ฐ€ ๋ Œ๋”)์ปดํฌ๋„ŒํŠธ ํ•„์š”๊ถŒ์žฅ ํ‘œ์ค€
Tool Call ๊ธฐ๋ฐ˜๊ฐ€๋Šฅ์•ˆ์ „LLM์ด ํ˜ธ์ถœํ•ด์•ผtool-based generative UI
Raw HTML๊ฐ€๋ŠฅXSS ์œ„ํ—˜์™„์ „ ์ž์œ ๋น„๊ถŒ์žฅ
MCP-UI (iframe)๊ฐ€๋Šฅ์ƒŒ๋“œ๋ฐ•์Šค์›นํŽ˜์ด์ง€๊ธ‰Microsoft+Shopify ์ŠคํŽ™

๊ณต์‹ Generative UI ์ŠคํŽ™

generative-ui-specs.mdx:12-16:

์ŠคํŽ™์œ ์ง€๋ณด์ˆ˜ํŠน์ง•
A2UIGoogleJSONL ๊ธฐ๋ฐ˜ ์ŠคํŠธ๋ฆฌ๋ฐ, ํ”Œ๋žซํผ ๋ฌด๊ด€
Open-JSON-UIOpenAI๋‚ด๋ถ€ declarative UI ์Šคํ‚ค๋งˆ ํ‘œ์ค€ํ™”
MCP-UIMicrosoft + Shopifyiframe ๊ธฐ๋ฐ˜ ์™„์ „ ๊ฐœ๋ฐฉ ์ŠคํŽ™

AG-UI๋Š” ์ด ๋ชจ๋“  ์ŠคํŽ™์„ ๋„ค์ดํ‹ฐ๋ธŒ ์ง€์›ํ•˜๋ฉฐ, ActivityMessage.content์˜ Record<string, any>๊ฐ€ ๊ฐ ์ŠคํŽ™์˜ JSON์„ ๊ทธ๋Œ€๋กœ ์‹ค์–ด ๋‚˜๋ฅธ๋‹ค.

Tool-based Generative UI ์˜ˆ์‹œ

agent.ts:27-29:

const systemMessage = new SystemMessage({
  content: 'Help the user with writing Haikus. If the user asks for a haiku,
            use the generate_haiku tool to display the haiku to the user.'
});

LLM์ด generate_haiku({title, lines}) ํˆด ํ˜ธ์ถœ โ†’ ์‹ค์ œ ์‹คํ–‰ ์—†์Œ โ†’ ํˆด ํ˜ธ์ถœ ์ž์ฒด๊ฐ€ UI ์ง€์‹œ. ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ tool call์˜ arguments๋ฅผ ๋ฐ›์•„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ Œ๋”๋ง.

Raw HTML ์ „๋‹ฌ ์‹œ ์ฃผ์˜

// ๋ณด์•ˆ ์ฃผ์˜: dangerouslySetInnerHTML ํŒจํ„ด
yield {
  type: "ACTIVITY_SNAPSHOT",
  messageId: "html-1",
  activityType: "RAW_HTML",
  content: { html: "<div class='card'>...</div>" }
}

LLM์ด ์ƒ์„ฑํ•œ HTML์„ ๊ทธ๋Œ€๋กœ ๋ Œ๋”ํ•˜๋ฉด <script> ์ฃผ์ž… ๊ฐ€๋Šฅ. ์‹ค๋ฌด์—์„œ๋Š” JSON ์Šคํ‚ค๋งˆ๋ฅผ ์“ฐ๊ณ  ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ Œ๋”ํ•˜๋Š” ๊ฒƒ์ด ํ‘œ์ค€์ด๋ฉฐ, ์›นํŽ˜์ด์ง€๊ธ‰ ์ž์œ ๋„๊ฐ€ ํ•„์š”ํ•˜๋ฉด MCP-UI์˜ iframe ์ƒŒ๋“œ๋ฐ•์Šค๋ฅผ ์“ด๋‹ค.

์ „์ฒด ์‹œํ€€์Šค โ€” ํˆด ํ˜ธ์ถœ ํฌํ•จ end-to-end ํ๋ฆ„

์‚ฌ์šฉ์ž๊ฐ€ โ€œ๋„์ฟ„ ๋‚ ์”จ ์•Œ๋ ค์ค˜โ€๋ฅผ ๋ฌผ์—ˆ์„ ๋•Œ์˜ ์ „ ๊ตฌ๊ฐ„ ํ๋ฆ„.

sequenceDiagram
    autonumber
    participant U as User
    participant FE as Frontend
    participant AD as AG-UI Adapter
    participant LG as LangGraph
    participant API as OpenAI API

    U->>FE: ๋„์ฟ„ ๋‚ ์”จ ์•Œ๋ ค์ค˜
    FE->>AD: POST /run<br/>AG-UI UserMessage ํฌํ•จ

    Note over AD: aguiMessagesToLangChain ๋ณ€ํ™˜
    AD->>LG: graph.stream with LangChain messages
    LG->>API: OpenAI chat.completions - LC ๋‚ด๋ถ€ ๋ณ€ํ™˜
    API-->>LG: streaming - choice.delta.tool_calls

    LG-->>AD: LangGraph events
    Note over AD: LangGraph event๋ฅผ AG-UI Event๋กœ ๋ณ€ํ™˜

    AD-->>FE: SSE - RUN_STARTED
    AD-->>FE: SSE - TEXT_MESSAGE_START
    AD-->>FE: SSE - TOOL_CALL_START - weather
    AD-->>FE: SSE - TOOL_CALL_ARGS - city tokyo
    AD-->>FE: SSE - TOOL_CALL_END

    FE->>FE: ํ”„๋ก ํŠธ์—”๋“œ ํˆด ์‹คํ–‰<br/>๋˜๋Š” ์„œ๋ฒ„ ์žฌํ˜ธ์ถœ
    FE->>AD: ํˆด ๊ฒฐ๊ณผ ํฌํ•จ ์žฌ์š”์ฒญ

    AD->>LG: ํˆด ๊ฒฐ๊ณผ์™€ ๋ˆ„์  ๋ฉ”์‹œ์ง€
    LG->>API: 2์ฐจ ์ถ”๋ก 
    API-->>LG: ์ตœ์ข… ์‘๋‹ต
    LG-->>AD: streaming
    AD-->>FE: SSE - TEXT_MESSAGE_CONTENT<br/>delta - ์˜ค๋Š˜ ๋„์ฟ„๋Š”
    AD-->>FE: SSE - TEXT_MESSAGE_END
    AD-->>FE: SSE - RUN_FINISHED
    FE->>U: ์˜ค๋Š˜ ๋„์ฟ„๋Š” 15๋„

๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜ โ€” Snapshot vs Streaming

MESSAGES_SNAPSHOT (์™„์ „ ๊ต์ฒด)

์ „์ฒด ๋Œ€ํ™” ๋ฐฐ์—ด์„ ํ•œ ๋ฒˆ์— ๊ต์ฒด. ์ดˆ๊ธฐํ™” / ์žฌ์—ฐ๊ฒฐ / ๋Œ€๊ทœ๋ชจ ๋ณ€๊ฒฝ ์‹œ ์‚ฌ์šฉ.

default.ts:551-596 ์˜ ๋ณ‘ํ•ฉ ๋กœ์ง์ด ์ฃผ๋ชฉํ•  ๋งŒํ•จ:

// activity / reasoning ๋ฉ”์‹œ์ง€๋Š” ์„œ๋ฒ„ ์Šค๋ƒ…์ƒท์— ์—†์–ด๋„ ์œ ์ง€
const isClientOnlyRole = (role: string) =>
  role === "activity" || role === "reasoning";
 
messages = messages
  .filter((m) => isClientOnlyRole(m.role) || snapshotMap.has(m.id))
  .map((m) => (isClientOnlyRole(m.role) ? m : snapshotMap.get(m.id)!));

ํด๋ผ์ด์–ธํŠธ ์ „์šฉ role์€ ์Šค๋ƒ…์ƒท์ด ๋ฎ์–ด์“ฐ์ง€ ์•Š๋Š”๋‹ค โ€” ํ”„๋ก ํŠธ์—”๋“œ ์ƒํƒœ ๋ณด์กด์„ ์œ„ํ•œ ์„ค๊ณ„.

Streaming (์ ์ง„ ๊ตฌ์ถ•)

TEXT_MESSAGE_START    โ†’ { messageId, role }
TEXT_MESSAGE_CONTENT  โ†’ { messageId, delta }  (NํšŒ)
TEXT_MESSAGE_END      โ†’ { messageId }

TOOL_CALL_START โ†’ TOOL_CALL_ARGS (JSON ์กฐ๊ฐ๋“ค) โ†’ TOOL_CALL_END

์ฃผ์˜: TOOL_CALL_ARGS.delta๋Š” JSON ๋ฌธ์ž์—ด์˜ ์กฐ๊ฐ์ด๋ผ ์ค‘๊ฐ„ ํŒŒ์‹ฑ ์‹œ ๊นจ์ง„๋‹ค. END๊นŒ์ง€ ์Œ“๊ณ  JSON.parse๊ฐ€ ์›์น™์ด๋ฉฐ default.ts:405-410 ๊ตฌํ˜„๋„ ๊ทธ๋ ‡๊ฒŒ ๋˜์–ด ์žˆ๋‹ค.

๋ฒค๋” ์ค‘๋ฆฝ์„ฑ์˜ ๊ตฌ์ฒด์  ์ด๋“

flowchart LR
    CLIENT["Client"]
    PROTO["AG-UI<br/>(ํ‘œ์ค€ ํฌ๋งท)"]
    ADAPTER["Adapter"]
    F1["OpenAI format"]
    F2["Anthropic format"]
    F3["Custom format"]
    M1(("OpenAI<br/>๋ชจ๋ธ"))
    M2(("Claude<br/>๋ชจ๋ธ"))
    M3(("Custom<br/>๋ชจ๋ธ"))

    CLIENT <--> PROTO
    PROTO <--> ADAPTER
    ADAPTER <--> F1
    ADAPTER <--> F2
    ADAPTER <--> F3
    F1 <--> M1
    F2 <--> M2
    F3 <--> M3

    classDef agui fill:#b3d9ff,stroke:#0066cc,color:#000
    classDef adapter fill:#ffd9b3,stroke:#cc6600,color:#000
    classDef vendor fill:#e1d5e7,stroke:#9673a6,color:#000
    classDef model fill:#f8cecc,stroke:#b85450,color:#000

    class CLIENT,PROTO agui
    class ADAPTER adapter
    class F1,F2,F3 vendor
    class M1,M2,M3 model

ํด๋ผ์ด์–ธํŠธ๋Š” AG-UI ํฌ๋งท๋งŒ ์•Œ๋ฉด ๋˜๋ฏ€๋กœ ๋ชจ๋ธ ์Šค์œ„์นญ์ด ์–ด๋Œ‘ํ„ฐ ๊ต์ฒด๋กœ ๋๋‚œ๋‹ค. MCP/A2A์™€ ๊ตฌ๋ถ„๋˜๋Š” AG-UI๋งŒ์˜ ํฌ์ง€์…˜์ด ์—ฌ๊ธฐ์— ๊ตฌ์ฒด์ ์œผ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค.

์ฒดํฌ ํฌ์ธํŠธ

  1. AssistantMessage.content๊ฐ€ optional์ธ ์ด์œ  โ€” toolCalls๋งŒ์œผ๋กœ๋„ ์˜๋ฏธ๊ฐ€ ์„ฑ๋ฆฝ
  2. ToolMessage์— name ํ•„๋“œ๊ฐ€ ์—†๋Š” ์ด์œ  โ€” BaseMessage๋ฅผ extend ์•ˆ ํ•˜๊ณ  ๋…๋ฆฝ ์Šคํ‚ค๋งˆ. role+toolCallId๋กœ ์ถฉ๋ถ„
  3. ActivityMessage.content๊ฐ€ Record<string, any>์ธ ์ด์œ  โ€” ์ปค์Šคํ…€ UI ์Šคํ‚ค๋งˆ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์ˆ˜์šฉ
  4. Streaming ์ค‘ ์—ฐ๊ฒฐ ๋Š๊น€ ๋ณต๊ตฌ โ€” ์žฌ์—ฐ๊ฒฐ ํ›„ MESSAGES_SNAPSHOT์œผ๋กœ ๋™๊ธฐํ™”
  5. encryptedValue ์ˆ˜๋ช… โ€” ๋‹ค์Œ ํ„ด ์š”์ฒญ ์‹œ ์›๋ณธ ๊ทธ๋Œ€๋กœ ์žฌ์ „์†ก, ํด๋ผ์ด์–ธํŠธ๋Š” ๋‚ด์šฉ ๋ฌด๊ด€
  6. ๋ฌธ์„œ โ†” ์ฝ”๋“œ ๋„ค์ด๋ฐ ๋ถˆ์ผ์น˜ โ€” encryptedContent(๋ฌธ์„œ)๋Š” ์‹ค์ œ๋กœ encryptedValue(์ฝ”๋“œ). ์ฝ”๋“œ๊ฐ€ ์ •๋‹ต

์ฐธ๊ณ  ๋ฌธ์„œ