news 2026/9/13 3:11:47

CopilotKit 只读 Agent Context 集成验证指南:Google ADK 环境下的 useAgentContext 端到端 QA 实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CopilotKit 只读 Agent Context 集成验证指南:Google ADK 环境下的 useAgentContext 端到端 QA 实践

CopilotKit 只读 Agent Context 集成验证指南:Google ADK 环境下的 useAgentContext 端到端 QA 实践

【免费下载链接】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 与 Google ADK(Agent Development Kit)集成仓库中的一份端到端质量验证文档展开,系统讲解只读 Agent Context(useAgentContext)功能在showcase/integrations/google-adk集成示例中的完整测试路径与底层实现原理。读者将掌握如何验证"前端只读上下文 → CopilotKit 会话状态 → ADK 后端按轮注入系统提示词 → Agent 实时感知最新值"这条数据链路的每个环节,并理解其源码级实现依据,可直接用于自行搭建与复测同类只读上下文功能。

文档定位与验证目标

本验证文档位于 showcase/integrations/google-adk/qa/readonly-state-agent-context.md,属于google-adk集成示例的 QA 测试规范。它覆盖的演示功能在 manifest.yaml 中被登记为readonly-state-agent-context("Frontend provides read-only context to the agent via useAgentContext"),归类为agent-state能力。

该功能解决的核心问题是:前端应用通常掌握用户身份、时区、近期行为等私有信息,但 Agent 默认无从知晓useAgentContext允许前端把这些 JSON 可序列化数据以"只读"方式发布给 Agent——Agent 每一轮都能读取最新值,但没有能力修改这些值,从而保证"前端仍是这些数据的唯一事实来源(single source of truth)"。

前置条件

依据原文档,执行该 QA 用例前需满足三项条件:

  1. Demo 已部署且可访问:即/demos/readonly-state-agent-context页面能够正常加载;
  2. Agent 后端健康:通过/api/health检查后端存活。仓库中 route.ts 的GET处理函数会向后端AGENT_URL(默认http://localhost:8000)发起带 3 秒超时的健康探测,并返回agent_status: "reachable" | "unreachable (原因)",可作为检查手段;
  3. Agent 已挂载readonly_state_agent_context_agent必须在 ADK 服务端注册。它定义在 readonly_state_agent_context_agent.py 中,并在 registry.py 中通过"readonly-state-agent-context": AgentSpec(readonly_state_agent_context_agent)挂载进AGENT_REGISTRY。注册表注释说明:agent_server.py会遍历该注册表,把每个条目以<agent_name>路径挂载为 ADKAgent 中间件,Next.js 的/api/copilotkit路由再把同名请求代理到对应后端路径。

测试目标页面结构

被测试的页面是 page.tsx,其结构包含:

  • Agent Context 卡片data-testid="context-card"):由 demo-layout.tsx 渲染,分为 Identity(身份)、Recent Activity(近期活动)、Published Context(已发布上下文 JSON 预览)三个区域;
  • 角落的弹出式聊天窗口<CopilotPopup>组件,占位文本为 "Ask about your context...";
  • 描述文本:页面标题下方明确写道 "Edit fields below and watch the data flow into the agent. The agent can read this context, butcannot modifyit.",即该卡片与聊天窗口构成一个可视化的"上下文监视器(Agent Context Inspector)"。

页面同时通过useConfigureSuggestions注册了三条建议快捷语(见 suggestions.ts),并配置为available: "always"

测试步骤详解

以下步骤完整继承自原 QA 文档,并结合仓库源码标注了对应的可验证点。

步骤 1:基础功能

  • 导航到readonly-state-agent-context演示页面;
  • 验证 "Agent Context" 卡片可见(data-testid="context-card")——该卡片在 demo-layout.tsx 中以data-testid="context-card"标注;
  • 验证描述文本引用了useAgentContext:"Edit fields below and watch the data flow into the agent. The agent can read this context, but cannot modify it.";
  • 验证弹出式聊天在角落渲染,占位文本为 "Ask about your context..."——对应 page.tsx 中CopilotPopuplabels={{ chatInputPlaceholder: "Ask about your context..." }}配置;
  • 通过聊天发送一条基础消息(如 "Hello");
  • 验证 Agent 以文本消息回复。

