- docs/02 架构设计: 新增 aichat审查/异步审批构想/流式渲染调研/generating状态机/密钥迁移健壮性/工作流脚本执行边界/条件表达式引擎/F-07 trait下沉/Agent架构说明/任务推进构想/功能创意池;更新功能决策记录+归档/对抗论证/文档记录规范/经验记录
- docs/03 模块文档: 新增 AI对话引擎/DAG引擎详解;更新 df-knowledge/df-nodes/df-storage/df-workflow/df-ai
- docs/05 代码审查: 新增 全栈审查/全局review/架构审查/近期改动审查/工作区多角度走查/自研memo流式渲染审查
- docs/09 问题排查: 新增 aichat-apikey-401
- docs/INDEX+README 索引同步;docs/todo 待办看板(2026-06-15 汇总)
- PROGRESS.md Sprint 22-25;URGENT.md 加急清单快照(5 项 P0 已全修)
- scripts/cleanup_orphan_tasks.{py,sh} 孤儿任务清理工具
- .gitignore 补 *.broken.bak + tmp/ 噪音排除
15 KiB
AI 对话引擎(Agentic Loop)
创建:2026-06-14 | 来源:基于
src-tauri/src/commands/ai/实际代码核对编写 关联模块文档:df-ai-AI集成模块(crate 层:Provider / ContextManager / 工具基础设施) 关联架构文档:Agent架构说明-2026-06-14.md(能力边界盘点)
一、概述
DevFlow 的 AI 对话不是简单的"发消息→收回复",而是一个 ReAct 循环(Reason + Act):LLM 流式回复 → 调用工具 → 执行工具 → 结果回传 LLM → 循环,直到 LLM 不再需要工具(返回纯文本)或达到最大轮次。
核心代码位于 src-tauri/src/commands/ai/,11 个子模块协作:
commands/ai/
├── mod.rs — 模块入口 + glob 重导出 + AiSession 定义
├── commands.rs — 17 个 IPC 命令(send/approve/reject/clear/switch…)
├── agentic.rs — ReAct 循环主体(run_agentic_loop / try_continue_agent_loop)
├── stream_recv.rs — 流式接收(idle timeout / 断连检测 / 停止信号)
├── conversation.rs — 持久化 + Token 累加器 + 截断函数
├── audit.rs — 工具执行审计(process_tool_calls / audit_finalize / build_approval_reason)
├── tool_registry.rs — 12 个 AI 工具注册 + 路径校验
├── prompt.rs — system prompt 构建 + provider 获取
├── skills.rs — 技能联想(SKILL.md frontmatter 解析)
├── title.rs — 对话标题自动生成
└── knowledge_inject.rs — 知识注入 + 知识提炼
二、ReAct 循环详解
核心流程
用户发消息
│
▼
┌─ for iteration in 0..MAX_AGENT_ITERATIONS(10) ──────────────┐
│ │
│ 1. 用户停止? → 收尾退出 │
│ │
│ 2. 构建请求消息 │
│ ├─ system prompt(技能 + 知识注入) │
│ ├─ 历史消息(超预算时裁剪,保护工具三元组 + 最近 6 条) │
│ └─ 工具定义(12 个内置工具) │
│ │
│ 3. LLM 并发限流(全局 3 / 单对话 2 双层 Semaphore) │
│ │
│ 4. stream_llm(流式接收) │
│ ├─ 逐 chunk 推送 AiTextDelta 到前端 │
│ ├─ 累积 tool_calls(按 index 排序) │
│ ├─ idle 120s timeout → 判定断连 │
│ └─ 流尽未收 finished → 丢弃残缺 │
│ │
│ 5. 有 tool_calls? │
│ ├─ 无 → 最终文本,break(正常结束) │
│ └─ 有 → process_tool_calls │
│ ├─ Low 风险 → 自动执行 │
│ └─ Medium/High → 进 pending_approvals,暂停循环 │
│ │
│ 6. 全自动完成 → 继续下一轮 │
│ 有 pending → return(generating 保持 true,等审批恢复) │
│ │
└─ 达 10 轮 → 正常结束 ────────────────────────────────────────┘
│
▼
收尾(后台 spawn,不阻塞 Completed 事件)
├─ save_conversation(消息 + token 累加落库)
├─ maybe_spawn_extraction(知识提炼)
└─ ensure_conversation_title(标题生成)
退出条件
| 条件 | 处理 |
|---|---|
| LLM 只返回文本(无 tool_calls) | 正常结束,emit AiCompleted |
| 有工具待审批 | 暂停循环,generating 保持 true,等 ai_approve → try_continue_agent_loop 恢复 |
达 MAX_AGENT_ITERATIONS(10) |
正常结束 |
| 用户请求停止 | 已生成文本入库后退出,emit AiCompleted |
| 流式错误(idle timeout / 断连) | emit AiError,generating = false,退出 |
审批恢复
当用户审批通过最后一个 pending 工具后,try_continue_agent_loop 检测到 generating && pending_approvals.is_empty(),spawn 新的 run_agentic_loop 恢复循环。恢复前 emit AiAgentRound 通知前端新建 assistant 消息(审批结果不应追加到发起工具调用的旧消息)。
三、三重可靠性保险
| 保险层 | 机制 | 防什么 | 代码位置 |
|---|---|---|---|
| 连接层 | connect_timeout(30s),不设总 timeout |
连不上无限 hang;总 timeout 会误砍流式长任务(流式可持续数分钟) | openai_compat.rs / anthropic_compat.rs |
| 流式层 | 每个 chunk 间 idle timeout 120s | 连上后中途静默无限 hang | stream_recv.rs |
| 前端层 | 60s streaming watchdog | 后端任何路径漏发收尾事件 → 前端永久 streaming=true 卡死 |
stores/ai.ts(useAiEvents composable) |
审批等待时 watchdog 暂停(不计超时),避免用户思考时间被误判。
断连丢弃残缺
维护 finished_received 标志;流尽未收到 finished 信号 → emit AiError 并丢弃残缺响应,不当完整入库。防脏历史污染对话记录。
停止生成保留文本
AiSession.stop_flag: Arc<AtomicBool> + 多检查点响应(循环顶 / stream 内 / 工具执行前)。停止后保留已生成文本——用户主动停止 ≠ 丢弃成果。
四、12 个 AI 工具
工具清单
工具定义在 tool_registry.rs::build_ai_tool_registry,编译期硬编码(无运行时动态注册)。
| 风险 | 工具 | 说明 |
|---|---|---|
| Low(自动执行) | list_projects |
列出项目(排软删,截断 50 条) |
| Low | list_tasks |
列出任务(可按 project_id 筛选) |
| Low | list_ideas |
列出灵感 |
| Low | read_file |
读文件(offset/limit 分页,1MB 上限) |
| Low | list_directory |
列目录(噪音剪枝 + 1000 条上限) |
| Medium(需审批) | create_project |
创建项目(可选 path/stack 一步绑定) |
| Medium | update_project |
改字段(复用 CRUD 白名单校验) |
| Medium | create_task |
创建任务 |
| Medium | create_idea |
创建灵感 |
| Medium | write_file |
写文件(自动建父目录,1MB 上限) |
| Medium | bind_directory |
项目绑定代码目录 + 探测技术栈 |
| High(需审批) | delete_project |
删项目(软删进回收站) |
| High | run_workflow |
运行工作流(当前返回提示,未实装) |
工具三要素同源
每个工具一次 registry.register 同时定义:name + description + schema + RiskLevel + handler 闭包。handler 即唯一执行路径,schema+risk+实现同源,消除双轨。
路径安全
validate_path:禁..路径遍历、禁.ssh/.aws/.gnupg/AppData/ProgramData/Windows/System32等敏感目录resolve_workspace_path:双层校验(词法starts_with+canonicalize解析 symlink),锚定workspace_root
审计
每次工具调用写 ai_tool_executions 表(V9 建),audit_finalize 落盘 executed/rejected + 结果。
已知限制
- 截断 50 条无翻页:三个 list 工具
truncate(50)硬截断,无 offset/limit,AI 不知道有数据被漏掉(待改进) - "能写不能跑":AI 有
write_file但无run_command,无法形成"写→跑→改"闭环(待改进) - 工具集封闭:编译期硬编码,无 MCP 客户端,AI 运行时不能新增/修改工具
五、审批门控机制
流程
AI 要执行 create_project(Medium 风险)
│
▼
process_tool_calls → 不自动执行,写入 ai_tool_executions(status=pending)
│
▼
emit AiApprovalRequired → 前端 ToolCard 显示审批按钮
│
├─ 用户点「同意」→ ai_approve → 执行工具 → try_continue_agent_loop 恢复
└─ 用户点「拒绝」→ ai_reject → 跳过执行 → 恢复循环
持久化
pending 审批写入 DB(ai_tool_executions 表 status='pending'),重启后 restore_pending_approvals 从 DB 恢复。ai_conversation_switch 用 retain 保其他对话的 pending(不清空全局 HashMap)。
审批卡片信息
build_approval_reason(audit.rs)为 9 种工具拼接 reason 含项目名(resolve_project_label 查项目名,查不到 fallback「(项目已不存在, id=xxx)」)。前端 ToolCard.vue 对 id/project_id 字段特化回显项目名。
与工作流审批的区别
| AI 工具审批 | 工作流审批 | |
|---|---|---|
| 触发 | AI 对话中调 Medium/High 工具 | DAG 执行到 HumanNode |
| 事件 | AiApprovalRequired |
HumanApprovalRequest / HumanApprovalResponse |
| 恢复 | ai_approve → try_continue_agent_loop |
approve_human_approval IPC → EventBus broadcast |
| 通道 | 独立链路 | 独立链路(EventBus broadcast) |
两条审批链路完全独立,不共享事件类型或通道。
六、上下文窗口管理(ContextManager)
实现在
crates/df-ai/src/context.rs,解决长对话 token 暴涨导致context_length_exceeded死锁。
分组滑动窗口
消息历史:[旧] U₁ A₁(tool) T(result) A₁' U₂ A₂(tool) T(result) A₂' U₃ A₃ [新]
↑ ↑
可淘汰区 保护区(最后6条)
淘汰单位 = 工具调用三元组(原子性同进同出):
Assistant(tool_calls) + Tool(result)* + Assistant(紧随文本)
- 保护区:
PROTECT_COUNT = 6(≈ 最近 2 个完整用户轮次),永不裁 - 裁剪只影响发送视图:
build_for_request返回裁剪版给 LLM;all_messages_clone返回全量给持久化 - Token 估算零依赖:
chars × 0.35(保守 ±15%),不引入 tiktoken-rs(5MB BPE 数据文件对 Tauri 打包不友好) - 预算公式:
(max_tokens 128k − output_reserve 8192) × safety_ratio 0.85 ≈ 101K tokens
关键方法
| 方法 | 用途 |
|---|---|
push(message) |
追加消息并计 token、更新缓存(push 不裁剪,裁剪统一在 build_for_request) |
build_for_request(sys_tokens) |
返回裁剪后的消息列表 + 是否发生裁剪 |
all_messages_clone() |
返回全量消息(持久化 / 标题生成) |
restore_from_messages(msgs) |
切换对话时重建缓存 |
replace_tool_result_content(tool_call_id, new_content) |
审批通过/拒绝时回填工具结果(反向 rposition 命中最近一条) |
七、Token 累加
两协议语义差异
| OpenAI | Anthropic | |
|---|---|---|
| usage 时机 | 末 chunk 一次性给全量 | message_start(input)+ message_delta(output) |
| output_tokens | 最终值 | 累计值(非增量),直接覆盖不累加 |
跨 loop 实例累加
审批暂停→恢复 spawn 全新 run_agentic_loop 实例,新 loop 局部累加器从 0 起。save_conversation 的 upsert 路径 token 读旧值叠加(非覆盖),保证跨 loop 实例的对话总用量正确。
loop 实例 1:prompt=500, completion=200 → save(旧值 None + 500/200)
│ 审批暂停
▼
loop 实例 2:prompt=800, completion=300 → save(旧值 500/200 + 800/300 = 1300/500)
八、LLM 并发控制
LlmConcurrency(state.rs):双层 Semaphore,运行时可调。
| Semaphore | 默认 permits | 限流对象 |
|---|---|---|
| global | 3 | 全部 LLM 调用(stream_llm / 标题 / 提炼) |
| per_conv | 2 | 单对话并发 |
- permit 仅覆盖
stream_llm调用本身;工具执行(process_tool_calls)是本地操作无 RPM 成本,permit 在 stream 后立即释放 - Semaphore 重建用「软收敛」策略(替换内层 Arc,旧 permit 不受影响)
九、知识库集成
知识注入(对话开始时)
build_knowledge_context(state, query, config) -> String:
auto_inject == false→ 返回空(零开销)hybrid_search()top-3(LIKE 或 LIKE+向量混合)- 每条命中调
increment_reuse_count(fire-and-forget) - 格式化为 markdown,注入 system prompt 头部
知识提炼(对话结束后)
extract_knowledge_from_conversation(db, conv_id, provider_cfg):
- 后台 spawn,提炼失败仅 warn 不阻断
- 取最后 6 条 user/assistant 消息 → LLM JSON 输出
- parse 失败整批丢弃;成功逐条写
candidate(待人工审核)
maybe_spawn_extraction(...):agentic loop 两处正常退出路径(max iterations / 无工具调用 break)统一调用。
十、切对话不中断路由
Sprint 8 实现:生成中可切换对话不打断。
- 后端给所有 event 加
conversation_id+ spawn 前快照 conv_id - 前端按 id 路由:后台对话事件不污染当前视图
switchConversation加_latestSwitchId丢弃过期响应
十一、Agent 能力边界(四个"无")
当前系统的硬边界,做设计时不能假设超出这些能力:
| 能力 | 现状 | 影响 |
|---|---|---|
| 配置/调用外部工具 | ❌ 无 MCP 客户端,工具全编译期硬编码 | 工具集封闭 |
| 自造/迭代工具 | ❌ AI 运行时不能新增/修改工具 | 不能为特定任务临时造工具 |
| 执行类工具 | ❌ 无 run_command/run_script |
AI 能写代码但不能运行验证("能写不能跑") |
| agent ↔ workflow 打通 | ❌ run_workflow 工具是空壳 |
AI 不能在对话中触发工作流 |
相关文档
- df-ai AI 集成模块 — crate 层(Provider / ContextManager / 工具基础设施)
- Agent架构说明 — 能力边界盘点
- df-workflow 工作流引擎 — DAG 引擎
- DAG 引擎详解 — 基于代码的引擎详解
- 经验记录 — 踩坑/约定/技巧