# intelligent_cabin BERT 接入 · 冲突分析与整合方案

> 作者：AI 分析  
> 日期：2026-05-26  
> 状态：待你确认

---

## 背景速览

| 侧 | 项目 | 技术栈 | 角色 |
|---|---|---|---|
| **Canvas（前端/编排侧）** | ai-canvas / Next.js | TypeScript + React | 画布渲染、Artifact 管理、LLM 编排、工业工具调用（DBus/PLC）|
| **BERT 服务侧** | intelligent_cabin | Python / FastAPI | 本地 NLU：intent + slot，语音控制场景 |

这两个项目目前是独立的，尚未对接。

---

## 一、冲突点（需要明确决策的地方）

### 冲突 1：Domain 语义完全不匹配 ⚠️ 重要

**问题**：`intelligent_cabin/config/domain.yml` 里的 intent 全部来自车机场景（导航、空调、音乐、车窗、车灯……），与 `DBUS_API.md` 描述的线切割工控场景（电压、电流、速度、NC 加工、空走、回零）**毫无重叠**。

**后果**：如果直接把 BERT 服务接进来，意图识别的类别集合完全覆盖不了工控指令，识别结果几乎全是 unknown/reject。

**需确认**：
- [ ] 你们会用智能车机的这套预训练模型做迁移学习，还是重新训练工控专属的 BERT 模型？
- [ ] 还是说这个 BERT 服务只是作为基础推理框架，`domain.yml` 需要被替换成工控版 domain？

---

### 冲突 2：置信度阈值体系 vs. Canvas 路由体系

**Canvas 流程**（参考 `architecture_overview.html`）的技术视图里：
```
输入 → BERT NLU → confidence 判断 → [高置信+设备控制域] / [知识域/低置信] / [smalltalk]
```

**intelligent_cabin 的路由体系**（`router.py`）：
```
输入 → Classifier → FusionGrader → 
  decision = execute / clarify / route_to_cloud / reject
  阈值：execute_score=0.55, execute_margin=0.18, route_to_cloud=0.75
```

两套路由各有自己的 confidence 分级，**如果串联，会出现双重过滤**：
- BERT 服务输出的 `score` 未必能直接映射到 Canvas 的 `confidence ≥ 阈值` 判断
- Canvas 目前只依赖 BERT 输出的 `domain/intent/slot/confidence`，但 BERT 服务输出的是 `intent_id + score + decision`

**需确认**：
- [ ] Canvas 侧是否需要直接消费 `decision` 字段（execute/clarify/route_to_cloud/reject），还是只取 `intent_id + score`，Canvas 自己做二次路由？

---

### 冲突 3：`textAliases`（大流程 B）vs. BERT 意图识别的竞争

**Canvas 大流程 B（调机流程中）的路由逻辑**：
```
输入 → 优先匹配 textAliases（如"完成"/"下一步"/"确认"）
     → 未命中 → BERT NLU 重新识别
```

**intelligent_cabin** 里的 `DialogActEngine`（`dialog_act.py`）也在做相似的事情：
```python
affirm: ["确认", "好的", "继续", "可以", "确定"]
cancel: ["取消", "算了", "不用了", "停止"]
```

**冲突**：
- `textAliases` 是每个 Artifact Step 私有的，属于 Canvas 状态机层面的规则匹配
- `DialogActEngine` 是系统级对话行为分类，优先级/触发顺序未对齐
- 如果两套都跑，"确认"这个词可能被 `DialogActEngine` 吃掉后不再传给 Canvas 的步骤推进逻辑

**整合建议**：
- 大流程 B 中，textAliases 匹配应在 BERT 服务之前，Canvas 侧处理，不交给 BERT 服务
- 把 `dialog_acts.yml` 的 `affirm/deny/cancel/modify` 从 BERT 服务中剥离，改为 Canvas 路由层的前置规则
- 或者让 BERT 服务的 dialog_act 结果作为辅助 metadata 透传，Canvas 侧自行决策

---

### 冲突 4：slot 语义不兼容

**BERT 服务的 slot 体系**（按车机场景设计）：
- `temperature`：空调温度（16-30°C）
- `destination`：导航目的地
- `order_id`：订单号
- `song`：歌曲名