步骤 2:特性专项检查

初始上下文状态
  • 验证 Name 输入框(data-testid="ctx-name")默认为 "Atai"——对应 page.tsx 中useState("Atai")的初始值;
  • 验证 Timezone 下拉框(data-testid="ctx-timezone")默认为 "America/Los_Angeles"——对应同文件useState("America/Los_Angeles")
  • 验证 Timezone 下拉框提供六个时区选项:America/Los_AngelesAmerica/New_YorkEurope/LondonEurope/BerlinAsia/TokyoAustralia/Sydney。这些常量定义在 demo-layout.tsx 的TIMEZONES数组中,全部为 IANA 时区标识;
  • 验证 Recent Activity 复选框列表包含五项:"Viewed the pricing page"、"Added 'Pro Plan' to cart"、"Watched the product demo video"、"Started the 14-day free trial"、"Invited a teammate"——对应ACTIVITIES常量(demo-layout.tsx);
  • 验证默认勾选两项活动:"Viewed the pricing page"(ACTIVITIES[0])与 "Watched the product demo video"(ACTIVITIES[2])——对应 page.tsx 的初始 state;
  • 验证 Published Context JSON 预览(data-testid="ctx-state-json")显示{ "name": "Atai", "timezone": "America/Los_Angeles", "recentActivity": [...] }——该 JSON 由 demo-layout.tsx 中的publishedContext对象经JSON.stringify(publishedContext, null, 2)渲染,任何 state 变化都会立即反映其中。
建议快捷语(Suggestions)
  • 验证 "Who am I?" 建议可见;
  • 验证 "Suggest next steps" 建议可见;
  • 验证 "Plan my morning" 建议可见。

三条建议分别映射到不同的实际消息体(见 suggestions.ts):

建议标题实际发送给 Agent 的消息
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?"
Agent 读取用户名(useAgentContext)
  • 点击 "Who am I?" 建议(或直接询问 "What is my name?");
  • 验证 Agent 回复中以 "Atai" 称呼用户;
  • 将 Name 输入框(data-testid="ctx-name")修改为 "Jamie";
  • 验证 Published Context JSON 更新为"name": "Jamie"
  • 再次询问 "What is my name?";
  • 验证 Agent 此时回复 "Jamie"(而非 "Atai")。

这一步验证了上下文是按轮实时生效的:修改立即反映在 JSON 预览中,且 Agent 下一轮回复即使用新值,不存在"陈旧上下文(stale context)"。

Agent 读取时区
  • 将 Timezone 下拉框(data-testid="ctx-timezone")切换为 "Asia/Tokyo";
  • 验证 Published Context JSON 更新为"timezone": "Asia/Tokyo"
  • 点击 "Plan my morning" 建议;
  • 验证 Agent 在讨论时间时引用 Tokyo / JST / Asia/Tokyo。

注意这里的描述语义:useAgentContext在 page.tsx 中注册时附带的description为 "The user's IANA timezone (used when mentioning times)",这正是引导 LLM 在涉及时间的话题中使用该字段的提示语义。

Agent 读取近期活动
  • 取消所有默认活动,仅勾选 "Started the 14-day free trial" 和 "Invited a teammate";
  • 验证 Published Context JSON 显示新的recentActivity数组;
  • 点击 "Suggest next steps" 建议;
  • 验证 Agent 的回复引用 trial 和/或 invited-teammate 相关活动(而不再引用 pricing page 或 demo video)。

这一步验证了数组型上下文值的动态替换:Agent 每一轮拿到的都是最新的活动列表,且只依据当前可见的活动生成建议。

