传统机器人技术架构实现分析

0 阅读33分钟

传统机器人技术架构(传统非大模型方案)


传统机器人整体架构图与流程图

1. 整体架构图(分层框图)

flowchart TB
    subgraph 用户侧
        A[用户语音]
        A2[前端播放]
    end

    subgraph 前端硬件与SDK["前端硬件 / SDK"]
        B[录音 / 降噪 / VAD]
        B2[音频推流]
        B3[TTS 播放]
    end

    subgraph 云端服务["云端服务"]
        subgraph ASR["ASR 服务"]
            C[语音识别]
        end
        subgraph NLP["NLP 服务"]
            D1[分词]
            D2[词典 Dict]
            D3[文法 Grammar]
            D4[意图 / 槽位 / 领域]
        end
        subgraph 网关["API 网关"]
            G[鉴权 / 限流 / 服务发现 / 监控]
        end
        subgraph DM["DM 对话管理"]
            E1[对话状态维护]
            E2[多轮 / 填槽 / 兜底]
            E3[Lua:调下游技能 + 控对话]
            E4[技能分发决策]
        end
        subgraph SkillRouter["技能路由 Skill Manager"]
            F[domain / intent / 设备配置 / 显式指定]
        end
        subgraph 技能["具体技能(下游技能服务)"]
            S1[音乐]
            S2[视频]
            S3[天气]
            S4[有声]
            S5[医生 / 百科 …]
        end
        subgraph TTS["TTS 服务"]
            H[语音合成]
        end
    end

    subgraph 第三方["第三方 / 后台服务"]
        T[音乐 API / 天气 API / 百科 …]
    end

    A --> B --> B2 --> C
    C --> D1 --> D2 --> D3 --> D4
    D4 --> G
    G --> E1 --> E2 --> E3 --> E4
    E4 --> F
    F --> S1 & S2 & S3 & S4 & S5
    S1 & S2 & S3 & S4 & S5 --> T
    T --> E1
    E1 --> H
    H --> B3 --> A2

3. 场景绑定流程图(NLP Launcher 分发)

通过场景绑定,结构化的语义表示和对话流经 NLP Launcher 处理,分发给特定场景/功能。

flowchart LR
    subgraph 输入["NLP 输出(结构化语义)"]
        IN["domain: music_1<br/>intent: search_music<br/>slot: singer陈粒<br/>对话流: 无"]
    end

    subgraph Launcher["NLP Launcher"]
        L[场景绑定 / 分发]
    end

    subgraph 场景["场景 / 功能"]
        M1[音乐]
        M2[视频]
        M3[有声]
        M4[天气]
        M5[…]
    end

    IN --> L
    L -->|实线| M1
    L -.->|虚线| M2
    L -.->|虚线| M3
    L -.->|虚线| M4
    L -.->|虚线| M5

NLP Launcher 与场景绑定(本质定义)

  • Launcher(桌面)的本质:是调度 App 的工具。传统机器人的 App 同时支持语音和图形界面,因此既能通过触屏调度,又能通过语音调度。
  • NLP Launcher 是什么语音交互的分发机制。NLP Launcher 通过分发语音指令,来调度不同的功能(App)。每个功能(App)在 NLP Launcher 下创建场景,通过场景绑定,NLP Launcher 在收到语音指令后就能把特定的信息分发给对应的场景/功能(如视频、有声、天气)。
  • 场景绑定是什么
    1. 文法、对话流绑定在具体的场景/功能里(如视频、有声、天气);
    2. 再把该功能绑定到负责分发的 NLP Launcher 下。
      NLP Launcher 根据这些绑定关系,把语音指令的处理结果(领域 domain、意图 intent、词槽 slot、对话流)分发给对应的场景/功能。

NLP Launcher 是干啥的?如何识别是哪个技能?同时命中多个怎么办?

  • NLP Launcher 是干啥的?
    NLP Launcher 是语音指令的分发中枢:接收 NLP 产出的结构化语义(domain、intent、slots)和对话流信息,根据你在平台里配的场景绑定,把当前这句话分发给对应的场景/功能(如音乐、视频、有声、天气、医生等)。
    可以理解为:Launcher 不负责理解语义(那是 NLP 的事),只负责「这句话已经识别成 domain=X、intent=Y 了,该交给哪个场景处理」。每个 App 在 Launcher 下挂若干场景,文法 + 对话流绑定到具体场景上,Launcher 按绑定关系把请求丢给对应场景,后续由该场景下的 DM、技能路由、具体技能去执行。

  • 如何识别是哪个技能的?
    分两步:

    1. Launcher 先认「场景」:根据 NLP 的 domain(和 intent) 查场景绑定表,得到「这条请求属于哪个场景」(如 music → 音乐场景)。
    2. 该场景内再选「具体技能」:同一场景下可能有多条技能(例如音乐场景下有网易云、QQ 音乐等),由 DM + 技能路由(Skill Router) 按设备配置、用户显式指定、意图优先级/置信度等,在候选里选出一个技能去调。
      所以:识别到「哪个技能」= Launcher 识别到场景 + 该场景内技能路由选出唯一技能
  • 如果同时命中多个技能呢?
    同一句用户话可能命中多个 domain/多个意图(例如既像音乐又像有声),或同一 domain 下多个技能都满足。处理方式:

    • 多 domain/多意图:按平台配置的领域/意图优先级对候选排序,只取第一个作为当次请求的结果,后续 DM 和技能只针对这一个结果执行。
    • 同一 domain 下多技能:由技能路由按设备/渠道默认、用户显式指定、置信度再排一次序,同样只选一个技能执行。
      未配置优先级时,文档写的是「结果随机」或按系统默认顺序取第一个,因此若要行为可预期,需要在平台上把领域意图优先级技能优先级/默认技能配好。
  • 典型歧义示例:音乐 vs 有声/FM
    同一句话可能同时命中不同场景的句式和槽位,例如用户说「打开郭德纲相声」:

    • 音乐技能可能命中:把「郭德纲相声」当成歌单/专辑类内容,走 music 场景;
    • 有声/FM 技能可能命中:把「相声」当成有声读物/电台内容,走 fm / 有声场景。
      此时 NLP 可能产出多个候选(如 domain=music 与 domain=fm 都命中),最终走哪个场景由 Launcher 的领域/意图优先级决定:平台里若把「有声」或「FM」配得比「音乐」高,就会进有声/FM 技能;反之则进音乐技能。若要固定行为(例如相声统一走 FM),需在领域意图优先级里把对应 domain 的先后顺序配好。