**线切割工控的 slot 需求**（从 `DBUS_API.md` 推导）：
- `speed`：加工速度（mm/min，1-9999）
- `voltage`：放电码/电压
- `current`：跟踪值/电流
- `workpiece_id`：工件编号（0-8）
- `knife_id`：刀号（1-11）
- `param_type`：参数类型（voltage/current/servo）
- `nc_path`：NC 文件绝对路径
- `axis`：轴编号（0-5，X/Y/Z/U/V/C）

两套 slot 定义**完全不同**，需要重写 `domain.yml` 和 `rewrite_engine.py` 里的工控版本。

---

### 冲突 5：rewrite_engine 的上下文改写逻辑是车机特化的

`rewrite_engine.py` 里写死了：
```python
_AC_CONTEXT_INTENTS = {"cabin_set_ac", "cabin_ac_on", "cabin_ac_off", ...}
_AC_DEFAULT_TEMPERATURE = 24
_AC_STEP = 2
_AC_MIN_TEMPERATURE = 16
_AC_MAX_TEMPERATURE = 30
```

这些都是车机空调的业务逻辑，工控场景需要换成类似：
```python
_PARAM_CONTEXT_INTENTS = {"wirecut_set_speed", "wirecut_set_voltage", "wirecut_set_current"}
_SPEED_STEP = 5  # mm/min
_VOLTAGE_STEP = 5  # 放电码
```

**但这恰恰也是最有价值可以复用的能力**：多轮相对调节（"再快一点"→ 改写为 "速度设为 85mm/min"）在工控场景同样刚需。

---

## 二、可以直接整合的能力（不需要改动或改动很小）

### ✅ 整合点 1：FastAPI 服务直接作为 NLU 微服务接入

Canvas（Next.js 侧）可以通过 HTTP 调用 `POST /api/v1/agent/chat` 或 `POST /api/v1/agent/fill-slots`，作为 Canvas 的 "感知语义层"。

接入位置：Canvas 的"BERT NLU 意图识别"节点（技术流程视图第 2 层）

```
Canvas 输入 → HTTP POST → intelligent_cabin :8000/api/v1/agent/chat
          ← {intent_id, score, slots, decision}
Canvas 路由层根据 intent_id + score 继续分发
```

### ✅ 整合点 2：分级融合决策逻辑（router.py）可以直接复用

`MultiStageIntentMatcher` 里的 `execute / clarify / route_to_cloud / reject` 四态决策逻辑，与 Canvas 流程中"高置信执行 / 知识域兜底 / smalltalk" 的三路分发思路高度一致，可以直接用。

Canvas 只需要消费 BERT 服务返回的 `decision` 字段，然后：
- `execute` → Canvas 走工具调用路径（DBus）
- `clarify` → Canvas 展示澄清确认卡
- `route_to_cloud` → Canvas 走 LLM 兜底路径
- `reject` → Canvas 走 smalltalk / fallback

### ✅ 整合点 3：会话状态管理（session_store.py）

`SessionState` 里的 `context_memory` 和 `slots` 持久化机制，可以支持工控场景的多轮短句恢复（比如 "再快一点" → 记住上次速度值）。

### ✅ 整合点 4：多命令拆分（planner.py）

现有的 `sequence workflow` 和 `conditional workflow` 对工控多命令（"先回零，再开始加工"）直接可用。

### ✅ 整合点 5：高风险确认机制（dialog_rules.py）

`requires_confirmation` + `confirmation_required_risk_levels` 这套机制，完全可以映射到 Canvas 流程中"操控确认卡"的场景（Canvas 大流程 A 的场景 1），直接支持危险操作二次确认。

---

## 三、需要新建或重写的工控专属配置

### 3.1 新建 `config/domain_wirecut.yml`

需要把 DBUS_API.md 里的每个接口映射成 intent：

