- 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 等变量说明
8.7 KiB
任务系统 (Task System)
任务系统为 Agent 提供 规划 → 执行跟踪 → 状态持久化 → 跨 turn 恢复 的完整闭环,参考 Claude Code s12 Task System 和 s17 Autonomous Agents 设计。
概览
flowchart TD
subgraph RT["AgentRuntime (run_react_loop)"]
direction TB
S1["1. LLM 调用"]
S2["2. 检测 todo_write 调用"]
S3["3. persist_tasks() 持久化"]
S4["4. 每 3 步 nag reminder"]
S5["5. context.rs 恢复任务状态"]
end
RT --> TodoWrite["TodoWriteTool<br/>(纯格式化, 并发安全, 无副作用)"]
RT --> Context["context.rs<br/>restore_tasks_from_db()<br/>(每 turn 开始时注入)"]
RT --> TaskBoard["TaskBoard<br/>(跨 session 共享看板)<br/>can_start() / claim_task() (原子)<br/>list_available_tasks()"]
TodoWrite --> Persist["persist_tasks<br/>INSERT OR REPLACE INTO<br/>agent_tasks"]
Persist --> DB["SQLite: agent_tasks<br/>(session_id, task_id UNIQUE)"]
数据库 Schema
-- migrations/20260616000000_agent_tasks.sql
CREATE TABLE agent_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
task_id TEXT NOT NULL, -- LLM 生成的任务唯一标识
content TEXT NOT NULL DEFAULT '', -- 任务描述
status TEXT NOT NULL DEFAULT 'pending'
CHECK(status IN ('pending', 'in_progress', 'completed')),
blocked_by TEXT NOT NULL DEFAULT '[]', -- JSON 数组: ["task_1", "task_2"]
owner TEXT NOT NULL DEFAULT '', -- 归属 agent (lead / sub_xxx / teammate)
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES agent_sessions(session_id) ON DELETE CASCADE
);
-- 索引
CREATE UNIQUE INDEX idx_agent_tasks_session_task ON agent_tasks(session_id, task_id);
CREATE INDEX idx_agent_tasks_session ON agent_tasks(session_id);
CREATE INDEX idx_agent_tasks_status ON agent_tasks(session_id, status);
关键设计点:
- (session_id, task_id) 联合唯一索引:支持
INSERT ... ON CONFLICT DO UPDATE的 upsert 语义 blocked_by存 JSON 数组:如["1", "2"]表示依赖任务 1 和 2 必须先完成,形成 DAGowner字段(migrations/20260618000000_agent_identity.sql引入):支持多 Agent 场景下的任务归属
任务状态机
stateDiagram-v2
[*] --> pending: 初始状态
pending --> pending: LLM 标记 completed<br/>(回退)
pending --> in_progress: claim_task()<br/>或 LLM 标记 in_progress
in_progress --> completed: LLM 标记 completed
completed --> [*]: 终点状态
note right of in_progress: 同一时刻只能有一个
约束规则:
- 最多一个
in_progress:TodoWriteTool 在格式化输出时检测并警告(tools/todo.rs:115-118) - DAG 依赖:TaskBoard::can_start() 检查所有 blockedBy 依赖是否为 completed(
task_board.rs:33-63) - 原子认领:claim_task() 使用乐观并发控制,
WHERE owner IS NULL保证不被重复认领 - 应用层验证:persist_tasks 只做自引用检测,不做完整的循环依赖检测(文档明确标注)
核心组件
TodoWriteTool (tools/todo.rs)
LLM 通过 function calling 调用的工具,声明参数:
{
"todos": [
{"id": "1", "content": "搜索相关文献", "status": "completed"},
{"id": "2", "content": "分析论文方法", "status": "in_progress", "blockedBy": ["1"]},
{"id": "3", "content": "撰写综述", "status": "pending", "blockedBy": ["2"]}
]
}
设计原则 — 工具层与持久化层分离:
TodoWriteTool::execute()只做格式化和约束校验,不写数据库is_concurrency_safe()返回true(纯格式化,无副作用)- 实际的 SQLite 持久化由
AgentRuntime::run_react_loop()在检测到todo_write调用后统一完成(runtime/mod.rs:845-854)
persist_tasks() (tools/todo.rs)
pub async fn persist_tasks(
db: &SqlitePool,
session_id: &str,
todos: &[serde_json::Value],
owner: &str, // "lead" / "sub_xxx" / teammate name
) -> anyhow::Result<()>
- 使用
INSERT ... ON CONFLICT(session_id, task_id) DO UPDATE SET ...实现 upsert - 自动跳过任务对自身的引用(
blockedBy中包含自身 ID 时忽略并记录 warn) - 作为公开 API 导出(
tools/mod.rs:49),允许 teammate、外部调用者直接操作
TaskBoard (task_board.rs)
共享任务看板,提供跨 Agent 的任务可见性:
| 方法 | 功能 | 并发策略 |
|---|---|---|
can_start(session_id, task_id) |
遍历 blockedBy,检查所有依赖是否 completed | 只读,天然安全 |
claim_task(session_id, task_id, claimant) |
原子认领(status→in_progress, owner→claimant) | WHERE owner IS NULL OR owner = '' |
list_available_tasks(limit) |
列出所有可认领的 pending 任务,跨 session | 只读,附加 can_start 解析 |
认领 SQL 的原子性保证:
UPDATE agent_tasks SET owner = ?, status = 'in_progress'
WHERE session_id = ? AND task_id = ?
AND (owner IS NULL OR owner = '' OR status = 'pending')
rows_affected() > 0 表示认领成功,否则已被他人认领。
ReAct 循环中的集成点
在 AgentRuntime::run_react_loop() 中有三个关键集成点:
todo_write 检测与持久化 (mod.rs:752-855)
// 检测 todo_write 调用
let called_todo_write = tool_calls.iter().any(|tc| tc.function.name == "todo_write");
if called_todo_write {
steps_since_last_todo = 0; // 重置 nag 计数器
}
// 工具执行完成后持久化
if called_todo_write {
for prep in &prepared_calls {
if prep.tool_name == "todo_write" {
if let Some(todos) = prep.args.get("todos").and_then(|t| t.as_array()) {
let _ = persist_tasks(db, sid, &todos_vec, "lead").await;
}
}
}
}
进度催促 Nag Reminder (mod.rs:589-596)
每 3 步未调用 todo_write,自动向 LLM 注入提醒:
提醒:你已经连续多步未更新任务计划。建议调用 todo_write 工具复盘当前进度并规划后续步骤。
nag_after_steps = 3,steps_since_last_todo 在每次非 todo_write 调用后递增。
跨 Turn 任务恢复 (context.rs:32-34)
每个新 turn 开始时,restore_tasks_from_db() 从 agent_tasks 表查询当前 session 的所有任务,格式化后注入 LLM 上下文:
[当前任务状态]
以下是上次会话中持久化的任务计划,请基于最新状态继续工作:
✅ [1] 文献检索
✅ [2] 文献分析
🔄 [3] 撰写综述 (依赖: 1,2)
⏳ [4] 最终校对 (依赖: 3)
使用 todo_write 工具更新任务进度。
System prompt 中也包含提示(system_prompt.rs:66):
任务状态会在每轮开始时从数据库恢复,请基于最新状态继续工作。
子代理与任务隔离
子代理 (SubAgentRunner) 与父代理在 Task 层面保持隔离:
- 子代理拥有独立的 ToolRegistry 和 ReAct 循环(
subagent.rs:248-254) - 子代理消息标记
agent_name = "sub_xxx",父代理加载历史时只加载agent_name = "lead"的消息(session.rs:78) - 子代理不直接操作父代理的 agent_tasks,只返回文本摘要
agent_tasks.owner字段已为 teammate 场景预留
辅助机制:工具结果持久化 (tools/persist.rs)
与 Task 系统协同工作的工具输出持久化:
| 维度 | Task 系统 | Persist 系统 |
|---|---|---|
| 回答的问题 | "要做什么" | "看到了什么" |
| 触发条件 | LLM 调用 todo_write | 工具输出超过 max_output_chars |
| 存储位置 | SQLite agent_tasks 表 | 磁盘文件 |
| 幂等保证 | ON CONFLICT DO UPDATE | 独占创建 (create_new) |
当工具输出超过 AGENT_MAX_TOOL_OUTPUT_CHARS(默认 4000)时,完整内容写入 {library_dir}/tool-results/{tool_call_id}.txt,返回 <persisted-output> XML 占位符,LLM 后续可通过 read_file 读取完整内容。
相关文件
| 文件 | 职责 |
|---|---|
src/agent/tools/todo.rs |
TodoWriteTool 定义 + persist_tasks() |
src/agent/task_board.rs |
TaskBoard — 跨 session 共享看板 |
src/agent/runtime/mod.rs |
ReAct 循环中的 nag/persist 集成 |
src/agent/runtime/context.rs |
restore_tasks_from_db — 跨 turn 恢复 |
src/agent/runtime/system_prompt.rs |
系统提示词中的 Task 指引 |
src/agent/tools/persist.rs |
工具输出磁盘持久化(辅助机制) |
src/agent/subagent.rs |
子代理中的 Task 隔离 |
migrations/20260616000000_agent_tasks.sql |
agent_tasks 表 DDL |
migrations/20260618000000_agent_identity.sql |
owner 字段引入 |