AstroResearch/docs/architecture/agent/system-prompt.md
Asfmq 698d007f39 feat: Agent 安全纵深防御、Checkpoint 快照、会话 Rewind/Branch、自进化
Skill、流式执行优化与系统架构全面升级

  本次提交对标 Claude Code 与 Hermes-Agent 的工程细节,在安全、可靠性、
  会话管理、自我进化四个维度进行了系统性加固,变更总量 48 文件 / +12680 -2292 行。

  ═══════ 安全纵深防御 ═══════

  1. Hardline 硬阻止层 (src/agent/runtime/hardline.rs, +534 行)
     - 不可绕过的危险命令拦截(关重启、磁盘擦除、Fork 炸弹、rm -rf /、kill -1)
     - 反规避标准化管线: ANSI 序列剥离 → Unicode NFKC → shell 反斜杠还原 → 空字面量清理
     - 在 PermissionChecker 之前执行,YOLO/Bypass 模式下同样生效
     - 集成到 executor Phase 2,被拒绝工具直接注入错误结果

  2. Permission 优先级裁决器 (src/agent/runtime/permission.rs, +200 行)
     - 7 层正式优先级规则 (P0 Deny → P7 Allow),带冲突日志
     - explain() 方法支持审计追溯
     - Hook PermissionRequired 与 Checker 结果的正确叠加逻辑

  ═══════ Checkpoint 文件快照系统 ═══════

  3. git2 原生快照 (src/agent/runtime/checkpoint.rs, +920 行)
     - 基于 git2 bare repo,内容寻址自动去重
     - 文件变更操作前自动触发 (file_write/file_edit/run_bash)
     - 每目录每 turn 最多一次快照,防止同一轮重复
     - 支持 list/diff/restore API + pre-rollback 安全快照
     - 旧快照自动 prune(保留最近 N 个)+ 按目录隔离 ref
     - 排除规则自动过滤 node_modules/target/.git/*.pdf 等
     - 集成到 executor: 文件操作前 ckpt.ensure_checkpoint()

  ═══════ 错误恢复系统大升级 ═══════

  4. 21 种 FailoverReason 分类 (src/agent/runtime/error_recovery.rs, +1200 行)
     - 参考 Hermes-Agent error_classifier.py
     - 8 步分类管线: provider-specific → HTTP status → text pattern → error body → fallback
     - is_retryable / should_compress / should_failover / is_permanent 方法
     - Context Overflow 自动修复: 从错误消息提取 token 限制,自动下调预算
     - RecoveryStep::AdjustMaxTokens 实现 (参考 Claude Code 自动修复)
     - 向后兼容 ErrorKind 别名

  ═══════ 会话 Rewind / Branch / Retry 体系 ═══════

  5. 完整 undo 栈 (src/agent/runtime/session.rs, +800 行 + 2 迁移脚本)
     - Rewind (软删除): active=0 标记,审计 trail 保留,LLM 不可见
     - Restore (撤销回退): 冲突检测——回退后有新消息则拒绝,引导使用 Branch
     - Branch: 分叉会话,复制所有 active=1 消息到新会话
     - Retry: 硬删除最后一轮对话,返回原消息文本供前端重提交
     - 数据库: agent_messages.active 列 + agent_sessions.rewind_count + parent_session_id
     - API: 4 个新端点 (/branch, /retry, /rewind, /rewind/restore)
     - load_history_for_agent 全面使用 active=1 过滤

  ═══════ Hooks 系统模块化重构 ═══════

  6. 单文件 → 7 模块体系 (src/agent/hooks/)
     hooks.rs (994 行) 拆分为:
     - mod.rs    — 入口 + HookRegistry + SessionHookManager
     - types.rs  — 类型定义 (Context, TaggedContext, PermissionRequestAction 等)
     - traits.rs — AgentHook + AsyncAgentHook + 15 种生命周期事件
     - matcher.rs — 工具名/参数匹配 + session 作用域过滤
     - dispatch.rs — 并行调度引擎 (run_pre/post_tool_use 等)
     - registry.rs — 注册/注销/查询
     - builtins.rs — CancellationHook + MetricsHook + AuditLogHook + ContextDeduplicator

     关键改进:
     - run_pre_tool_use 并行执行所有匹配 hooks,聚合 Block/MutateInput/Continue
     - TaggedContext 带完整来源标记的上下文注入 (hook_name + event)
     - ContextDeduplicator 单 dispatch cycle 内内容哈希去重
     - AsyncAgentHook 支持 fire-and-forget 异步 hooks

  ═══════ Executor 并发执行升级 ═══════

  7. 三阶段管道重写 (src/agent/runtime/executor.rs, +600 行)
     - Phase 1: 死循环检测 + 参数解析 (不变)
     - Phase 2: Hardline 预检查 (新增) → PermissionChecker (改进)
     - Phase 3: ToolPartitioner 分区 → 逐批次执行 (重写)
       - 并行批次内 FuturesUnordered 并发
       - 串行批次确保非并发安全工具独占执行
       - Checkpoint 预触发集成
     - Hook 上下文注入: system-reminder 格式 + ContextDeduplicator 去重
     - Hook 阻塞错误详细记录

  ═══════ 流式执行真正的流式调度 ═══════

  8. StreamingExecutor 重写 (src/agent/runtime/streaming_executor.rs, ~400 行变更)
     - on_tool_use 中对并发安全工具立即 tokio::spawn,不等待 flush
     - executing_non_concurrent 标志阻塞后继工具直到独占工具完成
     - JoinHandle 管理替代自定义 cancel channel
     - completed_queue 按流顺序 yield
     - Sibling Abort 通过 broadcast channel + tokio::select! 竞速
     - ToolContext 实现 Clone (支持 per-task 上下文复制)

  ═══════ 自改进 Skill 系统 ═══════

  9. PatternDetector + SkillCreator + Curator (src/agent/skills/, +1500 行)
     - PatternDetector: 扫描 agent_messages 表,检测跨 session 重复工具调用模式
     - SkillCreator: 将高置信度模式自动生成 SKILL.md (YAML frontmatter + 工作流步骤)
     - SelfImprovePipeline: 一站式 模式检测 → 创建 → 质量审查
     - Curator: 分析 skill 使用统计,标记 stale/deprecated,建议清理
     - Skill frontmatter 新增 pinned 字段 (禁止 Curator 自动清理)

  ═══════ 基础设施优化 ═══════

  10. 系统提示词缓存 (src/agent/runtime/system_prompt.rs + mod.rs)
      - SystemPromptCache: 首次计算后永久复用,/clear 时失效
      - 新增 SAFETY / SYSTEM_CONTEXT / TOOL_USAGE 静态 section
      - 环境/tools/skills/memory 动态 section 通过 get_or_compute 缓存

  11. ToolRegistry schema 缓存 (src/agent/tools/mod.rs)
      - schema_cache + schema_generation 版本号
      - 工具变更/过滤器变更时自动失效
      - precompute_definitions() 预计算 (AgentRuntime 初始化时调用)

  12. 迭代摘要融合 (src/agent/compact.rs, +100 行)
      - 参考 Hermes context_compressor.py
      - CollapseLog 追踪压缩历史,支持溢出合并
      - extract_prior_summary: 提取已有摘要融入新压缩

  13. SubAgent 系统提示词模块化 (src/agent/tools/subagent.rs)
      - 复用 5 个标准 section + 子代理专有上下文 section
      - 独立 ToolRegistry 构建工具列表

  ═══════ 前端 — CSS 变量主题系统 ═══════

  14. 全新主题变量体系 (dashboard/src/index.css + App.tsx + 各面板)
      - CSS 自定义属性: --bg-card, --text-main, --text-muted, --border-precision
      - 语义化颜色: --accent-blueprint, --accent-star
      - 全面替换硬编码 Tailwind 颜色 (slate-xxx → var(--xxx))
      - 文献入库提示优化 ("核心知识节点" 替代 "向量块")
      - ReaderPanel 样式变量化
2026-06-22 20:29:37 +08:00

355 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 系统提示词架构 (System Prompt Architecture)
AstroResearch Agent 系统提示词采用**模块化 Section 组装 + 简单首次缓存**架构,参考 Claude Code 的 System Prompt 设计并针对实际场景裁剪。
## 整体分层
```mermaid
graph TB
subgraph L5["Layer 5: 运行时注入"]
Nudge["nudge / 任务恢复 / 后台通知"]
end
subgraph L4["Layer 4: 提示词压缩 + CollapseLog"]
Compress["snip → micro → auto → aggressive_micro"]
Collapse["CollapseLog commit / overflow"]
end
subgraph L3["Layer 3: Skill 动态加载"]
Skill["Layer1 提醒 → Layer2 全文注入"]
end
subgraph L2["Layer 2: 子代理模块化提示词"]
SubSP["6 section 组装器"]
end
subgraph L1["Layer 1: 主代理 SystemPrompt 组装"]
MainSP["9 个 section 模块化组装<br/>静态(5) → 动态(4)"]
Cache["SystemPromptCache<br/>首次计算,永久复用"]
end
L5 --> L4 --> L3 --> L2 --> L1
L1 --> Cache
```
---
## 核心组装器 (`src/agent/runtime/system_prompt.rs`)
### 数据结构
```rust
pub struct SystemPrompt {
sections: Vec<(&'static str, String)>,
}
```
有序 section 列表。`assemble()` 用 `"\n\n"` 拼接所有 section。顺序即最终 prompt 中出现的顺序——静态内容在前,动态内容在后。
### Section 缓存 (`SystemPromptCache`)
简化设计:**首次计算,永久缓存**。因为 session 生命周期内 CWD、platform、OS、model、工具注册表均不变不需要 TTL 过期机制。仅在 `/clear``/compact` 事件时调用 `invalidate_all()` 全局失效。
```rust
pub struct SystemPromptCache {
entries: HashMap<&'static str, String>,
}
impl SystemPromptCache {
// 首次计算,后续命中缓存
pub fn get_or_compute(&mut self, name: &str, compute: impl FnOnce() -> String) -> String;
// compute 返回 OptionSome 缓存并返回None 不缓存
pub fn get_or_compute_optional(&mut self, name: &str, compute: impl FnOnce() -> Option<String>) -> Option<String>;
// 显式失效
pub fn invalidate(&mut self, name: &str);
pub fn invalidate_all(&mut self);
}
```
缓存策略:静态 section + environment + tools 全部通过 `get_or_compute` 缓存。skills 和 memory 不缓存——前者通过文件监听热更新,后者受 `save_memory` 工具实时影响。
### 静态 Section 常量6 个)
| 常量 | 内容 | 行数 |
|------|------|------|
| `IDENTITY_SECTION` | 身份声明 | 1 |
| `PRINCIPLES_SECTION` | 核心行为准则9 条) | 9 |
| `SYSTEM_CONTEXT_SECTION` | system-reminder 标签说明 + 自动压缩 | 3 |
| `TOOL_USAGE_SECTION` | 专用工具优先、并行调用、todo_write | 5 |
| `SAFETY_SECTION` | 可逆性、影响范围、确认机制 | 5 |
### 组装顺序
`AgentRuntime::system_prompt()` 组装 9 个 section
```
[1] identity ← 静态(首次计算后永久缓存)
[2] principles ← 静态
[3] system_context ← 静态
[4] tool_usage ← 静态
[5] safety ← 静态
[6] environment ← 动态首次计算后缓存session 内不变)
[7] tools ← 动态首次计算后缓存ToolRegistry session 内不变)
[8] skills ← 动态(不缓存,文件监听热更新)
[9] memory ← 动态不缓存save_memory 实时更新)
```
**为什么没有 TTL**CWD、platform、OS、model、tools 在 session 生命周期内全部不变。首次计算即永久正确TTL 是多余的复杂度。
**为什么没有 cache_control 边界标记**`cache_control: {"type": "ephemeral"}` 是 Anthropic API 专有特性。我们的模型DeepSeek/Qwen 等 OpenAI 兼容 API不支持。静态内容前置的顺序本身已足够让服务端按内容哈希自然缓存。
---
## 动态 Section 详解
### environment section
```rust
fn build_environment_section(&self) -> String {
// 包含工作目录、Git 仓库状态、平台、OS 版本、日期、模型名称
// 以及 Agent 配置摘要(最大步数、工具超时)
}
```
示例输出:
```
# 环境信息
- 工作目录: /home/user/project
- Git 仓库: 是
- 平台: linux
- OS 版本: Linux 7.0.0-22-generic
- 日期: 2026-06-22
- 当前模型: deepseek-v4-pro
- 最大推理步数: 8
- 工具超时: 120 秒
```
### tools section
```rust
// 从 ToolRegistry.definitions() 生成工具名称 + 80 字符摘要
// ToolRegistry 内部有 schema_cache工具注册表不变时复用上次计算结果
for def in self.tool_registry.definitions() {
let short_desc = def.function.description
.split('。').next() // 取第一句
.chars().take(80) // 截断 80 字符
.collect();
tools_desc.push_str(&format!("- {}: {}\n", def.function.name, short_desc));
}
```
完整 JSON Schema 通过 API `tools` 参数单独传递,不在 system prompt 中重复。
### skills section — 两层加载
**Layer 1 (system-reminder)**`SkillRegistry.build_reminder()` 列出所有 `user_invocable=true` 的技能名称 + 描述 + when_to_use每个约 20 tokens。
```xml
<system-reminder>
The following skills are available for use with the Skill tool:
- methodology: 天体物理研究方法论指南 - When user asks about research methodology
When a skill matches the user's request, invoke load_skill BEFORE generating any other response...
</system-reminder>
```
**Layer 2 (load_skill 工具)**LLM 按需调用,从 `skills/{name}/SKILL.md` 加载完整内容(~2000 tokens支持 `${SKILL_DIR}` / `${SESSION_ID}` 变量替换和 fork 执行模式。
详见 [skills.md](skills.md)。
### memory section
`{library_dir}/memory/` 加载,**按 mtime 降序排列**(最新在前),取前 5 条,注入 `<project-memory-context>` XML 块。支持时效警告、过期标记、语义选择。详见 [memory.md](memory.md)。
---
## 上下文初始化 (`src/agent/runtime/context.rs`)
`build_initial_context()` 流程:
```
1. 加载历史消息agent_messages 表)
2. 如果第一条不是 system 角色 → 插入系统提示词
3. 追加当前用户问题
4. [可选] 追加任务状态恢复提醒agent_tasks 表)
```
运行时干预通过**注入 user 消息**实现(不修改 system prompt
| 触发条件 | 注入内容 |
|:---|:---|
| TodoWrite 3 步未更新 | "提醒:建议调用 todo_write 工具复盘进度" |
| Token 预算 diminishing returns | "检测到重复操作模式,请直接给出最终答案" |
| 达到最大步数 | "已执行 N 步(最大 M 步),请直接给出最终答案" |
| 后台任务完成 | "[后台任务完成] ✅ tool_name: summary" |
---
## 子代理模块化系统提示词 (`src/agent/tools/subagent.rs`)
子代理使用完整模块化系统提示词(不再硬编码 96 字符):
```
[1] identity ← 与父代理相同
[2] subagent_context ← "在独立的子任务上下文中工作,请专注于完成这项任务"
[3] principles ← 与父代理相同
[4] system_context ← 与父代理相同
[5] tool_usage ← 与父代理相同
[6] safety ← 与父代理相同
[7] tools ← 运行时从 ToolRegistry 生成
```
预构建 `ToolRegistry`,在构造系统提示词前获取 `definitions()`。`SubAgentRunner` 新增 `new_with_registry_and_hooks()` 构造函数。详见 [subagent.md](subagent.md)。
---
## 上下文压缩 + CollapseLog (`src/agent/compact.rs`)
### 四层压缩策略
| 层 | 方法 | API | 行为 |
|:---|:---|:---|:---|
| 0 | `snip_compact` | 无 | 消息 >50 时截断中间段,保留头 3 + 尾 |
| 1 | `micro_compact` | 无 | 较早工具结果替换为 `[Previous: used {name}]` 占位符 |
| 2 | `auto_compact` | 1 次 | LLM 摘要对话历史 |
| 3 | `aggressive_micro` | 无 | 保留最近 2 条工具结果 |
### CollapseLog
`compress_context_with_hooks_and_log()` 在每次压缩后记录结构化 commit
```rust
log.commit(CollapseMethod::LlmSummary, (after_count, before_count), summary);
```
超 5 条 commits 时触发**溢出合并**,将最早的 commits 合并为摘要注入消息列表。详见 `collapse.rs`
---
## Hook 系统与提示词交互
9 个生命周期事件:
| Hook | 与提示词的关系 |
|:---|:---|
| `OnSessionStart` | 提示词组装前触发 |
| `PreToolUse::MutateInput` | 注入 `additional_context`(追加为 user 消息) |
| `PostToolUse::MutateOutput` | 修改工具输出(影响 LLM 看到的 context |
| `PreCompact / PostCompact` | 压缩前后记录指标 + CollapseLog commit |
| `OnSubagentStart/Stop` | 传递子代理 prompt 和结果摘要 |
详见 [hooks.md](hooks.md)。
---
## Claude Code 工具按需发现机制(参考分析)
> 当前项目工具数量较少(~25 个),尚未实现此机制。以下为 Claude Code 的设计分析,作为未来工具增长时的参考。
### 要解决的问题
当接入大量 MCP 工具30+ 服务器100+ 工具)时,所有工具的完整 JSON Schema 在每轮 API 调用中占据大量上下文——绝大多数工具从未被调用,却每轮都在消耗 token。Claude Code 的解决方案:**延迟加载 + 按需发现**。
### 两层架构
**Layer 1: `defer_loading` 标记**
工具分为两类:
| 类型 | 行为 | 示例 |
|------|------|------|
| 常驻工具 | 始终在 `tools` 数组中,立即可用 | Read、Write、Bash、Glob、Grep、Task、TodoWrite |
| 延迟工具 | 标记 `defer_loading: true`,仅在模型主动发现后才加入 `tools` 数组 | MCP 工具、EnterPlanMode、NotebookEdit、LSP 工具 |
判断逻辑(`isDeferredTool()`
```
1. alwaysLoad == true → 永不延迟
2. isMcp == true → 始终延迟(除非 MCP server 设 _meta['anthropic/alwaysLoad']
3. shouldDefer == true → 延迟
```
**Layer 2: ToolSearch 工具 + tool_reference 块**
模型通过 `ToolSearch` 工具按需发现延迟工具:
```
模型: ToolSearch(query: "github create PR")
系统: 返回 tool_reference 块 → [mcp__github__createPullRequest, mcp__github__listPRs]
下次 API 调用: 这两个工具的完整 schema 加入 tools 数组
```
`tool_reference` 是一个特殊的 content block 类型API 服务端收到后会展开为完整工具定义,模型可以在后续 turn 直接调用。
### 消息流转
```
Turn N:
tools 数组 = 常驻工具 + ToolSearch + 之前发现过的延迟工具
<available-deferred-tools> 块列出所有可发现的延迟工具名称
→ 模型调用 ToolSearch(query: "slack")
→ tool_result 包含 tool_reference 块: [mcp__slack__sendMessage]
Turn N+1:
tools 数组 = 常驻工具 + ToolSearch + [mcp__slack__sendMessage, ...]
→ 模型可以直接调用 mcp__slack__sendMessage
```
### 发现状态持久化
`extractDiscoveredToolNames()` 从消息历史中扫描所有 `tool_reference` 块,提取已发现的工具名。压缩时通过 compact boundary marker 的 `preCompactDiscoveredTools` 字段保存已发现集合,防止压缩丢失发现状态。
### 工具发现状态变更通知
两个机制告诉模型有哪些可发现工具:
**Legacy**`<available-deferred-tools>` 块作为 user 消息前置注入(每次工具池变更都会 bust prompt cache
**Modern**`deferred_tools_delta` 附件——diff 上次通知的工具池仅发送增量变更added/removed避免 bust cache。
### ToolSearch 搜索方式
- **精确选择** `select:ToolA,ToolB` — 按名称直接取工具
- **关键词搜索** `github create PR` — 搜索工具名 + searchHint + description加权评分
- **必选词** `+slack send``+` 前缀表示必须匹配
### 对项目的适用性
| 当前状态 | 是否需要 |
|---------|---------|
| ~25 个工具schema 总共 ~8K tokens | **暂不需要** |
| 无 MCP 工具接入 | 不需要 |
| 工具数量稳定 | 不需要 |
**触发条件**:当工具数量超过 ~50 或接入 MCP 服务器时,可以按以下步骤接入:
1. 为 MCP 工具设置 `shouldDefer: true` / `isMcp: true`
2. 注册 `ToolSearch` 工具
3. 在 ToolRegistry 中维护 `deferred_tool_names` 集合
4. API 调用时过滤 tools 数组 + 注入 `<available-deferred-tools>`
5. 压缩时保存 `preCompactDiscoveredTools` 快照
---
## 相关文件
| 文件 | 职责 |
|:---|:---|
| `src/agent/runtime/system_prompt.rs` | SystemPrompt 组装器 + 6 个静态常量 + SystemPromptCache |
| `src/agent/runtime/mod.rs` | `system_prompt()` + `build_environment_section()` |
| `src/agent/runtime/context.rs` | `build_initial_context()` — 上下文初始化 + 任务恢复 |
| `src/agent/skills.rs` | SkillRegistry — 两层技能加载 + 热重载 |
| `src/agent/memory/mod.rs` | MemoryManager — 按 recency 排序的记忆注入 |
| `src/agent/tools/mod.rs` | ToolRegistry — schema_cache + definition_filter |
| `src/agent/tools/subagent.rs` | 子代理模块化系统提示词构建 |
| `src/agent/subagent.rs` | SubAgentRunner — new_with_registry_and_hooks |
| `src/agent/compact.rs` | 四层压缩 + CollapseLog 集成 |
| `src/agent/compact/collapse.rs` | CollapseLog — 压缩历史记录 + 溢出合并 |
| `src/agent/hooks/` | 生命周期 hook + 提示词交互 |
| `docs/architecture/agent/system-prompt-optimization-plan.md` | 优化计划文档(背景、方案、对比分析) |
---
## 设计要点
1. **静态前置,动态后置**:内容不变的 section 先出现,服务端自然按哈希缓存
2. **首次计算,永久缓存**session 内一切不变,不需要 TTL仅在 /clear 时全部失效
3. **模块化 section**:各 section 独立管理便于调试、增删、A/B 测试
4. **两层 skill 加载**20 tokens 列表 vs 2000 tokens 全文,按需加载
5. **子代理完整提示词**:共享主代理的静态常量 + 独立 tools 列表
6. **CollapseLog 追踪**:每次压缩记录结构化 commit溢出自动合并
7. **运行时 nudge**:干预通过 user 消息注入,不修改 system prompt开闭原则