场景绑定 / 分发 是咋实现的?规则是啥?

  • 实现方式(平台侧)
    在 传统机器人 的场景管理里,每个 NLP Launcher 下会挂多个场景(如音乐、视频、有声、天气、医生等)。每个场景要配置「绑定」关系:把哪些 domain(以及可选的 intent、对话流) 算作属于这个场景。也就是说:

    • 你先在「文法配置」里定义好 domain/intent(如 music、play_music);
    • 再在「场景管理」里给每个场景指定:当 NLP 输出 domain=某某(或 domain+intent=某某)时,这条请求归这个场景处理。
      同时,文法对话流在配置时会关联到某个场景,所以 Launcher 知道「这段文法/这条对话流属于哪个场景」。
  • 实现方式(运行时)
    Launcher 收到 NLP 的输出(domain、intent、slots、对话流等)后:

    1. 场景绑定表:看当前 domain(和 intent)被绑到了哪个场景;
    2. 若当前已在某条对话流中,可能按「对话流归属场景」直接继续交给该场景;
    3. 把请求(含 domain、intent、slots、对话流)分发给匹配到的场景,由该场景下的 DM、技能路由、具体技能去执行。
  • 规则是啥(匹配规则)

    • 主键一般是 domain:一条请求先看 domain 属于哪个场景;可细化为 domain + intent(同一 domain 下不同 intent 可绑到不同场景或同场景不同逻辑)。
    • 一对一或多对一:一个 domain 通常只绑一个场景(如 music → 音乐场景);若多个场景都声明能处理同一 domain,则靠领域/意图优先级排序,取第一个。
    • 对话流:若请求里带了「当前对话流」,可用来决定继续在哪个场景(对话流本身在配置时已挂在某场景下)。
    • 未命中:若 domain 没有绑定到任何场景,走兜底逻辑(如默认场景或拒识)。
  • 小结
    场景绑定 = 在平台里配置「domain(+ intent/对话流)→ 场景」的映射;分发 = Launcher 用 NLP 输出查这张映射,把请求转发给对应场景。规则就是:按 domain(及可选 intent、对话流)匹配绑定表,多命中时按优先级取第一个

