• **Capabilities(μ—­λŸ‰ μ„ μ–Έ)**λŠ” AG-UI ν”„λ‘œν† μ½œμ—μ„œ μ—μ΄μ „νŠΈκ°€ λŸ°νƒ€μž„μ— μžμ‹ μ΄ μ§€μ›ν•˜λŠ” κΈ°λŠ₯을 ν΄λΌμ΄μ–ΈνŠΈμ— λ…ΈμΆœν•˜λŠ” 발견(discovery) λ©”μ»€λ‹ˆμ¦˜
  • AbstractAgent의 getCapabilities()κ°€ λ°˜ν™˜ν•˜λŠ” νƒ€μž…ν™”λœ λŠ₯λ ₯ μŠ€λƒ…μƒ·
  • ν˜‘μƒ(negotiation)이 μ•„λ‹Œ 단방ν–₯ μ„ μ–Έ λ°©μ‹μ˜ μΈν„°νŽ˜μ΄μŠ€
  • λͺ…μ‹œλ˜μ§€ μ•Šμ€ ν•„λ“œλŠ” β€œλ―Έμ„ μ–Έ(unknown)β€œμœΌλ‘œ ν•΄μ„λ˜λŠ” λΆ€λΆ„ μ„ μ–Έ κ°€λŠ₯ν•œ μ˜΅μ…”λ„ ꡬ쑰
  • 도ꡬ λ“±λ‘Β·ν•΄μ œμ²˜λŸΌ μƒνƒœκ°€ λ³€ν•˜λ©΄ λ‹€μŒ ν˜ΈμΆœμ— μ¦‰μ‹œ λ°˜μ˜λ˜λŠ” 라이브 μŠ€λƒ…μƒ·

ν•΄λ‹Ή κ°œλ…μ΄ ν•„μš”ν•œ 이유

  • ν΄λΌμ΄μ–ΈνŠΈκ°€ μ—μ΄μ „νŠΈ κΈ°λŠ₯을 μΆ”μΈ‘ν•˜κ±°λ‚˜ ν•˜λ“œμ½”λ”©ν•˜μ§€ μ•Šκ³ λ„ UI/ν”Œλ‘œμš°λ₯Ό μ μ‘μ μœΌλ‘œ ꡬ성할 수 μžˆμ–΄μ•Ό 함
  • 도ꡬ·좔둠·멀티λͺ¨λ‹¬Β·HITL λ“± 이질적인 κΈ°λŠ₯ꡰ을 단일 ν‘œμ€€ μŠ€ν‚€λ§ˆλ‘œ λ…ΈμΆœν•  ν†΅μΌλœ 창ꡬ가 ν•„μš”
  • λŸ°νƒ€μž„μ— 도ꡬ가 μΆ”κ°€/μ œκ±°λ˜κ±°λ‚˜ λͺ¨λ“œκ°€ λ°”λ€” λ•Œ λ™μ μœΌλ‘œ 반영될 수 μžˆμ–΄μ•Ό 함
  • μ—μ΄μ „νŠΈ λ§ˆμΌ“ν”Œλ ˆμ΄μŠ€Β·λΌμš°νŒ… λ‘œμ§μ—μ„œ μ—μ΄μ „νŠΈ λΉ„κ΅Β·μ„ νƒμ˜ 객관적 κ·Όκ±°κ°€ ν•„μš”

AS-IS

sequenceDiagram
    autonumber
    participant Client
    participant Agent
    Client->>Client: "이 μ—μ΄μ „νŠΈκ°€ tools μ§€μ›ν•˜λ‚˜?" μΆ”μΈ‘
    Client->>Agent: run() with tool calls
    Agent-->>Client: λŸ°νƒ€μž„ 였λ₯˜ λ˜λŠ” λ¬΄μ‹œλ¨
    Note over Client: ν•˜λ“œμ½”λ”©λœ 가정에 의쑴<br/>μ‹€νŒ¨ 후에야 미지원 발견

TO-BE

sequenceDiagram
    autonumber
    participant Client
    participant Agent
    Client->>Agent: getCapabilities()
    Agent-->>Client: AgentCapabilities snapshot
    Client->>Client: tools.supported, reasoning.supported λ“± 검사
    Client->>Client: 지원 κΈ°λŠ₯만 UI에 λ…ΈμΆœ (Adaptive UI)
    Client->>Agent: run() β€” κ²€μ¦λœ κΈ°λŠ₯만 μ‚¬μš©
    Agent-->>Client: 정상 응닡

핡심 원칙 4κ°€μ§€

원칙 1. Discovery only β€” λ°œκ²¬μ΄μ§€ ν˜‘μƒμ΄ μ•„λ‹ˆλ‹€

μ—μ΄μ „νŠΈκ°€ μžμ‹ μ΄ ν•  수 μžˆλŠ” 것을 μ„ μ–Έν•  뿐이고, ν΄λΌμ΄μ–ΈνŠΈκ°€ λŠ₯λ ₯을 μš”μ²­Β·κ΅μ„­ν•˜μ§€ μ•ŠλŠ”λ‹€. 단방ν–₯이닀.

