Files
DevFlow/docs/03-模块文档/AI对话引擎-2026-06-14.md
绝尘 04032a2a8d 重构: 文档汇总+进度看板+孤儿任务清理脚本+gitignore 噪音排除
- 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/ 噪音排除
2026-06-15 05:14:21 +08:00

15 KiB
Raw Blame History

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 + ActLLM 流式回复 → 调用工具 → 执行工具 → 结果回传 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 → returngenerating 保持 true等审批恢复      │
│                                                              │
└─ 达 10 轮 → 正常结束 ────────────────────────────────────────┘
    │
    ▼
收尾(后台 spawn不阻塞 Completed 事件)
├─ save_conversation消息 + token 累加落库)
├─ maybe_spawn_extraction知识提炼
└─ ensure_conversation_title标题生成

退出条件

条件 处理
LLM 只返回文本(无 tool_calls 正常结束emit AiCompleted
有工具待审批 暂停循环,generating 保持 true,等 ai_approvetry_continue_agent_loop 恢复
MAX_AGENT_ITERATIONS(10) 正常结束
用户请求停止 已生成文本入库后退出emit AiCompleted
流式错误idle timeout / 断连) emit AiErrorgenerating = 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.tsuseAiEvents 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_executionsV9 建),audit_finalize 落盘 executed/rejected + 结果。

已知限制

  • 截断 50 条无翻页:三个 list 工具 truncate(50) 硬截断,无 offset/limitAI 不知道有数据被漏掉(待改进)
  • "能写不能跑"AI 有 write_file 但无 run_command,无法形成"写→跑→改"闭环(待改进)
  • 工具集封闭:编译期硬编码,无 MCP 客户端AI 运行时不能新增/修改工具

五、审批门控机制

流程

AI 要执行 create_projectMedium 风险)
    │
    ▼
process_tool_calls → 不自动执行,写入 ai_tool_executionsstatus=pending
    │
    ▼
emit AiApprovalRequired → 前端 ToolCard 显示审批按钮
    │
    ├─ 用户点「同意」→ ai_approve → 执行工具 → try_continue_agent_loop 恢复
    └─ 用户点「拒绝」→ ai_reject  → 跳过执行 → 恢复循环

持久化

pending 审批写入 DBai_tool_executionsstatus='pending'),重启后 restore_pending_approvals 从 DB 恢复。ai_conversation_switchretain 保其他对话的 pending不清空全局 HashMap

审批卡片信息

build_approval_reasonaudit.rs)为 9 种工具拼接 reason 含项目名(resolve_project_label 查项目名,查不到 fallback「(项目已不存在, id=xxx)」)。前端 ToolCard.vueid/project_id 字段特化回显项目名。

与工作流审批的区别

AI 工具审批 工作流审批
触发 AI 对话中调 Medium/High 工具 DAG 执行到 HumanNode
事件 AiApprovalRequired HumanApprovalRequest / HumanApprovalResponse
恢复 ai_approvetry_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 返回裁剪版给 LLMall_messages_clone 返回全量给持久化
  • Token 估算零依赖chars × 0.35(保守 ±15%),不引入 tiktoken-rs5MB 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_startinput+ message_deltaoutput
output_tokens 最终值 累计值(非增量),直接覆盖不累加

跨 loop 实例累加

审批暂停→恢复 spawn 全新 run_agentic_loop 实例,新 loop 局部累加器从 0 起。save_conversation 的 upsert 路径 token 读旧值叠加(非覆盖),保证跨 loop 实例的对话总用量正确。

loop 实例 1prompt=500, completion=200 → save(旧值 None + 500/200)
    │ 审批暂停
    ▼
loop 实例 2prompt=800, completion=300 → save(旧值 500/200 + 800/300 = 1300/500)

八、LLM 并发控制

LlmConcurrencystate.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-3LIKE 或 LIKE+向量混合)
  • 每条命中调 increment_reuse_countfire-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 不能在对话中触发工作流

详见 Agent架构说明-2026-06-14.md


相关文档