平台配置操作步骤(参考 传统机器人 开发者文档

以下为在猎户开发者平台中,从创建 App/场景到绑定对话流的典型操作顺序,便于与上文「场景绑定」对应。

步骤操作位置说明
1. 新建 APP场景管理 → APP 和场景点击右上角「新建」,填写 APP 名称APP ID(ID 可在「发布应用」中生成)。初始状态下组织下无 APP,需先建 APP。
2. 新建场景同上,每个 APP 后APP 建好后默认有一个场景 Main。可点击该 APP 后的「添加场景」新增场景;场景名仅支持英文,同 APP 下不可重名。新增后会生成场景 ID。功能简单的 APP 可只使用 Main。
3. 配置领域、意图、词槽(文法/词典)NLP 配置 → 编辑意图 / 文法配置 / 词典配置领域(domain):同一类数据/服务,英文命名且唯一(如 music、music_1)。意图(intent):用户对领域数据的操作,英文动词(如 search_music)。词槽(slot):实现意图所需参数(如歌手、城市、日期);可设必填追问话术(缺槽时多轮追问)。文法:把用户句匹配为 domain + intent + slots;词典:实体/词表,供文法占位符识别。先配好词典与文法,NLU 才能产出结构化结果。
4. 配置对话流NLP 配置 → 对话流管理新建对话流(如「音量多轮」),在画布上添加 触发单元(domain+intent 条件)、回复单元(话术)、功能单元(可选)、分支(如「再大点」「再小点」)等,定义多轮逻辑。对话流在创建/编辑时可关联到具体场景
5. 编辑场景并绑定场景管理 → APP 和场景 → 某场景「编辑」点击场景后的「编辑」。场景编辑即把当前场景与设备对话(含意图与对话流)做关联。默认无关联,需点击「新增配置」→ 选择 设备组对话(即已配置好的意图+对话流)→ 确认。完成后点「确定」完成编辑。
6. 搜索与维护同上列表页支持按 APP 名称、场景名称、关联设备组、关联领域 搜索场景和 APP,便于后续维护。

顺序小结新建 APP → 新建场景 → 配置领域/意图/词槽(文法+词典)→ 配置对话流并关联场景 → 编辑场景,绑定「设备组 + 对话」。绑定后,NLP Launcher 才能把语音指令的处理结果(domain、intent、slot、对话流)分发给对应场景/功能。

关键模块拆解

NLP Launcher

  • 作用:整个 NLP 系统的入口分发器,所有用户输入都会先到这里。
  • 逻辑:根据用户说的话(NLU 产出的 domain/intent 等),判断应该交给哪个子场景去处理(如 global_command、weather、workflow),实现多技能的路由。
  • 特点:本身不处理具体业务,只负责「调度」。

global_command(全局命令场景)

  • 作用:NLP Launcher 下的一个特殊子场景,用来处理全局通用指令,例如:
    • 唤醒词、退出、返回上一级(go_back)
    • 音量调节、停止播放等设备控制
  • 特点优先级高,无论当前在哪个业务场景,全局命令都能被优先识别和执行。

编辑场景

  • 作用:点击「编辑场景」后,在该场景里可以:
    • 绑定对应的领域(Domain)(如音乐、天气、导航)
    • 配置该场景下支持的意图(Intent)
    • 关联对应的对话流(Workflow),定义多轮对话逻辑
  • 本质:把 NLU(文法 + 词典)DM(对话流) 绑定到一个场景里,让系统知道「这个场景能处理什么话、怎么处理」。

完整工作流(真实逻辑)

  1. 用户说一句话 → 先到 NLP Launcher
  2. Launcher 根据规则,把请求分发到对应的子场景(如 weather、music、global_command)
  3. 子场景里的文法/词典做 NLU 解析,识别出意图和槽位(若尚未在 Launcher 前统一做 NLU,则按实际架构可能在该场景内或前置完成)
  4. 触发该场景绑定的对话流(Workflow),执行 Go + Lua 脚本逻辑
  5. 最后生成回复或执行动作,经 TTS 或技能服务返回用户

即:用户输入 → Launcher 分发 → 子场景 → 该场景 NLU + 绑定的对话流 → Go+Lua 执行 → 回复/动作

4. 对话流示例:远程医生多轮对话

以「远程医生」为例:用户说「胃疼怎么办」→ 识别意图 → TTS 反问 → 根据用户响应分支结束或转百科。

绘制或引用时请勿截断:NLP 识别须写全 domain=doctor_1intent=ask;TTS 询问须写全整句「身体不舒服?需要帮您呼叫远程医生吗」。

flowchart TB
    Start["用户输入<br/>如「胃疼怎么办」"] --> NLP["NLP 识别<br/>domain=doctor_1 且 intent=ask"]
    NLP --> TTS["TTS 询问<br/>身体不舒服? 需要帮您呼叫远程医生吗"]
    TTS --> Judge{"判断 domain / 用户响应"}

    Judge -->|用户确认<br/>domain=general_command_3<br/>intent=confirm 或 点击确认| End1["结束对话,结果回传"]
    Judge -->|3s 超时| End2["结束对话"]
    Judge -->|用户取消<br/>domain=general_command_3<br/>intent=cancel 或 点击取消| End3["将 query「胃疼怎么办」<br/>传给百科服务,并结束对话"]

    End2 --> End2b["结束"]

一、整体语音链路

用户语音
  ↓
【前端硬件/SDK】(音箱、机器人、APP)
  ↓
【ASR 服务】(语音识别)
  ↓
【NLP 服务】(分词、词典、文法解析、意图/槽位)
  ↓
【网关】(鉴权、限流、环境隔离、服务发现)
  ↓
【DM 对话管理】(Dialogue Manager,核心调度;多轮时用 session 维护上下文)
  ↓
【技能路由 / Skill Manager】
  ↓
【具体技能(下游技能服务,非 Lua 实现)】  ← 多轮且缺槽时 DM 不走到这里,先 TTS 追问,等用户下一句再走整条链路
  ↓
【第三方服务/后台服务】(音乐、天气、视频、控制等)
  ↓
【DM 汇总结果】
  ↓
【TTS 服务】(播报:执行结果 或 追问话术)
  ↓
前端播放

记忆里的链路 ASR → NLP → 网关 → DM(Lua)→ 技能 完全正确。其中 Lua 在 DM 层,负责调用下游技能服务和控制 DM 对话;具体技能本身是独立的下游服务,不是用 Lua 实现的。下面按每一层的职责、实现、以及你配置的词典/文法如何串进去说明。

多轮对话在整体链路里如何体现?
上面这条链路是每一轮都会走的:用户说一句 → ASR → NLP → 网关 → DM(带 session)→ Lua/对话流判断。

  • 若槽位已齐或无需补槽:DM 调技能 → 结果 → TTS → 结束或进入下一话题。
  • 若缺槽:DM 不调技能,而是根据 Lua/对话流返回追问话术(如「您想听哪首歌?」)→ TTS 播报 → 本轮结束,但 session 里保留当前 intent 和已填槽位;用户下一句再说话时,重新走一遍 ASR → NLP → … → DM,DM 把新一句的解析结果和 session 里已有内容合并,再判断是继续追问还是槽位齐了去调技能。
    所以多轮对话 = 同一套链路多跑几轮,由 DM 的 session + 对话流/Lua(缺槽则追问、槽满则调技能) 决定每一轮是「追问」还是「执行技能」。

先弄清这几件事

domain、intent、slots、对话流 分别指啥?

NLP 输出和 Launcher/DM 用的四个核心概念,统一定义如下:

名词英文是啥举例
Domain领域最高层的业务分类:这句话属于哪一大类功能。Launcher 主要靠 domain 决定把请求分发给哪个场景。音乐、天气、音量、视频、医生、通用指令
Intent意图在领域下的具体动作:用户在该领域里要做的操作,一个 domain 下可有多个 intent。播放音乐、暂停音乐、搜索音乐、查天气、调大/调小音量
Slot槽位执行意图所需的参数:从用户话里抽出来的键值对,缺了就要多轮追问补全。歌手、歌曲名、城市、日期、音量档位
对话流多轮对话的流程配置:当前若处于某条对话流中,会带「对话流」信息,DM 按对话流决定下一轮追问还是执行;对话流在配置时挂在某场景下。无(首句)/ 某条对话流 ID 或名称

平台里 domain/intent 常用英文标识(如 music、play_music),便于绑定与分发;上面举例用中文便于理解。Launcher 根据 domain(和 intent) 做场景绑定与分发。

文法配置、词典配置到底是干啥的?

  • 一句话
    词典 = 系统要识别的「词都是啥」(歌手名、歌名、城市名…);文法 = 系统要识别的「句子长什么样、对应哪个意图和哪些槽位」。
    用户说一句话 → 先用词典认出里面的实体(谁、什么、哪里)→ 再用文法规则匹配句式,得到「领域 + 意图 + 槽位」。

  • 举个例:用户说「播放周杰伦的七里香」

    • 词典:配置了「周杰伦」属于歌手、「七里香」属于歌曲名,分词/实体识别才能把这两个词标成「歌手」「歌曲」。
  • 文法:配置了规则如 播放 <歌手> 的 <歌曲> → 对应领域=音乐、意图=播放音乐、槽位 singer=xxx, song=xxx。(<歌手> / <歌曲> 是文法里的占位符,由词典识别的实体来填。)

    • 合起来:词典把「周杰伦」「七里香」识别成实体,文法把整句匹配到该规则,输出 domain=music, intent=play_music, slots: singer=周杰伦, song=七里香。 所以:不配词典,系统不知道「周杰伦」是歌手;不配文法,系统不知道这句话是「播放音乐」而不是别的意图。

示例:文法配置和词典配置长什么样?

下面用「音乐」和「天气」两个领域,给出你在平台里实际会配的大致内容(格式依平台界面可能略有差异,逻辑一致)。


1)词典配置示例

