AstroResearch/docs/architecture/agent/context.md
Asfmq f6df9d8136 feat: Agent 思考模式前端可控、子代理全链路持久化、权限系统、工具 ID 追踪体系、前端面板与文档架构重构
- AgentConfig/LlmClient 新增 enable_thinking 参数,前端 SSE 请求传递 thinking
  开关,仅千问/DashScope 时启用
  - 完善权限系统,支持细粒度的权限控制和用户权限申请
  - delegate_research 工具重命名为 subagent,SubAgentTool/SubAgentRunner 重构
  - 子代理消息(system/user/assistant/tool)持久化到 agent_messages 表,带 agent_name 标识
  - 子代理活动日志(工具调用列表+思考摘要)注入返回结果,Hooks 获得正确 session_id 和 subagent_name
  - LLM 工具调用 ID 回退生成 UUID(llm.rs),ToolCall/ToolResult SSE 事件增加 id/tool_call_id 双字段
  - ToolContext 扩展 session_id/sse_tx/enable_thinking 字段,executor 统一注入而非构造函数传参
  - agent_messages 新增 metadata+raw_json 列,agent_sessions 暴露 summary 字段
  - 删除文件级 transcript 快照(compact.rs),改为依赖 DB 持久化
  - ResearchAgentPanel 重写:TimelineItem 类型替代 StreamStep,支持会话历史回放
  - 新增 AgentMetricsPanel/AskUserQuestionCard/AuditLogViewer 三个前端组件,types.ts 完整类型定义
  - docs/architecture/ 分层重组:概览/核心模块/核心工作流 + agent/ 子目录 11 篇专题文档
  - docs/api.md 补充 RAG/Target/Agent 接口,docs/development.md 新建开发指南
  - .env.example 完全重写,补充 FALLBACK_MODEL 等变量说明
2026-06-18 01:21:02 +08:00

16 KiB
Raw Blame History

Agent 上下文管理系统

AstroResearch Agent 的上下文管理是一个多层防御架构,涵盖从系统提示词组装、运行时预算监控、多级压缩、错误恢复到跨会话持久化的完整生命周期。

架构概览

graph TB
    subgraph 构建阶段["构建阶段"]
        SP["SystemPrompt<br/>模块化组装"]
        HL["+ 历史加载"]
        TR["+ 任务恢复"]
        SL["+ 技能列表"]
        PM["+ 项目记忆"]
    end

    subgraph 运行时监控["运行时监控"]
        TB["TokenBudget<br/>三级渐进Nudge"]
        DR["DimReturns"]
        TD["Todo提醒"]
        BN["后台通知"]
        LD["死循环检测"]
    end

    subgraph 压缩阶段["压缩阶段"]
        L0["snip (L0)"]
        L1["micro (L1)"]
        L2["auto (L2)"]
        L3["aggro_micro (L3)"]
        L4["identity (L4)"]
        L0 --> L1 --> L2 --> L3 --> L4
    end

    subgraph 恢复阶段["恢复阶段"]
        ER["ErrorRecovery<br/>5级恢复阶梯"]
        B4["429指数退避"]
        CB["熔断器保护"]
        FC["file_cache<br/>恢复注入"]
    end

    subgraph 持久化层["持久化层"]
        AM["agent_msgs"]
        AT["agent_tasks"]
        MM["MEMORY.md"]
        TJ["trajectory"]
        AL["audit_log"]
    end

    构建阶段 --> 运行时监控 --> 压缩阶段 --> 恢复阶段 --> 持久化层

1. 上下文构建 (runtime/context.rs + runtime/session.rs)

1.1 构建流程

每个 turn 开始时 build_initial_context() 按以下顺序构建 LLM 消息列表:

  1. agent_messages 表加载历史消息(按 agent_name='lead' 隔离,排除子代理消息)
  2. 若历史中无 System 消息,在最前面插入 System Prompt
  3. 追加当前用户问题
  4. agent_tasks 表恢复持久化任务状态,格式化为 [当前任务状态] 消息块

1.2 System Prompt 模块化组装 (runtime/system_prompt.rs)

静态与动态 section 分离,静态 section 在前以最大化 prompt cache 命中率:

顺序 Section 类型 内容
1 identity 静态 "你是一位专业的天体物理学研究助手…"
2 tools 动态 从 ToolRegistry 生成工具名称 + 一行描述(~20 tokens/tool
3 skills 动态 从 SkillRegistry 构建 <system-reminder> 技能列表(~20 tokens/skill
4 memory 动态 从 MemoryManager 加载最近 5 条项目记忆
5 principles 静态 9 条核心工作原则主动搜索、引用来源、LaTeX 格式等)

1.3 会话生命周期 (session.rs)

  • create_or_resume_session():新建会话生成 UUID恢复会话验证存在性并计算 turn_index
  • load_history_for_agent():按 session_id + agent_name 加载,还原 role/content/tool_calls/tool_call_id/thought 字段
  • 消息隔离:agent_name="lead" 仅加载主代理历史,"*" 加载全部(调试用)

2. 上下文运行时监控 (runtime/mod.rs ReAct 循环内)

每一步 LLM 调用前执行以下检查:

2.1 Token 预算管理 (runtime/token_budget.rs)

软限制: AGENT_TOKEN_SOFT_LIMIT  (默认 32,000)
硬限制: AGENT_TOKEN_HARD_LIMIT  (默认 40,000)

三级渐进式 Nudge(每步检查,按优先级仅注入一条):

级别 条件 图标 消息语义
near_soft total_spent ≥ soft_limit × 80% 💡 "Token 预算提示…请注意控制后续步骤的深度"
over_soft total_spent ≥ soft_limit 🟡 "Token 预算警告…请尽快总结关键发现"
over_hard total_spent ≥ hard_limit 🔴 "已耗尽…请立即总结并给出最终答案"

Diminishing Returns 检测

  • 条件3+ 次延续 + 连续 2 次检查的 token 增量 < 500
  • 触发后:强制终止工具调用,注入"请直接给出最终答案"消息,调用 final_answer_without_tools()

2.2 TodoWrite Nag 提醒

连续 3 步未调用 todo_write → 注入提醒消息,防止模型陷入无计划循环。

2.3 后台任务通知注入 (background.rs)

慢速操作download_paper, parse_paper通过 bg_task_run 异步执行。 每轮 LLM 调用前,BgNotificationQueue::drain() 收集已完成结果并注入:

[后台任务完成] ✅ download_paper: 2024A&A... (task_abc123): 下载成功...

2.4 死循环检测 (DuplicateDetector)

  • 同一 (tool_name, arguments) 连续调用 ≥ 3 次 → 注入错误 tool_result 跳过
  • 同时在 metrics 中记录 duplicate_detections

3. 上下文压缩系统 (compact.rs)

3.1 五层压缩策略

compress_with_fallback() 按顺序执行,每层后检查是否需要继续:

graph LR
    L0["Layer 0: snip_compact<br/>零 API 调用<br/>消息超 MAX_MESSAGES 时截断"]
    L1["Layer 1: micro_compact<br/>零 API 调用<br/>替换早期工具结果为占位符"]
    L2["Layer 2: auto_compact<br/>LLM 摘要<br/>历史压缩为 &lt;500 字中文"]
    L3["Layer 3: aggressive_micro<br/>零 API 调用<br/>激进占位符 keep_recent=2"]
    L4["Layer 4: identity_inject<br/>零 API 调用<br/>注入身份确认块"]
    L0 --> L1 --> L2 --> L3 --> L4
触发条件 算法 API 调用 关键参数
snip (L0) 消息数 > MAX_MESSAGES (默认 50) find_safe_cut_point 切中间段 → 插入占位消息 HEAD_KEEP=3, tail_keep=47
micro (L1) L0 后仍超 context_char_limit × 1.5 工具结果 → [Previous: used {tool_name}] keep_recent=8
auto (L2) L1 后仍超限 LLM 生成中文摘要替换历史 摘要 ≤500 字
aggressive_micro (L3) L2 后仍超限 同 L1 但仅保留最近 2 条工具结果 keep_recent=2
identity (L4) 压缩后消息 ≤4 条 注入 [身份确认] 天体物理学研究助手身份

3.2 压缩触发机制 (在 ReAct 循环中)

flowchart TD
    A["estimated_tokens > token_budget.soft_limit?"]
    A --> B["检查熔断器 can_attempt()"]
    B -->|"Closed / HalfOpen"| C["执行 snapshot_compress_restore()"]
    B -->|"Open"| D["跳过,记录警告"]
    C --> E{"messages.len() < before_len ?"}
    E -->|"是"| F["record_success()"]
    E -->|"否"| G["record_failure()"]

Token 估算策略:

  • 优先使用 API 返回的精确 prompt_tokens
  • 辅以增量估算:新增消息数 × (content.len() + 4) 字符估算
  • 首次无 API 数据时回退到 rough_estimate_tokens

3.3 手动压缩 (compress_context 工具触发)

  • LLM 调用 compress_context 工具 → 设置 pending_manual_compress = true
  • 下一轮循环中执行压缩(若刚已自动压缩则跳过)
  • 手动压缩不受熔断器限制,成功后重置熔断器

3.4 安全切割点 (find_safe_cut_point)

确保不破坏 assistant(tool_calls) ↔ tool_result 配对的算法:

  1. 计算候选切割点 messages.len() - desired_keep
  2. 若切割点落在 tool 消息上 → 向前追溯到对应 assistant(tool_calls) 一并保留
  3. 向前扫描孤立 assistant(tool_calls)(无 tool_result 配对)→ 切点前移

3.5 压缩熔断器 (runtime/circuit_breaker.rs)

防止无限自动压缩的三态熔断器,创建于 AgentRuntime::new(),跨 turn 共享:

stateDiagram-v2
    [*] --> Closed: 初始状态
    Closed --> Closed: record_success() → 重置计数
    Closed --> Open: 连续失败 3 次
    Open --> HalfOpen: 5 分钟后自动恢复
    HalfOpen --> Closed: record_success()
    HalfOpen --> Open: record_failure()
    Closed --> Closed: reset() (手动压缩成功后)
参数 说明
MAX_CONSECUTIVE_FAILURES 3 连续失败次数阈值
AUTO_RECOVERY_TIMEOUT_SECS 300 熔断后自动尝试恢复的等待时间
压缩成功判定 messages.len() < before_len 消息数减少即视为成功

3.6 递归守卫

COMPACTING AtomicBool — 压缩内部触发的 LLM 调用可能再次触发压缩,递归守卫通过 compare_exchange 防止嵌套压缩死循环。


4. 文件缓存与压缩集成 (runtime/file_cache.rs)

压缩前后的文件状态保护循环 (snapshot_compress_restore)

压缩前
  ├─ FileStateCache.to_snapshot()      → 保存所有已读文件快照(按时间戳降序)
  └─ FileStateCache.clear()            → 清空缓存

压缩后
  ├─ restore_from_snapshot(max=5)      → 恢复最近 5 个文件到 LRU 缓存
  └─ build_restore_context(max=5)      → 生成 "[压缩后恢复: {path}]" 块注入消息
参数 说明
MAX_ENTRIES 100 缓存条目上限
MAX_CACHE_SIZE_BYTES 25 MB 缓存内容总大小上限
POST_COMPACT_MAX_FILES_TO_RESTORE 5 压缩后恢复的文件数
POST_COMPACT_MAX_CHARS_PER_FILE 4,000 每文件恢复内容上限
FILE_UNCHANGED_STUB 静态字符串 文件未变时返回的占位消息

LRU 淘汰策略:容量超限时自动驱逐最久未使用的条目。


5. 错误恢复系统 (runtime/error_recovery.rs)

5.1 错误分类 (classify_error)

ErrorKind 检测关键词
RateLimited 429, rate limit, rate_limit, too many requests
Overloaded 529, overloaded, overload, service overloaded
PromptTooLong prompt_too_long, context length, 413, context_window_exceeded, input length
TokenExhausted max_tokens, token limit, token_exhausted, maximum context length
Timeout timeout, timed out, deadline exceeded, 408, 504
ModelError 默认归类,携带原始错误字符串

5.2 恢复阶梯

LLM 流式调用失败后的 5 级恢复:

步骤 RecoveryStep 操作 适用错误
1 AggressiveCompact snip + micro(keep_recent=2) PromptTooLong, TokenExhausted
2 ReactiveCompact LLM 摘要压缩 同上
3 EscalateTokens 提升 hard_limit → 64,000 同上
4 MultiTurn 注入分步恢复消息 同上
5 Surface 放弃恢复,暴露错误 不可恢复错误ModelError 直接到此)