步骤 3:错误处理

  • 将 Name 输入框清空(空字符串)后询问 "What is my name?"——Agent 应优雅处理(不崩溃)。前端层面,demo-layout.tsx 对空用户名显示 "Anonymous" 兜底,头像取userName.charAt(0).toUpperCase() || "?";后端层面,_inject_context对空值条目会跳过(见下文原理);
  • 发送空聊天消息——输入应被拒绝且不报错;
  • 验证正常使用过程中无控制台错误;
  • 验证 Agent无法修改上下文值(Name / Timezone / Activity 复选框始终由用户控制)。

预期结果

原文档明确列出以下验收标准:

  • 上下文卡片与聊天在 3 秒内加载完成;
  • Agent 在 10 秒内响应;
  • 每次对 Name / Timezone / Recent Activity 的修改都立即反映在 Published Context JSON 中;
  • Agent 每一轮的回复都反映当前上下文值(无陈旧上下文);
  • 无 UI 错误或布局破坏。

底层实现原理:从 useAgentContext 到 ADK before-model 回调

理解测试步骤背后的机制,需要追踪这条完整链路。QA 文档断言的所有"实时性"与"只读性"行为,都能在源码中找到对应实现。

前端:useAgentContext 钩子

演示页面通过三次调用发布上下文(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 });

钩子本身实现在 packages/react-core/src/v2/hooks/use-agent-context.tsx。其关键行为包括:

  • 入参类型AgentContextInputdescription(人类可读的描述)与value(任意JsonSerializable值)组成;
  • 值序列化:非字符串的value通过JSON.stringify转为字符串后发布——这就是recentActivity数组能够进入 Agent 视线的原因;
  • 生命周期管理:钩子在useLayoutEffect中调用copilotkit.addContext({ description, value })注册上下文,并在 effect 清理时调用removeContext(id)反注册。当description或序列化后的stringValue变化时 effect 重跑,实现"改即发布、即时生效"。

中间层:上下文如何进入会话状态

依据 agent_config_agent.py 的模块注释(同一机制的姊妹实现),useAgentContext发布的条目经 ag-ui-adk 中间件落地为state["copilotkit"]["context"],是一个{description, value}字典的列表——同一页面上多个组件可以各自发布,条目共存于该列表。

后端:before_model_callback 按轮注入

ADK 侧的LlmAgent配置在 readonly_state_agent_context_agent.py:

readonly_state_agent_context_agent = LlmAgent( name="ReadonlyStateAgentContextAgent", model=get_model(), instruction=_INSTRUCTION, tools=[AGUIToolset()], before_model_callback=_inject_context, after_model_callback=stop_on_terminal_text, )

_inject_context是整条链路的核心,它实现了三个关键设计:

  1. 状态容错:从callback_context.state中读取state["copilotkit"]["context"]时,对state缺失、非 dict、context非 list 等情况逐级兜底为空列表并记录 warning 日志,避免形变状态导致请求失败;
  2. 格式化注入块_format_context把条目列表渲染为以固定签名[agent-context] frontend-supplied context:开头、以结束标记Treat this context as read-only background information.结尾的文本块,每个条目一行- {description}: {value};无有效条目时返回None不注入;
  3. 剥离旧块(strip-prior-block):注入前先在system_instruction中查找上一轮的签名与结束标记,找到则只把旧块从原文中切除并保留头部与尾部,再拼接新块。若签名存在但找不到结束标记,则保持原文不动并告警,避免误删用户内容。这一步是"无陈旧上下文"验收标准的实现保障——每一轮都重建上下文块,而不是累加。

同时注意注入块末尾那句结束标记本身就是一条指令:"Treat this context as read-only background information."——它与静态_INSTRUCTION("The frontend passes read-only context entries via useAgentContext; they are added to your system prompt every turn. Use them when relevant.")共同构成了"Agent 可读但不可改"的行为约束,这也是 QA 文档"验证 Agent 无法修改上下文值"一项的提示词级保障。