词典 = 一张「词 → 属于哪个槽位/类型」的表。NLP 分词后查表,把词标成对应槽位,供文法规则里的占位符填值。

词条槽位/类型说明
周杰伦、陈粒、邓紫棋singer歌手名,填到「播放/搜索音乐」的歌手槽
七里香、光年之外、小半song歌曲名,填到歌曲槽
北京、上海、杭州city城市名,填到「查天气」的城市槽
今天、明天、后天date日期,填到天气日期槽
网易云、QQ音乐、酷狗music_source音乐来源,用于技能路由时「用户显式指定」

同义词也可在词典里配,例如「播」→ 同义「播放」,「查」→ 同义「查询」。
不配「周杰伦」进 singer:用户说「播放周杰伦的七里香」时,系统可能识别不出「周杰伦」是歌手,槽位为空或错误。

词典配置具体例子(仿平台,可直接照抄扩展)

在平台「词典配置」里按「槽位/类型」建词表,每条一行「词 → 属于哪个槽」。示例:

# 槽位: singer(歌手)
周杰伦  陈粒  邓紫棋  毛不易  李荣浩

# 槽位: song(歌曲名)
七里香  光年之外  小半  消愁  年少有为

# 槽位: city(城市)
北京  上海  杭州  深圳  广州

# 槽位: date(日期)
今天  明天  后天  周一  周末

# 同义词(动作词,供文法匹配)
播 -> 播放   放 -> 播放   想听 -> 播放
查 -> 查询   查一下 -> 查询

分词得到「周杰伦」时查词典 → 标为 singer;「七里香」→ song;「播」按同义词当「播放」参与文法。


2)文法配置示例

文法 = 一组「句式模板 → 对应哪个 domain、哪个 intent、哪些 slot」的规则。用户说的话和模板匹配上,就得到结构化结果。

句式规则(占位符用尖括号)domainintent槽位
播放 <singer> 的 <song>musicplay_musicsinger, song
我想听 <song>musicplay_musicsong
搜索 <singer> 的歌musicsearch_musicsinger
暂停 / 暂停播放musicpause_music(无)
<city> 今天天气怎么样weatherquery_weathercity, date(今天)
查一下 <city> 明天天气weatherquery_weathercity, date(明天)

占位符 <singer><song><city> 等必须和词典里配置的槽位/类型名一致,解析时用词典识别出的实体去填。
例如用户说「播放周杰伦的七里香」→ 匹配第一行规则 → 输出 domain=music, intent=play_music, slots: {singer=周杰伦, song=七里香}

文法配置具体例子(仿平台规则 + 匹配过程)

在平台「文法配置」里为每个 domain 写多条规则:句式模板 + domain + intent + 要抽取的 slot。下面给几条可照抄的规则,并跟两条「用户说 → 匹配 → 输出」的完整例子。

规则句式模板(占位符与词典槽位名一致)domainintent抽取的 slot
G1播放 <singer> 的 <song>musicplay_musicsinger, song
G2我想听 <song>musicplay_musicsong
G3放一首 <song>musicplay_musicsong
G4搜索 <singer> 的歌musicsearch_musicsinger
G5暂停 / 暂停播放 / 别放了musicpause_music
G6<city> 今天天气怎么样weatherquery_weathercity, date
G7查一下 <city> 明天天气weatherquery_weathercity, date