每步有 has_attempted 守卫,防止无限循环。

5.3 429/529 退避重试(独立快速路径)

RateLimited/Overloaded 不走恢复阶梯,独立执行指数退避重试(最多 10 次):

  • 延迟公式:min(500 × 2^attempt, 32,000) + deterministic jitter
  • 支持 retry_after header 解析
  • 529 连续 3 次过载 → 尝试切换到 FALLBACK_MODEL 环境变量指定的备用模型
  • 重试期间检查用户取消信号

6. 子代理上下文隔离 (subagent.rs)

父代理通过 delegate_research 委托子任务给子代理,子代理拥有独立的上下文环境:

组件 隔离方式
消息上下文 全新 [system_prompt, user_prompt],不含父代理中间工具调用
工具访问 共享 ToolRegistry可配置受限 registry如仅只读工具
Hook 管道 完整的 PreToolUse/PostToolUse + PermissionChecker
进度通知 通过 progress_tx 向父代理发送 Thought/ToolCall/ToolResult SSE 事件
最终返回 仅返回 [子代理活动记录] + [子代理结论] 文本摘要
消息持久化 agent_name=sub_xxx 写入父会话的 agent_messages

子代理也有独立的上下文压缩(同 compact::compress_context)、死循环检测(同 DuplicateDetector 逻辑)、强制终止(超 max_steps 时调用 force_final_answer)。


