CopilotKit 与 Langroid 集成实战:用 useAgentContext 实现只读 Agent 上下文(Readonly State)
【免费下载链接】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 展示项目(showcase)中 Langroid 集成模块的只读 Agent 上下文功能展开,讲解如何通过useAgentContext将前端 UI 状态(用户身份、时区、最近活动等)以单向、只读的方式发布给后端 Langroid Agent,并详细拆解官方 QA 验证清单(showcase/integrations/langroid/qa/readonly-state-agent-context.md)中每一条测试步骤的预期行为与底层实现。读完本文,你将掌握useAgentContext的调用方式、上下文数据的传输链路(Next.js Runtime → AG-UI → Langroid Agent Server),以及如何用 Playwright E2E 测试固化这类"只读上下文"行为。
一、功能定位:让 Agent"知道"但"不能改"的 UI 状态
在 CopilotKit 的前端框架体系中,Agent 有时需要了解"用户当前在界面上看到了什么",例如:
- 当前登录用户的显示名(identity);
- 用户所在的 IANA 时区(用于回答"现在几点"这类问题);
- 用户在应用内的最近活动记录(用于推荐下一步操作)。
这些值属于纯输入(input):Agent 只需要读取,绝不应该被 LLM 通过工具调用反向修改。为此,CopilotKit 提供了useAgentContext—— 一条单向的 UI → Agent 通道。
官方文档(showcase/shell-docs/src/content/docs/shared-state/agent-readonly.mdx)对它的设计定位概括为:
"props for the agent"(给 Agent 传的 props)。
与完整的共享状态(shared state,Agent 可以调用工具把状态写回 UI)不同,useAgentContext发布的值:
- 在每一轮对话中都会通过运行时的上下文注入被 Agent 看到;
- 没有任何 setter,也没有任何可写回的工具;
- 组件卸载时自动注销(例如离开当前页面,"当前记录"上下文随之消失)。
因此它天然适合用户身份、功能开关、当前选中的记录、滚动位置这类"UI 拥有、Agent 只读"的数据。
二、核心 API:useAgentContext 的调用方式与参数
Langroid 集成模块的 demo 页面位于 showcase/integrations/langroid/src/app/demos/readonly-state-agent-context/page.tsx,其核心调用如下:
useAgentContext({ description: "The currently logged-in user's display name", value: userName, }); useAgentContext({ description: "The user's IANA timezone (used when mentioning times)", value: userTimezone, }); useAgentContext({ description: "The user's recent activity in the app, newest first", value: recentActivity, });每个useAgentContext调用只接受两个字段:
| 参数 | 含义 | 注意事项 |
|---|---|---|
description | 一段简短的人类可读标签,Agent 会与 value 一起看到 | 官方文档强调它"像参数 docstring 一样重要",直接影响 LLM 理解该值的用途 |
value | 要发布的实际值(字符串、数组等) | 值变化时(React 重渲染)自动刷新注册项;组件卸载时自动移除 |
从 demo 可以看到,三处调用的value分别来自三个独立的useState:
const [userName, setUserName] = useState("Atai"); const [userTimezone, setUserTimezone] = useState("America/Los_Angeles"); const [recentActivity, setRecentActivity] = useState<string[]>([ ACTIVITIES[0], // "Viewed the pricing page" ACTIVITIES[2], // "Watched the product demo video" ]);官方文档同时指出:useAgentContext并不关心 value 来自哪里——可以是本地useState、React Context、Redux、查询缓存等任意来源,唯一要求是值的标识足够稳定,避免 React 渲染循环。在真实应用中,这些值通常会来自 auth provider、路由 hook 或领域状态仓库。
三、demo 界面:Context 卡片与实时 JSON 预览
demo 页面布局由 demo-layout.tsx 实现,整个界面被称为 "Agent Context Inspector",包含三张卡片:
- Identity 卡片:展示 Name(文本框)与 Timezone(下拉选择,包含
America/Los_Angeles、Asia/Tokyo等 6 个时区),以及一个根据名字首字母和时区区域动态渲染的头像; - Recent Activity 卡片:5 项可勾选的活动(如 "Viewed the pricing page"、"Started the 14-day free trial"),勾选状态决定该活动是否"Visible to the agent";
- Published Context 卡片:全宽度的 JSON 预览区,实时展示真正广播给 Agent 的载荷:
{ "name": "Atai", "timezone": "America/Los_Angeles", "recentActivity": ["Viewed the pricing page", "Watched the product demo video"] }这个 JSON 预览是 QA 验证的核心观察点——所有字段编辑都会立即反映到该 JSON 中,形成"所见即 Agent 所得"的可视化反馈闭环。
demo 还在 suggestions.ts 中通过useConfigureSuggestions预置了三条与只读上下文强相关的建议提问:
- "Who am I?" → "What do you know about me from my context?"
- "Suggest next steps" → "Based on my recent activity, what should I try next?"
- "Plan my morning" → "What time is it in my timezone and what should I do for the next hour?"
这三条建议恰好覆盖了 QA 清单中要验证的 Agent 行为(身份识别、活动推荐、时区感知)。
四、数据链路:上下文如何从 UI 到达 Langroid Agent
只读上下文并不直接"拼"进前端代码,而是走一条完整的前后端链路,由两部分组成:
4.1 前端运行时代理(Next.js route.ts)
showcase/integrations/langroid/src/app/api/copilotkit/route.ts 使用@copilotkit/runtime/v2的CopilotRuntime与createCopilotRuntimeHandler创建单一路由(mode: "single-route"),并通过HttpAgent(来自@ag-ui/client)将请求按AG-UI 协议代理到后端 Langroid Agent Server(默认http://localhost:8000,可用环境变量AGENT_URL覆盖):
const AGENT_URL = process.env.AGENT_URL || "http://localhost:8000"; function createAgent(path = "/") { return new HttpAgent({ url: `${AGENT_URL}${path}` }); }该 route 将包括"readonly-state-agent-context"在内的 30 余个 demo agent 名称统一注册到同一个后端统一 Agent 上(代码注释明确说明"Read-only agent context — frontend exposes useAgentContext; same agent")。也就是说,只读上下文完全由前端运行时注入,后端 Agent 本身不需要任何专用工具。
4.2 后端 Langroid Agent Server(agent_server.py)
showcase/integrations/langroid/src/agent_server.py 是一个 FastAPI 服务。由于 Langroid 没有原生的 AG-UI 适配器,它实现了一个自定义 SSE 端点,把 Langroid 的ChatAgent与 AG-UI 事件流相互转换。文件头注释明确指出:
The Next.js CopilotKit runtime proxies requests here via AG-UI protocol.
从 agui_adapter.py 的代码可以看到,@app.post("/")的run_agent会把请求体强转为RunAgentInput(RunAgentInput(**body)),再进入 AG-UI 的 SSE 事件管道。前端运行时在每一轮将useAgentContext注册的条目注入消息历史后,Agent 便能在该轮回答中感知到这些上下文值——这正是 QA 中"Agent 能引用上下文"的底层原因。
4.3 页面接线
demo 页面的外层通过<CopilotKit runtimeUrl="/api/copilotkit" agent="readonly-state-agent-context">绑定运行时与 Agent,再挂载CopilotPopup(占位提示为 "Ask about your context...")作为对话入口。整个结构是:React 状态 → useAgentContext 注册 → CopilotKit runtime 注入 → AG-UI 代理 → Langroid 统一 Agent 读取。
五、QA 验证清单逐条拆解:默认值、操作与预期
原 QA 文档(showcase/integrations/langroid/qa/readonly-state-agent-context.md)定义了 6 条人工验收步骤,下面逐条给出其预期行为与源码依据。
5.1 导航到 /demos/readonly-state-agent-context
页面路由由src/app/demos/readonly-state-agent-context/page.tsx提供,浏览器访问后应渲染出三张 Context 卡片与右下角的 Copilot 弹出式聊天窗。
5.2 验证 context card 默认 Name 为 "Atai"
useState("Atai")是 Name 的默认值,Identity 卡片与 Published Context JSON 中的"name": "Atai"应同时呈现。E2E 测试用page.getByTestId("identity-name")断言其文本为 "Atai"(见下文测试章节)。
5.3 将 Name 改为 "Jordan",验证 published JSON 更新
修改 Name 输入框会触发setUserName,useAgentContext注册项随之刷新,Published Context 的 JSON 预览立即变为"name": "Jordan"。这说明上下文与 UI 状态是实时绑定的,无需重新刷新页面或重新建立会话。
5.4 切换 recent-activity 复选框,验证 JSON 更新
toggleActivity对recentActivity数组做包含/排除切换:
const toggleActivity = (activity: string) => { setRecentActivity((prev) => prev.includes(activity) ? prev.filter((a) => a !== activity) : [...prev, activity], ); };每次勾选/取消,recentActivity数组变化,JSON 预览中的活动列表同步增减。默认勾选的是ACTIVITIES[0](Viewed the pricing page)与ACTIVITIES[2](Watched the product demo video)。
5.5 提问 "What do you know about me?",Agent 应引用上下文值
这是整个功能的核心验证:点击 "Who am I?" 建议后,Agent 的回答应包含从上下文读到的身份信息(如名字)。在真实 LLM 环境下,回答会引用当前发布的 context 字段;在 CI 的 aimock 录制环境下,该提示词对应固定的 fixture,回复以 "I see you're Atai" 开头(见 E2E 测试注释),证明useAgentContext的数据确实到达了模型侧。
5.6 修改时区后提问 "What time is it for me?",Agent 应反映新时区
将 Timezone 从默认的America/Los_Angeles改为其他时区(如Asia/Tokyo)后,userTimezone上下文更新,Agent 回答时间类问题时将基于新的时区作答。这验证了 description("The user's IANA timezone (used when mentioning times)")对模型行为的指导作用。
六、E2E 测试:把 QA 清单固化为可回归的断言
QA 清单的人工步骤在 showcase/integrations/langroid/tests/e2e/readonly-state-agent-context.spec.ts 中被完整自动化(测试文件头注释明确标注 "QA reference: qa/readonly-state-agent-context.md")。测试覆盖了四个关键场景:
- 页面加载:断言
context-card可见、composer 占位符 "Ask about your context..." 可见; - 编辑联动:将 Name 填入 "Jamie" 后断言 JSON 出现
"name": "Jamie";将时区切换为Asia/Tokyo后断言 JSON 出现"timezone": "Asia/Tokyo"; - 身份问答:先断言 Identity 卡默认显示 "Atai" / "America/Los_Angeles" / 头像 "A",再点击 "Who am I?" 建议,断言助手回复以 "I see you're Atai" 开头(aimock fixture 固定输出);
- 活动勾选默认值:断言 "pricing page" 与 "product demo video" 两个复选框默认处于 checked 状态;
- 活动问答:点击 "Suggest next steps" 后,断言助手回复引用默认活动("Since you recently viewed the pricing page and watched the product demo video...")。
测试注释还特别说明了测试策略:两个建议提示词被固定映射到 aimock 确定性 fixture,以保证 CI 稳定性;在 Railway 真实部署上,同样的提示词会得到引用已发布上下文字段的真实 LLM 回复——"证明端到端 useAgentContext 接线正确"。
七、只读上下文与共享状态的选择边界
Langroid 集成模块同时提供了共享状态的同类 demo(如 shared-state-read-write.py 对应的读+写 demo,后端通过set_notes等工具写回状态并发射STATE_SNAPSHOT事件)。两者边界可归纳为:
| 维度 | useAgentContext(只读) | Shared State(读+写) |
|---|---|---|
| 方向 | UI → Agent 单向 | UI ↔ Agent 双向 |
| Agent 可否修改 | 不能(无 setter、无写回工具) | 可以(通过工具调用 mutate) |
| 典型场景 | 登录用户、时区、功能开关、当前记录 | 需要双方协作编辑的工作区(笔记、偏好、进度) |
| 卸载行为 | 组件卸载自动注销 | 显式管理 |
官方文档(agent-readonly.mdx)给出的选择建议是:当值是一个"输入"而非"字段"——即"每一轮传给 Agent 的上下文对象",而不是"双方共同编辑的工作区"——时,优先使用useAgentContext;当需要同时支持读与写时,才升级为完整的 共享状态。
八、小结
Langroid 集成中的 Readonly State (Agent Context) demo 完整演示了 CopilotKit 只读上下文的标准用法:前端通过useAgentContext({ description, value })发布身份、时区、活动等 UI 状态,Next.js Runtime 借助 AG-UI 协议代理到 Langroid Agent Server,使 Agent 每一轮都能感知这些值但永远无法修改它们。QA 清单的 6 条步骤(导航、默认值校验、字段编辑联动、JSON 实时更新、身份问答、时区感知问答)既是人工验收的 checklist,也在 E2E 测试中固化为自动化断言。对于任何需要"让 Agent 理解当前 UI 语境"但又必须保证数据不被 Agent 篡改的场景,这一模式都是可直接复用的参考实现。
【免费下载链接】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),仅供参考