- 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 等变量说明
175 lines
5.8 KiB
Markdown
175 lines
5.8 KiB
Markdown
# Agent 架构概览
|
||
|
||
AstroResearch 内置了一个基于 **ReAct** (Thought → Action → Observation) 范式的科研智能体引擎 (`src/agent/`),参考 Claude Code 的分层设计。以下对各子系统的架构、数据流和内部逻辑进行完整说明。
|
||
|
||
### 整体架构
|
||
|
||
```mermaid
|
||
graph TD
|
||
subgraph API["API 层"]
|
||
SSE["SSE /api/chat/agent"]
|
||
Sessions["Session CRUD"]
|
||
Metrics["GET /api/chat/metrics"]
|
||
Audit["GET /api/chat/sessions/:id/audit"]
|
||
AskUser["问答 /api/chat/questions + /api/chat/answer"]
|
||
end
|
||
|
||
subgraph Runtime["AgentRuntime — ReAct 引擎"]
|
||
RunTurn["run_turn() 主入口"]
|
||
SP["SystemPrompt 组装器"]
|
||
CtxBuild["Context Builder 上下文构建"]
|
||
ReAct["ReAct 主循环"]
|
||
Streaming["streaming.rs 流式处理"]
|
||
Executor["executor.rs 并行执行"]
|
||
Finalize["finalize.rs 会话收尾"]
|
||
TokenBudget["token_budget.rs"]
|
||
CircuitBreaker["circuit_breaker.rs"]
|
||
end
|
||
|
||
subgraph Tools["工具系统 (tools/)"]
|
||
AgentTool["AgentTool trait"]
|
||
Registry["ToolRegistry"]
|
||
FS["filesystem/ (6 工具)"]
|
||
Astro["astro/ (7 工具)"]
|
||
AskUserT["ask_user"]
|
||
SubAgentT["subagent/delegate_research"]
|
||
TeamT["team/ (4 工具)"]
|
||
BG["background (2 工具)"]
|
||
end
|
||
|
||
subgraph CrossCutting["横切关注点"]
|
||
Hooks["HookRegistry (9 事件)"]
|
||
Skills["SkillRegistry (两层加载)"]
|
||
Memory["MemoryManager (项目记忆)"]
|
||
Permission["PermissionChecker"]
|
||
FileCache["FileStateCache (Read 去重)"]
|
||
end
|
||
|
||
subgraph DB["持久化"]
|
||
AgentSessions["agent_sessions"]
|
||
AgentMessages["agent_messages"]
|
||
AgentTasks["agent_tasks"]
|
||
AgentAudit["agent_audit_log"]
|
||
end
|
||
|
||
SSE --> RunTurn
|
||
RunTurn --> SP
|
||
RunTurn --> CtxBuild --> DB
|
||
RunTurn --> ReAct
|
||
ReAct --> Streaming --> Tools
|
||
ReAct --> Executor --> Tools
|
||
ReAct --> TokenBudget
|
||
ReAct --> CircuitBreaker
|
||
ReAct --> Finalize
|
||
|
||
Hooks -.-> ReAct
|
||
Hooks -.-> Tools
|
||
Skills -.-> Tools
|
||
Memory -.-> SP
|
||
Permission -.-> Tools
|
||
FileCache -.-> Tools
|
||
```
|
||
|
||
---
|
||
|
||
### ReAct 运行循环 (`runtime/`)
|
||
|
||
主循环由 `AgentRuntime::run_turn()` 驱动,分为 4 个阶段:
|
||
|
||
#### 完整生命周期
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant FE as 前端 SSE
|
||
participant RT as AgentRuntime
|
||
participant DB as SQLite
|
||
participant LLM as LLM API
|
||
participant Tools as ToolRegistry
|
||
|
||
FE->>RT: POST /api/chat/agent { question, session_id? }
|
||
|
||
Note over RT: Phase 1 — 会话管理
|
||
RT->>DB: create_or_resume_session()
|
||
alt 新会话
|
||
DB-->>RT: session_id = uuid, turn_index = 0
|
||
else 恢复会话
|
||
DB-->>RT: 验证存在 + 计算 turn_index
|
||
end
|
||
RT->>RT: 触发 OnSessionStart hook
|
||
RT-->>FE: SSE session { session_id, title }
|
||
|
||
Note over RT: Phase 2 — 上下文构建
|
||
RT->>RT: build_initial_context()
|
||
RT->>RT: ① 组装 SystemPrompt (静态 section + 记忆注入)
|
||
RT->>DB: ② 加载历史消息 load_history_for_llm()
|
||
RT->>DB: ③ 恢复未完成任务 (agent_tasks)
|
||
RT->>RT: ④ 检查压缩/清理上下文
|
||
RT->>DB: ⑤ 保存用户消息
|
||
RT->>RT: ⑥ 运行 PreToolUse hooks 过滤
|
||
|
||
Note over RT: Phase 3 — ReAct 循环
|
||
loop 每步迭代 (step ≤ max_steps)
|
||
RT->>LLM: chat_stream(messages + tool_defs)
|
||
LLM-->>RT: ReasoningDelta / TextDelta / ToolCallsComplete
|
||
RT-->>FE: SSE thought / text_delta / tool_call
|
||
|
||
alt 无工具调用 → 最终答案
|
||
RT->>DB: 保存 assistant 消息
|
||
RT-->>FE: SSE text_delta → usage → done
|
||
Note over RT: break 循环
|
||
else 有工具调用
|
||
RT->>RT: 检查 token 预算 + 熔断器
|
||
RT->>RT: validate_and_prepare() — 去重 + 过滤
|
||
RT->>Tools: execute_parallel() — 并行执行
|
||
Tools-->>RT: (tool_call_id, name, output)
|
||
RT-->>FE: SSE tool_result { tool_call_id, name, output }
|
||
RT->>DB: 保存 tool 消息 + 审计日志
|
||
RT->>RT: 运行 PostToolUse hooks
|
||
RT->>RT: 检测压缩需求 (auto_compact)
|
||
end
|
||
RT->>RT: 检测循环终止条件
|
||
end
|
||
|
||
Note over RT: Phase 4 — 会话收尾
|
||
RT->>DB: 更新 turn_count + updated_at
|
||
RT->>DB: calculate_and_persist_metrics()
|
||
RT->>RT: 运行 OnSessionStop hook
|
||
RT-->>FE: SSE done
|
||
```
|
||
|
||
#### 并行工具执行模型
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant ReAct as ReAct 循环
|
||
participant Val as validate_and_prepare
|
||
participant Exec as execute_parallel
|
||
participant T1 as Tool A
|
||
participant T2 as Tool B
|
||
participant FE as 前端 SSE
|
||
|
||
ReAct->>Val: LLM 返回 [tool_call_a, tool_call_b]
|
||
Val->>Val: 去重检测 + 权限验证
|
||
Val-->>ReAct: prepared_calls[] + has_duplicate 标志
|
||
ReAct->>FE: 发送 tool_call SSE (逐一)
|
||
ReAct->>Exec: 启动 execute_parallel()
|
||
par 并行执行
|
||
Exec->>T1: tool_a.execute(args_a)
|
||
Exec->>T2: tool_b.execute(args_b)
|
||
end
|
||
T1-->>Exec: ToolOutput { content, is_error }
|
||
Exec-->>FE: SSE tool_result (立即推送)
|
||
T2-->>Exec: ToolOutput { content, is_error }
|
||
Exec-->>FE: SSE tool_result (立即推送)
|
||
Exec-->>ReAct: Vec<(tool_call_id, name, args, output, cancelled)>
|
||
ReAct->>ReAct: PostToolUse hooks + 审计日志 + 持久化
|
||
```
|
||
|
||
- **并发上限**:由 `max_concurrent_tools` 环境变量控制,默认不限制
|
||
- **Sibling Abort**:仅 `causes_sibling_abort() = true` 的工具(如 `download_paper`)能在出错时中断兄弟任务
|
||
- **InterruptBehavior**:`Block` 工具(如 `ask_user`)不可被用户取消;`Cancel` 工具可在取消信号时中断
|
||
- **超时控制**:每个工具有独立超时,默认 120s(`AGENT_TOOL_TIMEOUT_SECS`)
|
||
|
||
---
|
||
|