例 1:用户说「我想听陈粒的小半」

  • 分词:我、想、听、陈粒、的、小半
  • 词典:陈粒→singer,小半→song;想听→播放
  • 文法:命中 G2「我想听 <song>」→ domain=music, intent=play_music, 抽 slot:song=小半
  • 输出:{ "domain": "music", "intent": "play_music", "slots": { "song": "小半" } }

例 2:用户说「北京今天天气怎么样」

  • 分词:北京、今天、天气、怎么样
  • 词典:北京→city,今天→date
  • 文法:命中 G6「<city> 今天天气怎么样」→ domain=weather, intent=query_weather, slots: city=北京, date=今天
  • 输出:{ "domain": "weather", "intent": "query_weather", "slots": { "city": "北京", "date": "今天" } }

3)二者如何配合(一句话)

  • 词典:告诉系统「周杰伦」是 singer、「七里香」是 song、「北京」是 city。
  • 文法:告诉系统「播放 <singer> 的 <song>」这句话 → 属于 music 领域、play_music 意图,并抽出 singer、song 两个槽。
  • 两者一起,才能把「播放周杰伦的七里香」变成 {domain=music, intent=play_music, slots: {singer=周杰伦, song=七里香}},供后面 DM 和技能使用。

这里的 NLP 用的是机器学习吗?

  • 不是。 猎户这条链路里,意图/槽位/领域的解析是规则式 NLU:靠你配置的词典 + 文法做匹配,不用深度学习/大模型来做意图识别。
  • 通常会用机器学习的是 ASR(语音转文字)和 TTS(文字转语音);而「这句话是啥意图、槽位填了啥」是在 NLP 里用规则+词典算出来的,所以可解释、可配置、可热更新,但泛化能力不如模型。

网关层在哪儿、干啥?

  • 位置NLP 输出之后DM / 技能 / 第三方服务调用之前。所有「去调具体技能或第三方 API」的请求都先经过网关。
  • 职责:鉴权、限流、环境隔离(测试/预发/线上)、服务发现、日志与监控。
  • 下面「每层详细职责」里会单独写一档 4)网关

二、每层详细职责(传统机器人真实架构)

1)前端硬件 / SDK

  • 负责录音、音频采集、降噪、VAD
  • 把音频流推给云端 / 本地 ASR
  • 接收最终 TTS 音频并播放

2)ASR(语音识别)

  • 猎户自研或私有化部署的 ASR:音频 → 文本
  • 输出:text + confidence
  • 可热更新领域语言模型(你配的词典会影响这里的识别率

3)网关层(API 网关 / 服务网关)

  • 在链路中的位置:NLP 输出(domain / intent / slots)之后,DM 和技能、第三方服务之前。所有对「具体技能」和「第三方 API」的请求都经过网关,再转发到下游。
  • 职责:鉴权、限流、环境隔离(测试/预发/线上)、服务发现、日志与监控、链路追踪。
  • 所以你说「ASR → NLP → 网关 → DM → 技能」里的网关就是这一层,有独立一层描述。

4)NLP(核心:你配的词典 + 文法就在这一层)

这一层是传统规则式 NLU:用词典 + 文法做意图/槽位解析,不用机器学习做意图识别(ASR/TTS 可能用 ML,但意图解析是规则匹配)。

4.0 NLP 服务内部流程(说清楚顺序与职责)

平台里 NLP 服务 内部是按下面顺序跑的,和流程图一致:

  用户文本(ASR 输出)
        ↓
  ┌─────────────┐
  │    分词      │  把整句切成词/ token
  └─────────────┘
        ↓
  ┌─────────────┐
  │  词典 Dict   │  查词典配置:实体类型、同义词、槽位
  └─────────────┘
        ↓
  ┌─────────────┐
  │ 文法 Grammar │  用文法配置:匹配句式 → domain / intent / slots
  └─────────────┘
        ↓
  输出:domain、intent、slots  →  交给 NLP Launcher / DM / 技能路由

每一步做什么、和你配置的「词典」「文法」怎么对应,说清楚如下。

步骤名称输入做什么你配置的对应关系
1分词用户文本(ASR 来的)把整句切成词/ token,例如「我想听周杰伦的歌」→ 我、想、听、周杰伦、的、歌不直接配;可选热词/领域词影响 ASR 或分词粒度
2词典 Dict分词结果词典配置:哪些词是实体、属于哪个槽位/类型(如「周杰伦」→ 歌手)、同义词(如「想听/播放/放」→ 播放)。给每个词打上语义标签,供文法用词典配置:词条 → 槽位/类型/同义词,见下文 4.1
3文法 Grammar带词典标签的文本文法配置的规则做模式匹配:哪种词序+实体组合对应哪个 domain / intent,并抽槽位(如 [播放] + [歌手] + [歌曲] → domain=music, intent=play_music, slots: singer=周杰伦)。输出结构化结果文法配置:句式规则 → domain、intent、slot,见下文 4.2
4输出文法结果得到 domain、intent、slots(及对话流等),交给下游无单独配置;即 4.3 的 JSON/层级结构

和 Launcher、技能识别的关系

  • NLP 服务 的最终输出(domain、intent、slots)就是 NLP Launcher 的输入:Launcher 根据这些字段做场景绑定,把请求分发给对应场景。
  • 识别是哪个技能:先由 Launcher 按 domain/intent 确定场景,再在该场景内由 DM/技能路由按设备配置、用户显式指定、优先级等选出唯一一个技能
  • 同时命中多个:多 domain/多意图时按领域意图优先级取第一个;同场景下多技能时按默认/显式指定/置信度再取一个。详见前文「NLP Launcher 是干啥的?如何识别技能?多命中怎么办?」。

