Files
DevFlow/docs/03-模块文档/AI对话引擎-2026-06-14.md
绝尘 ff3f153d45 修复: 安全加固+DRY 收敛+文档同步+测试补齐
安全:
- ScriptNode 默认黑名单兜底(rm/del/format/shutdown/mkfs/dd)
- bind_directory 分段 .. 检测替代 contains 子串(对齐 tool_registry)
- ai_providers 白名单移除 api_key(防 update_field 旁路写明文)

DRY:
- useAiEvents 抽 cleanupTerminatedConversation 统一三分支收尾
- 新增 useStoreAction 工具,4 个 store 替换 38 处 try/catch 样板

文档:
- df-core → df-types 批量替换(ARCHITECTURE/PROGRESS/SQLite-CRUD)
- INDEX 补齐 9 漏列文档(单对话并行多轮/跑题试验/工程系统设计等)
- Agent架构说明 死链修复(../构想审查/)
- AI对话引擎工具清单改为数量+按风险分组(不再用固定数字)
- ARCH 状态标签 设计阶段 → Phase 2 验证

测试:
- df-relay 新增 registry_test: ConnRegistry 路由 + RelayState + 16 项单测
2026-06-29 21:57:07 +08:00

15 KiB
Raw Permalink 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 内 / 工具执行前)。停止后保留已生成文本——用户主动停止 ≠ 丢弃成果。


四、AI 工具清单

工具注册

工具定义在 tool_registry.rs::build_ai_tool_registry编译期硬编码(无运行时动态注册)。工具统计截至 2026-06-29后续增删见该函数 registry.register 调用点)。

按风险等级分组

Low自动执行 — 只读查询类,不触发审批。包含:list_projects/get_project_countlist_tasks/get_task_countlist_task_links/get_task_treelist_ideaslist_trashread_fileread_symbol(AST符号解析)、list_directoryfile_infogrep(跨文件内容搜索)、search_fileslist_project_serviceslist_project_modulesget_project_timelinegit_status/git_diff/git_log(只读)。

Medium需审批 — 写入类,需人工批准。包含:create_project/update_project/bind_directorycreate_task/update_task/delete_taskadvance_task/move_task_queue/update_contentcreate_task_link/remove_task_linkcreate_ideaadd_project_servicewrite_file/patch_file/append_filegit_commit/git_branchhttp_request(SSRF防护含DNS rebinding检查)。

High需审批 + 默认谨慎) — 高风险,默认自动执行模式会全部拒绝。包含:delete_project/restore_project/purge_projectdelete_file/rename_filerun_command(默认超时 60s)、run_workflow(联动任务推进)、git_merge(冲突返回冲突文件列表)。

工具三要素同源

每个工具一次 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 + 结果。

已知限制

  • 工具集封闭:编译期硬编码,无 MCP 客户端AI 运行时不能新增/修改工具
  • AI 不能造工具AI 能 write_file 写脚本但不会变成可调用工具(要重编译)
  • agent ↔ workflow 仍未完全打通run_workflow 现已能联动任务推进,但 ScriptNode 能力未暴露给 agent loop

五、审批门控机制

流程

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


相关文档