From a71980e4bf225f53fb17002c5a52439c401910ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A9=B9=E6=96=87=E6=B6=9B?= <53+mp_1787300862_s8r25x@noreply.git.brainwork.club> Date: Wed, 16 Sep 2026 15:21:38 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=20CALM=20=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E5=AF=B9=E7=85=A7=EF=BC=8C=E8=AF=B4=E6=98=8E=20Copilo?= =?UTF-8?q?t=20=E6=9E=B6=E6=9E=84=E6=9D=A5=E6=BA=90=E4=B8=8E=E5=8F=96?= =?UTF-8?q?=E8=88=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/CALM设计对照.md:CALM 四步架构、Commands DSL 清单、逐层对照表(含代码位置) - 明确采纳项(取消意图分类/理解与执行分层/槽位确认)、简化项、差异缺口清单 - 记录超出 CALM 的偏好学习增量,以及对外表述口径与生产落地映射 - README:修正 docs/ 目录引用,补充设计来源章节 --- README.md | 20 ++- docs/CALM设计对照.md | 291 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 docs/CALM设计对照.md diff --git a/README.md b/README.md index 37f70a1..4930003 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,26 @@ npm run preview # 本地预览构建产物 │ ├── MessageItem.jsx / GenCardView.jsx / TaskCardView.jsx # 消息流三种消息 │ ├── OneClickModal.jsx # 一键发品弹窗(配置→进度→完成 状态机) │ └── Toast.jsx -├── Prompt提示词设计文档.md # 各工具背后的 Prompt 设计规范(可直接落地真实 LLM) -└── AI领教营/ # 项目方法论笔记(AI 价值/边界/验证框架) +└── docs/ # 设计文档 + ├── CALM设计对照.md # 架构来源与设计取舍(Rasa CALM 范式逐层对照) + ├── Prompt提示词设计文档.md # 各工具背后的 Prompt 设计规范(可直接落地真实 LLM) + └── 追问.md # 方法论笔记(AI 价值 / 边界 / 验证框架) ``` +### 设计来源:Rasa CALM 范式 + +本项目的对话编排架构参考 **Rasa CALM**(Conversational AI with Language Models)的四步架构: + +``` +① LLM 生成 Commands → ② 校验并更新对话状态 → ③ Conversation Repair → ④ 规则引擎确定性执行 +``` + +关键判断是**只有第 ① 步用模型,「理解」之外全是确定逻辑** —— 由此获得可解释、可中断、可兜底的特性,满足商家侧对内容落库前人工复核的要求。 + +对应到代码:`lib/agent.js` = 理解层(等价 `SetSlot` / `StartFlow`),`data/genData.js` = 流程定义层(等价 `flows.yml`),`store/useAppStore.js` 的四条编排流 = 执行层(等价规则引擎)。 + +完整的逐层对照、采纳与简化、以及与 CALM 的差异清单,见 [`docs/CALM设计对照.md`](docs/CALM设计对照.md)。 + ### 架构要点 - **数据驱动渲染**:v1 中拼接 HTML 字符串的生成卡片,v2 重构为结构化数据(`head / lines / foot / tips`),按钮全部是真实 React 事件 diff --git a/docs/CALM设计对照.md b/docs/CALM设计对照.md new file mode 100644 index 0000000..9518aef --- /dev/null +++ b/docs/CALM设计对照.md @@ -0,0 +1,291 @@ +# CALM 设计对照:从 Rasa CALM 到本 Demo + +> 本文说明本项目「商品发布 AI 助手(Copilot / Agent)」的**架构来源与设计取舍**。 +> 核心参考范式为 **Rasa CALM**(Conversational AI with Language Models), +> 项目在意图理解层与流程执行层的分层方式,直接借鉴了 CALM 的四步架构。 + +--- + +## 1. 参考源 + +| 编号 | 来源 | 类型 | 链接 | +|---|---|---|---| +| R1 | Rasa CALM 原始论文(arXiv:2402.12234) | 一手 · 论文 | https://arxiv.org/pdf/2402.12234 | +| R2 | Rasa Pro 文档 · Dialogue Understanding | 一手 · 官方文档 | https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding | +| R3 | Rasa Pro 文档 · Conversation Repair | 一手 · 官方文档 | https://rasa.com/docs/rasa-pro/concepts/conversation-repair | +| R4 | Rasa Studio 可视化流程编排 | 一手 · 官方文档 | https://rasa.com/docs/studio/user-guide/create-flow | +| R5 | 《大模型与任务型对话的结合:Rasa CALM》 | 二手 · 社区解读 | https://mp.weixin.qq.com/s/L_0IeGVeTml_N-Ypb6AwEQ | +| R6 | tod-in-context-learning 参考实现 | 一手 · 代码 | https://github.com/RasaHQ/tod-in-context-learning | + +> 证据分级说明:R1–R4、R6 为论文/官方文档/官方代码仓库(一手);R5 为第三方技术解读(二手,用于快速建立术语认知,关键结论均回溯至 R1/R2 核对)。 + +--- + +## 2. CALM 要解决什么问题 + +### 2.1 传统任务型对话的 pipeline + +业界标准做法是三段式: + +``` +语义理解(意图识别 + 词槽抽取)→ 对话管理(状态更新 + 策略选择)→ 话术生成 +``` + +其内核是一条强假设: + +``` +f(对话状态) = 对话 action +``` + +而**对话状态的编码强依赖意图类别的识别**。一旦遇到预置意图之外、或本身就模糊的表达,整条链路就会退化成僵硬的分支判断——鲁棒性不足。 + +典型反例(来自 R5 的转述): + +- 用户说「订餐厅」——可能是订中餐、订西餐,也可能只是查询,本身就难以归入单一类别; +- 用户说了一段很长的澄清文本——无法映射到预设类别体系里。 + +### 2.2 CALM 的主张 + +> **意图分类不是一件合理的事情**,因为人类表达意图可不仅仅是一个类别。 + +既然分类不合理,那就**取消意图分类**,让大模型直接把用户意图表达为**一组动作(Commands)**——即论文所述 *Commands as a Domain-Specific Language*。 + +--- + +## 3. CALM 的四步架构 + +```mermaid +flowchart TD + A["用户最新消息 + 对话历史"] --> B["① LLM 生成 Commands
f(任务说明, 全部 flow 描述,
当前 flow 与 slots, 对话历史) = commands"] + B --> C["② 校验 Commands 合理性
对话管理引擎更新对话状态
(当前 flow + slots)"] + C --> D["③ Conversation Repair
判断是否需修复并更新状态"] + D --> E["④ 规则引擎按确定逻辑执行
输出 Action 及对应话术"] + E --> F["表单/系统状态变更 + 回复用户"] + + style B fill:#EEEDFE,stroke:#534AB7 + style E fill:#E1F5EE,stroke:#0F6E56 +``` + +**架构的关键判断:只有第 ① 步用大模型,其余三步都是确定逻辑。** + +这一点决定了系统的工程性质:大模型负责它擅长的「理解」,规则引擎负责它擅长的「确定执行」。可解释性、可测试性、可兜底性都建立在这条边界上。 + +### 3.1 Commands DSL 清单 + +| Command | 语义 | 示例 | +|---|---|---| +| `StartFlow(flow_name)` | 启动一个业务流程 | `StartFlow(transfer_money)` | +| `SetSlot(slot, value)` | 填充槽位 | `SetSlot(recipient, John)` | +| `CancelFlow()` | 取消当前流程 | 用户说「算了」「stop that」 | +| `Clarify(flow_1, flow_2, ...)` | 歧义澄清 | 用户只说「contacts」→ `Clarify(list, add, remove)` | +| `SkipQuestion()` | 跳过当前追问 | 用户说「下一个问题」 | +| `SearchAndReply()` | 走知识库作答 | 非任务型知识问询 | +| `ChitChat()` | 闲聊 | 无其他可执行动作时兜底 | +| `HumanHandoff()` | 转人工 | 用户表达不满或明确要求 | + +**关键设计**:`SetSlot` 中的值不再是通用实体(如「John」被识别为 person、45 被识别为 number),而是**直接用 flow 定义中的槽位语义来承接**——理解与流程定义在同一套语汇下对齐,消除了 NLU 中间表示的语义损耗。 + +### 3.2 Flow 的定义形态 + +flow = `description` + `steps`,其中 `steps` 只允许若干枚举类型(`action` / `collect` / `set_slots` / `noop`),并支持 `next` 分支与 `if` 条件: + +```yaml +flows: + transfer_money: + description: This flow lets users send money to friends and family. + steps: + - collect: transfer_money_recipient + description: the name of a person + - id: "ask_amount" + collect: transfer_money_amount_of_money + - action: check_transfer_funds + next: + - if: not slots.transfer_money_has_sufficient_funds + then: + - action: utter_transfer_money_insufficient_funds + next: "ask_amount" + - else: transfer_money_final_confirmation +``` + +配套三个配置文件:`domain.yml`(话术/action/slot 声明)、`config.yaml`(LLM 版本与超时)、`endpoints.yml`(服务地址)。 + +--- + +## 4. 本 Demo 的逐层对照 + +本 Demo 是**纯前端可交互原型**(无后端、不接真实 LLM),因此第 ① 步的「理解」以规则/关键词实现,但**分层结构与数据契约完全按 CALM 的形态设计**,替换为 LLM function calling 即可落地(见 §7.1)。 + +| # | CALM 概念 | 本 Demo 的实现 | 代码位置 | +|---|---|---|---| +| 1 | 意图不再分类,直接生成 Commands | 不走 intent 分类,直接产出「工具序列 + 槽位值」 | `app/src/lib/agent.js` | +| 2 | `SetSlot(slot, value)` | `parseBrief(text)` → `{ dest, days, type, theme }` | `agent.js:26` | +| 3 | `StartFlow(flow_name)` 序列 | `parseMultiTools(text)` → 命中的工具类型数组(保序去重) | `agent.js:52` | +| 4 | Commands DSL 的取值域 | `toolKeywords` 关键词表(8 工具)+ `DEST_MAP` / `THEME_MAP` / `TYPES` 值域映射 | `agent.js:11,19,23,40` | +| 5 | 全量流程触发 | `agentIntentRe` 命中 → 启动 8 步全量流程 | `agent.js:8` | +| 6 | `flows.yml`:flow 定义(description + steps) | `agentSteps`(8 步自主发品流程定义)/ `ocStepsDef`(6 步一键发品流程定义) | `data/genData.js:10,22` | +| 7 | `actions/*.py`:action 实现 | `buildGen(type, ctx)` → `{ pre, card, data }` 结构化生成 | `data/genData.js:54` | +| 8 | domain 中的 action / slot 声明 | `cardMap`(工具 → 表单卡片锚点)/ `actionLabel`(工具 → 动作名) | `data/genData.js:48,32` | +| 9 | 对话状态:当前 flow + slots | `form`(槽位)+ `generatedRef`(各工具最新产物)+ `serialRef`(当前等待点) | `store/useAppStore.js:42,63,64` | +| 10 | 规则引擎确定性执行 | `agentRun` / `multiToolRun` 的 `for` 循环 + 任务清单状态机 | `useAppStore.js:325,351` | +| 11 | slot 的 `ask_before_filling` | `serialConfirm`:生成卡片后 Promise 挂起,等 5s 自动倒计时或手动确认 | `useAppStore.js:204,236` | +| 12 | 重新收集槽位 / 分支重走 | `serialRegen`「换一版」:重新生成当前步骤卡片并重置倒计时 | `useAppStore.js:214` | +| 13 | Conversation Repair(偏题/打断) | `sendMessage` 开头:若存在等待中的步骤,先自动确认收口再处理新消息 | `useAppStore.js:383` | +| 14 | `ChitChat()` / `KnowledgeAnswer()` 兜底 | `CANNED` 关键词兜底 + 3 条上下文感知的闲聊回复 | `useAppStore.js:29,410` | +| 15 | 执行过程的可见性(flow steps 逐条执行) | 任务清单卡片:`todo → doing → done` 三态 + 每步完成备注 | `useAppStore.js:253,265`;`components/TaskCardView.jsx` | +| 16 | 最终确认(final confirmation) | 生成卡片的「⚡ 一键填入」与「🔄 换一版」双动作 | `components/GenCardView.jsx` | + +### 4.1 一次完整交互的等价展开 + +以 CALM 的样例句式对照本 Demo 的实际行为: + +``` +用户:帮我发个海南亲子 6 天自由行 + +CALM 侧等价输出: + StartFlow(publish_product) + SetSlot(dest, 海南(三亚)) + SetSlot(theme, 亲子家庭) + SetSlot(days, 6天5晚) + +本 Demo 实际执行: + parseBrief() → { dest: '海南(三亚)', theme: '亲子家庭', days: '6天5晚' } + applyBriefToForm()→ 槽位回写左侧表单 + agentIntentRe → 命中「发」→ 启动 8 步全量流程 + agentRun() → 逐工具 buildGen → 生成卡片 → 等确认 → applyGen 回填 +``` + +--- + +## 5. 采纳了什么 + +### 5.1 取消意图分类,直接生成动作(采纳) + +本 Demo **不存在 intent 分类器**。用户的自然语言直接映射为「要启动哪些流程 + 各槽位取值」,与 CALM 的主张一致。 + +这条选择在本场景的价值尤其明显:商家说「帮我优化一下」,实际上可能是要标题、要详情、要主图——强行归入一个 intent 会立刻失真;而输出「工具序列」天然可容纳这种多目标表达(`parseMultiTools` 返回的就是一个数组)。 + +### 5.2 理解层与执行层分离,且只有理解层是「智能」的(采纳) + +这是 CALM 最值得复用的工程判断,也是本 Demo 的核心架构约束: + +- **理解层**(`lib/agent.js`):唯一需要「聪明」的地方,负责把自然语言转成结构化动作; +- **执行层**(`store/useAppStore.js` 的四条编排流):纯确定性循环,每一步都可解释、可中断、可测试; +- **定义层**(`data/genData.js`):流程与产物以声明式数据描述,与执行引擎解耦。 + +由此得到三个直接收益: + +1. **可解释**:每一步「为什么调用这个工具」都能回溯到一次解析结果; +2. **可中断**:执行中任何时候都能插入确认、换版、切换自动/手动模式; +3. **可测试**:解析层与生成层可分别单测,无需端到端跑 UI。 + +### 5.3 human-in-the-loop 的槽位确认(采纳并强化) + +CALM 在 flow 中通过 `ask_before_filling: true` 让槽位在填充前先向用户确认。本 Demo 把这个机制从「槽位级」提升到「模块级」: + +- `serialConfirm` 让每个工具生成后先出卡片,用户确认才回填表单; +- 支持 `autoConfirmMode` 运行中随时切换(⚡自动 5s / ✋手动无限等); +- `serialRegen` 提供「换一版」——等价于 CALM 重新收集槽位后重走分支。 + +对 B 端商家场景而言,这个机制的必要性高于 C 端对话:**AI 生成的内容会直接进入商品详情页,落库前必须由人过一道眼。** + +--- + +## 6. 简化了什么 + +| CALM 的做法 | 本 Demo 的简化 | 简化理由与代价 | +|---|---|---| +| 第 ① 步由 LLM 生成 Commands | 关键词表 + 正则解析(`toolKeywords` / `parseBrief`) | 纯前端 demo 不引入模型依赖;代价是泛化能力受限,仅覆盖演示话术。**替换点已预留**(见 §7.1) | +| flow 定义用 YAML 配置 + 支持 `next` 分支与 `if` 条件 | `agentSteps` / `ocStepsDef` 为硬编码声明式数组,线性执行 | 演示流程本身线性(8 步固定顺序);代价是新增流程或分支需改代码 | +| 对话状态收敛为一个 `dialogue_state` 对象 | 状态分散在 `form` / `generatedRef` / `serialRef` | 复用 React 单向数据流;代价是缺少统一的「当前 flow / 当前槽位」视图 | +| Commands 显式落成可审计的中间表示 | 解析结果直接驱动执行,无 `commands` 中间层 | 少一次数据转换;代价是失去命令级日志与回放能力(见 §7.2) | + +--- + +## 7. 差异与演进方向 + +### 7.1 已被预留的替换点:意图理解接 LLM + +`lib/agent.js` 的三个导出(`agentIntentRe` / `parseBrief` / `parseMultiTools`)构成一个**窄接口**:入参为自然语言,出参为「工具序列 + 槽位值」。 + +落地真实 LLM 时,只需把这三个函数替换为一次 function calling 调用,**执行层(`agentRun` / `multiToolRun` / `startOC`)零改动**: + +``` +自然语言 + ├─ 现状:关键词/正则解析(前端模拟) + └─ 目标:LLM function calling,按 CALM 的 Prompt 模板组织 + f(全部 flow 描述, 当前 flow 与 slots, 对话历史, 用户消息) = commands +执行层:不变 +``` + +CALM 的 Prompt 模板(R1/R2)可直接作为契约蓝本:把「可用流程及其描述与槽位」「当前 flow 与槽位取值」「可用动作枚举」「槽位填充约束」四段填进 system prompt,要求模型**先输出分析、再逐行输出动作**。 + +### 7.2 尚未覆盖的 CALM 能力(下一版清单) + +| 缺口 | 现状行为 | 补齐后的目标行为 | 对应 Command | +|---|---|---|---| +| 歧义澄清 | 用户只说「帮我优化一下」→ 落到闲聊兜底 | 反问「您是要优化标题、详情还是配图?」 | `Clarify(title, intro, detail, ...)` | +| 显式取消 | 说「算了」→ 落到闲聊兜底 | 中止当前流程、回滚已生成步骤 | `CancelFlow()` | +| 跳过当前步骤 | 说「跳过这步」→ 落到闲聊兜底 | 跳过当前槽位收集,继续后续步骤 | `SkipQuestion()` | +| 转人工 | 无 | 输出转人工指令并携带上下文 | `HumanHandoff()` | +| 命令中间表示 | 解析结果直接执行 | 生成 `commands[]` → 校验 → 执行,可日志化、可回放、可单测 | — | +| 对话状态视图 | 状态散在三个 ref | 统一 `dialogueState = { activeFlow, currentStep, slots }` | — | +| 流程声明式配置 | 硬编码数组 | 外置 `flows.json`(description + steps + next 分支) | — | + +其中**「命令中间表示」优先级最高**。它是 §5.2 三项收益(可解释/可中断/可测试)的落点:一旦 commands 成为显式数据结构,就可以做命令级单测、命令级日志、以及「同一段对话回放不同模型输出」的评估——这正是 CALM 把 Commands 称为 DSL 的用意。 + +--- + +## 8. 超出 CALM 的部分:偏好学习 + +CALM 的边界止于「理解 + 执行」,**不处理跨会话的用户习惯沉淀**。本 Demo 在此之上加了一层产品化增量: + +| 机制 | 实现 | 代码位置 | +|---|---|---| +| 偏好沉淀 | 每次发品写入 5 条偏好条目(路线天数、人群文案风格、话术偏好、定价策略、促销习惯) | `lib/oc.js:28` | +| 跨会话持久化 | `localStorage`(键 `fliggy_oc`) | `lib/oc.js:8,16` | +| 成长等级 | Lv.1 通用模板(0–2 次)→ Lv.2 风格匹配(3–9 次)→ Lv.3 爆款复刻(10+ 次) | `lib/oc.js:21` | +| 下次预填 | 一键发品弹窗自动回填上次配置并提示风格沿用语 | `store/useAppStore.js:419` | +| 双通道沉淀 | 对话式自主发品与弹窗式一键发品共用同一套记忆 | `store/useAppStore.js:305` | + +**定位说明**:CALM 解决「怎么把话说准、把事做对」;偏好学习解决「怎么越用越贴合这个商家」。二者正交——偏好作为上下文注入理解层的 Prompt,不改变执行层的确定性。 + +--- + +## 9. 对外表述口径 + +需要向他人解释本项目的设计来源时,建议采用如下表述(避免把 CALM 的作用夸大或缩小): + +> 任务型对话的经典做法是把用户表达归入一个预设意图类别,再走状态机选动作。这个假设在商家场景下很容易失效——「帮我优化一下」可能 одновременно 指向标题、详情和主图。 +> +> 我参考的是 Rasa CALM 的思路:**不做意图分类,让模型直接生成一组动作**(启动哪个流程、填哪些槽位),再交给确定性的流程引擎执行。这套架构里只有「理解」这一步是模型在做事,剩下的执行、确认、修复全是确定逻辑——这正好满足商家侧对**可解释、可中断、可兜底**的要求。 +> +> 在这之上我做了一层 CALM 没有的东西:**商家使用习惯库**。每次发品沉淀偏好,下次自动预填并按商家风格生成,让产品「越用越好用」。 + +--- + +## 10. 落地对应关系(接真实 LLM / 后端 Agent Loop) + +| 本 Demo(前端原型) | 生产环境对应物 | +|---|---| +| `lib/agent.js` 关键词解析 | LLM function calling,按 CALM Prompt 模板构造上下文 | +| `data/genData.js` `buildGen()` | 各工具的生成服务(Prompt 见 `Prompt提示词设计文档.md`) | +| `store/useAppStore.js` 编排流 | 后端 Agent Loop:命令生成 → 校验 → 状态更新 → 执行 → 回填 | +| `agentSteps` / `ocStepsDef` | 流程配置中心(flow 声明式定义,支持分支与版本) | +| `lib/oc.js` `localStorage` | 商家画像 / 习惯库服务,作为个性化上下文注入 Prompt | +| `serialConfirm` 前端确认 | 同构的落库前人工复核节点(保持前端一致交互) | +| 任务清单卡片 | 执行过程可观测性:每步工具调用、耗时、结果状态 | + +--- + +## 附:术语对照 + +| CALM 术语 | 本项目术语 | 说明 | +|---|---|---| +| Command | 动作 / 指令 | 理解层的输出单元 | +| Flow | 工具流程 / 任务清单 | 如「8 步自主发品」「6 步一键发品」 | +| Flow steps | 流程步骤 | `agentSteps` 的每一项 | +| Slot | 表单字段 / 上下文槽位 | 目的地、天数、类型、人群 | +| Action | 工具(Tool) | 8 个生成工具 | +| Dialogue Understanding | 意图解析 | `parseBrief` / `parseMultiTools` | +| Conversation Repair | 打断收口 / 兜底回复 | `sendMessage` 的等待点收口与闲聊兜底 | +| `ask_before_filling` | 逐工具确认 | `serialConfirm` 卡片确认 |