| intent_id | 对应 DBus 方法 | 示例语句 | 需要 slot |
|---|---|---|---|
| `wirecut_start_run` | `startRun()` | "开始加工" / "启动" | 无 |
| `wirecut_stop_run` | `stopRun()` | "停止加工" / "停机" | 无 |
| `wirecut_pause_run` | `pauseRun()` | "暂停" / "变频暂停" | 无 |
| `wirecut_home_all` | `homeAll()` | "全轴回零" / "回零" | 无 |
| `wirecut_start_kongzou` | `startKongZou()` | "开始空走" | 无 |
| `wirecut_stop_kongzou` | `stopKongZou()` | "停止空走" | 无 |
| `wirecut_set_speed` | `setSpeed(speed)` | "速度调到80" / "加快一点" | `speed:int` |
| `wirecut_set_voltage` | `setVoltage(vol)` | "电压设为90" | `voltage:int` |
| `wirecut_set_current` | `setCurrent(cur)` | "电流设为5" | `current:int` |
| `wirecut_get_status` | `getStatus()` | "查一下状态" / "当前状态" | 无 |
| `wirecut_load_nc` | `loadNC(path)` | "加载NC文件" | `nc_path:str` |
| `wirecut_set_discharge` | `SetDischargePara(...)` | "设置工件0刀1放电码80" | `workpiece_id, knife_id, param_type, value` |

### 3.2 重写 `rewrite_engine.py` 里的工控上下文改写规则

```python
# 工控版示例
_SPEED_CONTEXT_INTENTS = {"wirecut_set_speed"}
_SPEED_STEP = 5          # mm/min 每步调节
_SPEED_MIN = 1
_SPEED_MAX = 9999

_VOLTAGE_CONTEXT_INTENTS = {"wirecut_set_voltage"}
_VOLTAGE_STEP = 5

# 改写规则
"再快一点"  → "速度设为 {last_speed + 5} mm/min"
"再慢一点"  → "速度设为 {last_speed - 5} mm/min"
"电压再高一点" → "电压设为 {last_voltage + 5}"
```

### 3.3 更新 `dialog_acts.yml` 适配工控确认场景

当前 `dialog_acts.yml` 的 `affirm/deny/cancel` 词表可以保留，但需要补充工控场景的特化语句：

```yaml
# 工控场景补充
affirm:
  + "执行" / "开始" / "运行" / "启动"
cancel:
  + "停机" / "急停" / "中止"
inform:
  + 纯数字（速度值/电压值/电流值的slot填写）
```

---

## 四、关于 dialog_act 和 rewrite_engine 能否通过配置文件驱动

**直接回答：当前已经部分支持，但工控场景需要扩展。**

### 4.1 dialog_act 配置化现状

`dialog_act.py` 里的 `DialogActEngine` 是数据驱动的：
```python
@dataclass
class DialogActEngine:
    patterns: dict[str, tuple[str, ...]] = field(default_factory=...)
```

`config_loader.py` 里已经实现了从 `dialog_acts.yml` 加载：
```python
def _load_dialog_act_engine(self) -> DialogActEngine:
    ...
    return DialogActEngine(patterns={item.act_id: tuple(item.phrases) for item in parsed.acts})
```

**✅ 结论**：`dialog_acts.yml` 已经是配置文件，直接修改 `config/dialog_acts.yml` 就能修改 act 词表，**无需改代码**。

**但是有个缺口**：当前 `dialog_acts.yml` 只支持 `字符串包含` 匹配（`phrase in normalized`），没有支持正则或数值范围匹配。工控场景里 "85" 这样的纯数字（作为速度 slot 的 `inform`）目前是靠 Python 代码里的 `if re.search(r"\d", normalized): return "inform"` 处理的，这个逻辑是硬编码在 `dialog_act.py` 里的，暂时没有配置化。

**建议优化**（详见第五节）。

### 4.2 rewrite_engine 配置化现状

`rewrite_engine.py` 里的上下文改写逻辑**全部硬编码**，没有配置文件支持：

```python
_AC_CONTEXT_INTENTS = {"cabin_set_ac", ...}  # 硬编码
_AC_DEFAULT_TEMPERATURE = 24                  # 硬编码
_AC_STEP = 2                                  # 硬编码
```

**❌ 问题**：不同设备（线切割 / 激光切割 / 注塑机 / 其他）的参数名、步长、范围都不同，每台设备都要改代码是不合理的。

**建议优化**（详见第五节）。

---

## 五、建议优化：将 rewrite_engine 和 dialog_act 配置化

这是你关心的核心问题，以下是具体方案。

### 5.1 新增 `config/context_rewrite.yml`

