256 lines
5.9 KiB
TypeScript
256 lines
5.9 KiB
TypeScript
|
|
/**
|
|||
|
|
* AG-UI 状态同步示例
|
|||
|
|
*
|
|||
|
|
* 展示 Agent 与前端的 Snapshot-Delta 状态同步模式
|
|||
|
|
*
|
|||
|
|
* 参考文档: modules/events.md (行 1067-1155)
|
|||
|
|
*
|
|||
|
|
* 状态管理模式:
|
|||
|
|
* 1. StateSnapshot - 完整状态快照(初始同步/周期性刷新)
|
|||
|
|
* 2. StateDelta - 增量更新(JSON Patch RFC 6902)
|
|||
|
|
* 3. MessagesSnapshot - 消息历史快照
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import {
|
|||
|
|
AbstractAgent,
|
|||
|
|
RunAgent,
|
|||
|
|
RunAgentInput,
|
|||
|
|
EventType,
|
|||
|
|
BaseEvent,
|
|||
|
|
} from "@ag-ui/client"
|
|||
|
|
import { Observable } from "rxjs"
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Agent 状态定义示例
|
|||
|
|
*/
|
|||
|
|
interface AgentState {
|
|||
|
|
user: {
|
|||
|
|
name: string
|
|||
|
|
preferences: {
|
|||
|
|
theme: "light" | "dark"
|
|||
|
|
language: string
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
session: {
|
|||
|
|
currentPage: number
|
|||
|
|
itemsPerPage: number
|
|||
|
|
totalItems: number
|
|||
|
|
}
|
|||
|
|
formData?: {
|
|||
|
|
[key: string]: any
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class StateSyncAgent extends AbstractAgent {
|
|||
|
|
private state: AgentState = {
|
|||
|
|
user: {
|
|||
|
|
name: "Alice",
|
|||
|
|
preferences: {
|
|||
|
|
theme: "light",
|
|||
|
|
language: "en",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
session: {
|
|||
|
|
currentPage: 1,
|
|||
|
|
itemsPerPage: 10,
|
|||
|
|
totalItems: 100,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
run(input: RunAgentInput): RunAgent {
|
|||
|
|
const { threadId, runId } = input
|
|||
|
|
|
|||
|
|
return () =>
|
|||
|
|
new Observable<BaseEvent>((observer) => {
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.RUN_STARTED,
|
|||
|
|
threadId,
|
|||
|
|
runId,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 1. 发送初始状态快照
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.STATE_SNAPSHOT,
|
|||
|
|
snapshot: this.state,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 2. 发送消息历史快照
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.MESSAGES_SNAPSHOT,
|
|||
|
|
messages: [
|
|||
|
|
{
|
|||
|
|
id: "msg_1",
|
|||
|
|
role: "user",
|
|||
|
|
content: "Show me page 2",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "msg_2",
|
|||
|
|
role: "assistant",
|
|||
|
|
content: "Loading page 2...",
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 3. 模拟状态变化 - 分页更新
|
|||
|
|
setTimeout(() => {
|
|||
|
|
// 发送 Delta 更新(JSON Patch 格式)
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.STATE_DELTA,
|
|||
|
|
delta: [
|
|||
|
|
{ op: "replace", path: "/session/currentPage", value: 2 },
|
|||
|
|
],
|
|||
|
|
})
|
|||
|
|
}, 500)
|
|||
|
|
|
|||
|
|
// 4. 模拟用户偏好更新
|
|||
|
|
setTimeout(() => {
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.STATE_DELTA,
|
|||
|
|
delta: [
|
|||
|
|
{ op: "replace", path: "/user/preferences/theme", value: "dark" },
|
|||
|
|
],
|
|||
|
|
})
|
|||
|
|
}, 1000)
|
|||
|
|
|
|||
|
|
// 5. 添加新字段(表单数据)
|
|||
|
|
setTimeout(() => {
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.STATE_DELTA,
|
|||
|
|
delta: [
|
|||
|
|
{
|
|||
|
|
op: "add",
|
|||
|
|
path: "/formData",
|
|||
|
|
value: {
|
|||
|
|
searchQuery: "AG-UI tutorial",
|
|||
|
|
filters: ["beginner", "typescript"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
})
|
|||
|
|
}, 1500)
|
|||
|
|
|
|||
|
|
// 6. 周期性完整快照(确保状态一致性)
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const updatedState: AgentState = {
|
|||
|
|
...this.state,
|
|||
|
|
session: {
|
|||
|
|
...this.state.session,
|
|||
|
|
currentPage: 2,
|
|||
|
|
},
|
|||
|
|
user: {
|
|||
|
|
...this.state.user,
|
|||
|
|
preferences: {
|
|||
|
|
...this.state.user.preferences,
|
|||
|
|
theme: "dark",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
formData: {
|
|||
|
|
searchQuery: "AG-UI tutorial",
|
|||
|
|
filters: ["beginner", "typescript"],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.STATE_SNAPSHOT,
|
|||
|
|
snapshot: updatedState,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
observer.next({
|
|||
|
|
type: EventType.RUN_FINISHED,
|
|||
|
|
threadId,
|
|||
|
|
runId,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
observer.complete()
|
|||
|
|
}, 2000)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 前端状态管理示例(接收端)
|
|||
|
|
*/
|
|||
|
|
class StateManager {
|
|||
|
|
private state: AgentState | null = null
|
|||
|
|
|
|||
|
|
handleEvent(event: BaseEvent) {
|
|||
|
|
switch (event.type) {
|
|||
|
|
case EventType.STATE_SNAPSHOT:
|
|||
|
|
// 完整替换状态
|
|||
|
|
this.state = (event as any).snapshot as AgentState
|
|||
|
|
console.log("[State] Snapshot received:", this.state)
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
case EventType.STATE_DELTA:
|
|||
|
|
// 应用 JSON Patch 增量更新
|
|||
|
|
if (this.state) {
|
|||
|
|
const patches = (event as any).delta
|
|||
|
|
this.state = this.applyPatches(this.state, patches)
|
|||
|
|
console.log("[State] Delta applied:", patches)
|
|||
|
|
console.log("[State] Current state:", this.state)
|
|||
|
|
}
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
case EventType.MESSAGES_SNAPSHOT:
|
|||
|
|
console.log("[Messages] Snapshot:", (event as any).messages)
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 应用 JSON Patch 操作(简化实现)
|
|||
|
|
* 生产环境应使用 json-patch 库
|
|||
|
|
*/
|
|||
|
|
private applyPatches(state: AgentState, patches: any[]): AgentState {
|
|||
|
|
const newState = JSON.parse(JSON.stringify(state))
|
|||
|
|
|
|||
|
|
for (const patch of patches) {
|
|||
|
|
const { op, path, value } = patch
|
|||
|
|
const pathParts = path.split("/").filter(Boolean)
|
|||
|
|
let target: any = newState
|
|||
|
|
|
|||
|
|
// 导航到目标对象的父级
|
|||
|
|
for (let i = 0; i < pathParts.length - 1; i++) {
|
|||
|
|
target = target[pathParts[i]]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const lastKey = pathParts[pathParts.length - 1]
|
|||
|
|
|
|||
|
|
switch (op) {
|
|||
|
|
case "replace":
|
|||
|
|
target[lastKey] = value
|
|||
|
|
break
|
|||
|
|
case "add":
|
|||
|
|
target[lastKey] = value
|
|||
|
|
break
|
|||
|
|
case "remove":
|
|||
|
|
delete target[lastKey]
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return newState
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用示例
|
|||
|
|
const agent = new StateSyncAgent()
|
|||
|
|
const stateManager = new StateManager()
|
|||
|
|
|
|||
|
|
agent
|
|||
|
|
.runAgent({
|
|||
|
|
runId: "run_state_sync",
|
|||
|
|
threadId: "thread_123",
|
|||
|
|
messages: [],
|
|||
|
|
tools: [],
|
|||
|
|
context: [],
|
|||
|
|
})
|
|||
|
|
.subscribe({
|
|||
|
|
next: (event) => {
|
|||
|
|
stateManager.handleEvent(event)
|
|||
|
|
},
|
|||
|
|
complete: () => console.log("\n[Complete] State sync demo finished"),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
export { StateSyncAgent, StateManager, AgentState }
|