Skip to content

ADR-155: 自然语言控场景 — 叠加于 AiService 管线之上

  • 状态: 🟢 已完成
  • 日期: 2026-07-20(初版),2026-07-28(重写),2026-07-28(终版对齐实现)
  • 相关: ADR-093(声明式菜单 Schema,动作闭集来源)、ADR-196(AiService 传输层前置)、ADR-197(统一动作注册表,本 ADR 的 catalog 由 registry 驱动)、ADR-176(双适配器模式)

背景

原 ADR-155 写于 ADR-196 之前,当时「客户端+流式+面板」尚未落地。现已实现:ADR-196 交付了完整 LLM 传输层 + ADR-197 统一动作注册表(41 动作全域迁移),本 ADR 的 NL 控制作为 ADR-196 的叠加层,复用诊断面板并在其上新增「控制」模式。


前置依赖

依赖ADR实际组件状态
LLM 传输层ADR-196AiService 接口 + go/browser 适配器 + resolveAi()
流式 SSE + tool_callsADR-196sse.ts 解析器 + go-adapter.ts AsyncIterable + delta.tool_calls 聚合
流式渲染ADR-196settings-diagnostic.ts _renderStreamingChunk()
诊断面板ADR-196settings-diagnostic.ts 435 行三分区面板
动作统一注册表ADR-197action-registry.ts + 5 个域 action-def 文件,共 41 动作
参数适配器ADR-155 Phase 1param-adapters.ts 4 个通用适配器
意图分发器ADR-155 Phase 1intent-dispatcher.ts + action-executor.ts
Go tool_callsADR-155 Phase 1tools.go + client.go tool_calls 聚合 + ai_binding.go 事件推送
E2E 测试ADR-155 Phase 1ai-control.spec.ts 3 个测试

设计决策

1. 复用诊断面板,加「控制」模式切换

诊断面板顶部加「诊断 / 闲聊 / 控制」tabs(role="tablist" + aria-selected),控制模式下对话下方展示「待执行操作卡」。

