module/file.py:function_namedocs/decisions.md 另行记录Context = 每次 LLM 调用时发送的完整消息列表。管理目标:
两者互相影响:压缩可能清掉已注入的内容,需要重新注入。
[system] System Prompt(角色 + 内置 skills) ← 静态注入,trace 创建时
[user] 第一条用户消息
[assistant] ...
[tool] ... ← 可能包含周期注入的 context
[assistant] ...
[tool] ... ← 可能包含动态注入的 skill
[user] 第 N 条用户消息
[assistant] 待模型生成
Trace 创建时构建一次,后续续跑不重复发送。
内容:角色 prompt + 按 preset/call-site 过滤的内置/项目 skills。
优先级:messages 中的 system message > config.system_prompt > preset.system_prompt > DEFAULT_SYSTEM_PREFIX
实现:cyber_agent/core/runner.py:_build_system_prompt
每 CONTEXT_INJECTION_INTERVAL(当前=5)轮迭代,框架自动向模型的 tool_calls 中追加一条 get_current_context 调用(如果模型本轮未主动调用)。
注入内容(_build_context_injection 构建):
注入形态:合成的 tool_call + tool_result 消息对。模型看到的效果等同于"我之前主动调用了 get_current_context"。
[assistant] tool_calls: [..., get_current_context()] ← 框架自动追加
[tool] { 当前时间, GoalTree, Collaborators... } ← 工具正常执行
特点:
实现:cyber_agent/core/runner.py(CONTEXT_INJECTION_INTERVAL, 工具执行后的自动注入逻辑)
详细文档:架构设计 § Context Injection Hooks
区别于模型通过 skill() 工具主动加载 skill,指定注入是调用方在 runner.run() 时声明需要哪些 skill,由框架自动注入。
System Prompt 中的内置 skills 是启动时一次性注入的。但某些 skill(如 Librarian 的查询策略/上传策略):
skill 工具统一管理调用方通过 runner.run() 的 inject_skills 参数声明(不放在 RunConfig 中,因为同一个 agent 的不同调用可能需要不同的 skill):
# Librarian ask 场景
async for item in runner.run(
messages=[{"role": "user", "content": "[ASK] 查询内容"}],
config=config,
inject_skills=["ask_strategy"], # 本次调用需要的 skill
skill_recency_threshold=10, # 最近 N 条消息内有就不重复注入
):
...
# Librarian upload 场景(同一个 agent,不同 skill)
async for item in runner.run(
messages=[{"role": "user", "content": "[UPLOAD:BATCH] 数据"}],
config=config,
inject_skills=["upload_strategy"],
skill_recency_threshold=10,
):
...
框架在工具执行阶段自动注入(与 context 周期注入同级处理)。
注入形态:合成的 skill("xxx") tool_call + tool_result 消息对,复用现有 skill 工具。
[user] [ASK] 有没有工具能做人物姿态控制?
[assistant] tool_calls: [skill("ask_strategy")] ← 框架自动生成
[tool] { ask_strategy skill 完整内容 } ← skill 工具正常执行
[assistant] 让我来检索... ← 模型真实输出
在 trace.context 中维护已注入 skill 的记录:
trace.context["injected_skills"] = {
"ask_strategy": {
"message_id": "msg_abc123", # tool_result 消息的 ID
"sequence": 42 # 最新注入的消息序列号
}
}
注入前检测流程:
injected_skills[skill_id].message_idrecency_threshold 条消息内(从当前提交给模型的列表中查,不按编号计算) → 跳过| 方式 | 触发者 | 场景 |
|---|---|---|
| 指定注入(本节) | 调用方,通过 run(inject_skills=...) |
已知任务类型,确保策略在场 |
| 主动加载 | 模型自己调 skill() 工具 |
遇到未预见场景,自主判断需要什么 |
两者都走 skill 工具,消息格式一致。
指定注入的 skill 文件遵循现有 skill 体系(Markdown + frontmatter),按项目组织:
knowhub/agents/skills/ # KnowHub Librarian 的 skills
├── ask_strategy.md # 查询检索策略
└── upload_strategy.md # 上传编排策略
Recursive revision 2 不把父 Agent 的完整 Context 复制给子 Agent,而是分成根锚点、当前任务合同和逐层授权引用。
新建 Recursive 根 Trace 必须提供 objective / completion_criteria / constraints。框架规范化后在根 Trace 保存唯一副本和 SHA-256;创建任意子孙时重新读取该权威副本,中间 Agent 无法重新总结或覆盖。
get_current_context 中各出现一次,并固定进入 Validator 输入。实现:cyber_agent/core/context_policy.py:normalize_root_task_anchor,cyber_agent/core/runner.py:AgentRunner._prepare_new_trace
父级通过 TaskBrief 显式下发:
objective / reason
completion_criteria / expected_outputs
parent_findings / context / constraints
cyber_agent/core/context_policy.py:normalize_task_brief 负责边界校验:
NaN/Infinity。constraints,子级只能追加,不能删除。agent() 调用使用同一规范化逻辑,之后精确匹配。task_protocol;REVISE_CHILD 续跑时统一替换,旧版本内容不变。子 Trace 还会获得必要的 Trace 血缘、固化工具能力快照、树级预算根 ID
和协议状态。不会自动继承父级完整消息历史、RunConfig.context、内部
运行对象、未筛选 Tool Result 或祖先 GoalTree。Recursive 本地 agent() 因此不接受
messages;需要继承的业务信息必须写入 TaskBrief。
TaskBrief 可携带 context_refs: [{ref_id, version}]。框架当前只生成两类不可变快照:
task_brief:直接父级某一版本的完整任务合同;根创建第一层时没有该引用。reviewed_task_result:一次已完成父级审核的 TaskReport + ValidationResult + TaskReview。快照以规范化内容 SHA-256 作为版本。模型只能转授当前 Trace 本地目录已授权的精确 ref_id/version;孩子获得本地副本后通过 read_context_ref 按需读取,不会遍历其他 Trace。任意一层不再转授,更深后代立即失去访问权;不能按 Trace ID 自由读取祖先、兄弟、其他树或其他 UID。
固定上限:每个 Trace 8 个引用,单个快照 16,000 字符,全部快照 64,000 字符。已审核结果超限时审核仍成功,但不生成可转授引用并返回警告。REVISE_CHILD 会清空旧授权,回溯会按 granted_at_sequence 删除截断点之后的授权。
get_current_context 只展示 Anchor、TaskBrief 版本、引用元数据和静态字符用量,不自动展开快照。Token 用量仍以模型返回并进入 ResourceUsage 的 prompt_tokens 为准。
实现:cyber_agent/core/context_policy.py:build_child_context_access,cyber_agent/tools/builtin/context.py:read_context_ref
Validator 的输入不由被验收 Agent 自行组装。
cyber_agent/core/validation.py:build_validation_packet 固定保留 RootTaskAnchor、TaskBrief、
TaskReport、ValidationPlan、已解析 Artifact 和根候选答案,再按最新消息优先纳入
持久化主路径和真实 Tool Result。
unknown,不会删减标准或截断真实产物后通过。reasoning_content 会被移除。read_context_ref 后产生的 Tool Result 才会进入主路径验收轨迹。完整字段和验收流程见 Recursive 任务协议。
压缩分两级,通过 RunConfig.goal_compression 控制 Level 1 行为:
| 模式 | 值 | Level 1 行为 | Level 2 行为 |
|---|---|---|---|
| 不压缩 | "none" |
跳过 Level 1 | 超限时直接进入 Level 2 |
| 完成后压缩 | "on_complete" |
每个 goal 完成时立刻压缩该 goal 的消息 | 超限时进入 Level 2 |
| 超长时压缩 | "on_overflow" |
超限时遍历所有 completed goal 逐个压缩 | Level 1 后仍超限则进入 Level 2 |
默认值:"on_overflow"
对单个 completed/abandoned goal 的压缩逻辑:
msg.goal_id == goal.id)goal 工具goal(...) 的 assistant 消息及其对应的 tool result(包括 add、focus、under、done 等操作)goal(done=...) 的 tool result 内容替换为 "具体执行过程已清理"压缩后的 history 片段示例:
... (前面的消息)
[assistant] tool_calls: [goal(focus="1.1")]
[tool] goal result: "## 更新\n- 焦点切换到: 1.1\n\n## Current Plan\n..."
[assistant] tool_calls: [goal(done="1.1", summary="前端使用 React...")]
[tool] goal result: "具体执行过程已清理"
... (后面的消息)
on_complete 模式在 goal 工具执行 done 操作后,立刻对该 goal 执行压缩。优点是 history 始终保持精简,缺点是如果后续需要回溯到该 goal 的中间过程,信息已丢失(存储层仍保留原始消息)。
触发点:cyber_agent/core/runner.py(工具执行后检测 goal(done=...) 调用)
on_overflow 模式在 _manage_context_usage 检测到 token 超限时,遍历所有 completed goal,逐个执行压缩,直到 token 数降到阈值以下或所有 completed goal 都已压缩。如果仍超限,进入 Level 2。
触发点:cyber_agent/core/runner.py:_manage_context_usage
实现:cyber_agent/trace/compaction.py:compress_completed_goals
触发条件:Level 1 之后 token 数仍超过阈值(默认 context_window × 0.5)。
通过侧分支队列机制执行,force_side_branch 为列表类型:
knowledge.enable_extraction 控制):进入 reflection 侧分支,LLM 可多轮调用工具提取知识knowledge_eval 侧分支,评估待评估知识compression 侧分支,LLM 生成 summary侧分支队列示例:
["reflection", "compression"]["knowledge_eval", "compression"]["compression"]压缩完成后重建 history 为:system prompt + 第一条 user message + summary(含详细 GoalTree)
实现:cyber_agent/core/runner.py:_agent_loop(侧分支状态机), cyber_agent/core/runner.py:_rebuild_history_after_compression
主路径无工具调用(任务完成)时,如果 knowledge.enable_completion_extraction 为 True,通过侧分支进入反思,完成后退出循环。
to_prompt() 支持两种模式:
include_summary=False(默认):精简视图,用于日常周期性注入include_summary=True:含所有 completed goals 的 summary,用于 Level 2 压缩时提供上下文messages/branch_type 和 branch_id 标记,查询主路径时自动过滤parent_sequence 树结构实现跳过,无需 compression events 或 skip list压缩会移除消息,包括之前注入的 skill 和 context 内容。不同注入类型的应对策略:
| 注入类型 | 压缩影响 | 恢复策略 |
|---|---|---|
| System Prompt | Level 2 重建时保留 | 无需额外处理 |
| Context 周期注入 | 可能被 Level 1/2 移除 | 固定间隔自动重注入(下一个 interval 即恢复) |
| Skill 动态注入 | 可能被 Level 1/2 移除 | message ID 检测 → 自动重注入 |
Skill 重注入的触发点:每次 runner 准备构建 LLM 调用前,检查 injected_skills 中记录的 message_id 是否仍在当前 history 中。不在则标记为需要重新注入,在下一轮工具执行阶段合成 tool_call。
| 文档 | 内容 |
|---|---|
| 架构设计 | Agent 框架完整架构 |
| Recursive 任务协议 | TaskBrief 边界、Validator 输入和审核流程 |
| Skills 指南 | Skill 文件格式、分类、加载机制 |
| Prompt 规范 | 信息分层、条件注入原则 |
| Trace API | 压缩和反思的 REST API |