上下文注入:应用状态如何成为 Agent 的感知
上下文注入:应用状态如何成为 Agent 的感知
学习目标
理解 CopilotKit 的上下文注入机制:
ContextStore的内部实现useCopilotReadable如何将 React 状态注入 Agent 上下文- 上下文在每次 Agent 运行中的自动附加
- 上下文数据的 Token 优化策略
前置知识
本章涉及上下文管理与记忆系统的通用原理,建议先阅读:
下文假设你已理解上述概念,直接聚焦 CopilotKit 的上下文注入实现。
项目实践
ContextStore 内部实现
CopilotKitCore 内部的 ContextStore 是一个键值对存储:
ContextStore 结构(伪代码):{ contexts: Map<description, { description: string, data: JSON }>,
set(description, data) → 设置或更新上下文 delete(description) → 移除上下文 getAll() → Record<description, Context> // 每次 Agent 运行时自动调用}关键特性:
- 键是
description(人类可读的描述文本) - 值是任意 JSON 可序列化数据
- 每次 Agent 运行时,
getAll()返回的上下文自动包含在RunAgentInput中
useCopilotReadable Hook
React 开发者通过 useCopilotReadable 声明式注入组件状态:
useCopilotReadable({ description: "当前页面内容", value: pageContent});
useCopilotReadable({ description: "用户选中的商品", value: selectedProduct, category: "product" // 可选分类});工作原理(源码验证):
- Hook 调用
core.getContextStore().set(description, value) - 值变化时自动更新 ContextStore
- Agent 运行时获取所有上下文
useMakeCopilotDocumentReadable
针对文档/页面的专用上下文注入:
useMakeCopilotDocumentReadable({ title: document.title, content: markdownContent, metadata: { url: window.location.href }});与 useCopilotReadable 的区别:
useCopilotReadable:注入任意 JSON 数据useMakeCopilotDocumentReadable:专门用于文档内容(标题、内容、元数据)
上下文在 RunAgentInput 中的结构
每次 Agent 运行时,上下文作为 RunAgentInput 的一部分发送:
RunAgentInput 结构(伪代码):{ messages: [...], // 聊天消息历史 tools: [...], // 可用工具列表 context: { // 应用上下文 "当前页面内容": "页面文本...", "用户选中的商品": { name: "商品A", price: 99 } }, threadId: "...", state: {...}}上下文被注入到 Agent 的系统提示中,使其能够感知当前应用状态。
上下文变更通知
CopilotKitCoreSubscriber 提供 onContextChanged 回调:
core.subscribe({ onContextChanged: ({ context }) => { // 上下文更新时的响应逻辑 }});注意:上下文更新是实时的,但 Agent 只在下次运行时获取最新上下文。
问题与规避
| 问题 | 规避策略 |
|---|---|
| 上下文数据过大导致 Token 膨胀 | 只注入必要数据;使用摘要/压缩策略(如只传页面标题和摘要而非全文) |
| 上下文更新过于频繁 | useCopilotReadable 内部有节流机制;避免在 value 中放入频繁变化的引用 |
| 敏感数据泄露到 Agent | 避免在 value 中传入 token、密码等;中间件层做数据过滤 |
| 上下文描述文本不唯一 | 后注册的覆盖先注册的;建议使用结构化描述(如 “页面:xxx” 而非 “页面”) |
设计取舍
声明式 vs 命令式上下文注入
声明式(useCopilotReadable):
- 优势:React 风格,组件自行声明需要暴露的状态
- 代价:每个组件独立注册,可能导致冗余数据
命令式(core.getContextStore().set() 直接调用):
- 优势:集中管理,精确控制注入时机
- 代价:与 React 生命周期解耦,需要手动管理
选择策略:组件级上下文用 Hook,全局上下文用直接调用。
上下文 vs 消息历史
| 维度 | 上下文 | 消息历史 |
|---|---|---|
| 内容 | 应用状态(页面内容、选中数据等) | 聊天对话 |
| 更新方式 | 实时替换(同一 key 的最新值) | 追加(不可变历史) |
| 生命周期 | 随组件挂载/卸载变化 | 随线程持久化 |
| 用途 | 提供当前状态的”快照”感知 | 提供对话的”历史”感知 |
参考来源
packages/core/src/core/context-store.tspackages/react-core/src/hooks/use-copilot-readable.tspackages/react-core/src/hooks/use-make-copilot-document-readable.ts