원칙 2. Dynamic β€” 호좜 μ‹œμ μ˜ 라이브 μƒνƒœ

getCapabilities()λŠ” μΊμ‹œλœ 정적 메타데이터가 μ•„λ‹ˆλΌ 호좜 μ‹œμ μ˜ ν˜„μž¬ μƒνƒœλ‹€. 도ꡬλ₯Ό λ“±λ‘ν•˜λ©΄ λ‹€μŒ 호좜의 tools.items에 κ³§λ°”λ‘œ λ°˜μ˜λœλ‹€.

let caps = await agent.getCapabilities()
console.log(caps.tools?.items?.length) // 5
 
agent.registerTool(newTool)
 
caps = await agent.getCapabilities()
console.log(caps.tools?.items?.length) // 6

원칙 3. Optional β€” κ΅¬ν˜„ μžμ²΄κ°€ μ˜΅μ…˜

κ΅¬ν˜„ν•˜μ§€ μ•Šμ€ μ—μ΄μ „νŠΈλŠ” undefinedλ₯Ό λ°˜ν™˜ν•œλ‹€. λ”°λΌμ„œ ν΄λΌμ΄μ–ΈνŠΈλŠ” 항상 await agent.getCapabilities?.() ν˜•νƒœμ˜ μ˜΅μ…”λ„ μ²΄μ΄λ‹μœΌλ‘œ ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€.

원칙 4. Absent = unknown β€” λΆ€μž¬λŠ” 미지원이 μ•„λ‹ˆλΌ λ―Έμ„ μ–Έ

tools ν•„λ“œλ₯Ό μƒλž΅ν•œ 것이 β€œλ„κ΅¬ 미지원”을 λœ»ν•˜μ§€λŠ” μ•ŠλŠ”λ‹€. μ„ μ–Έν•˜μ§€ μ•Šμ•˜μŒμ„ μ˜λ―Έν•  뿐이닀. λͺ…μ‹œμ μœΌλ‘œ μ°¨λ‹¨ν•˜λ €λ©΄ supported: falseλ₯Ό 써야 ν•œλ‹€.

AgentCapabilities μΈν„°νŽ˜μ΄μŠ€ β€” 11개 μΉ΄ν…Œκ³ λ¦¬

μΉ΄ν…Œκ³ λ¦¬μ—­ν• λŒ€ν‘œ ν•„λ“œ
identityμ—μ΄μ „νŠΈ 메타데이터name, type, version, provider, documentationUrl
transport전솑 방식 (AG-UI Serialization)streaming(SSE), websocket, httpBinary(protobuf), pushNotifications, resumable
tools도ꡬ 호좜supported, items[], parallelCalls, clientProvided
output좜λ ₯ 포맷structuredOutput, supportedMimeTypes
stateμƒνƒœ/λ©”λͺ¨λ¦¬snapshots, deltas, memory, persistentState
multiAgent닀쀑 μ—μ΄μ „νŠΈ ν˜‘λ ₯supported, delegation, handoffs, subAgents
reasoningμΆ”λ‘ /사고 λ…ΈμΆœsupported, streaming, encrypted
multimodalμž…μΆœλ ₯ λͺ¨λ‹¬λ¦¬ν‹°input.{image,audio,video,pdf,file}, output.{image,audio}
executionμ‹€ν–‰ μ œμ–΄codeExecution, sandboxed, maxIterations, maxExecutionTime
humanInTheLoop인간 κ°œμž…supported, approvals, interventions, feedback
customν‘œμ€€ μ™Έ ν™•μž₯Record<string, unknown> (escape hatch)
interface AgentCapabilities {
  identity?: IdentityCapabilities
  transport?: TransportCapabilities
  tools?: ToolsCapabilities
  output?: OutputCapabilities
  state?: StateCapabilities
  multiAgent?: MultiAgentCapabilities
  reasoning?: ReasoningCapabilities
  multimodal?: MultimodalCapabilities
  execution?: ExecutionCapabilities
  humanInTheLoop?: HumanInTheLoopCapabilities
  custom?: Record<string, unknown>
}

tools.items vs RunAgentInput.tools

tools.itemsλŠ” μ—μ΄μ „νŠΈκ°€ λ‚΄μž₯ν•œ 도ꡬ, RunAgentInput.toolsλŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ λŸ°νƒ€μž„μ— μ£Όμž…ν•˜λŠ” 도ꡬ닀. λ‘˜μ€ μ„œλ‘œ λ‹€λ₯Έ 좜처의 도ꡬ λͺ©λ‘μ΄λ©°, tools.clientProvided: trueλŠ” ν›„μžλ₯Ό 받아듀일 수 μžˆλ‹€λŠ” μ‹ ν˜Έλ‹€.

state β€” μŠ€λƒ…μƒ·κ³Ό λΈνƒ€μ˜ ꡬ뢄