2. 意图解析:原生 tool_calls + prompt 约束双轨

  • Go 桌面端:SSE delta.tool_calls 原生聚合,finish_reason: 'tool_calls' 时 emit tool_call 事件(client.go
  • 浏览器端:TS 侧优先调 provider 原生 function_calling;不支持时降级为 prompt 约束 + parseActionFromLLM() regex 提取(intent-dispatcher.ts,已弃用,保留兼容)
  • 前端统一通过 executeActionById() 执行(action-executor.ts 纯叶子模块)

3. 动作来源:ADR-197 统一注册表

action-catalog.tsbuildToolSchemas() 遍历注册表生成 JSON Schema 工具定义,buildToolCatalogText() 生成 prompt 约束文本。工具命名模式:<domain>:<verb>-<noun>

4. 安全:用户显式确认 pending 卡

不自执行。控制模式下所有解析结果先入 pending-action 卡(含 action 名+参数),用户点「应用」才执行。destructive 动作加 showConfirm 二次确认。


实施记录

Phase 1(~420 行 TS + ~80 行 Go):核心管线 + 8 高频动作

#模块文件说明
1工具编目core/ai/action-catalog.tsbuildToolSchemas() → JSON Schema; buildToolCatalogText() → 文本清单
2参数适配器core/ai/param-adapters.tsenum(同义词)/color(hex→元组)/range(clamp)/entity(模糊搜索)
3意图分发器core/ai/intent-dispatcher.tsparseActionFromLLM(text)executeAction(id, rawParams)
4动作定义core/ai/action-registry-defs.ts注册 8 个高频控制动作
5应用层执行器core/action-executor.tsexecuteActionById() 统一入口
6控制模式 UImenus/settings-diagnostic.ts第三个 tab + pending-action 卡 + 应用/取消
7Go tool schemainternal/app/llm/tools.goToolSchema/ToolFunction 结构体
8Go tools 透传internal/app/llm/client.goChatRequest.Tools + delta.tool_calls 聚合
9Go 事件推送internal/app/ai_binding.goai:tool_call 事件
10types 扩展core/ai/types.tsChatRequest.tools + ChatChunk.tool_call
11go-adaptercore/ai/go-adapter.tstools 透传 + 事件订阅
12薄封装menus/library-actions.tsloadLibraryModel/loadLibraryMotion 导出
13i18n 5 语言core/i18n/locales/*.ts各 +8 key(control/controlFormat/pending/apply/cancel 等)
14E2E 测试e2e/ai-control.spec.ts3 个验收测试

Phase 2(迁移 settings 域,13 动作 → SSE tool_calls 原生)

  • sse.ts: parseSseStream 增加 toolAccums Map,delta.tool_calls 按 index 聚合 id/name/arguments
  • client.go: StreamEvent 新增 ToolName/ToolArgs/ToolId + SSE scanner 聚合
  • action-defs/settings-actions.ts: 13 动作注册(12 SETTINGS_ACTION + set-lang)
  • settings-actions.ts: 移除 SETTINGS_ACTIONS Record,委托 executeActionById
  • settings-diagnostic.ts: 发送 tools: buildToolSchemas(),处理原生 tool_call chunk

Phase 3(迁移 scene 域,4 动作)

  • action-defs/scene-actions.ts: 注册 screenshot:current/batch、scene:save/undo
  • scene-menu.ts: 移除 SCENE_ACTIONS Record,委托 executeActionById

Phase 4(迁移 motion 域,10 动作)

  • action-defs/motion-actions.ts: 注册 lipsync/clear/retarget/model:pause/reset/pose/loop/procmotion
  • motion-popup.ts: 替换 6 个 if/else 分支,保留导航分支

Phase 5(迁移 env + library 域,6 动作)

  • action-defs/env-actions.ts: 3 纹理绑定动作(particle/sky/stars)
  • action-defs/library-actions-def.ts: 3 动作(rescan、import-file、set-formation)
  • env-menu.ts: envOnItemClick 委托 executeActionById
  • library-browse.ts: onItemClick 3 个动作分支委托 executeActionById

Phase 6(控制模式体验闭环:执行反馈 + 破坏性动作撤销)

兑现 UX「操作结果可理解」与「操作结果可撤销」两项。均在 menus/settings-diagnostic.ts,标 [doc:adr-155]

  • 执行结果反馈executeAction() 返回 {success, message};应用后按路径分发——
    • prompt 回退路径(无 tool_call):直接写入助手消息 ai.control.resultSuccess / resultFailed
    • tool_call 路径:结果攒入 _pendingToolResults,队列清空后统一回填 tool 消息(与 Phase 6‑前的多 tool_call 回填机制一致)。
  • 破坏性动作撤销:仅当 action.destructive 且执行成功时记录 _lastUndoable = { label };hint 区(_renderControlHint)置顶渲染「撤销」入口(ai.control.undoHint + undo 按钮,data-testid=ai:control:undo-row);点击调 executeAction('scene:undo', {}),成功后清 _lastUndoable 并写 ai.control.undone
  • destructive 二次确认:应用前对 action.destructiveshowConfirm(ai.control.confirmDestructive)
  • i18n:新增 ai.control.resultSuccess / resultFailed / undo / undoHint / undone(五语言)。

已知遗留

  • NL 动作 label 本体仍硬编码中文(NL label 国际化需单独 ADR)。
  • i18n-check 历史缺失 key 待专项补齐(非本路线引入)。

总动作清单(41 个)

注册文件数量动作 ID 示例
控制(ADR-155 Phase 1)core/ai/action-registry-defs.ts8ai:control:setLightIntensityai:control:loadModel
设置(Phase 2)core/action-defs/settings-actions.ts13settings:set:clearextractcachesettings:set-lang
场景(Phase 3)core/action-defs/scene-actions.ts4screenshot:currentscene:save
动作(Phase 4)core/action-defs/motion-actions.ts10lipsync:togglemotion:model:pause
环境(Phase 5)core/action-defs/env-actions.ts3env:bind-particle-textureenv:bind-sky-texture
模型库(Phase 5)core/action-defs/library-actions-def.ts3library:rescanlibrary:set-formation

首批 8 个高频动作(当前实现)

工具参数真实约束handler
setLightIntensitydirIntensity: number0–1,步长 0.05setLightState({ dirIntensity })
setLightColordirColor: colorhex #rrggbb[r,g,b] 元组 (÷255)setLightState({ dirColor })
setCameraModemode: enumorbit/freefly/surround(同义词:follow→freefly)setCameraMode(mode)
setEnvPresetpreset: enumdawn/noon/sunset/night/overcast/neonapplyEnvPreset(preset)
toggleGround(none)无参 togglesetEnvState({ groundVisible: !current })
loadModelname: entity模糊搜索→库匹配→onModelRowClickloadLibraryModel(name)
loadMotionname: entity模糊搜索 VMD→replaceMotionloadLibraryMotion(name)
setPerformancemode: enumquality/balanced/performance(同义词:high→quality, low→performance)setPerformanceMode(mode)

修订记录

日期修订
2026-07-20初版
2026-07-28重写:对齐 ADR-196,追加命名约定/ARIA/E2E 规格
2026-07-28终版:对齐 Phase 1–5 实现完成态,更新动作清单、实施记录、8 动作表
2026-07-21修复菜单首次点击竞态:各菜单模块原用 import().then() 异步注册动作,却在同步栈立即 dispatch,首次点击注册未就绪→“不支持的操作”。改为顶层静态 import 注册函数;scene/motion/library 因与其 action-defs 存在循环依赖,注册调用保留于首次点击同步执行点(破环);env-actions 无回边→顶层静态注册。
2026-07-28回填漏记的 Phase 6(控制模式体验闭环):代码已落地的「执行结果反馈(resultSuccess/resultFailed)」与「破坏性动作撤销入口(_lastUndoable + scene:undo)」之前未记入本 ADR,现据 [doc:adr-155] 代码事实客观补记。
2026-07-21遗留待办——label 国际化全部完成:在机制层 + control 样板域基础上,将剩余全部动作的 label 按 ai.actions.<domain>.<name> 标准迁移(motion 15 + settings 13 + scene 4 + env 3 + library 3),5 语言各补全 key(base 1829→1868)。至此全量动作 label 均为 i18n key,LLM tool description 与 UI 显示都随语言切换。i18n-check 验证 ai.actions.* 无缺失、占位符一致。剩余遗留:i18n-check 历史 171 个缺失 key(ai.config/ai.status/ai.errorAdvice/downloads 等,非本线引入)待专项补齐。