# intelligentrouting **Repository Path**: akinggw/intelligentrouting ## Basic Information - **Project Name**: intelligentrouting - **Description**: `intelligentrouting` 是一个**离线、可嵌入、跨平台**的中文 NLU 引擎,以 C++17 静态库形式提供。它不依赖任何在线服务,所有推理(分词、向量嵌入、命令匹配)均在本地完成,适合嵌入到桌面端、边缘端或对隐私/时延敏感的业务系统中做"自然语言 → 结构化命令"的转换层。 核心思路是**"嵌入相似度匹配 + 语义角色规则抽取 + 置信度分层决策"**三段式: 1. 用 - **Primary Language**: C++ - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-21 - **Last Updated**: 2026-09-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # intelligentrouting(智能路由 · NLU 自然语言理解引擎) > 一个面向中文的**自然语言理解(NLU, Natural Language Understanding)**静态库:把一句中文文本解析成结构化命令,匹配标准命令库、抽取槽位参数、打置信度分,并可执行绑定的 Lua 脚本生成业务结果。 - 语言:C++17 - 构建:qmake(Qt 5.15 工具链,但**不依赖 Qt 库**,`CONFIG -= qt`) - 命名空间:`nlu`,类名 PascalCase 带 `C` 前缀(如 `CNluPipeline`、`CBgeEmbedder`) - 许可证:木兰宽松许可证 v2(Mulan PSL v2) --- ## 一、这是一个什么系统? `intelligentrouting` 是一个**离线、可嵌入、跨平台**的中文 NLU 引擎,以 C++17 静态库形式提供。它不依赖任何在线服务,所有推理(分词、向量嵌入、命令匹配)均在本地完成,适合嵌入到桌面端、边缘端或对隐私/时延敏感的业务系统中做"自然语言 → 结构化命令"的转换层。 核心思路是**"嵌入相似度匹配 + 语义角色规则抽取 + 置信度分层决策"**三段式: 1. 用 BGE ONNX 量化模型把文本转向量,与标准命令库的预计算嵌入做余弦相似度匹配; 2. 用 cppjieba 分词 + 业务词典做语义角色标注(TIME/PLACE/ACTION/TARGETS/PERSON),再按角色连续片段抽取槽位; 3. 用 `confidence = 0.6 * similarity + 0.4 * ruleMatchRatio` 打分,高置信直出、中置信合并、低置信回退 LLM; 4. 命中后通过 `ScriptHandler` 回调执行命令绑定的 Lua 脚本,生成最终业务结果。 --- ## 二、能干什么? | 能力 | 说明 | |------|------| | 中文分词 + 词性标注 | 基于 cppjieba(主词典 / HMM / 用户词典) | | 业务领域语义角色标注 | 通过 `business_dict.json` 把"明天/工地/搬砖/小王"等词映射到 TIME/PLACE/TARGETS/PERSON | | 文本向量化嵌入 | BGE INT8 量化 ONNX 模型,512 维,均值池化 + L2 归一化 | | 标准命令库匹配 | 余弦相似度(归一化后即点积),取最优命令 | | 规则参数提取 | 按语义角色把相邻同角色的词合并为槽位片段 | | 置信度评分与分层决策 | 高置信直出 / 中置信合并 / 低置信回退 LLM | | Lua 脚本执行 | 命令命中后执行对应 Lua 函数生成业务结果(通过回调解耦,核心库不绑定 Lua) | | 批量多线程处理 | `processBatch()` 支持多线程工作池 | **典型场景**:输入 `"明天上午10点让小王到工地上搬砖,别迟到了。"`,系统输出 - 匹配命令 `cmd_assign_task` - 参数:时间=`明天上午10点`、谁=`小王`、目标=`搬砖` - Lua 结果:`"已安排小王在明天上午10点去搬砖,请勿迟到。"` --- ## 三、架构是怎么样的? ### 3.1 目录结构 ``` intelligentrouting/ ├── intelligentrouting.pro # 顶层工程(subdirs:srcs + test) ├── build.bat # Windows 一键构建(vcvars64 + qmake + nmake) ├── includes/ # 公共头文件(库对外接口) │ ├── NluTypes.h # 核心数据类型 │ ├── NluPipeline.h # 流水线门面类 CNluPipeline │ ├── BusinessDict.h # 业务词典 CBusinessDict │ ├── JiebaTokenizer.h # 结巴分词器 CJiebaTokenizer │ ├── BertTokenizer.h # BERT WordPiece 分词器 CBertTokenizer │ ├── BgeEmbedder.h # BGE 嵌入器 CBgeEmbedder │ ├── CommandLibrary.h # 标准命令库 CCommandLibrary │ ├── RuleExtractor.h # 规则提取器 CRuleExtractor │ ├── ConfidenceScorer.h # 置信度评分器 CConfidenceScorer │ └── Logger.h # 日志器 CLogger(单例) ├── srcs/ # 源文件实现(编译为 intelligentrouting.lib 静态库) ├── test/ # 测试/示例程序(含 Lua 脚本引擎封装) │ ├── main.cpp # 测试入口(4 个示例 + 交互模式) │ └── LuaScriptEngine.h/.cpp # CLuaScriptEngine(LuaBridge + Lua 5.4.7) ├── commons/ # 公共工具头(signal.hpp / json.hpp) ├── third_party/ # 第三方库(cppjieba/onnxruntime/luabridge/log4z/...) └── bin/ # 构建产物 + 运行时配置/资源 ├── intelligentrouting.lib # 静态库 ├── nlu_test.exe # 测试程序 ├── onnxruntime.dll # ONNX Runtime 动态库 ├── configs/ # 配置文件(见 §五) └── resources/ # 模型与词典(bge/、cppjieba/) ``` ### 3.2 核心流水线(门面模式) `CNluPipeline` 是整个系统的统一入口,内部组合 6 个子组件: ``` ┌──────────────────────────────────────────────┐ │ CNluPipeline │ │ │ 输入文本 ───► │ ┌───────────────┐ ┌──────────────────┐ │ (std::async 并行) │ │ CJiebaTokenizer│ │ CBgeEmbedder │ │ │ │ +CBusinessDict │ │ +CBertTokenizer │ │ │ └──────┬────────┘ └────────┬─────────┘ │ │ │ TaggedWord[] │ float[] 嵌入 │ │ ▼ ▼ │ │ ┌───────────────────────────────────────┐ │ 余弦相似度匹配 │ │ CCommandLibrary.match() │ │ │ └──────────────────┬────────────────────┘ │ │ ▼ │ │ ┌───────────────────────────────────────┐ │ 按角色抽取槽位 │ │ CRuleExtractor.extract() │ │ │ └──────────────────┬────────────────────┘ │ │ ▼ │ │ ┌───────────────────────────────────────┐ │ 置信度评分 │ │ CConfidenceScorer.calc() │ │ │ └──────────────────┬────────────────────┘ │ │ ▼ │ │ ┌───────────────────────────────────────┐ │ 执行命令 Lua 脚本 │ │ ScriptHandler (Lua 回调) │ │ │ └──────────────────┬────────────────────┘ │ │ ▼ │ NluResult ◄── │ 结构化结果 │ └──────────────────────────────────────────────┘ ``` ### 3.3 处理流程(`process()`) 1. **并行启动**:`std::async` 同时执行分词标注和文本嵌入; 2. **命令匹配**:`CCommandLibrary::match()` 计算输入嵌入与命令库预计算嵌入的余弦相似度,取最优; 3. **阈值过滤**:相似度 < `SIMILARITY_MIN_THRESHOLD`(0.50) 判定为不支持命令; 4. **规则提取**:`CRuleExtractor::extract()` 将相邻同语义角色的词合并为片段; 5. **置信度计算**:`confidence = 0.6 * similarity + 0.4 * ruleMatchRatio`; 6. **分层决策**: - `confidence >= 0.78`:高置信,直接使用第一层结果; - `0.55 <= confidence < 0.78`:中置信,合并处理; - `confidence < 0.55`:低置信,回退到 LLM(返回错误信息提示); 7. **参数填充**:将抽取的角色片段填入命令参数,校验必填项; 8. **Lua 执行**:通过 `ScriptHandler` 回调执行命令绑定的 Lua 函数,生成最终业务结果。 ### 3.4 核心数据类型(`NluTypes.h`) ```cpp enum SemanticRole { ROLE_UNKNOWN, ROLE_TIME, ROLE_PLACE, ROLE_ACTION, ROLE_TARGETS, ROLE_PERSON, ROLE_OTHER }; struct TaggedWord { std::string word; std::string posTag; SemanticRole role; }; struct CommandParam { std::string name; std::string key; bool required; SemanticRole role; std::string value; }; struct StandardCommand { std::string id; std::vector texts; std::vector params; std::string luaScript; std::string luaFunction; std::vector> embeddings; }; struct NluResult { bool matched; std::string commandId; double confidence; double similarity; std::vector params; std::string luaResult; std::string errorMessage; std::vector taggedWords; }; ``` ### 3.5 关键常量 | 常量 | 值 | 含义 | |------|----|------| | `SIMILARITY_MIN_THRESHOLD` | 0.50 | 相似度最低阈值 | | `CONFIDENCE_HIGH_THRESHOLD` | 0.78 | 高置信阈值(直出) | | `CONFIDENCE_LOW_THRESHOLD` | 0.55 | 低置信阈值(回退 LLM) | | `SIMILARITY_WEIGHT` / `RULE_WEIGHT` | 0.6 / 0.4 | 置信度权重 | | `BGE_DEFAULT_MAX_SEQ_LEN` | 128 | BGE 最大序列长度 | | `BGE_HIDDEN_SIZE` | 512 | BGE 嵌入维度 | ### 3.6 设计要点 - **不依赖 Qt**:虽然用 qmake 构建,但 `CONFIG -= qt`、`QT -= core gui`,纯 C++17 实现,便于嵌入其他项目。 - **Lua 解耦**:核心静态库不包含 Lua,Lua 引擎在 `test/` 中,通过 `ScriptHandler` 回调解耦,集成方可自行替换为其他脚本引擎。 - **线程安全**:`CJiebaTokenizer`、`CLuaScriptEngine` 用 `std::mutex` 保护;`process()` 用 `std::async` 并行;`processBatch()` 支持多线程工作池。 - **性能优化**:BGE 用 INT8 量化(`model_qint8.onnx`);命令嵌入预计算并缓存到 JSON;ONNX Session 设置 `SetIntraOpNumThreads(1)` + 全图优化。 - **嵌入归一化**:`CBgeEmbedder::embed()` 均值池化后 L2 归一化,因此 `CCommandLibrary::match()` 中余弦相似度退化为点积。 - **跨平台**:`#ifdef _WIN32` 处理路径分隔符和宽字符;`.pro` 同时配置 win32 / unix 选项。 --- ## 四、使用例子 ### 4.1 构建与运行 **Windows 一键构建**(`build.bat`,使用前需修改其中硬编码的 Qt 路径): ```bat call vcvars64.bat set PATH=%PATH%;\bin qmake intelligentrouting.pro -spec win32-msvc nmake release ``` **通用构建**: ```bash qmake intelligentrouting.pro make release # Linux: make;Windows: nmake release ``` 构建产物:`bin/intelligentrouting.lib`(静态库)、`bin/nlu_test.exe`(测试程序)、`bin/onnxruntime.dll`(运行时依赖)。 **运行测试程序**: ```bash bin\nlu_test.exe # 执行 4 个内置示例 bin\nlu_test.exe interactive # 交互模式,输入 quit/exit 退出 ``` ### 4.2 在你自己的项目中集成 ```cpp #include "NluPipeline.h" #include "LuaScriptEngine.h" // test/ 中的示例封装,可替换 #include "Logger.h" // 1. 初始化日志 nlu::CLogger::getInstance().init("log", nlu::CLogger::LV_INFO); // 2. 创建流水线和 Lua 引擎 nlu::CNluPipeline pipeline; nlu::CLuaScriptEngine luaEngine; luaEngine.init(); // 3. 注入 Lua 脚本处理器(通过回调解耦,核心库不绑定 Lua) pipeline.setScriptHandler( [&luaEngine](const std::string& script, const std::string& funcName, const std::map& params, std::string& error) -> std::string { return luaEngine.executeScript(script, funcName, params, error); }); // 4. 初始化流水线(加载配置和模型) pipeline.init(configDir, resourceDir); // 5. 处理自然语言 nlu::NluResult result = pipeline.process("明天上午10点让小王到工地上搬砖,别迟到了。"); // result.matched -> true // result.commandId -> "cmd_assign_task" // result.similarity -> 0.8x // result.confidence -> 0.7x // result.params -> [{time,"明天上午10点"}, {person,"小王"}, {target,"搬砖"}] // result.luaResult -> "已安排小王在明天上午10点去搬砖,请勿迟到。" // 6. 批量多线程处理 std::vector inputs = { /* ... */ }; std::vector results = pipeline.processBatch(inputs, 4); ``` ### 4.3 内置测试输入 | # | 输入 | 预期 | |---|------|------| | 1 | 明天上午10点让小王到工地上搬砖,别迟到了。 | 命中 `cmd_assign_task` | | 2 | 后天下午3点让小李到现场施工。 | 命中 `cmd_assign_task` | | 3 | 今天下午在会议室开会。 | 命中 `cmd_arrange_meeting` | | 4 | 帮我订一张机票。 | 不在命令库中,测试未匹配场景 | --- ## 五、配置文件 所有配置位于 `bin/configs/`,资源位于 `bin/resources/`。 ### 5.1 `business_dict.json`(业务词典) 将业务领域词汇映射到语义角色,用于增强分词后的角色标注: ```json { "version": "1.0", "entries": [ {"word": "明天", "role": "TIME"}, {"word": "工地", "role": "PLACE"}, {"word": "搬砖", "role": "TARGETS"}, {"word": "小王", "role": "PERSON"} ] } ``` ### 5.2 `standard_commands.json`(标准命令库) 每条命令包含多种表达方式、参数定义和 Lua 处理脚本: ```json { "commands": [ { "id": "cmd_assign_task", "texts": ["明天上午10点让小王到工地上搬砖,别迟到了。", "..."], "params": [ {"name": "时间", "key": "time", "required": true, "role": "TIME"}, {"name": "谁", "key": "person", "required": true, "role": "PERSON"}, {"name": "目标", "key": "target", "required": true, "role": "TARGETS"} ], "luaScript": "function handle_command(params) ... end", "luaFunction": "handle_command" } ] } ``` 内置 3 条命令:`cmd_assign_task`(派工)、`cmd_arrange_meeting`(安排会议)、`cmd_go_to_work`(去现场施工)。 ### 5.3 `command_embeddings.json`(嵌入缓存) 命令库所有 `texts` 的预计算嵌入向量缓存,避免每次启动都跑一遍 ONNX 推理。首次启动若不存在则自动计算并保存。 ### 5.4 资源文件 - `bin/resources/bge/model_qint8.onnx`:BGE INT8 量化模型(约 22.8 MB,512 维) - `bin/resources/bge/vocab.txt`:BERT 词表 - `bin/resources/cppjieba/*`:结巴分词词典(主词典 / HMM / 用户词典 / IDF / 停用词) --- ## 六、第三方依赖 | 依赖 | 用途 | 位置 | |------|------|------| | cppjieba | 中文分词与词性标注 | `third_party/cppjieba/` | | onnxruntime | ONNX 模型推理引擎 | `third_party/onnxruntime/` | | nlohmann/json | JSON 解析(单头文件) | `commons/json.hpp` | | log4z | 异步日志库 | `third_party/log4z/` | | sigslot | 信号槽(日志事件回调) | `commons/signal.hpp` | | luabridge + Lua 5.4.7 | Lua 脚本引擎(仅测试程序) | `third_party/luabridge/` | | tinyxml2 | XML 解析(已引入,核心未使用) | `third_party/tinyxml2/` | | threadpool | 线程池(已引入,核心用 std::thread 自实现) | `third_party/threadpool/` |