AG-UI State Managementμ—μ„œ λ‹€λ£¨λŠ” STATE_SNAPSHOT(전체 ꡐ체)κ³Ό STATE_DELTA(JSON Patch 기반 λΆ€λΆ„ κ°±μ‹ , delta) 쀑 μ–΄λ–€ 것을 이벀트둜 λ°œν–‰ν•˜λŠ”μ§€λ₯Ό 각각 snapshots/deltas ν”Œλž˜κ·Έλ‘œ μ„ μ–Έν•œλ‹€. persistentStateλŠ” 같은 thread λ‚΄μ—μ„œ run κ°„ μƒνƒœκ°€ μœ μ§€λ˜λŠ”μ§€ μ—¬λΆ€λ‹€.

reasoning.encrypted β€” 무엇을 μ˜λ―Έν•˜λ‚˜

encrypted: trueλŠ” zero-data-retention λͺ¨λ“œλ‘œ, ν΄λΌμ΄μ–ΈνŠΈλŠ” 읽을 수 μžˆλŠ” μΆ”λ‘  ν…μŠ€νŠΈ λŒ€μ‹  뢈투λͺ…ν•œ encryptedValue ν•„λ“œλ₯Ό λ°›λŠ”λ‹€. UI에 μΆ”λ‘  νŒ¨λ„μ„ κ·ΈλŒ€λ‘œ λ…ΈμΆœν•˜λ©΄ μ•ˆ λœλ‹€λŠ” μ‹ ν˜Έλ‹€.

κ΅¬ν˜„ μ˜ˆμ‹œ

μ»€μŠ€ν…€ μ—μ΄μ „νŠΈμ—μ„œ μ„ μ–Έ

import { AbstractAgent, AgentCapabilities } from "@ag-ui/client"
 
class MyAgent extends AbstractAgent {
  async getCapabilities(): Promise<AgentCapabilities> {
    return {
      identity: {
        name: "my-agent",
        description: "A custom agent with tool support",
        version: "1.0.0",
      },
      transport: {
        streaming: true,
      },
      tools: {
        supported: true,
        items: this.getRegisteredTools(),
        clientProvided: true,
      },
      state: {
        snapshots: true,
        deltas: true,
      },
    }
  }
}

μ„ μ–Έν•˜μ§€ μ•Šμ€ μΉ΄ν…Œκ³ λ¦¬(reasoning, multimodal, execution λ“±)λŠ” μžλ™μœΌλ‘œ β€œλ―Έμ„ μ–Έβ€ μƒνƒœκ°€ λœλ‹€.

ν΄λΌμ΄μ–ΈνŠΈ ν™œμš© νŒ¨ν„΄ 3κ°€μ§€

νŒ¨ν„΄ 1. Adaptive UI β€” UI μ»΄ν¬λ„ŒνŠΈ κ°€μ‹œμ„± μ œμ–΄

const caps = await agent.getCapabilities?.()
 
if (caps?.reasoning?.supported) showReasoningPanel()
if (caps?.multiAgent?.subAgents?.length) showSubAgentSelector(caps.multiAgent.subAgents)
if (caps?.humanInTheLoop?.approvals) enableApprovalWorkflow()

νŒ¨ν„΄ 2. Feature Gating β€” 미지원 κΈ°λŠ₯ 사전 차단

const canUseStructuredOutput = caps?.output?.structuredOutput ?? false
const canStream = caps?.transport?.streaming ?? false

λŸ°νƒ€μž„ μ‹€νŒ¨ λŒ€μ‹  사전에 λΉ„ν™œμ„±ν™”ν•˜μ—¬ μ‚¬μš©μž κ²½ν—˜μ„ μΌκ΄€λ˜κ²Œ μœ μ§€ν•œλ‹€. ?? false둜 λ―Έμ„ μ–Έ μΌ€μ΄μŠ€λ₯Ό μ•ˆμ „ν•œ μͺ½(미지원)으둜 μ’νžˆλŠ” 것이 κ΄€μš© νŒ¨ν„΄μ΄λ‹€.

νŒ¨ν„΄ 3. Custom Capabilities β€” 톡합별 ν™•μž₯

const rateLimit = caps?.custom?.rateLimit as
  | { maxRequestsPerMinute: number }
  | undefined
 
if (rateLimit) configureThrottling(rateLimit.maxRequestsPerMinute)

ν‘œμ€€ μΉ΄ν…Œκ³ λ¦¬μ— μ—†λŠ” 정보(레이트 리밋, λΉ„μš© 단가, 벀더 μ‹λ³„μž λ“±)λŠ” custom으둜 λ…ΈμΆœν•œλ‹€. νƒ€μž…μ€ 톡합별 ν•©μ˜μ— μ˜μ‘΄ν•˜λ―€λ‘œ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ 단언(assert)이 ν•„μš”ν•˜λ‹€.

μ°Έκ³  λ¬Έμ„œ