路由层:前后端名称映射

route.ts 中的agentNames数组包含"readonly-state-agent-context",每个名称在buildAgents中映射为一个HttpAgent({ url: \${AGENT_URL}/${name}` }),即前端请求经 CopilotRuntime 代理到http://localhost:8000/readonly-state-agent-context`,与注册表挂载路径一一对应。

自动化验证支撑

仓库为同一套用例提供了 Playwright 自动化测试:tests/e2e/readonly-state-agent-context.spec.ts。测试文件的头部注释明确指出它以本文对应的 QA 文档为参考("QA reference: qa/readonly-state-agent-context.md"),可以视为该 QA 规范的机器可执行版本。值得关注的点包括:

  • 确定性断言:例如 "Who am I?" 建议点击后断言助手回复以 "I see you're Atai" 开头、"Suggest next steps" 后断言 "Since you recently viewed the pricing page and watched the product demo video"——这些回复由 aimock 夹具(showcase/aimock/d5-all.json)固定,保证 CI 中稳定;而在 Railway 部署环境下相同提示词会得到真实 LLM 回复,从而端到端证明useAgentContext接线正确;
  • 覆盖与 QA 文档对应:测试覆盖了页面加载、编辑 name/timezone 后 JSON 预览更新、建议点击后身份识别、默认勾选活动、活动驱动回复等与 QA 步骤一一对应的场景;
  • 严格的 testid 契约:测试断言了context-cardctx-namectx-timezonectx-state-jsonidentity-nameidentity-timezoneidentity-avataractivity-*等 testid,QA 手工用例与自动化用例共享同一套 DOM 契约,二者互为校验。

延伸:与可写共享状态的对比

仓库中还提供了shared-state-readshared-state-read-writeshared-state-streaming等共享状态演示(见 manifest.yaml),它们走的是agent.setState/PredictStateMapping可写双向通道。本 QA 文档对应的readonly-state-agent-context恰好与之形成对照:useAgentContext是"前端到 Agent"的单向、只读数据通道,前端保留数据所有权,Agent 只被授权读取。选择哪种模式取决于数据所有权需求——当 UI 状态必须由用户界面独占控制、Agent 仅需知情时,只读上下文是更安全、更符合直觉的方案。

小结

本文以 readonly-state-agent-context.md 为骨架,完整继承了其前置条件、三大类测试步骤与预期结果,并结合仓库源码解释了每条断言背后的实现依据:前端useAgentContext的注册与清理、中间层对state["copilotkit"]["context"]的落地、ADKbefore_model_callback的按轮注入与旧块剥离、以及 Playwright 自动化用例对同一契约的机器化验证。对于需要在 CopilotKit 生态中实现"前端只读上下文供给 Agent"的开发者,该 QA 文档与其配套源码是一份可直接复用的参考实现与验收清单。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 3:10:16

微博公开数据爬取实战:登录态维护、文本清洗与中文词云生成

简介&#xff1a;基于Python的微博数据采集与词云可视化项目源码包&#xff0c;面向计算机相关专业的毕业设计、课程设计以及爬虫与文本分析入门学习者。项目采用Scrapy框架搭建完整爬虫工程&#xff0c;包含爬虫核心逻辑、中间件、管道处理、设置配置与自定义工具模块&#xf…

作者头像 李华
网站建设 2026/9/13 3:08:51

DeepSeek Harness本地部署实战:从环境准备到跑通第一个任务

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 3:07:21

Vector Doris Sink 实战:通过 Stream Load 将日志批量写入 Apache Doris

Vector Doris Sink 实战&#xff1a;通过 Stream Load 将日志批量写入 Apache Doris 【免费下载链接】vector A high-performance observability data pipeline. 项目地址: https://gitcode.com/GitHub_Trending/vect/vector Vector 的 doris sink 负责将日志数据投递到…

作者头像 李华