4.1 词典(Dict)

  • 领域词表
  • 同义词
  • 实体:歌手、歌曲、城市、日期、设备名…
  • 作用:在「分词」之后、「文法」之前,给词打上类型/槽位标签,让文法规则能命中并填槽。

4.2 文法(Grammar / CFG)

你在平台配的就是这个,句式规则例如:

  • 播放 <歌手> 的 <歌曲>
  • 打开 <应用>
  • 音量 <数值>

输出intent 意图、slots 槽位、domain 领域。

4.3 NLP 最终输出结构

平台侧解析后的结果在配置界面中常以层级结构呈现(见下图示意):先按 domain 分领域,再在领域下挂多个 intent,每个 intent 下挂该意图需要的 slot。这正是文法 + 词典产出的结构,也是 DM / 技能路由的输入。

  • domain(领域):最高层的业务分类,如音乐、天气、音量、视频、医生。
  • intent(意图):在领域下的具体动作,如播放音乐、暂停音乐、搜索音乐、查天气。
  • slot(槽位):执行意图所需的参数,如歌手、歌曲名、城市、日期;对应抽取结果如 singer:陈粒song:七里香
                    domain: 音乐 (music)
                    /        |        \
        搜索音乐(search)  暂停音乐(pause)  播放音乐(play)
                /    |    \
        singer:陈粒  name:七里香  ...
{
  "intent": "play_music",
  "slots": {
    "singer": "周杰伦",
    "song": "七里香"
  },
  "domain": "music"
}

4.4 和文法/词典、技能选择的关系

  • 文法规则:定义 domain + intent 的匹配模板(哪种句式 → 哪个领域、哪个意图)。
  • 词典配置:负责填充 slot 的值(歌手、歌曲名、城市等实体)。
  • 技能选择:DM 和技能路由用 domain 先锁定大类,再用 intentslots 决定具体调哪个技能、传什么参数。

一句话NLU = 把用户输入拆解成 domain + intent + slot 的规则引擎(文法定义匹配模板,词典填充槽位)。

5)DM 对话管理(Dialogue Manager)

猎户这套 DM 是真正的中枢

5.1 DM 做什么

  • 维护对话上下文(上一轮说啥、槽位是否完整)
  • 多轮对话状态管理
  • 反问缺失槽位
  • 拒答、兜底、纠错
  • 技能分发决策

5.2 猎户特色:Lua 在 DM 层(调下游技能 + 控对话)

具体技能并不是 Lua 插件化:技能是独立的下游服务(音乐、天气、视频等),用各自的后端实现。Lua 只做两件事:在 DM 里调用下游技能服务(根据 intent/slots 选技能、发请求),以及控制 DM 对话(多轮、填槽、反问、兜底)。每个领域/意图对应一段 Lua,负责「该不该追问、调哪个技能、返回什么话术」。优点:Lua 热更新、不用发版即可改对话逻辑和路由,技能服务可独立部署迭代。

Lua 脚本长什么样?下面是一个精简但完整的示例(接口名和字段依实际平台可能略有差异,逻辑一致):

-- 入口:收到 NLP 解析结果后由 DM 调用
function on_intent(domain, intent, slots, session)
    -- 按领域分流
    if domain == "music" then
        return handle_music(intent, slots, session)
    end
    if domain == "weather" then
        return handle_weather(intent, slots, session)
    end
    return { type = "fallback", text = "暂不支持该功能" }
end

-- 音乐领域:多意图 + 缺槽反问
function handle_music(intent, slots, session)
    if intent == "play_music" or intent == "search_music" then
        -- 缺歌名时可追问(多轮填槽)
        if not slots.song and not slots.singer then
            return { type = "ask_slot", slot = "song", text = "您想听哪首歌?" }
        end
        -- 决定用哪个音乐技能(多音乐服务选择可在这里或 skill_router 里做)
        return skill_router.route("music", slots)
    end
    if intent == "pause_music" then
        return skill_router.route("music", { action = "pause" })
    end
    return { type = "fallback", text = "没听懂,再说一次?" }
end

-- 天气领域
function handle_weather(intent, slots, session)
    if intent == "query_weather" then
        if not slots.city then
            return { type = "ask_slot", slot = "city", text = "您要查哪个城市的天气?" }
        end
        return skill_router.route("weather", slots)
    end
    return { type = "fallback", text = "暂不支持该天气指令" }
end

说明

  • 入参domainintentslots(表)、session(会话),来自 NLP + 网关后的 DM。
  • 按 domain / intent 分支:不同意图走不同逻辑,或调不同技能。
  • 缺槽时:返回 ask_slot,DM 会做多轮追问(例如「您想听哪首歌?」),下一轮用户补全后再进同一段 Lua。
  • 调技能skill_router.route("music", slots) 表示调用下游的音乐技能服务,并带上槽位;具体用哪个音乐服务(网易云/QQ 音乐等)由技能路由层按设备配置/用户显式指定等决定。技能本身是独立服务,不是 Lua 写的。
  • 兜底:无法匹配时返回 fallback,由 DM 播报默认话术。

5.3 对话流管理界面(平台示意)

这是 传统机器人 里配置多轮对话的界面,核心是对话流(Workflow),也就是 DM(对话管理)的可视化配置方式

下图说明(猎户开发者平台「对话流管理」— 音量多轮)
下图为猎户开发者平台中 NLP 配置 → 对话流管理 的配置界面,示例为一条名为「音量多轮」的对话流(标题:音量的多轮对话)。图中元素对应关系如下:

图中元素含义
START对话流入口。
触发单元(蓝)触发条件,通常用 domain + intent 限制(如 domain=volume, intent=adjust),即 NLU 输出的结构化结果;用户说「调音量」等会命中此处,进入该对话流。
回复单元(绿)多轮对话中机器的回复,可配置「音量已调大」「还需要再调吗?」等话术。
「再大点」「再小点」分支上的再次触发:用户在同一对话流中继续说「再大点」「再小点」,会命中对应分支,跳转到新的回复单元,形成多轮调节。

左侧「插入对话单元」提供触发单元(蓝)、功能单元(橙)、回复单元(绿);画布上通过拖拽、连线构成 START → 触发单元 → 回复单元 → 分支(再大点/再小点)→ 更多回复单元 的流程图。该流程图即 DM 多轮逻辑的可视化,背后由 Go + Lua 执行。

  • 触发单元:定义这条对话流什么时候被激活,通常是绑定一个 domain + intent(例如 domain=volume, intent=adjust),对应的就是 NLU 输出的结构化结果
  • 回复单元:配置机器在不同节点下的回复话术,例如「音量已调大」「还需要再调吗?」。
  • 分支节点(如「再大点」「再小点」):根据用户后续输入的意图决定对话跳转方向;这部分逻辑在实现上是用 Go 解析 Lua 脚本驱动的状态机。

一句话对话流 = 可视化的 Lua 脚本,用来定义多轮对话的跳转逻辑和回复规则。

左侧可插入触发单元(蓝)、功能单元(橙)、回复单元(绿)。典型画布:START → 触发单元(domain+intent)→ 回复单元 → 分支(再大点/再小点)→ 更多回复单元

5.4 对话流的本质:状态机

DM = 状态机对话流 = 状态机的可视化配置Lua 脚本 = 状态机的执行逻辑。传统机器人的 DM 就是用这种状态机模型,通过 Go 框架调度 Lua 脚本,来管理多轮对话的上下文和流转。

以披萨订单流程图为例,和你在平台里配的「音量多轮」对话流是同一类东西

  • Edit Order 相当于对话的核心状态,用户在这个状态下可以执行「Add Pizza」「Add Drink」等操作。
  • 每一个箭头和分支,都对应对话流里的一个跳转条件(如用户说「再大点」→ 走某一分支的回复单元)。

一句话DM = 状态机,对话流 = 状态机的可视化配置,Lua 脚本 = 状态机的执行逻辑。

其他例子:订餐场景 开始→编辑订单↔(加饮料/加披萨/删除)→确认;音量多轮 触发→回复单元→用户「再大点」/「再小点」→再进回复单元,形成循环直到满意或超时。

完整闭环示例(音量调大)

  • 用户说:「把音量再调大点」
  • NLU:通过词典 + 文法,拆解成 domain=volume, intent=increase, slots={value: +2}(或类似)
  • DM:根据 NLU 结果触发「音量多轮」对话流,进入对应状态节点,执行 Lua 脚本逻辑
  • 输出:DM 调用回复单元,生成「好的,音量已调大」的回复,并等待用户下一轮输入

这就是 NLU → DM → 输出 的完整闭环:NLU 产出结构化结果,DM 用对话流/Lua 决定状态与回复,再经 TTS 播报。

5.5 多轮对话完整流程(在链路中的体现)

多轮对话不是「另一条链路」,而是同一套链路多轮执行,由 DM 用 session 维护上下文,配合 Lua/对话流 的「缺槽追问、槽满执行」逻辑实现。下面用「听音乐」缺歌名为例,把多轮完整走一遍。

第 1 轮

步骤说明
用户说「我想听歌」(没说歌名/歌手)
ASR转成文本
NLP分词 → 词典 → 文法,得到 domain=music, intent=play_music, slots={}(song/singer 都缺)
Launcher分到「音乐」场景
DM带上 session(当前可为空或已有历史)把 (domain, intent, slots) 交给 Lua
Lua发现 slots.songslots.singer 都没有 → 返回 { type = "ask_slot", slot = "song", text = "您想听哪首歌?" }不调技能
DM把追问话术交给 TTS 播报,并在 session 里记下:当前意图=play_music、已填 slots=空、等待补 song
用户听到「您想听哪首歌?」

第 2 轮

步骤说明
用户说「七里香」
ASR → NLP得到 domain=music, intent=play_music(或仅识别出 song=七里香,DM 用 session 补全 intent)
DMsession 取出上一轮的 intent 和已有 slots,把本轮新解析的 slot(如 song=七里香)合并进去,得到 slots 齐了(或仍缺 singer,可再追问)
Lua发现 slots 已够 → skill_router.route("music", slots)调下游音乐技能
技能拉歌、播放;结果回传 DM
DM汇总回复 → TTS 播报(如「好的,正在播放七里香」),多轮结束

流程小结(多轮时怎么循环)

用户第 N 句
    → ASR → NLP → 网关 → DM(带上 session)
    → Lua / 对话流 判断:槽位齐了吗?
        → 否:返回追问话术 → TTS 播报 → 写 session → 等用户第 N+1 句(再走一遍上面)
        → 是:调技能 → 结果 → TTS 播报 → 清/更新 session,本话题结束

和对话流配置的关系:平台里「触发单元」用 domain/intent 选中的对话流,在多轮里会一直生效——同一对话流内的回复单元分支决定每一轮机器说什么(追问还是执行结果);功能单元可在某一步调接口。session 由 DM 维护,保证「上一轮填了哪些槽、当前在哪个对话流」被下一轮复用。

