AstroResearch/docs/architecture/agent/tasks.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

8.7 KiB
Raw Blame History

任务系统 (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 必须先完成,形成 DAG
  • owner 字段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: 同一时刻只能有一个

约束规则:

  1. 最多一个 in_progressTodoWriteTool 在格式化输出时检测并警告(tools/todo.rs:115-118
  2. DAG 依赖TaskBoard::can_start() 检查所有 blockedBy 依赖是否为 completedtask_board.rs:33-63
  3. 原子认领claim_task() 使用乐观并发控制,WHERE owner IS NULL 保证不被重复认领
  4. 应用层验证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 = 3steps_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 字段引入