7. 跨会话持久化

7.1 数据库表

持久化内容 上下文用途
agent_sessions session_id, title, model, turn_count, metadata 会话生命周期管理
agent_messages role, content, thought, tool_calls(JSON), tool_call_id, token_count, metadata, raw_json(完整 ChatMessage), agent_name 历史恢复 + 审计追溯
agent_tasks task_id, content, status, blocked_by, owner 跨 turn 任务状态恢复
agent_audit_log session_id, step, tool_name, status, elapsed_ms, output_preview 完整审计追踪

7.2 文件系统记忆 (memory/)

  • Agent 可通过 save_memory 工具将重要信息写入 ~/.claude/projects/{project}/memory/
  • 四类记忆:user, feedback, project, reference
  • 写入时门控:内容质量检查 + Jaccard 相似度去重70% 阈值)
  • 每 turn 结束时 fire-and-forget 自动提取候选记忆
  • 下次会话的 system prompt 中自动加载最近 5 条记忆

7.3 Trajectory 导出

每 turn 结束时,TrajectoryExporter::export() 将完整会话轨迹导出到文件系统,用于调试和审计。


8. Lifecycle Hooks 与上下文事件

Hook 触发时机 上下文影响
OnSessionStart 会话创建/恢复 通知生命周期开始
PreToolUse 工具执行前 可 MutateInput 注入上下文、Block 阻止
PostToolUse 工具执行后 可 MutateOutput 修改结果、审计日志写入
OnStepComplete 每步结束 日志消息数/预算使用率
OnPreCompact 压缩前 记录消息数/预估 tokens
OnPostCompact 压缩后 记录新消息数/压缩方法
OnSubagentStart 子代理启动 通知子代理创建
OnSubagentStop 子代理停止 记录结果摘要
OnSessionStop 会话终止 清理取消状态、记录终止原因

9. SSE 流事件 — 前端上下文同步

Agent 内部上下文通过 SSE 事件实时同步到前端:

Session → Thought → (ToolCall ↔ ToolResult)* → TextDelta → Usage → Done

tool_call.id 贯穿全链路LLM 生成 ID → 前端精确匹配 tool_call/tool_result 条目 → 审计日志关联。


10. 环境变量配置

变量 默认值 作用
AGENT_MAX_STEPS 8 ReAct 最大迭代步数
AGENT_TOOL_TIMEOUT_SECS 120 工具执行超时(秒)
AGENT_MAX_TOOL_OUTPUT_CHARS 4000 工具输出截断长度(字符)
AGENT_CONTEXT_CHAR_LIMIT 16000 上下文压缩触发点(字符估算)
AGENT_TOKEN_SOFT_LIMIT 32000 Token 预算软限制(触发 nudging
AGENT_TOKEN_HARD_LIMIT 40000 Token 预算硬限制(触发强制动作)
AGENT_MAX_MESSAGES 50 snip_compact 触发阈值(消息数)
FALLBACK_MODEL 529 连续过载 3 次时的备用模型

11. 关键源文件索引

文件 职责
src/agent/compact.rs 五层压缩 + 安全切割 + 递归守卫 + 身份注入
src/agent/runtime/mod.rs ReAct 循环 + 压缩触发 + 后台通知 + todo nag
src/agent/runtime/context.rs 初始上下文构建 + 任务状态恢复
src/agent/runtime/session.rs 会话 CRUD + 历史消息加载
src/agent/runtime/system_prompt.rs 模块化 System Prompt 组装
src/agent/runtime/token_budget.rs Token 预算 + 三级 Nudge + Dim Returns
src/agent/runtime/circuit_breaker.rs 压缩熔断器
src/agent/runtime/error_recovery.rs 5 级恢复阶梯 + 错误分类 + 退避重试
src/agent/runtime/file_cache.rs 文件状态缓存 + 压缩快照/恢复
src/agent/runtime/streaming.rs 流式响应 + 取消竞速
src/agent/runtime/executor.rs 工具验证 + 并行执行 + Hook 集成
src/agent/runtime/finalize.rs 会话收尾 + 记忆提取 + 轨迹导出
src/agent/subagent.rs 子代理上下文隔离 + 独立 ReAct 循环
src/agent/background.rs 后台任务队列 + 通知注入
src/agent/hooks.rs 9 个生命周期事件 + 内置 3 Hook
src/agent/skills.rs 两层技能加载 + 热重载 + 条件激活
src/agent/tools/memory.rs save_memory 工具 + 写入门控