学习笔记 · Obsidian
Deep Agents Python:上下文、记忆、检索与子代理
核心思想
上下文工程不是把所有资料塞给模型,而是在正确时间、用正确格式、给正确执行单元提供刚好够用的信息。Deep Agents 通过五类机制实现:
| 机制 | 生命周期 | 典型内容 |
|---|---|---|
| 输入上下文 | 每次运行组装 | system prompt、AGENTS.md、技能元数据、工具说明 |
| Runtime context | 单次 invoke | user_id、角色、连接、短期凭证、开关 |
| 图状态 | thread 内可变并可 checkpoint | messages、自定义计数、todos、异步任务索引 |
| 上下文压缩 | 接近窗口上限时 | 大工具结果卸载、旧消息摘要 |
| 长期记忆 | 跨 thread | 偏好、规则、经验、知识文件 |
把身份、凭证等不可变运行信息放 context;把需要回放和检查点的可变信息放 state;把大材料放 filesystem;不要通过聊天消息模拟这三者。
系统提示如何组装
主代理最终系统消息大致按以下来源形成:
- 调用方 system_prompt
- Deep Agents 基础提示
- memory 中的 AGENTS.md 与使用规则
- skills 位置、名称、描述与使用规则
- 文件系统和 execute 工具说明
- 子代理 task 工具说明
- 调用方中间件追加的提示
- HITL 提示
system_prompt 是静态的。若提示需要根据用户角色、feature flag 或 store 内容变化,使用 dynamic_prompt 中间件读取 runtime context/store。若只有工具需要这些信息,直接在工具的 ToolRuntime 中读取,不必额外污染系统提示。
未使用的内置工具仍会在每轮携带 schema;确定永不使用时用 excluded_tools 移除,先降低基础上下文,再依赖运行中的卸载和摘要。
Context 与 State
Runtime context
- 由 context_schema 定义,用 invoke / ainvoke 的 context 参数传入。
- 不会自动暴露给模型,只有工具或中间件读取并写入消息时模型才看到。
- 会自动传播给同步子代理。
- 适合 user_id、权限角色、API client、数据库连接和 feature flag。
自定义 State
- 需要 deepagents 0.6.6+。
- state_schema 必须继承 DeepAgentState,保留 messages 的 DeltaChannel reducer,避免 checkpoint 随历史呈非线性膨胀。
- 适合全运行计数器、累积结果、审计标记和工具/中间件共享的可变数据。
- 声明式同步 SubAgent 继承父 state schema;已编译子图和远程 AsyncSubAgent 不会自动继承。
自动上下文压缩
每个 create_deep_agent 默认已包含卸载和摘要,不需额外开启。
大结果卸载
- 工具结果超过默认 20,000 token:写入 backend,只在消息里保留文件路径和前 10 行预览。
- write/edit 的巨大输入已存在文件中;上下文达到窗口约 85% 时,较旧工具调用可被路径引用替换。
- Agent 之后用 read_file、grep 或分页读取局部内容。
卸载的原则是“把可寻址数据从对话移到文件”,不是删除证据。后端和路由选择因此直接影响恢复能力。
自动摘要
当上下文到模型 max_input_tokens 的约 85%,且没有更多可卸载内容时:
- LLM 生成包含意图、产物、进展和后续步骤的结构化文字摘要。
- 原始消息的文本渲染写入 filesystem 作为可检索记录。
- 默认保留约 10% 最近 token。
- 无模型 profile 时,退化为约 170,000 token 触发并保留 6 条消息。
- 模型抛出标准 ContextOverflowError 时,立即摘要并用摘要 + 最近消息重试。
还可加入 summarization tool middleware,让 Agent 在阶段边界主动调用 compact_conversation。主动压缩不会关闭 85% 的自动兜底。
多模态提醒:旧图片、音视频和文件块进入摘要区后不会原样保留,只剩文字摘要。重要媒体必须落到后端/对象存储并保留引用。
记忆
Deep Agents 的长期记忆是文件系统支持的,memory 参数通常指向 AGENTS.md。它启动时总是加载,适合稳定且每轮都重要的规则,不适合把完整历史或大知识库塞进去。
作用域
| Scope | namespace 示例 | 适用 | 风险 |
|---|---|---|---|
| User | user_id | 个人偏好、个人技能 | 推荐默认,隔离清晰 |
| Agent/Assistant | assistant_id | Agent 共同身份与经验 | 任意用户写入会影响他人 |
| Organization | org_id | 合规、品牌、公共知识 | 通常必须只读 |
| User + Assistant | user_id, assistant_id | 同用户在不同 Agent 间隔离 | namespace 更细,治理更稳 |
共享可写记忆是提示注入通道。组织政策、开发者技能和公共知识默认只读,由应用或管理员更新;用户记忆默认按 user_id 隔离。不要从模型生成的文本直接构造 namespace。
记忆类型与更新
- 语义记忆:事实、偏好、约束。
- 程序记忆:如何做事,通常以 Skills 表达。
- 情节记忆:过去会话、发生顺序与结果,由 checkpoint thread 保存,可包装 thread search 工具检索。
热路径更新即时可用,但增加延迟且让 Agent 同时做任务与归档。后台 consolidation Agent 可定时搜索近期会话、抽取事实并合并记忆;cron 周期必须与 lookback 窗口一致,否则重复处理或漏数据。共享文件并发写有最后写入覆盖风险,宜按主题拆文件或串行归并。
Skills
Skill 是带 YAML frontmatter 的 SKILL.md 目录,可附 scripts、references 和 assets。它与 memory 的关键区别是渐进披露:
| 层级 | 加载内容 | 时机 |
|---|---|---|
| 1 | name 与 description | Agent 启动 |
| 2 | 完整 SKILL.md | 任务匹配后 |
| 3 | 脚本、参考、模板 | 指令明确要求时 |
编写原则
- description 必须同时说明“做什么”和“何时触发”;发现阶段模型只看到它。
- 相近技能描述要有明确边界;重叠太多时合并。
- 主文件聚焦决策与步骤,详细资料拆到一层引用文件;避免深链。
- 给出输入、输出、成功标准和边界场景。
- 官方建议正文控制在 500 行/约 5,000 token 内;SKILL.md 超过 10 MB 会在发现阶段跳过。
- name 为小写字母数字和连字符,1–64 字符,并与父目录同名。
- 多个 skill source 同名时后者覆盖前者,可按“基础 → 用户 → 项目”显式排序。
- SDK 不会自动扫描 CLI 的用户技能目录,所有 source 必须显式传入。
Skill 与子代理
- 默认 general-purpose 子代理继承主 Agent 的 skills。
- 自定义子代理不继承,需在 spec 里显式列 skills。
- 父子各自有独立 SkillsMiddleware,已加载状态不会互相传播。
- 沙箱外 backend 中的脚本只能读,不能直接在沙箱里执行;运行前需上传,结束后需要时再同步回去。
生产可把共享技能路由到组织 StoreBackend 并拒绝写,把个人技能路由到 user namespace 并允许写或要求 interrupt。
Retrieval 与 RAG
检索解决有限上下文和训练知识静态的问题。已有 SQL、CRM、文档库时,不必为了 RAG 重建向量库:可直接包装成 Agent 工具,或查询后把结果作为 2-step RAG 上下文。
| 架构 | 决策者 | 优点 | 代价 |
|---|---|---|---|
| 2-step RAG | 应用固定先检索后生成 | 可控、延迟可预测 | 灵活性低 |
| Agentic RAG | Agent 决定何时/如何检索 | 多源、可迭代 | 延迟和调用数不确定 |
| Hybrid RAG | 固定骨架 + Agent 验证/修正 | 兼顾控制与弹性 | 系统更复杂 |
向量知识库典型流水线是 load → split → embed → vector store → retriever。检索只是返回候选证据;生产还需检索质量、来源引用、答案 groundedness 和注入防护。
大规模 Agentic RAG 推荐“检索 → 卸载 → 委派”:
- 检索工具只把命中块写到 /retrieved/ 并返回路径。
- 多个子代理分别读取、检索和摘要文件。
- 主 Agent 只接收带来源的简洁报告并综合。
- 可用 rubric 检查引用与结论是否被证据支持。
检索内容是外部不可信数据。提示“把文档当数据”只能降低风险,不能可靠阻断间接提示注入;最终输出仍需验证引用是否来自允许路径、结论是否匹配原文。
同步子代理
同步子代理调用会阻塞 supervisor,直到返回一个最终结果。适合:
- 多步调查会污染主上下文。
- 专业领域需要独立工具、模型、提示或技能。
- 主 Agent 只需要最终摘要。
不适合简单一步任务、必须连续共享中间推理、或启动开销高于收益的场景。
默认自动提供 general-purpose 子代理,除非已有同名自定义项。完全关闭 task 工具应在 HarnessProfile 禁用 general-purpose,并且不传任何同步 subagents;不能粗暴删除 SubAgentMiddleware。
声明式 SubAgent
关键字段:
- name、description、system_prompt 必填。
- tools 默认继承父工具;显式 tools 会整体覆盖。
- model 默认继承。
- middleware 与 skills 默认不继承。
- interrupt_on 默认继承,子配置覆盖。
- permissions 默认继承,子配置整体替换。
- response_format 可让父 Agent 获得稳定 JSON。
描述决定父 Agent 何时委派,应具体、动作导向;工具集应最小;system prompt 要规定工具使用与返回格式;结果应简洁,大材料写文件。
CompiledSubAgent 适合复杂 LangGraph 子图,必须已经 compile,并有 messages state key。
上下文与追踪
父 runtime context 自动传播。若某字段只属于一个子代理,可用 namespaced key 或 context schema 中的独立字段。共享工具可读取 config metadata 的 lc_agent_name 判断调用者。LangSmith 也用 lc_agent_name 过滤协调者和不同子代理运行。
动态子代理
动态子代理 = 解释器中的 JavaScript 通过 task() 调度子代理。适合规模不定、需要循环/批处理/多轮筛选的工作。
常见编排形态:
- classify and act:先分类,再路由给不同专家。
- fan-out and synthesize:同类任务并行后聚合。
- adversarial verification:独立验证并过滤误报。
- generate and filter:多解生成、打分、择优。
- tournament:两两评审淘汰。
- loop until done:发现、去重,直到无新增。
task 接收 description、subagentType 和可选 responseSchema。传入 schema 后返回值已是 JavaScript 对象。解释器 mode=thread 时可跨轮保留工作集。
动态调度当前依赖 beta 解释器,Python 需 3.11+。它不走普通 task 工具审批路径;父 Agent 的 interrupt_on 不会逐个拦截 task()。需要治理时审批 eval,并限制可用子代理与 PTC 工具。
异步子代理
异步子代理把工作提交到 Agent Protocol server,立即返回 task ID,supervisor 可继续和用户对话。
| 能力 | 同步 | 异步 |
|---|---|---|
| supervisor 等待 | 阻塞 | 非阻塞 |
| 中途更新 | 不支持 | update_async_task |
| 取消 | 不支持 | cancel_async_task |
| 跨交互状态 | 无 | 独立 thread 持久 |
| 适用 | 必须拿到结果再继续 | 长任务、并行任务、需要中途驾驶 |
中间件提供 start、check、update、cancel、list 五个工具。update 使用 interrupt multitask 策略中断旧 run,并在同一 thread 的完整历史上用新指令重启,task ID 不变。
任务元数据存入独立 async_tasks state channel,而不是只存在工具消息里,因此消息摘要后仍能列出任务。状态报告前必须实时 check/list;对话历史中的状态一律视为过期。
传输:
- 省略 url:ASGI 同进程传输,所有 graph 同一 langgraph.json;零网络开销,推荐起点。
- 指定 url:HTTP 远程 Agent Protocol;适合独立扩缩容或不同团队维护。
本地 worker 数至少覆盖 supervisor + 并行 subagent 数,否则任务会排队。Async Subagents 是 preview,接口可能变化。
Rubric 自评循环
RubricMiddleware(deepagents 0.6.5+,beta)用独立 grader model 判断“是否完成”:
- 工作 Agent 产出。
- grader 对照 rubric 逐项给 verdict。
- needs_revision 时把缺口反馈给工作 Agent。
- 直到 satisfied、failed、grader_error 或达到 max_iterations。
grader 可配专用工具,例如跑测试或读取产物,使评价基于证据而非只读对话。默认最大 3 轮;可通过 on_evaluation 或 custom stream 观察每轮。
Rubric 适合具有明确可验证标准的任务,不是通用“让回答更好”的魔法层。标准模糊会造成不稳定循环;成本与延迟至少增加一次 grader 调用,应设置迭代上限。
OpenWiki
OpenWiki 基于 Deep Agents 生成并维护 Markdown wiki,让编码 Agent 先读架构、集成、评测和流程,再按需查源码。Code 模式写入项目 openwiki/ 并在根 AGENTS.md/CLAUDE.md 放索引;Personal 模式生成本地个人 wiki。它提供的是可发现的持久上下文,不替代代码事实或正式 ADR。
决策速查
- 每次都必须看到:memory。
- 仅特定任务需要:skill。
- 需要执行动作:tool。
- 大量外部知识:retrieval + 文件卸载。
- 重型但需立即结果:同步 subagent。
- 规模不定的并行工作流:动态 subagent。
- 长时间、需中途更新/取消:异步 subagent。
- 明确验收清单:rubric。
- 仓库知识反复重建:OpenWiki。