6)技能路由 Skill Router + 技能是怎么选出来的?

NLP Launcher 负责按 domain/intent 把请求分到哪个场景(见上文「NLP Launcher 是干啥的?」);技能路由负责在同一场景内选出唯一一个要调用的技能(如音乐场景下选网易云还是 QQ 音乐)。

这一层就是你问的:多个音乐服务(或同领域多个技能),怎么选?

选择步骤(顺序可配置,一般如下):

  1. 按 domain 筛到一大类:例如 domain=music → 只考虑「音乐」下的所有技能,不会跑到天气、视频去。
  2. 按设备/渠道配置:这台设备或这个客户绑定了「默认音乐服务 = 网易云」→ 就只在该设备上用网易云技能。
  3. 按用户显式指定:用户说「用 QQ 音乐播放」→ 槽位或意图里带了「QQ 音乐」→ 直接选 QQ 音乐技能,覆盖默认。
  4. 按意图与置信度:若同一句命中多个意图/多个技能,按平台配置的意图优先级置信度排序,取第一个。

输出:唯一确定的「哪个技能 + 版本 + 环境」。

flowchart LR
    A[NLP 输出<br/>domain / intent / slots] --> B{domain?}
    B -->|music| C[音乐类技能候选]
    B -->|video| D[视频类技能候选]
    B -->|其他| E[其他场景]
    C --> F{设备/渠道默认?}
    F --> G{用户显式指定?}
    G --> H[按优先级/置信度选一个]
    H --> I[选定技能]

路由策略小结:domain 优先 → 设备/渠道配置 → 用户显式指定 → 意图匹配与置信度;最终只选出一个技能去执行。

7)技能 Skill(下游技能服务,非 Lua 实现)

猎户技能体系里,具体技能是独立的下游服务,不是用 Lua 实现的;由 DM 里的 Lua 调用这些技能服务。

  • 每个技能是独立服务:音乐技能、天气技能、扫地技能、扫地机器人技能…(各自后端实现,通过网关被 DM/Lua 调用)
  • 技能内部:校验槽位、调用第三方 API、拼接返回结果、构造回复话术,结果回传给 DM,再由 DM 控制后续对话或 TTS

7.1 多音乐服务怎么选?(你最关心的)

用户说「播放周杰伦」
   ↓
NLP 输出 intent: play_musicDM 看:
   设备配置 → 默认音乐服务是 A
   或 白名单/客户配置 → 只能用 B
   或 显式指令「网易云」→ 用 CSkillRouter 选择对应音乐技能
   ↓
音乐技能去拉歌、播放、控制

选择不是在 NLP,是在 DM + SkillRouter + 设备配置。

8)TTS 语音合成

  • 技能 / DM 返回文本
  • TTS 转音频
  • 回传给前端播放

文档来源


附图与章节对应(结合你提供的几张图)

内容对应文档位置
对话流管理界面传统机器人 对话流管理页:左侧「文法配置」「词典配置」「对话流管理」;画布上 START → 触发单元(domain=XXX, intent=XXX)→ 回复单元,分支「再大点」「再小点」等5.3 对话流管理界面先弄清:文法/词典
NLP 输出层级(音乐)domain 音乐 → intents 搜索音乐/暂停音乐/播放音乐 → slots(singer:陈粒, name:七里香, …)4.3 / 4.4 NLP 最终输出结构技能选择用 domain/intent/slots 作输入
订餐/状态机流程图开始 → 编辑订单 ↔ 加饮料/删除/加披萨(厚底/薄底)→ 确认5.4 对话流的本质:状态机,多轮可回环、状态转移的类比

三张图与完整闭环(总结)

第一张图:对话流管理(DM 层)
传统机器人 里配置多轮对话的界面,核心是对话流(Workflow),即 DM 的可视化配置。触发单元绑定 domain+intent(NLU 的结构化结果);回复单元配置各节点话术(如「音量已调大」「还需要再调吗?」);分支节点(如「再大点」「再小点」)根据用户后续意图跳转,逻辑由 Go 解析 Lua 脚本实现的状态机驱动。
对话流 = 可视化的 Lua 脚本,定义多轮对话的跳转逻辑和回复规则。

第二张图:状态机示例(DM 本质)
披萨订单流程图和「音量多轮」对话流是同一类东西:Edit Order = 对话核心状态,箭头与分支 = 对话流里的跳转条件。猎户 DM 用状态机模型,通过 Go 框架调度 Lua 脚本管理多轮对话的上下文和流转。
DM = 状态机,对话流 = 状态机的可视化配置,Lua 脚本 = 状态机的执行逻辑。

第三张图:NLU 语义框架(NLU 本质)
Domain(领域)= 最高层业务分类(音乐、天气、音量);Intent(意图)= 领域下的具体动作(播放音乐、暂停音乐);Slot(槽位)= 执行意图所需的参数(歌手、歌曲名)。文法规则定义 domain+intent 的匹配模板,词典配置填充 slot。
NLU = 把用户输入拆解成 domain + intent + slot 的规则引擎。

三者关系(完整闭环)

  • NLU:词典 + 文法把用户说的「把音量再调大点」拆成 domain=volume, intent=increase, slot={value: +2}
  • DM:根据 NLU 结果触发「音量多轮」对话流,进入对应状态节点,执行 Lua 逻辑。
  • 输出:DM 调用回复单元生成「好的,音量已调大」,经 TTS 播报,并等待用户下一轮输入。