```yaml
# context_rewrite.yml
param_contexts:
  - intent_ids: ["wirecut_set_speed"]
    slot_name: "speed"
    unit: "mm/min"
    step: 5
    min_value: 1
    max_value: 9999
    default_value: 80
    up_phrases: ["再快一点", "加快", "速度调高", "快一点"]
    down_phrases: ["再慢一点", "减慢", "速度调低", "慢一点"]
    rewrite_template: "速度设为 {value} mm/min"

  - intent_ids: ["wirecut_set_voltage"]
    slot_name: "voltage"
    unit: ""
    step: 5
    min_value: 0
    max_value: 200
    default_value: 90
    up_phrases: ["电压高一点", "电压调高"]
    down_phrases: ["电压低一点", "电压调低"]
    rewrite_template: "电压设为 {value}"

  - intent_ids: ["wirecut_set_current"]
    slot_name: "current"
    unit: ""
    step: 1
    min_value: 0
    max_value: 30
    default_value: 5
    up_phrases: ["电流大一点", "跟踪值高一点"]
    down_phrases: ["电流小一点", "跟踪值低一点"]
    rewrite_template: "电流设为 {value}"
```

然后修改 `rewrite_engine.py`，从 `context_rewrite.yml` 驱动逻辑，而不是硬编码。

### 5.2 扩展 `config/dialog_acts.yml` 支持 inform 的数值捕获

```yaml
# dialog_acts.yml 新增字段
acts:
  - act_id: inform
    phrases: []
    # 新增：数值模式检测
    numeric_patterns: ["\\d+"]   # 包含数字就认为是 inform
```

并在 `dialog_act.py` 里支持 `numeric_patterns` 字段，这样 "85" / "22度" 是否算 inform 就可以通过配置控制。

### 5.3 `Settings`（config.py）里增加工控设备配置项

`app/core/config.py` 里的 `Settings` 类目前通过环境变量驱动，建议增加工控配置路径：

```python
# 新增到 Settings 类
context_rewrite_config_path: str = "config/context_rewrite.yml"
device_domain_config_path: str = "config/domain_wirecut.yml"  # 可按设备切换
```

这样不同设备（线切割、激光机）只需要切换 `.env` 文件里的 `AGENT_DEVICE_DOMAIN_CONFIG_PATH` 就能换一套 domain + rewrite 配置，**不改代码**。

---

## 六、整合架构建议（整体链路）

```
语音输入
    ↓ ASR（前端 / 设备侧）
文本输入
    ↓
Canvas 路由层（Next.js / TypeScript）
    ├── [大流程 B 激活] textAliases 匹配 → 直接推进 Artifact 步骤（不过 BERT）
    └── [其他] HTTP → intelligent_cabin :8000
                            ↓
                     context rewrite（工控版配置化）
                            ↓
                     BERT NLU（工控 domain）
                            ↓
                     fusion decision
                         ├── execute → Canvas 走 DBus 工具调用
                         ├── clarify → Canvas 渲染 Artifact 确认卡
                         ├── route_to_cloud → Canvas 走 LLM 兜底 + 知识库
                         └── reject → Canvas smalltalk / fallback
```

---

## 七、待你确认的清单

| # | 问题 | 选项 |
|---|---|---|
| 1 | BERT 模型是否会重训成工控版，还是继续用车机模型迁移？ | A. 重训工控 BERT  B. 车机→工控迁移  C. 暂时用 mock/keyword |
| 2 | Canvas 是消费 BERT 的 `decision` 字段，还是只取 `intent_id+score` 自己路由？ | A. 消费 decision  B. 自路由 |
| 3 | 大流程 B 的 textAliases 匹配，确认在 Canvas 侧做，不经过 BERT 服务？ | A. 是  B. 也经过 BERT 但 BERT 优先级低 |
| 4 | rewrite_engine 配置化的优先级？现在要做还是等工控 domain 确定后一起做？ | A. 现在就做  B. 等 domain 定了再做 |
| 5 | dialog_acts.yml 的 `inform` 数值识别是否需要配置化？ | A. 配置化  B. 保持硬编码即可 |
| 6 | 不同设备部署时，是一个 BERT 服务实例 + 不同 `.env` 配置，还是多个实例？ | A. 单实例多配置  B. 多实例 |

---

*本文档由 Antigravity 生成，请确认后继续推进实现。*
