메시지
메시지 (Messages)
AG-UI 프로토콜에서 메시지가 어떻게 구조화되고 주고받아지는지 설명해 드릴게요. 메시지는 사용자와 AI 에이전트 사이의 대화 히스토리를 나타내고, 어떤 AI 서비스를 쓰든 정보를 교환하는 표준화된 방식을 제공해요.
출처: 문서
본문
메시지는 AG-UI 프로토콜에서 통신의 중추를 이룬답니다. 사용자와 AI 에이전트 사이의 대화 히스토리를 나타내며, 사용 중인 AI 서비스와 무관하게 정보를 교환할 수 있는 표준화된 방식을 제공해요.
메시지 구조
AG-UI 메시지는 벤더 중립(vendor-neutral) 형식을 따릅니다. 그래서 서로 다른 AI 프로바이더 사이에서도 일관된 구조를 유지하면서 호환성을 보장해요. 이 덕분에 애플리케이션이 클라이언트 측 구현을 바꾸지 않고도 OpenAI, Anthropic, 커스텀 모델 같은 AI 서비스 사이를 전환할 수 있어요.
기본 메시지 구조는 이렇게 생겼어요.
interface BaseMessage {
id: string // Unique identifier for the message
role: string // The role of the sender (user, assistant, system, tool, reasoning)
content?: string // Optional text content of the message
name?: string // Optional name of the sender
encryptedContent?: string // Optional encrypted content for privacy-preserving state continuity
metadata?: Record<string, any> // Optional extra information attached to the message
}
role 판별자(discriminator)는 "user", "assistant", "system", "tool", "developer", "activity", "reasoning" 중 하나가 돼요. 구체적인 메시지 타입은 필요한 필드를 추가해 이 형태를 확장합니다.
모든 메시지 타입은 metadata를 담아요. 이것은 이벤트에서 메시지가 조립되면서 누적되는, 키로 여는(open-by-key) 선택적 객체예요. 도구 호출은 각자 자신의 메타데이터를 가집니다. 규칙은 Metadata를 참고하세요.
encryptedContent필드는 프라이버시 보존 워크플로를 가능하게 해요. 민감한 콘텐츠(예: 추론 체인)를 원문 그대로 노출하지 않고 턴 사이에 전달할 수 있게 하죠. 특히 제로 데이터 보존(ZDR) 컴플라이언스와store:false시나리오에서 유용합니다.
메시지 타입
AG-UI는 대화의 서로 다른 참여자를 다루기 위해 여러 메시지 타입을 지원해요.
사용자 메시지 (User Messages)
최종 사용자에서 에이전트로 가는 메시지예요.
interface UserMessage {
id: string
role: "user"
content: string | ContentPart[] // Text or multimodal input from the user
name?: string // Optional user identifier
}
type ContentPart =
| TextPart
| ImagePart
| AudioPart
| VideoPart
| DocumentPart
interface DataSource {
type: "data"
value: string
mimeType: string
}
interface UrlSource {
type: "url"
value: string
mimeType?: string
}
interface FileSource {
type: "file"
value: string // a handle the provider issued; opaque, never fetched
provider?: string // who issued it, e.g. "openai"
mimeType?: string
}
type PartSource = DataSource | UrlSource | FileSource
interface TextPart {
type: "text"
text: string
}
interface ImagePart {
type: "image"
source: PartSource
metadata?: Record<string, unknown>
}
interface AudioPart {
type: "audio"
source: PartSource
metadata?: Record<string, unknown>
}
interface VideoPart {
type: "video"
source: PartSource
metadata?: Record<string, unknown>
}
interface DocumentPart {
type: "document"
source: PartSource
metadata?: Record<string, unknown>
}
Python에서는 이전의
BinaryInputContent모델은 deprecated이며, 호환성 경로로 임시로 남아 있습니다.
이 구조는 전통적인 평문 입력이 계속 동작하게 하면서도, 같은 메시지 안에서 이미지, 오디오 클립, 업로드된 파일 같은 더 풍부한 페이로드를 지원해요.
어시스턴트 메시지 (Assistant Messages)
AI 어시스턴트에서 사용자에게 가는 메시지예요.
interface AssistantMessage {
id: string
role: "assistant"
content?: string // Text response from the assistant (optional if using tool calls)
name?: string // Optional assistant identifier
toolCalls?: ToolCall[] // Optional tool calls made by the assistant
encryptedContent?: string // Optional encrypted content for state continuity
}
시스템 메시지 (System Messages)
에이전트에 제공되는 지시나 컨텍스트예요.
interface SystemMessage {
id: string
role: "system"
content: string // Instructions or context for the agent
name?: string // Optional identifier
}
도구 메시지 (Tool Messages)
도구 실행의 결과를 나타내요.
interface ToolMessage {
id: string
role: "tool"
content: string // Result from the tool execution
toolCallId: string // ID of the tool call this message responds to
error?: string // Optional error message if the tool execution failed
encryptedValue?: string // Optional encrypted reasoning for state continuity
}
핵심 포인트:
toolCallId는 결과를 원래 도구 호출에 연결해 줍니다.error로 도구 실행 실패를 나타낼 수 있어요.encryptedValue로 에이전트가 도구 결과를 어떻게 해석·처리했는지와 관련한 암호화된 체인-오브-쏘트를 붙일 수 있어요.
액티비티 메시지 (Activity Messages)
프런트엔드에만 존재하는 구조화된 UI 메시지예요. 진행, 상태, 모델에 보내면 안 되는 커스텀 시각 요소 같은 데 쓰입니다.
interface ActivityMessage {
id: string
role: "activity"
activityType: string // e.g. "PLAN", "SEARCH", "SCRAPE"
content: Record<string, any> // Structured payload rendered by the frontend
}
핵심 포인트
ACTIVITY_SNAPSHOT과ACTIVITY_DELTA를 통해 내보내져 실시간으로 갱신 가능한 UI(체크리스트, 단계, 검색 진행 중 등)를 지원합니다.- 프런트엔드 전용: 에이전트에 전달되지 않으므로 필터링도 LLM 혼란도 없어요.
- 커스터마이징 가능: 자신만의
activityType과content를 정의하고 그에 맞는 UI 컴포넌트를 렌더링할 수 있어요. - 스트리밍 가능: 오래 걸리는 작업 중에도 시간에 따라 갱신할 수 있어요.
- 커스텀 이벤트를 지속 가능한 메시지 객체로 바꿔 저장/복원을 돕습니다.
개발자 메시지 (Developer Messages)
개발이나 디버깅에 사용되는 내부 메시지예요.
interface DeveloperMessage {
id: string
role: "developer"
content: string
name?: string
}
추론 메시지 (Reasoning Messages)
에이전트의 내부 추론이나 체인-오브-쏘트 과정을 나타내는 메시지예요.
interface ReasoningMessage {
id: string
role: "reasoning"
content: string // Reasoning content (visible to client)
encryptedValue?: string // Optional encrypted reasoning for state continuity
}
액티비티 메시지와 달리 추론 메시지는 에이전트의 내부 사고 과정을 나타내며, 프라이버시를 위해 암호화될 수 있어요. 또한 이후 턴에서 추가 처리를 위해 에이전트로 다시 보내지는 것을 전제로 해요.
핵심 포인트:
REASONING_MESSAGE_START,REASONING_MESSAGE_CONTENT,REASONING_MESSAGE_END이벤트를 통해 내보내집니다.- 가시성 제어: 콘텐츠를 사용자에게 보여줄 수 있고(요약으로), 완전히 암호화할 수도 있어요.
- 암호화된 값:
REASONING_ENCRYPTED_VALUE이벤트로 콘텐츠를 노출하지 않고도 메시지나 도구 호출에 암호화된 체인-오브-쏘트를 붙일 수 있어요. - 상태 연속성: 암호화된 추론 항목을 원문 체인-오브-쏘트를 노출하지 않고 대화 턴 사이에 전달할 수 있어요.
- 프라이버시 우선: 추론 능력을 보존하면서
store:false와 제로 데이터 보존(ZDR) 정책을 지원합니다. - 어시스턴트 메시지와 분리: 추론을 최종 응답과 분리해 대화 히스토리를 오염시키지 않습니다.
스트리밍 이벤트 라이프사이클은 Reasoning Events를 참고하세요.
벤더 중립성
AG-UI 메시지는 벤더 중립적으로 설계됐어요. 즉 다양한 AI 프로바이더가 쓰는 독점 형식으로 쉽게 변환하거나 그 형식에서 쉽게 되돌릴 수 있습니다.
// Example: Converting AG-UI messages to OpenAI format
const openaiMessages = agUiMessages
.filter((msg) => ["user", "system", "assistant"].includes(msg.role))
.map((msg) => ({
role: msg.role as "user" | "system" | "assistant",
content: msg.content || "",
// Map tool calls if present
...(msg.role === "assistant" && msg.toolCalls
? {
tool_calls: msg.toolCalls.map((tc) => ({
id: tc.id,
type: tc.type,
function: {
name: tc.function.name,
arguments: tc.function.arguments,
},
})),
}
: {}),
}))
이 추상화 덕분에 AG-UI는 사용 중인 AI 서비스와 무관하게 공통 인터페이스 역할을 할 수 있어요.
메시지 동기화
메시지는 두 가지 주요 메커니즘으로 클라이언트와 서버 사이에 동기화될 수 있어요.
전체 스냅샷
MESSAGES_SNAPSHOT 이벤트는 대화의 모든 메시지에 대한 전체 뷰를 제공해요.
interface MessagesSnapshotEvent {
type: EventType.MESSAGES_SNAPSHOT
messages: Message[] // Complete array of all messages
}
주로 이렇게 쓰입니다.
- 대화를 초기화할 때
- 연결이 끊긴 후
- 큰 상태 변화가 있을 때
- 클라이언트-서버 동기화를 보장하려 할 때
스트리밍 메시지
실시간 상호작용을 위해 새 메시지를 생성되는 대로 스트리밍할 수 있어요.
-
메시지 시작: 새 메시지를 만들고 있음을 알립니다.
interface TextMessageStartEvent { type: EventType.TEXT_MESSAGE_START messageId: string role: string } -
콘텐츠 스트리밍: 콘텐츠 덩어리를 준비되는 대로 보냅니다.
interface TextMessageContentEvent { type: EventType.TEXT_MESSAGE_CONTENT messageId: string delta: string // Text chunk to append } -
메시지 종료: 메시지가 완료됐음을 알립니다.
interface TextMessageEndEvent { type: EventType.TEXT_MESSAGE_END messageId: string }
이 스트리밍 방식은 즉각적인 피드백으로 반응성 좋은 사용자 경험을 제공해요.
메시지에서의 도구 통합
AG-UI 메시지는 도구 사용을 우아하게 통합해요. 에이전트가 작업을 수행하고 그 결과를 처리할 수 있게 하죠.
도구 호출 (Tool Calls)
도구 호출은 어시스턴트 메시지 안에 포함됩니다.
interface ToolCall {
id: string // Unique ID for this tool call
type: "function" // Type of tool call
function: {
name: string // Name of the function to call
arguments: string // JSON-encoded string of arguments
}
}
도구 호출이 포함된 어시스턴트 메시지 예시예요.
{
id: "msg_123",
role: "assistant",
content: "I'll help you with that calculation.",
toolCalls: [
{
id: "call_456",
type: "function",
function: {
name: "calculate",
arguments: '{"expression": "24 * 7"}'
}
}
]
}
도구 결과 (Tool Results)
도구 실행의 결과는 도구 메시지로 표현됩니다.
{
id: "result_789",
role: "tool",
content: "168",
toolCallId: "call_456" // References the original tool call
}
이것은 도구 사용의 명확한 체인을 만들어요.
- 어시스턴트가 도구 호출을 요청합니다.
- 도구가 실행되고 결과를 반환합니다.
- 어시스턴트가 결과를 참조하고 응답할 수 있습니다.
도구 호출 스트리밍
텍스트 메시지와 비슷하게, 도구 호출도 스트리밍해서 에이전트의 행동을 실시간으로 보여줄 수 있어요.
-
도구 호출 시작:
interface ToolCallStartEvent { type: EventType.TOOL_CALL_START toolCallId: string toolCallName: string parentMessageId?: string // Optional link to parent message } -
인자 스트리밍:
interface ToolCallArgsEvent { type: EventType.TOOL_CALL_ARGS toolCallId: string delta: string // JSON fragment to append to arguments } -
도구 호출 종료:
interface ToolCallEndEvent { type: EventType.TOOL_CALL_END toolCallId: string }
이 덕분에 프런트엔드는 에이전트가 추론을 구성해 가면서 도구가 점차 호출되는 모습을 보여줄 수 있어요.
실전 예제
여기 도구 사용이 포함된 대화의 완전한 예시가 있어요.
// Conversation history
;[
// User query
{
id: "msg_1",
role: "user",
content: "What's the weather in New York?",
},
// Assistant response with tool call
{
id: "msg_2",
role: "assistant",
content: "Let me check the weather for you.",
toolCalls: [
{
id: "call_1",
type: "function",
function: {
name: "get_weather",
arguments: '{"location": "New York", "unit": "celsius"}',
},
},
],
},
// Tool result
{
id: "result_1",
role: "tool",
content:
'{"temperature": 22, "condition": "Partly Cloudy", "humidity": 65}',
toolCallId: "call_1",
},
// Assistant's final response using tool results
{
id: "msg_3",
role: "assistant",
content:
"The weather in New York is partly cloudy with a temperature of 22°C and 65% humidity.",
},
]
결론
AG-UI의 메시지 구조는 벤더 중립성을 유지하면서 정교한 대화형 AI 경험을 가능하게 해요. 메시지가 표현·동기화·스트리밍되는 방식을 표준화함으로써, AG-UI는 사용 중인 AI 서비스와 무관하게 인간-에이전트 상호작용을 구현하는 일관된 방법을 제공합니다.
이 시스템은 단순한 텍스트 교환부터 복잡한 도구 기반 워크플로까지 모두 지원하며, 실시간 반응성과 효율적인 데이터 전송을 모두 최적화해요.
더 알아보기 (Learn more)
- 이벤트 (Events) — 메시지가 이벤트로 어떻게 흘러가는지, 그리고 추론 이벤트의 라이프사이클을 확인해 보세요.
- 에이전트 (Agents) — 메시지를 만들고 처리하는 에이전트를 알아보세요.
- 메타데이터 (Metadata) — 메시지 메타데이터가 쌓이는 규칙을 살펴보세요.