- AG-UI ์ด๋ฒคํธ ํ์ ์์คํ ์ Zod discriminated union ํ๋๋ก 33๊ฐ ์ด๋ฒคํธ์ schemaยทํ์ ยท๊ฒ์ฆ์ ๋จ์ผ ์ง์ค ๊ณต๊ธ์์ผ๋ก ๋ฌถ์ ์ค๊ณ
z.infer๋ก schema์์ TypeScript ํ์ ์ ์๋ ๋์ถํ๋ schema-first ์ํคํ ์ฒ- BaseEvent +
.passthrough()๋ก ํ์ฅ์ฑ๊ณผ ํ์ ์์ ์ฑ์ ๋์์ ํ๋ณดํ ๊ตฌ์กฐ
ํด๋น ๊ฐ๋ ์ด ํ์ํ ์ด์
- 33๊ฐ ์ด๋ฒคํธ ร ์นดํ ๊ณ ๋ฆฌ๋ณ ๋ค๋ฅธ ํ๋๋ฅผ if/else๋ก ๋ฐ์ผ๋ฉด ํ์ ์์ ์ฑ๊ณผ ๊ฐ๋ ์ฑ ๋์ ์์ค
- ํ๋กํ ์ฝ ์คํ(๋ฐํ์)๊ณผ TypeScript ํ์ (์ปดํ์ผํ์)์ด ๋ถ๋ฆฌ๋๋ฉด drift ๋ฐ์ โ ํ์ชฝ๋ง ์ ๋ฐ์ดํธ๋์ด ๋ฒ๊ทธ
- ๋ค์ค transport(SSE/Binary)์ ๋ค์ค SDK(TS/Python)๊ฐ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ฏ๋ก ํ๋์ schema๊ฐ source of truth์ฌ์ผ ์ผ๊ด์ฑ ์ ์ง
AS-IS โ ํ์ ๋ถ๊ธฐ๋ฅผ ์๋ if/else๋ก
function handle(event: { type: string; [k: string]: any }) {
if (event.type === "TEXT_MESSAGE_CONTENT") {
console.log(event.delta); // any โ ํ์
๋ฏธ๋ณด์ฅ
} else if (event.type === "RUN_STARTED") {
console.log(event.threadId); // any โ ํ์
๋ฏธ๋ณด์ฅ
}
// ์ ์ด๋ฒคํธ ์ถ๊ฐํด๋ ์ปดํ์ผ๋ฌ๊ฐ ๊ฒฝ๊ณ ์ ํจ
}TO-BE โ discriminated union์ผ๋ก ์๋ narrowing
function handle(event: AGUIEvent) {
switch (event.type) {
case EventType.TEXT_MESSAGE_CONTENT:
console.log(event.delta); // string์ผ๋ก ์๋ ์ขํ
break;
case EventType.RUN_STARTED:
console.log(event.threadId); // string์ผ๋ก ์๋ ์ขํ
break;
// ๋๋ฝ ์ exhaustiveness ๊ฒฝ๊ณ
}
}ํต์ฌ ์ค๊ณ 1 โ schema๊ฐ ๊ณง ํ์ ์ด๋ค
events.ts๋ ๋ชจ๋ ์ด๋ฒคํธ๋ฅผ Zod schema๋ก ๋จผ์ ์ ์ํ๊ณ , TypeScript ํ์
์ ๊ทธ๊ฑธ z.infer๋ก ๋์ถํ๋ค.
export const TextMessageContentEventSchema = BaseEventSchema.extend({
type: z.literal(EventType.TEXT_MESSAGE_CONTENT),
messageId: z.string(),
delta: z.string(),
});
export type TextMessageContentEvent = z.infer<typeof TextMessageContentEventSchema>;์ schema-first์ธ๊ฐ:
- ๋ฐํ์ ๊ฒ์ฆ(
schema.parse())๊ณผ ์ปดํ์ผํ์ ํ์ ์ ํ ์ ์๋ก ๋ ๋ค ์ป์ - ํ๋กํ ์ฝ ์คํ = schema โ ๋ณ๊ฒฝ ์ ๋๋ฆฌํํธ ๋ถ๊ฐ๋ฅ
- transport ๊ณ์ธต์ด wire์์ ๋ฐ์ ๊ฐ์ ๊ฐ์ schema๋ก ๊ฒ์ฆ โ ์ ๋ขฐ ๊ฒฝ๊ณ ๋ช ์ํ (AG-UI Serialization์์ ๋ค์ ๋ฑ์ฅ)
ํต์ฌ ์ค๊ณ 2 โ discriminated union์ผ๋ก 33๊ฐ๋ฅผ ํ๋๋ก
export const EventSchemas = z.discriminatedUnion("type", [
TextMessageStartEventSchema,
TextMessageContentEventSchema,
// ... 33๊ฐ
]);
export type AGUIEvent = z.infer<typeof EventSchemas>;discriminator๋ type ํ๋. ๊ฐ schema๊ฐ type: z.literal(EventType.X)๋ก ์๊ธฐ๋ฅผ ์ ์ธํ๋ฏ๋ก, TypeScript๊ฐ event.type์ ๋ณด๊ณ ๋๋จธ์ง ํ๋๋ฅผ ์๋ ์ขํ๋ค.
์ผ๋ฐ union๊ณผ ์ฐจ์ด:
A | B | C๋ narrowing์ด ์ฝํจ โ ์ฌ์ฉ์๊ฐ type guard๋ฅผ ์ง์ ์์ฑ- discriminated union์ ๊ณตํต ํ๋ ํ๋๋ก ์ปดํ์ผ๋ฌ๊ฐ ์๋ ๋ถ๊ธฐ
ํต์ฌ ์ค๊ณ 3 โ BaseEvent + .passthrough()
export const BaseEventSchema = z
.object({
type: z.nativeEnum(EventType),
timestamp: z.number().optional(),
rawEvent: z.any().optional(),
})
.passthrough();.passthrough()๋ ์ ์๋์ง ์์ ํ๋๋ ํต๊ณผ์ํค๋ Zod ์ต์
:
- ์ SDK ๋ฒ์ ์์ ํ๋๋ฅผ ์ถ๊ฐํด๋ ๊ตฌ๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ ๋ถ์์ง์ง ์์
rawEvent๋ก ์ธ๋ถ ์์คํ ์ด๋ฒคํธ๋ฅผ ๊ทธ๋๋ก ๋ณด์กด- forward compatibility ํ๋ณด โ ํ๋กํ ์ฝ ์งํ์ ์์ ํ
6๊ฐ ์นดํ ๊ณ ๋ฆฌ ํ๋์
์์ธํ ๋ผ์ดํ์ฌ์ดํดยทํ๋๋ AG-UI Event์ ์๋ค. ๋ณธ ๊ธ์ ํ์ ์์คํ ๊ด์ ์ ์ง์ค.
| ์นดํ ๊ณ ๋ฆฌ | ๋ํ ์ด๋ฒคํธ | ํจํด | ๊น์ ์ค๋ช |
|---|---|---|---|
| Lifecycle | RUN_STARTED / FINISHED / ERROR | ์คํ ๊ฒฝ๊ณ | AG-UI Event |
| Text Message | TEXT_MESSAGE_START / CONTENT / END | Start-Content-End | AG-UI Messages |
| Tool Call | TOOL_CALL_START / ARGS / END / RESULT | Start-Content-End + Result | AG-UI Tools |
| State | STATE_SNAPSHOT / DELTA | Snapshot-Delta | AG-UI State Management |
| Activity | ACTIVITY_SNAPSHOT / DELTA | Snapshot-Delta | AG-UI Event |
| Reasoning | REASONING_* | Nested Start-End | AG-UI Event |
๋ถ๊ฐ โ ํ์ ๋๊ตฌ 3์ข
export type AGUIEventByType = { [EventType.TEXT_MESSAGE_CONTENT]: TextMessageContentEvent; ... };
export type AGUIEventOf<T extends EventType> = AGUIEventByType[T];
export type EventPayloadOf<T extends EventType> = Omit<AGUIEventOf<T>, keyof BaseEventFields>;AGUIEventByType: type โ event ๋ฃฉ์AGUIEventOf<T>: ํน์ ํ์ ํ๋๋ง ๊บผ๋ด๊ธฐ (์:AGUIEventOf<EventType.RUN_STARTED>)EventPayloadOf<T>: BaseEvent ๊ณตํต ํ๋ ์ ์ธ โ factory ์ ๋ ฅ์ ํ์ํ ํ๋๋ง ๋ ธ์ถ
schema-first์ ์์ฐ์ค๋ฌ์ด ํ์. ํต์ฌ์ ์๋์ง๋ง ์ฝ๋ฌ์ธก ๋ณด์ผ๋ฌํ๋ ์ดํธ๋ฅผ ์ค์ฌ์ค๋ค.
์ค๊ณ ํ๊ณ โ ๋ค๋ฅธ ์ ํ์ง์ ๋น๊ต
| ์ ํ์ง | ์ฅ์ | ๋จ์ |
|---|---|---|
| TypeScript union๋ง (Zod ์์ด) | ์์กด์ฑ 0 | wire์์ ๋ฐ์ ๊ฐ ๋ฐํ์ ๊ฒ์ฆ ๋ถ๊ฐ |
| JSON Schema | ํ์ค / ๋ค์ธ์ด | TS ํ์ ๋์ถ์ ๋ณ๋ ๋๊ตฌ ํ์ |
| Protobuf๋ง | ๋ค์ธ์ด + ๊ฒ์ฆ | ๋ธ๋ผ์ฐ์ ์์ ๋ฌด๊ฑฐ์, schema ์งํ ์ ์ฝ |
| Zod discriminated union | TS 1๊ธ, ํ์ +๊ฒ์ฆ ํตํฉ | TS ์ธ ์ธ์ด๋ ๋ณ๋ ์ ์ ํ์ |
AG-UI๋ TS๋ Zod, Python์ Pydantic, wire๋ SSE+Protobuf โ ๊ฐ ๊ณ์ธต์ด ๊ฐ์ ๋ชจ์์ ๋ค๋ฅธ ๋๊ตฌ๋ก ํํ. โschema๊ฐ ์ง์ค ๊ณต๊ธ์โ์ด๋ผ๋ ์์น์ด ์ผ๊ด๋๋ค. ์ด wire ๋ณํ์ ์ค์ ๋์์ AG-UI Serialization์์ ๋ค๋ฃฌ๋ค.
๋ธ๋ก๊ทธ ๊ธ๋ก ์ฎ๊ธธ ๋ ํต์ฌ ํ๋ฆ
- Hook โ 33๊ฐ ์ด๋ฒคํธ๋ฅผ ์ด๋ป๊ฒ ํ์ ์์ ํ๊ฒ ๋ค๋ฃฐ๊น?
- ์์ธก โ ๊ทธ๋ฅ union type์ผ๋ก ์ถฉ๋ถํ ๊น? โ ๋ฐํ์ ๊ฒ์ฆ ๋๋ฝ, ์นดํ ๊ณ ๋ฆฌ๋ณ ํ๋ ๋ค๋ฆ
- ์ฝ๋ ํ์ธ โ Zod discriminated union +
z.inferํจํด - ํ๊ณ โ ์ Zod์ธ๊ฐ: ํ์ ๊ณผ ๊ฒ์ฆ์ ํ ์ ์์ ๋ฌถ๊ธฐ
- ๋ก๋ฐฅ โ ๊ทธ๋ฐ๋ฐ SSE wire format์์ ์ด๋ป๊ฒ ์ง๋ ฌํํ ๊น? โ 4ํธ AG-UI Serialization
์ฐธ๊ณ ๋ฌธ์
- AG-UI Event โ 28๊ฐ ์ด๋ฒคํธ์ ์๋ฏธ์ ๋ผ์ดํ์ฌ์ดํด (ํ๋กํ ์ฝ reference)
- AbstractAgent โ ์ด๋ฒคํธ๋ฅผ stream์ผ๋ก ๋ด๋ณด๋ด๋ ์ฃผ์ฒด (์๋ฆฌ์ฆ 2ํธ)
- AG-UI Package Structure โ 4๊ฐ ํจํค์ง ์ง๋ (์๋ฆฌ์ฆ 1ํธ)
- AG-UI Serialization โ wire format ๋ณํ (์๋ฆฌ์ฆ 4ํธ)
- docs/concepts/events.mdx
- sdks/typescript/packages/core/src/events.ts
- Zod discriminated unions