
CopilotKit 双向共享状态实战基于 LangGraph FastAPI 的前后端读写同一状态对象【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit导读本篇文章围绕 CopilotKit 集成仓库中的 Shared StateRead Write演示展开它展示了在 CopilotKit 与 LangGraph由 FastAPI 托管架构下前端 UI 与后端 Agent双向读写同一个状态对象的完整闭环前端通过agent.setState(...)把用户偏好写入state.preferences后端中间件每轮读取并将其注入 System Prompt从而实时影响模型回答反过来Agent 通过set_notes工具把笔记写回state.notes前端通过useAgent(...)订阅状态变化并即时重渲染。读完本文你将掌握 CopilotKit v2 中共享状态读写的完整调用链、核心源码实现以及可复制的实战写法。演示概览UI 与 Agent 双向共享状态该演示位于仓库showcase/integrations/langgraph-fastapi/src/app/demos/shared-state-read-write/配套的 LangGraph Agent 定义在showcase/integrations/langgraph-fastapi/src/agents/src/shared_state_read_write.py。整体交互闭环如下UI → Agent写入方向侧边栏表单姓名、语气、语言、兴趣通过agent.setState(...)写入state.preferences后端中间件PreferencesInjectorMiddleware在每一轮模型调用前读取该字段并注入 System Prompt。Agent → UI读取方向Agent 的set_notes工具把笔记写入state.notes侧边栏的笔记卡片在 Agent 每次更新后即时重渲染。完整回环在侧边栏修改偏好后Agent 的下一轮回复会立刻体现出来——语气、语言乃至直呼用户姓名。如何交互体验在侧边栏先填写偏好然后依次尝试以下指令Say hi and introduce yourself.Remember that I prefer morning meetings and that I dont eat dairy.Suggest a weekend plan based on my interests.观察 Agent 的回复如何随你的偏好变化而适配以及当你让它记住某些信息时侧边栏如何实时出现新的笔记卡片。这些引导语句在 suggestions.ts 中通过useConfigureSuggestions预置为三个启动建议按钮。前端用 useAgent 订阅状态、用 setState 写入状态订阅 Agent 状态变化页面入口 page.tsx 中DemoContent组件通过useAgent订阅整个 Agent 的状态变更const { agent } useAgent({ agentId: shared-state-read-write, updates: [UseAgentUpdate.OnStateChanged], });传入UseAgentUpdate.OnStateChanged后只要 Agent 通过工具或Command(update...)变更了自身状态例如set_notes写回notes该 hook 就会触发重渲染侧边栏面板随即反映最新值。这正是Agent → UI读取方向的订阅基础。读取状态的用法非常直接const agentState agent.state as RWAgentState | undefined; const preferences agentState?.preferences ?? INITIAL_PREFERENCES; const notes agentState?.notes ?? [];其中RWAgentState明确刻画了双向共享状态的形状——preferences由 UI 通过setState写入notes由 Agent 通过set_notes工具写入并被 UI 读取interface RWAgentState { preferences: Preferences; notes: string[]; }用 setState 完成 UI 写入同一段代码中agent.setState({ preferences, notes })承担了所有 UI 侧写入编辑表单与点击笔记Clear按钮都经由这一个调用。首次挂载时还会通过useEffect把初始偏好与空笔记种子写入 Agent 状态保证第一轮对话时后端就有数据可读useEffect(() { if (!agentState?.preferences) { agent.setState({ preferences: INITIAL_PREFERENCES, notes: [], } as RWAgentState); } }, []);偏好变更的处理函数体现了保留 Agent 已写入内容的细节——每次写入preferences时必须同时带上当前的notes否则会把 Agent 写好的笔记覆盖掉const handlePreferencesChange (next: Preferences) { agent.setState({ preferences: next, notes, // preserve what the agent has written } as RWAgentState); };清空笔记同样是写回 Agent 状态保留偏好不变从而在同一个字段上演示读写双向const handleClearNotes () { agent.setState({ preferences, notes: [] } as RWAgentState); };纯受控组件的设计边界preferences-card.tsx 与 notes-card.tsx 刻意保持不知晓 Agent的纯受控组件设计偏好卡片只是把每次编辑通过onChange冒泡给父级由 page.tsx 统一路由进agent.setState笔记卡片则只是渲染父级传入的state.notes唯一的写回行为Clear 按钮也以onClearprop 形式暴露。这种分层让状态接线集中在一层卡片可独立测试、可复用。偏好卡片底部还会以 JSON 形式实时展示当前共享状态便于调试验证对应pref-state-json测试锚点。偏好数据模型在 preferences-card.tsx 中定义export interface Preferences { name: string; tone: formal | casual | playful; language: string; interests: string[]; }页面骨架由 demo-layout.tsx 提供左侧为偏好卡片与笔记卡片右侧内嵌CopilotSidebar聊天面板并通过CopilotKit runtimeUrl/api/copilotkit agentshared-state-read-write将前端接入运行时与指定 Agent。后端LangGraph Agent 的读写实现Agent 状态 Schema后端定义了两个共享状态槽位——preferencesUI 写入、Agent 读取与notesAgent 写入、UI 读取class AgentState(BaseAgentState): Bidirectional shared state between UI and agent. preferences: Preferences notes: list[str]其中Preferences使用TypedDict声明name、tone、language、interests与前端Preferences接口一一对应形成前后端同一份状态契约。Agent → UIset_notes 工具通过 Command 写回set_notes是一个带ToolRuntime参数的工具它接收完整的最新笔记列表不是增量 diff通过返回Command(update{...})把notes与一条ToolMessage一并写回 Agent 状态tool def set_notes(notes: list[str], runtime: ToolRuntime) - Command: Replace the notes array in shared state with the full updated list. return Command( update{ notes: notes, messages: [ ToolMessage( contentNotes updated., tool_call_idruntime.tool_call_id, ) ], } )工具的 docstring 明确了使用约定当用户要求记住某事、或 Agent 观察到值得在 UI 面板呈现的信息时调用它必须传递完整列表已有笔记 新笔记且每条笔记保持简短 120 字符。UI → AgentPreferencesInjectorMiddleware 注入 System PromptPreferencesInjectorMiddleware继承自AgentMiddleware通过重写wrap_model_call同步与awrap_model_call异步两个钩子在每一轮模型调用前从request.state读取preferences拼接成一条SystemMessage前置到消息序列头部class PreferencesInjectorMiddleware(AgentMiddleware[AgentState, Any]): state_schema AgentState def wrap_model_call(self, request, handler): prefs request.state.get(preferences) or {} prefs_message self._build_prefs_message(prefs) if prefs_message is None: return handler(request) return handler(request.override(messages[prefs_message, *request.messages]))_build_prefs_message把姓名、语气、语言、兴趣逐行拼进提示词并追加一句Tailor every response to these preferences. Address the user by name when appropriate.根据这些偏好定制每条回复适当情况下直呼用户姓名。实现里还有一个值得注意的边界处理如果prefs非空但只包含未知键或空值就不会拼接出任何有效内容行len(lines) 1此时直接返回None、不注入消息避免对模型谎报存在偏好。组装 AgentLangGraph create_agent最终通过create_agent组装模型使用ChatOpenAI(modelgpt-4o-mini)工具只挂set_notes中间件同时挂载CopilotKitMiddleware()负责 CopilotKit 协议与 LangGraph 的对接和PreferencesInjectorMiddleware()并显式传入state_schemaAgentState与系统提示词graph create_agent( modelChatOpenAI(modelgpt-4o-mini), tools[set_notes], middleware[CopilotKitMiddleware(), PreferencesInjectorMiddleware()], state_schemaAgentState, system_prompt( You are a helpful, concise assistant. The users preferences are supplied via shared state and will be added as a system message at the start of every turn. Always respect them. When the user asks you to remember something, or when you observe something worth surfacing in the UI, call set_notes with the FULL updated list of short note strings (existing notes new). ), )部署接线langgraph.json 与运行时路由该 Agent 作为 FastAPI 变体遵循图导出 注册的标准接线流程langgraph.json 第 33 行注册图入口shared_state_read_write: ./src/agents/src/shared_state_read_write.py:graph由 langgraph-cli 托管的 FastAPI 服务器承载该图route.ts 第 131 行在 CopilotKit 运行时侧注册agents[shared-state-read-write] createAgent(shared_state_read_write);把 Next.js 运行时路由与 LangGraph 部署桥接起来manifest.yaml 中声明了shared-state-read-write演示的路由/demos/shared-state-read-write及涉及的前后端源码文件清单。测试验证E2E 回归保障演示配套的 Playwright 测试位于 tests/e2e/shared-state-read-write.spec.ts覆盖了三个层面的行为验证面板挂载断言Your preferences与Agent Scratch pad两个面板均可视启动建议渲染断言 Greet me、Remember something、Plan a weekend 三个建议按钮存在共享状态感知回复回归用例点击 Greet me 后断言助手回复包含/shared-state co-pilot/i且不包含通用 showcase 助手话术点击 Plan a weekend 后断言回复包含/interests panel/i且不包含通用内容营销计划模板。测试注释中记录了这些回归用例的由来此前 Say hi and introduce yourself. 会错误命中 feature-parity.json 中裸userMessage: hi的 fixture 并返回通用回复修复方式是新增子串匹配更长的共享状态专属 fixtured5-all.json并优先命中。这说明共享状态写入是否真正生效是可以通过 E2E 断言直接验证的。关键要点与可复用结论一个状态对象两个写入方preferences由前端经agent.setState写入、后端中间件读取notes由后端经set_notes工具 Command(update...)写入、前端经useAgent({ updates: [OnStateChanged] })读取。前端写入时注意携带对方已写入的字段避免相互覆盖。中间件是 UI 写入进入模型上下文的唯一通道PreferencesInjectorMiddleware.wrap_model_call/awrap_model_call每轮把request.state[preferences]注入 System Prompt使前端写入对模型可见空偏好时的None短路避免误导模型。工具返回Command(update...)是 Agent 写回 UI 的标准姿势set_notes同时更新notes与messages一次返回即可完成状态变更与对话消息两条副作用。接线三件套缺一不可langgraph.json注册图 → 运行时路由createAgent注册 → Next.js 页面通过CopilotKit与CopilotSidebar指定同一agentId三处 ID 必须保持一致。前后端契约用类型对齐前端Preferences接口与后端PreferencesTypedDict 字段一一对应name / tone / language / interests是双向共享状态保持稳定的关键。【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考