# RAG-ISS
**Repository Path**: dark2000/RAG-ISS
## Basic Information
- **Project Name**: RAG-ISS
- **Description**: 基于 **RAG(检索增强生成)** 架构的企业智能问答系统,集成**阿里百炼大模型**作为推理引擎,支持自然语言查询数据库、知识库检索问答、联网搜索三大核心功能。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-05-29
- **Last Updated**: 2026-07-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# RAG-ISS 智能分析师系统
## 一、时代困境:AI 时代的新挑战
数据资产「裸奔」:传统 RBAC 权限管得住菜单,却难以约束 AI 自由组合数据表、跨域查询的能力,企业核心数据访问边界模糊,如同「裸奔」一般,面临极高的泄露与合规风险。
知识资产「沉睡」:制度文档、项目资料与业务经验散落各处,形成信息孤岛。AI 虽能读取文字,却无法理解知识间的内在逻辑与业务关联,海量隐性价值被埋没,难以转化为生产力。
分析决策「迟缓」:传统 BI 分析流程冗长,从需求提报到报表产出耗时漫长。面对 AI 时代瞬息万变的市场,这种低效模式无法支撑实时化、敏捷化的业务决策,直接导致企业错失市场先机。
核心痛点总结:AI 技术的爆发式应用与传统管理体系的滞后形成鲜明矛盾,数据安全失控、知识价值沉淀难、决策响应缓慢三大困境叠加,成为阻碍企业数字化转型与高效发展的核心壁垒。
## 为什么需要这个系统?
企业每天产生海量的数据和文档,但真正能被利用的却寥寥无几——数据散落在各个业务系统,知识沉淀在员工脑中,报表依赖人工逐一编写。**RAG-ISS 智能分析师** 正是为解决这些问题而生:它像一个永不疲倦的数字助手,帮你把企业数据、文档、知识图谱串联起来,让每一个业务问题都能在几秒钟内得到有据可依的答案。
V2.0 在此基础上实现了从「功能工具」到「数智底座」的跨越,构建平台化、智能化、生态化、安全化的四层核心架构体系,打破数据孤岛与技术壁垒,让 AI 能力与业务场景深度融合,为企业决策提供全链路的智能支撑。它不再只是一个工具,而是一套从数据汇集、知识沉淀到智能决策的完整闭环架构,为企业的长期智能化升级提供可继承、可扩展的能力底座,支撑业务的持续创新与增长。

> V2.0 架构设计理念采用清晰的四层分层解耦设计,实现系统高内聚、低耦合特性。从用户交互到数据底座层层递进,确保系统的稳定性、灵活性与易扩展性,支撑业务快速迭代。
**核心引擎层(插件化智能中枢)**:统一管控身份权限(数智权限与智能洞察),多维度解析业务数据;RAG 增强检索与 NL2SQL 自然语言查询并行支撑;独立引擎模块可灵活插拔,快速适配新业务场景。
**前后端与数据层高效联动**:前端基于 Vue3 + Element Plus 提供统一交互入口,支持 SSE 流式输出;FastAPI 作为中枢提供高并发接口服务;底层兼容多源数据存储,满足多样化的检索与持久化需求,实现端到端的高效协同。
基于这一分层架构,V2.0 从四个维度构建核心能力:
- **平台化架构**:插件式能力引擎设计,支持功能模块的无限扩展与灵活插拔,快速适配企业不同发展阶段的业务与数智需求。
- **智能化内核**:搭载创新能力路由引擎,让系统从被动「答题器」进化为主动「决策伙伴」,精准匹配业务场景的深层智能需求。
- **生态化对接**:原生适配 MCP 协议标准,打通内外 AI 生态壁垒,实现多模型能力即插即用,快速聚合行业领先的智能能力。
- **安全化保障**:构建革命性数智权限体系,从架构层实现数据访问、模型调用的全链路管控,确保企业 AI 应用安全合规、可控可信。
---
## 它能解决什么问题
### 🧠 让企业知识不再「沉睡」
企业积累了大量的制度文档、项目资料、技术手册,但查阅效率低下,知识孤岛严重。本系统内置 **知识图谱方法论**,以「业务域 → 实体 → 属性 → 关系 → 文档」五要素 + 编码命名空间(`entity_code`)构建企业级知识网络:由管理员按业务边界组织知识树、将文档挂接到实体、并策展实体间关系,新建节点时由 LLM 辅助推荐语义编码;海量场景下更支持从业务系统程序化批量建树、以可视化图谱持续补连关联——将各业务领域、知识实体、关联文档串联起来,让知识从「死文档」变成可检索、可关联的活资产。
- **知识库问答**:员工可以直接问「公司的考勤制度是什么?」「这个项目的延期风险有哪些?」——系统从知识库中精准定位答案,并附上原文来源
- **知识图谱**:通过知识图谱的可视化展示,发现原本孤立的文档之间隐藏的联系,挖掘更大的知识价值
### 🗄️ 让数据资产「开口说话」
企业数据中台沉淀了大量业务数据,但业务人员不懂 SQL,技术人员不懂业务,数据价值难以释放。本系统依托 **智能问数** 和 **智能报表** 两大能力,让数据资产的价值真正被挖掘出来。
- **智能问数**:业务人员直接用自然语言提问——「上个月哪个区域的销售额最高?」——系统自动生成 SQL、查询数据库、并用表格和图表展示结果。系统还会在信息不足时主动追问,引导你补充条件
- **智能报表**:面对「做个销售综合看板」这样的复杂需求,系统自动拆解为多个子查询并行执行,实时生成分析报告,再自动设计可视化看板——过去需要分析师花半天完成的工作,现在几分钟即可。**数智权限体系**确保每个用户只能看到权限范围内的数据
### 🤖 让每个员工都有一个「智能办公助手」
数智分析师不仅仅是一个问答工具,更是每个员工的智能工作伴侣:
- **能力路由引擎**:插件式架构,各能力引擎独立封装。LLM 作为决策器,引导式选择最合适的能力,置信度不足时主动追问
- **多模态理解**:除了文字,还能「看懂」图片——上传一张产品截图问「这个产品的市场定位是什么?」系统都能理解
- **联网搜索**:当知识库中没有答案时,自动联网获取最新信息,确保回答的时效性
- **多端支持**:配套移动端应用,扫码即用,随时随地随身
- **完善后台**:RBAC 权限体系 + 员工管理 + 能力配置 + 域管理 + 系统监控,企业级管控能力
---
## 核心功能展示
### 1. 数智分析师——你的全能办公助手
统一对话入口,智能识别你的意图,自动调用最合适的能力:
- **💬 智能问答**:想知道制度文档?直接问
- **📊 智能问数**:想查数据?直接问,SQL 交给系统
- **📈 智能报表**:想看分析结论?直接说,报告和看板自动生成
- **🧠 知识图谱**:想了解知识关联?系统帮你理清脉络
- **🌐 联网搜索**:需要最新资讯?自动联网获取
- **🖼️ 多模态分析**:有图片要说?一起上传,图文理解

### 2. 智能问数——不懂 SQL 也能查数据
业务人员最头疼的问题——想看数据但不会写 SQL,现在一句话就能搞定:
- 说「各区域销售额排名」,系统自动匹配数据表、生成 SQL、执行查询、展示结果
- 支持复杂查询:多表关联、聚合统计、时间维度分析
- 查询结果图文并茂,表格 + 图表一目了然
#### 会话意图引导与追问模式
用户提问时,系统智能判断是"新话题"还是"继续追问":
| 判定 | 场景 | 示例 |
|------|------|------|
| **new(新话题)** | 用户提到新业务对象 | 上轮查"商机",本轮查"项目" |
| **continue(继续)** | 追加过滤条件 | 上轮查"商机统计",本轮"只看东区" |
| **ambiguous(模糊)** | 信息不完整 | 用户只说"帮我看看数据",系统反问需要哪些条件 |
追问场景下,系统会在对话界面展示三种状态提示:
- **纯追问**(无已识别数据):❓ 橙色横幅 + 追问引导
- **部分信息 + 追问**:已识别的维度/指标 + 虚线提示"信息不完整"
- **全部为空**:显示"正在识别查询意图..."
判定依据:系统记录每轮查询的 `tables_used`(LLM 返回的 SQL 中使用的表名),在下一轮构造历史时带上实体信息,让 LLM 有足够的上下文判断话题是否切换。

### 3. 知识库问答——企业知识随手可得
把制度文档、项目资料、技术手册都交给系统,员工随时问、随时答:
- 混合检索确保精准:向量语义 + 关键词 + 排序模型三级把关
- 每次回答都标注知识来源,点击即可查看原文,方便溯源
- 支持 PDF、Word、Excel、PPT、Markdown 等多种文档格式

查看知识来源参考:

企业知识图谱可视化展示:

### 4. 智能报表——让数据自己「讲故事」
还在手动做报表?试试一句话生成分析报告:
- 说「做一个销售综合看板」,系统自动分析意图、拆解为多个子查询、并行执行
- 报告文本实时流式输出——边生成边看,不用等待
- 看板后台异步构建,报告先出来,图表随后自动呈现
#### 报表权限注入
报表场景的权限管控面临一个独特挑战:LLM 需要在尚不知道子查询具体内容的情况下,提前做出权限合规的规划决策。为此,系统设计了一种**前瞻性权限约束机制**:
在意图分析的 Prompt 层,每张数据表被标注其**权限治理级别**——部门级表被声明为"作用域受限(需附加行级谓词)",个人级表被声明为"分析禁区(不可作为 GROUP BY 维度源)"。LLM 在子查询规划阶段即遵循这一约束语义,自动将分析维度从无权表迁移至其他等价维度。同时,约束具有**传递性**:通过 JOIN 关联表间接引用无权表的维度信息,同样在禁止之列,防止语义层面的权限逃逸。
这一机制的核心思想是——权限不再是被动的事后过滤,而是**嵌入 LLM 推理链路的事前约束**。
#### 看板智能拆分
当同一个数据集中同时存在"金额类"和"数量类"指标时(如"合同总额"与"合同数量"),看板方案生成时自动拆分为多个独立图表,避免量级差异过大导致小指标不可见:
- 前端 `getMergedCharts` 自动检测金额/数量语义冲突并拆分
- LLM 看板设计 prompt 明确要求金额和数量分开展示
- 兜底逻辑同样按语义分组,确保一致性
**报表文本输出:**

**完整分析报告(含流式生成过程):**

**可视化看板:**

### 5. 联网搜索——知识库之外的智能延伸
企业内部知识有限,但外部信息无限。当知识库中没有相关文档时,系统会自动联网搜索,获取最新、最权威的信息作为补充:
- 实时获取互联网最新资讯,回答时效性强的问题
- 搜索结果自动整理为结构化内容,便于阅读
- 可指定搜索范围或主题,聚焦特定领域信息
**使用场景示例:**
想了解「2026年美加墨世界杯最新战况」?系统会自动联网搜索并整理出赛事进展、积分榜、关键比赛等最新信息:

### 6. 多模态分析——不止能看懂文字
传统的问答系统只能处理文字,但现实工作中大量的信息以图片、图表、截图等形式存在。本系统支持 **多模态理解**,能够「看懂」图片内容并据此回答:
- 上传产品截图、流程图、数据图表、会议白板等图片
- 系统自动识别图片中的文字、图表、结构等信息
- 结合文字问题进行综合分析,给出完整答案
**使用场景示例:**
上传一张包含多个模型参数对比的图片,问「请分析」,系统能自动识别图片中的表格内容、对比维度,并给出结构化的分析结果:

### 7. 知识图谱管理——构建企业知识网络
不只管理文档,更管理文档之间的关系:
- 实体关系增删改、关联文档、树形目录浏览
- ECharts 力导向图直观展示实体间的关联网络
- 从知识中洞察业务关联,发现潜在价值

知识库实体管理(后台):

### 8. 强大的数智权限体系——基于 AI 的全新权限架构
#### 不止是权限控制,更是 AI 时代的安全底座
传统权限体系(RBAC)管得住菜单、管得住按钮,但管不住 AI。当大模型可以自由组合数据表、跨域查询、生成报告时,传统"一刀切"的权限模型彻底失灵——你无法在 SQL 生成前就预判 LLM 会访问哪张表、引用哪个字段。
RAG-ISS 的数智权限体系正是为此而生——它不是对传统 RBAC 的修补,而是**一套面向 AI 应用场景的原生权限架构**,从架构层面解决了"让 AI 既聪明又安全"的核心矛盾。这套体系贯穿企业智能业务的三大核心场景:**企业知识、企业数据、综合报告**,确保无论 LLM 调用哪个能力引擎,都在权限边界内运行。
#### 核心设计:让 LLM 在其位、谋其政
```
┌─────────────────────────────────────────────────────┐
│ 数智权限体系 │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐│
│ │ 企业知识 │ │ 企业数据 │ │ 综合报告 ││
│ │ (知识库/KG) │ │ (NL2SQL) │ │ (报表) ││
│ ├──────────────┤ ├──────────────┤ ├──────────────┤│
│ │ • 文档级权限 │ │ • 表级过滤 │ │ • 规划阶段注入 ││
│ │ • 实体级可见性 │ │ • 行级注入 │ │ • 执行阶段脱敏 ││
│ │ • 知识域隔离 │ │ • 列级脱敏 │ │ • 看板级隔离 ││
│ └──────────────┘ └──────────────┘ └──────────────┘│
│ │
│ 统一原则:角色驱动 → 权限合并 → 动态注入 → 执行兜底 │
└─────────────────────────────────────────────────────┘
```
#### 在企业知识上的应用
每位员工看到的"知识世界"是不同的:
- **文档级权限**:知识库中的制度文档、项目资料,按权限角色控制谁能查看、谁能检索,无权文档在检索阶段即被剔除,不会出现在 LLM 的回答来源中
- **实体级可见性**:知识图谱中的实体和关系,同样遵循权限规则。一位市场人员看到的客户关系图谱,与一位技术人员看到的系统架构图谱,都是各自权限范围内的内容
- **知识域隔离**:业务文档与人事文档天然隔离,市场人员不会意外看到薪酬数据,财务人员可以安全共享财务制度
#### 在企业数据上的应用
当 LLM 执行智能问数时,在 SQL 层面实现横向+纵向的双重管控:
- **横向(表级)**:用户无权访问的表,在进入 LLM 视野前就被过滤掉——LLM 根本"不知道"这张表的存在
- **纵向(行级)**:部门级权限自动注入 `WHERE perm_dept IN (...)`,个人级权限注入 `WHERE perm_emp = ?`,确保 LLM 生成的 SQL 天然受权限约束
- **列级**:敏感字段(如成本、利润率)告知 LLM 禁止 SELECT,执行后再做一次扫描脱敏,双重保障
#### 在综合报告上的应用
报表场景的权限管控比其他场景更复杂——因为它涉及"规划"和"执行"两个阶段:
- **规划阶段**:意图分析时,向 LLM 注入表格权限说明。个人级表(如 products)被明确标注"禁止作为分析维度",LLM 在拆分子查询时自动用其他维度替代
- **执行阶段**:每次子查询执行前注入行级条件,执行后对结果列做脱敏扫描
- **看板级隔离**:生成的看板方案同样受权限约束,用户只能看到自己有权访问的数据看板
#### 权限合并策略
- **OR 策略**:用户拥有多个角色时,任一角色允许即可访问
- **Deny 优先**:Deny 规则始终优先于 Allow,确保安全性
- **懒加载 + 缓存**:首次查询时展开权限规则,结果缓存复用
### 9. 配套的移动端——随时随地随身
数智分析师的能力不仅限于 PC 端,我们还提供了配套的移动端应用,让你随时随地使用智能分析能力:
- **扫码即用**:扫码即可在小程序中打开应用,无需额外安装
- **核心能力完整**:支持智能问数、知识库问答、报表查看等核心功能
- **移动端优化**:针对手机屏幕优化 UI 和交互体验,触摸操作流畅
- **消息推送**:关键报表生成后主动推送通知,不错过重要信息
移动端项目地址:[RAG-ISS-MOBILE](https://gitee.com/dark2000/RAG-ISS-MOBILE)
### 10. 能力路由引擎——让智能分析师的能力具有无限扩展的可能
系统采用**插件式能力引擎架构**,各能力引擎独立封装、可组合、可单独使用,新增能力只需注册新引擎即可:
**路由决策流程:**
```mermaid
graph LR
A[用户输入] --> B[动作词匹配
识别动作+对象组合]
A --> C[语义向量检索
匹配历史示例]
B --> D[LLM 路由决策器
综合分析+追问]
C --> D
D --> E[知识库问答]
D --> F[智能问数 NL2SQL]
D --> G[综合报表 Analyst]
D --> H[联网搜索]
D --> I[知识图谱]
D --> J[普通对话兜底]
D --> K{置信度不足?} --> L[追问用户
引导性问题]
```
路由决策器的工作方式,更像是一位经验丰富的专家在接手任务时的思考过程:
> **第一步:快速扫描** —— 听到用户的问题,先凭直觉判断"这大概是什么类型的事?是查数据、找文档、还是做分析?"(动作词初筛)
>
> **第二步:经验匹配** —— 回想一下以前遇到过类似的问题吗?当时是怎么处理的?如果高度相似,就沿用过去的经验;如果只是部分相似,就作为参考但不盲从(语义向量检索)
>
> **第三步:综合判断** —— 把直觉印象和过往经验放在一起权衡,如果两者指向同一个方向,信心十足地给出答案;如果存在分歧或拿不准,就坦诚地告诉用户"我还不确定您具体想做什么,请问...?",通过追问缩小范围,而不是硬猜一个结果(LLM 综合分析 + 追问引导)
这种设计让 LLM 不再是简单的"输入-输出"问答对,而是真正像人一样**先判断、再决策、不确定就问**,让 AI 从"答题器"进化为"决策伙伴"。
**引擎接入流程:**
1. 在 `capability_descriptions` 表中注册新能力的名称、描述和动作词
2. 在 `capability_examples` 表中添加示例文本(系统会自动生成变体)
3. 重建能力路由示例库向量索引
4. 在路由决策器中添加新能力的处理逻辑
5. 新能力即可被自动路由发现和调用
### 11. 原生适配 MCP 协议——在 AI 生态中即插即用
RAG-ISS 的插件式能力引擎架构与 MCP(Model Context Protocol)天然同构——每个能力引擎(Knowledge、NL2SQL、Analyst、KG、Web Search)本身就是标准化的 Tool 单元,可无缝发布为 MCP Server:
```
Knowledge Engine → knowledge_search(知识库检索 Tool)
NL2SQL Engine → data_query(自然语言查数据 Tool)
Analyst Engine → report_analyze(综合分析报表 Tool)
KG Engine → graph_query(知识图谱检索 Tool)
```
这意味着 RAG-ISS 的核心能力可以**脱离当前的前端 UI**,被任何支持 MCP 协议的 AI 工具直接调用——无论是 Claude Desktop、Cursor、VS Code,还是未来的 AI IDE,都能即插即用地接入企业数据与知识。反过来,RAG-ISS 的对话引擎也可以作为 MCP Client,去调用外部的 MCP Server,实现能力的无限外延。
这不仅是接口层面的适配,更是架构层面的设计选择:**让企业智能能力成为 AI 生态中可插拔的基础设施**。

### 12. 完善的后台管理体系——企业级管控能力
一套完整的企业后台管理体系,让管理者对系统拥有全局掌控力:
**登录页面:**

**系统概览仪表板:**

**能力路由管理——灵活配置问答路由规则:**

**完整的权限管理体系(RBAC + 员工管理):**
| 模块 | 作用 |
|------|------|
| **用户管理** | 创建员工账号、关联员工(自动带出部门)、分配角色/岗位、重置密码。部门字段由关联员工自动带出,不允许手动修改 |
| **员工管理** | 员工信息 CRUD,部门树形选择器,修改 dept_id 自动同步到 sys_user |
| **角色管理** | 定义角色权限,树形勾选菜单权限,按角色分配用户 |
| **菜单管理** | 自定义系统菜单层级和权限标识 |
| **部门管理** | 组织架构管理,支持树形结构和子部门 |
| **岗位管理** | 定义岗位及关联用户 |
| **字典管理** | 统一管理系统常用数据字典 |
**菜单管理:**

**部门管理:**

**岗位管理:**

**用户管理:**

**角色管理:**

**字典管理:**

---
## 它为什么强大
RAG-ISS 不只是一款 AI 智能助手产品——它是一套**企业 AI 数智化转型的体系化架构蓝图**。
它不是在某一个单点上解决问题,而是从架构层面系统性设计了从 **数据汇集 → 知识沉淀 → 智能决策** 的完整闭环。每项能力都是这张蓝图中不可或缺的一块拼图——它们互相咬合、层层递进,而非零散插件的简单堆砌。
更为重要的是,这套体系为企业的智能化升级提供了可继承的能力底座:无论是接入新的数据源、扩展新的能力引擎,还是叠加新的安全策略,都可以在现有框架中无缝嵌入,让每一次投入都成为长期资产的积累,而非重复建设的浪费。
本节不再泛泛罗列功能,而是深入代码,从**架构、路由、扩展性、统一底座、检索与知识、数据查询引擎、报表与分析引擎、安全可控、日志及监控、其他工程设计**十个维度,拆解这套体系"强在哪里"、以及关键流程与阈值如何落地——既是一份能力说明,也是一份可对照源码排查问题的设计索引。
---
### 一、架构先进:四层解耦 + 插件式引擎 + 统一底座
系统采用清晰的四层分层解耦设计(`code/api/main.py` 暴露 13 个路由模块,`/api/*`),实现高内聚、低耦合:
```mermaid
graph TD
A[前端展示层
Vue3 + Element Plus + ECharts
SSE 流式渲染 / 看板异步装配]
--> B[API 服务层
FastAPI
对话/会话/上传/知识库/KG/权限/看板/域]
--> C[能力路由层
DialogueEngine + CapabilityRouter
三路硬判定 · 方向一致性]
--> D[插件式能力引擎层
code/engines/*
统一 *_iter 事件生成器]
--> E[数据存储层
MySQL + ChromaDB
业务表 / 权限表 / 向量库]
```
- **插件式引擎是"形"**:每个能力引擎(知识库 / 智能问数 / 报表 / 知识图谱 / 联网搜索)都是独立的 `*_iter` 生成器,统一产出 `phase / step / token / done` 四类事件(`code/dialogue/engine.py`、`code/engines/*/engine.py`)。新增能力 = 登记 DB 词表 + 在 `_execute_by_capability`(`code/dialogue/engine.py:1720`)加一个分支 + 提供 `*_iter` 生成器,引擎与传输协议彻底解耦。
- **统一底座是"骨"**:所有 LLM 调用收敛到 `code/misc/llm_client.py`(`LLMClient` 单例),所有权限判定收敛到 `code/dao/smart_permission_service.py`。能力引擎只关心"业务",不各自造轮子。
- **流式天然解耦**:引擎以生成器产出事件,对话层桥接到 FastAPI 的 `EventSourceResponse`,看板异步生成不阻塞报告流——协议层与引擎层互不知晓彼此实现。
### 二、路由智能:可动态调度的能力分发网络
正是智能路由的存在,使"插件式引擎"由静态堆叠升级为**可被动态调度的能力网络**——这是"架构先进"得以成立的关键,并为下一节"扩展性强"提供支撑。
路由采用三路独立硬判定策略:动作意图、语义向量、LLM 兜底三者互不加权、互不混合,任一路达到硬阈值即直接拍板,从而兼顾准确率与响应效率。
- **动作意图匹配**:基于动作词与对象词双命中的规则判定。单匹配时直接路由且不调用 LLM;多匹配时将候选方向收窄为约束集合,交由后续判定裁决。
- **语义向量检索**:基于历史示例库的向量召回,当语义相似度达到直接路由阈值(0.80)时即确定能力,无需 LLM 介入。
- **LLM 兜底**:仅当前两路均无法判定时介入,由 LLM 综合动作词、向量候选与对话历史做出最终裁决;信息不足时以追问方式引导用户补全。
**方向一致性约束**:一旦能力方向由动作意图或向量检索确定,路由携带的推荐答案仅取自该方向,避免将某一能力的预设答案误注入另一能力的执行引擎,从而保证分发结果的准确与可控。
此外,路由在兜底阶段携带对话历史上下文,并支持追问与澄清路径,将"应交付哪个能力"的歧义收敛为明确选择后再行分发(同一能力内多轮续问与补条件的意图判定属于查询分析器范畴,详见后续设计分析章节)。
### 三、扩展性强:能力注册 + DB 动态加载 + MCP 即插即用
承接上节——插件式引擎与 MCP 协议**天然同构**:每个引擎就是一个标准 Tool,既能无缝发布为 MCP Server 被外部 AI 工具调用,也能作为 MCP Client 接入外部服务,让企业智能能力成为 AI 生态中**即插即用的基础设施**。
- **能力注册机制**:新增能力只需三步——在 `capability_dao` 登记动作词/对象词/示例 → 在 `_execute_by_capability`(`code/dialogue/engine.py:1720`)加分发分支 → 提供 `*_iter` 生成器。其余(SSE、权限、日志)全部复用。
- **DB 动态加载**:能力词表、Few-Shot 示例、表业务描述全部存于 MySQL(`rag_*` 系列表),通过 Web UI 维护,变更后自动触发向量索引重建——**不改代码即可扩展语义与示例**。
- **MCP 即插即用**:插件式引擎产出的标准事件契约,可直接映射为 MCP Tool 协议,能力在 AI 生态中零改造成复用。
### 四、统一的 LLM 调度底座:双模路由 · 步骤模型 · KV 缓存降本
这是横向贯穿全部引擎的"技术 backbone",也是"技术能力强大"的集中体现,收敛于 `code/misc/llm_client.py`:
- **双模自动路由**(`LLM_API_MODE=auto`,`_needs_openai_mode:20`):白名单模型走 Native `dashscope.Generation`,其余自动走 OpenAI 兼容端点兜底;`qwen3.6-` 系列强制 OpenAI(Native 不支持)。模型供应商切换对上层**零感知**。
- **按步骤指定模型**(`LLM_STEP_MODELS`):轻重任务分流——强模型负责 SQL 生成 / 子查询拆解,flash 轻模型负责结果分析 / 知识库 QA / 看板方案(`code/config.py:68` 注释示意 `dashboard_plan` 用 flash 足够),在质量与成本间精细平衡。
- **百炼 ephemeral KV 缓存降本**:`_apply_cache_control:92` 将 System Prompt 标记为 `cache_control:{"type":"ephemeral"}`,`DOMAIN_CACHE_CONTROL_ENABLED=True`(`code/config.py:136`)。同业务域查询的 System Prompt 前缀 100% 一致,缓存命中仅收原价 **10%**(注释明示)。预热 `LLM_WARMUP_ENABLED`(默认 False)由后台守护线程在启动时建立首条缓存,降低首次请求延迟。
- **多模态与 OCR**:`VISION_MODEL` 支撑图文对话,`OCR_MODEL` 独立路径处理 DOCX 内嵌图片 OCR,精度要求可分别配置。
### 五、检索与知识(知识库检索能力):为什么它比朴素 RAG 更强大
> 本节是第五节「检索与知识」设计分析视角的**全方位技术总结**。其论证主线不是"我们做了哪些功能",而是"**为什么这套架构在工程与技术维度上更强**"——朴素 RAG 的天花板是"切 chunk 塞向量库、召回相似文本",而本系统把一次企业知识查询升级为「结构可导航、证据可核对、边界可声明、权限可管控」的确定性知识服务。详见 5.0–5.11。
#### 6.1 知识库问答(RAG):把"查得到"升级为"答得准、信得过"
**① 三级混合检索——召回不偏科,是结构性的互补而非堆叠噪声**
向量语义召回(`top_k=10`)+ 关键词召回(`top_k=10`)→ 按权重融合(向量 0.4 / 关键词 0.6)→ 送 `gte-rerank-v2` 精排(`RERANK_CANDIDATE_K=30` → `RERANK_TOP_N=8`,`code/config.py:100-108`)。Embedding 用 `text-embedding-v4`、维度 1024、`EMBEDDING_MAX_WORKERS=5` 并发控制。
*为什么强大*:单路检索必有盲区——向量漏"精确术语/编号"、BM25 漏"语义近义"。三路并用使**高召回来自信号互补,而非靠放大 `top_k` 注入噪声**;粗排保召回、精排保精度,两层解耦,且向量失败降级 BM25、Reranker 失败回退混合分,Pipeline 可降级、不中断。
**② 反幻觉来源校验 + 可溯源闭环——幻觉在用户侧可一眼识别**
回答强制标注来源、输出 `【关键词引用】`,并校验关键词**确实出现在**原文片段中(`engine.py` `_parse_keywords` / `valid_kw`);前端点击来源即滚动定位到目标 chunk、关键词标黄(`ChatMessageList.vue` / `DocumentPreviewer.vue`)。
*为什么强大*:溯源不是"附个文件名"了事,而是把每句总结**回链到可定位、可比对**的原文与关键词——把反幻觉从"靠 Prompt 约束"落到"机制闭环",让用户自证而非盲信。
**③ 诚实兜底(knowledge_fallback)——宁可说清"为什么答不上"也不答错**
触发条件 `kb_not_found or not search_results`(`engine.py:398`);LLM 越界即声明"超出知识范围、不编造"(全局前缀 `llm_client.py:24-35` + `knowledge_fallback` prompt),并结构化追加「权限提示(被过滤条数)+ 知识库提示(答案源自模型自身知识)」。
*为什么强大*:通用方案常"答不上也硬编",本系统**边界自知、原因透明**,契合企业知识封闭、合规要求高的场景,且行为可预期、可审计。
**④ 摄取广度 + 资源自治——读得全、管得住**
`DocumentLoader`(`code/utils/document_loader.py`)支持 9 种格式并保留精细位置;DOCX 内嵌截图/扫描表格经 OCR(独立可配 `OCR_MODEL`)转文本、标记 `is_ocr` 禁止拆分,使"图里的字"也能进库被检索——覆盖纯文本 RAG 盲区。域重建索引时自动清理孤儿 segment;全部阈值集中在 `code/config.py` 可调可治理。
#### 6.2 知识图谱(两步法扩展):在向量层之上叠加"结构化知识层"
KG 扩展并非简单向量召回,而是带阈值的两步法(`code/config.py:226-237`、`code/engines/knowledge/engine.py`)。它以"域-实体-属性-关系-文档"五要素 + `entity_code` 编码命名空间构建图谱(5.1 节),把向量库"只有相似、没有关系"的盲区补成**可导航、可策展**的结构层。
| 阶段 | 含义 | 阈值 | 技术意图 |
|------|------|------|----------|
| 种子选择 | query↔实体描述余弦 Top-K | `SEED_TOPK=3`,`SEED_FLOOR=0.35` | 只在"确实相关"的实体上启动扩展,避免无关联想 |
| 邻居预筛 | query↔关系描述余弦 | `THETA3_FLOOR=0.45`(低于则跳过检索) | 关系层二次把关,无关关系不浪费检索代价 |
| Step1 | 每个邻居域捞 chunk 数 | `EXPAND_TOPK=3` | 限域横向召回,补纯语义检索对结构化关联的盲区 |
| Step2 终判 | chunk↔纯 query 余弦 | `THETA4_PASS=0.50`(低于丢弃) | 回到"片段是否真答问题",精度终判 |
| 合并封顶 | rerank 前总候选硬上限 | `EXPAND_MERGE_CAP=30` | 防止扩展爆炸,保证精排开销可控 |
*为什么强大*:该算法确保"相关实体 → 关系 → 片段"**逐层收敛**——每一步都有阈值闸门,既有召回广度又有精度控制,而非无脑扩召回。更关键的是,阈值不是拍脑袋:另有 `scripts/calibrate_kg_expansion.py` 基于真实查询做**实证校准**,使"扩展强度"可测量、可优化、可复现。这与权限过滤(5.8)协同,使图谱扩展既补召回又不越权、既智能又可控。
**小结**:6.1 解决"文本查得准、答得信",6.2 解决"关系理得清、扩展可控"——二者以编码化知识图谱为结构锚点、以三级混合检索为召回底座、以反幻觉溯源与诚实兜底为可信交付,共同构成一套**在严苛企业条件下仍稳定、准确、可审计**的知识检索体系。
### 六、数据查询引擎(NL2SQL):把"说句话查数据库"升级为"可控、可信、低成本"的企业级问数
> 本节是「数据查询引擎」设计分析视角的**全方位技术总结**,论证主线是"**为什么这套 NL2SQL 比朴素方案更强**"——朴素 NL2SQL 常止步于"一句话生成一条 SQL",而本系统在**成本、精度、自愈、权限、确定性**五个维度上都做了工程级加固。详细展开见设计分析章「六、数据查询引擎(NL2SQL)」。
#### 6.1 NL2SQL 核心链路:域路由降本 + 两阶段选表 + SQL 自修正
- **业务域路由降本** —— `get_domain` 关键字匹配 → 固定域完整 System Prompt,同域查询前缀 100% 一致,配合百炼 ephemeral KV 缓存命中率≈100%(`engine.py:134-208`、`config.py:130-136`)。
*为什么强大*:跳过动态选表与 Few-Shot 检索,单 system message 即生成 SQL;同域前缀不变使 KV 缓存命中≈100%、高频查询成本骤降(仅原价 10%)。这是把"每次查询都重算选表"变为"域级一次沉淀"的工程巧思,是企业大规模问答的成本命门。
- **两阶段表选择 + 三条短路**(`table_selector.py:408-530`):向量粗排(仅表数 > 5 启用)→ LLM 精排;当**高度确信**时触发跳过 LLM 的三条短路(Top1 ≥ 0.88 / 候选 ≤ 7 / 分数断崖差 ≥ 0.25),且粗排后做 `top1*0.82` 分数截断剔除噪声、Stage2 失败回退向量结果。
*为什么强大*:多数查询根本不需要 LLM 精排——短路把"高置信查询"的延迟与成本砍掉,又保留 LLM 精排兜底低置信场景,做到"**准且快**"而非"为保准而牺牲速度"。
- **SQL 自动修正**(`engine.py:658-705`):执行失败进入"错误分类 → Schema 诊断 → 本地规则修正 → LLM 修正"循环,合计上限 `SQL_FIX_MAX_ROUNDS=3`(`config.py:196`),Few-Shot 相似示例辅助;另有 `_auto_inject_enum_case_when` / `_deduplicate_select_columns` 安全网防 LLM 遗漏枚举/重复列。
*为什么强大*:把"一次生成即定生死"变成"**可诊断、可自愈**"——语法/枚举/列名错误能在 3 轮内自修,显著提升一次成功率、压低用户侧失败率。
#### 6.2 数智权限架构层内建:让越权 SQL 根本生成不出来
- **表级剔除**:`get_allowed_data_tables` 直接剔除无权表,LLM 根本不知其存在(`smart_permission_service.py:393`)。
- **行/列级注入**:`_apply_permission_to_schema`(`engine.py:1267-1335`)逐行改写域 Schema——删禁止字段、注入 `WHERE perm_dept IN (...)` / `perm_emp = ?`、权限字段 `perm_dept/perm_emp` 不进 Schema;意图分析阶段即约束个人级表"禁止作 GROUP BY 且关联表也不行"(约束传递性,`intent_analyzer.py:63-74`)。
*为什么强大*:权限不是"事后过滤结果",而是**嵌入生成链路的事前约束**——无权数据既不在视野、也不在 SQL 中,从架构层堵死"AI 自由组合数据表"的越权路径,比传统 RBAC 管菜单进了一大步。
**小结**:6.1 解决"查得准、查得快、查得稳",6.2 解决"查得安全"——域路由降本、两阶段选表+短路、SQL 自修正、架构层权限四者协同,使自然语言问数在企业严苛条件下依然准确、低成本、可审计。
### 七、报表与分析引擎(Analyst):并行子查询 · 量级冲突防护 · 前瞻性权限
- **子查询并行**:需求经意图分析 → 子查询拆解,最多 `MULTI_ROUND_MAX_SUB_QUERIES=4`(`code/config.py:204`)个并行执行(线程池),结果行截断 `MULTI_ROUND_MAX_RESULT_ROWS=10` 后送汇总 LLM,兼顾速度与上下文成本。
- **量级冲突三层防护**(金额/数量混排导致图表失真):
1. **LLM Prompt 规则**:金额、数量、量级差异 > 10 倍必须分图;
2. **后端兜底**:`report_generator._split_metrics_by_scale`(`code/engines/analyst/report_generator.py:152`)按关键词分类 + 均值 > 10 倍自动拆指标;
3. **前端终检**:`getMergedCharts` 的 `detectUnitClash`(`dialogue-frontend/.../DashboardViewer.vue:589`)以 10 倍为阈值拦截单位冲突。
- **前瞻性权限约束**:意图分析阶段即在 Prompt 注入每张表的权限治理级别——**部门级**"作用域受限,需附加 `perm_dept IN (...)` 行级谓词";**个人级**"⛔ 分析禁区,禁止作 `GROUP BY` 维度,且**其关联表也不行**(`code/engines/analyst/engine.py:275-283`),以约束传递性杜绝语义逃逸。
- **流式 + 异步看板**:报告文本实时流式输出;看板方案(LLM 生成的 `dashboard_plan` JSON)由后台线程异步生成,通过 metadata 落库(`code/dialogue/engine.py`),前端 3 秒轮询拉取并自动展开——**`done` 事件单收口**,避免重复写入(呼应第十节「其他设计」)。
### 八、安全可控:数智权限从架构层管住 AI
传统 RBAC 只管得住"菜单",却管不住 AI **自由组合数据表、跨域查询**的能力——这正是"时代困境"中数据资产"裸奔"的根因。数智权限(`code/dao/smart_permission_service.py`)从架构层解决,落地为三大原则:
- **让无权数据从 LLM 视野消失**:表级 `get_allowed_data_tables`(`smart_permission_service.py:393`)直接剔除无权表,LLM 根本不知道其存在,无从生成越权 SQL。
- **让有权数据天然受限**:行级在 Prompt 注入 `WHERE perm_dept IN (...)` / `perm_emp = ?`(`:237`、`:300-315` 域级默认条件补齐),LLM 生成的 SQL 天然带权限边界;列级在 Prompt 禁止 + 执行后扫描删除被拒列(`code/engines/nl2sql/engine.py:1333`)。
- **执行兜底**:结果返回前再扫描列级拒绝规则,确保即使 Prompt 被绕过也不泄露。
**合并策略**:多角色采用 **OR 策略**(任一允许即可)+ **Deny 优先**(先判 deny,`smart_permission_service.py:350`、`:421`)+ 懒加载缓存,安全与灵活兼得。
**三大场景全覆盖**:企业知识(文档级剔除 / 实体级可见性 / 知识域隔离)、企业数据(表/行/列)、综合报告(规划阶段注入权限说明 + 执行阶段行级注入 + 列脱敏 + 看板级隔离)。报表场景的约束具有**传递性**——域内含 `perm_*` 字段的表(含可能被 JOIN 的关联表)全部补齐行级条件。
### 九、日志及监控体系:全链路可观测,运维与优化双赋能
系统内置双层日志,覆盖从一次用户提问到最终答案的完整链路,后台运维人员无需翻代码即可还原「用户问了什么、系统怎么决策的、花了多久 / 多少 token、权限如何生效」:
- **对话日志(DialogLogger)**:`code/dialogue/dialog_logger.py` 将每轮对话完整写入 `log/dialog/YYYY-MM-DD/DIAL-*.txt`,记录用户原始输入、能力路由明细(`log_routing_detail`)、每一步 LLM 调用(模型 / 耗时 / 输入输出 token,`log_llm_call`)、执行动作(`log_action` / `log_execution`)、命中预设答案等。多轮交互、自然语言逐步明确的过程可完整回放。
- **全链路 Trace(request_tracer)**:`code/log_mgr/request_tracer.py` 提供单次请求的细粒度追踪,写入 `log/traces/REQ-*.txt`。从路由判定的逐条依据(动作词 / 向量候选 / LLM 裁决)、检索召回与表选择候选得分、SQL 生成与执行,到权限注入点,均经 `trace_section` / `trace_content` / `trace_search` / `trace_table` 结构化落盘,是排查「为什么答非所问 / 为什么慢 / 为什么越权」的首要依据。
- **前后端统一覆盖(描点)**:Web 前端与移动端共用同一后端 `/api/*` 接口层,用户的每一次提问、能力选择、看板查看等行为都会经后端 `DialogLogger` 与 `request_tracer` 统一落盘——用户访问情况在后台「系统监控 / 操作日志」集中可见,无需在两端分别埋点。
- **为优化与排障赋能**:两层日志共同构成可观测底座,既支撑线上问题分钟级定位(对照源码 `file:line` 与 trace 内容即可锁定根因),也为后续能力词表调优、阈值校准(`scripts/calibrate_kg_expansion.py`)、KV 缓存命中分析提供真实数据。
## 十、缓存体系:
11.1 KV缓存
11.2 向量缓存
### 十一、其他工程设计:流式一致性 · 生成确定性 · 缓存加速 · 资源治理
归纳让系统「可被信任、可被运维」的底层工程质量保障:
- **流式状态一致性(不重不丢)**:报表场景下引擎内部 `done` 事件**仅 yield 一次**,避免前端收到两个 `done` 导致看板重复写入/丢失(`code/engines/analyst/engine.py` 的 `_execute_report_iter:1932` 中 `AnalystTool` 早 break + 引擎单收口,详见第七节)。对话层对 `dashboard_plan` 通过 metadata 异步落库,前端轮询获取(`code/api/routes/chat.py:230`)。
- **生成确定性(约束注入边界)**:Few-Shot 注入受 `FEW_SHOT_SCORE_THRESHOLD=0.65`、`FEW_SHOT_TOP_K=3`、`FEW_SHOT_MAX_INJECT=2` 约束(`code/config.py:145-151`);两阶段选表在高度确信时跳过 LLM 短路(见第六节),保证结果确定且低延迟。
- **缓存加速**:查询结果缓存 `QUERY_CACHE_ENABLED=True`、`TTL=300s`、`LRU 1000` 条(`code/config.py:208-214`);百炼 ephemeral KV 缓存见第四节。
- **资源治理**:域管理重建索引时**自动清理孤儿 segment / 知识域**(直接扫描 ChromaDB 目录 UUID 对账);全部阈值集中在 `code/config.py`,可调可治理;KG 扩展阈值另有 `scripts/calibrate_kg_expansion.py` 做**证据化校准**。
---
### 核心能力速览(12 大维度)
> 以下为产品能力的速览清单,详细技术设计见上文各节。
1. **🤖 数智分析师** —— 统一对话入口,智能识别意图,自动调用最合适的能力引擎。一次对话解决多种业务需求
2. **📊 智能问数** —— NL2SQL 引擎让你用自然语言查数据库,无需 SQL 技能。内置会话意图引导与追问模式,主动帮用户补全查询条件,而不是丢给用户一条 SQL 了事
3. **🔍 知识库问答** —— 三阶段混合检索(向量语义 → 关键词 → 排序精排),确保每次回答都有据可依,答案附原文可溯源
4. **📈 智能报表** —— 多查询并行执行 + 异步看板生成,复杂报表分钟级交付。通过前瞻性权限约束机制在子查询规划阶段即嵌入权限推理;看板自动检测金额/数量量级冲突并拆分
5. **🌐 联网搜索** —— 知识库不足时自动联网获取最新信息,回答永不"不知道"
6. **🖼️ 多模态理解** —— 支持图片+文字混合输入,看懂图表、截图、产品图片等。VL/OCR 模型可独立配置,满足不同精度要求
7. **🔗 知识图谱** —— 将企业碎片化知识串联成网,ECharts 力导向图直观展示实体间的关联网络,发现隐藏价值
8. **🧩 能力路由引擎** —— 插件式架构,各能力引擎独立封装、可组合、可单独使用。LLM 作为决策器,综合动作词匹配、语义向量检索等多维信息,引导式选择最合适的能力;置信度不足时主动追问,让 LLM 不只是"答题器"而是"决策者"
9. **🔐 数智权限体系** —— 面向 AI 应用场景的原生权限架构,贯穿企业知识、企业数据、综合报告三大领域,在文档级、表级、行级、列级、报告级实现多层次权限管控。让 LLM 在权限边界内自由发挥
10. **📱 移动端** —— 配套移动端应用,扫码即用,核心能力完整,关键报表主动推送
11. **🔄 原生适配 MCP** —— 插件式引擎天然同构于 MCP 协议,可无缝发布为 MCP Server,被任何 MCP 兼容的 AI 工具调用;也可作为 MCP Client 接入外部服务,让企业智能能力成为 AI 生态中即插即用的基础设施
12. **⚙️ 完善的后台** —— 完整 RBAC 权限体系 + 员工管理(部门树形选择 + dept_id 双向同步)+ 能力路由配置 + 域管理(重建索引自动清理孤儿)+ 系统监控 + 多模型配置 + LLM 预热与 KV 缓存加速
---
## 设计分析
> 本节是对「它为什么强大」十维度概述的**逐项技术展开**。概述偏架构与结论,这里偏**代码、流程、算法与取舍**:
> 每一节都对应概述中的一个维度,深入其内部实现,说明"是怎么设计的、考虑了什么、规避了什么坑"。
>
> 编写顺序遵循概述的维度编号,但**一、架构先进**作为总结性维度,留待其余九点讨论完毕后再落笔——届时对整体架构的总结会更具体、更有支撑。本章节当前进度:
>
> | 维度 | 主题 | 状态 |
> |------|------|------|
> | 二 | 路由智能(能力路由) | ✅ 本节 |
> | 三 | 扩展性强 | ⏳ 待展开 |
> | 四 | 统一 LLM 调度底座 | ✅ 本节 |
> | 五 | 检索与知识 | ✅ 本节 |
> | 六 | 数据查询引擎(NL2SQL) | ✅ 本节 |
> | 七 | 报表与分析引擎(Analyst) | ⏳ 待展开 |
> | 八 | 安全可控 | ⏳ 待展开 |
> | 九 | 日志及监控体系 | ⏳ 待展开 |
> | 十 | 其他工程设计 | ⏳ 待展开 |
> | 一 | 架构先进(总结) | ⏳ 最后落笔 |
---
### 二、路由智能(能力路由)
能力路由(`code/dialogue/capability_router.py`)解决的核心问题是:**把一句自然语言,零歧义地、低成本地、可解释地分发给正确的能力引擎**。它经过多轮迭代,最终定型为一套"**两路硬判定 + 一路 LLM 兜底**"的设计,核心哲学写在模块 docstring 里——**不搞加权、不比大小、不混合**。
#### 2.0 总体流程
```mermaid
graph TD
IN[用户输入] --> GUARD{匿名 / 空输入?}
GUARD -->|是| C0[chat · method=anonymous/empty]
GUARD -->|否| A1["① 动作意图匹配
CapabilityMatcher.match_action_intent"]
A1 -->|单匹配 → 直接路由| R1["cap=命中能力
仅收集本方向推荐答案
不调用 LLM"]
A1 -->|多匹配| AC["allowed_caps = 命中集合
(方向约束)"]
A1 -->|无匹配| V0[进入向量]
AC --> V0
V0 --> V1["② 向量语义检索
knowledge_base.search_capability"]
V1 -->|best.score ≥ 0.80| R2["cap=最高分能力
覆盖多向约束
不调用 LLM"]
V1 -->|< 0.80| L0["③ LLM 兜底 _llm_route"]
AC -.方向约束.-> L0
L0 -->|返回 uncertain:| CL["追问 / 澄清 特殊路径
(pending_clarification)"]
L0 -->|返回具体能力| R3["cap=LLM 裁决
收窄到该方向推荐答案"]
R1 --> DONE[携带 detail 证据包 → 引擎]
R2 --> DONE
R3 --> DONE
CL --> DONE
```
在展开各判定路之前,需明确以下三项设计前提:
1. **LLM 位于路由链末端,仅在前两路均无法判定时介入**。动作意图(规则判定,零成本)与向量检索(单次 embedding)可覆盖绝大多数请求;兜底阶段调用 LLM 时采用 `capability_routing` 步骤模型(轻量级),从而将系统的调用成本与响应延迟控制在较低水平。
2. **三路判定相互独立、互不加权、互不混合**(`capability_router.py:9`)。动作意图的 `0.85` 为规则分值,向量的 `0.80` 为余弦相似度,二者量纲不同,相加不具备可比性;因此采用"任一路达到硬阈值即直接拍板"的独立判定策略。
3. **路由输出的不仅是能力标识,还附带 `detail` 证据包**(`recommended_answer` / `recommended_sql` / `recommended_hint` / `report_schema`)。下游引擎可据此执行短路逻辑(如预设回答直出、SQL 参考注入),构成路由对整体链路的第二项价值。
#### 2.1 动作意图分析(规则层 · 零成本)
实现位于 `code/dialogue/capability_matcher.py`,是一个纯规则匹配器,**不调用 LLM、不做向量化**,因此毫秒级、零费用、完全确定。
**词表来源**:三张词表(每个能力一组)从数据库 `capability_descriptions` 动态加载(`_ensure_loaded:29`,经 `capability_dao.get_all_descriptions`):
- `action_words`(动作词):如"查/查询/统计/分析"等;
- `object_words`(对象词):如"销售额/财报/制度/流程"等;
- `qualifier_words`(限定词):目前仅 `web_search` 使用(如"上网/联网")。
**匹配算法**(`match_action_intent:49`):
1. 遍历 `knowledge / data_query / report / web_search` 四个"可被词表命中"的能力(注意 `chat` 不在其中——它是 LLM 兜底时的默认项);
2. 对某个能力,要求**动作词与对象词同时命中**(子串包含,`_has_any:88`)才算匹配;
3. 命中则得基础分 `BASE_MATCH_SCORE = 0.85`(`capability_matcher.py:19`);
4. 仅 `web_search` 有额外 `QUALIFIER_BONUS = 0.10`(`:20`),命中限定词时升到 `0.95`(封顶 `MAX_SCORE = 0.99`),作为"明确要联网"的强信号。
**单匹配与多匹配的处理**:
- **单匹配**(仅一个能力命中)→ 路由直接确定能力方向,不再进入后续判定(`capability_router.py:147`)。
- **多匹配**(多个能力同时命中,例如"查阅公司财报"同时命中 `knowledge` 与 `data_query`)→ 路由将命中集合记为 `allowed_caps`(`:181`),作为后续向量检索与 LLM 裁决的**方向约束**,而非将请求直接交由 LLM 从全部能力中重新判定。
> **实现说明**:`capability_matcher.py` 模块 docstring 仍保留早期"多匹配返回 `{}`"的描述,而当前实现(`:81-85`)已改为返回全部候选并施加 `allowed_caps` 方向约束。本文以源码实现为准;该偏差属于文档滞后,不影响运行行为。
**设计依据**:采用"动作词 + 对象词双命中"而非单命中,是为了避免"查"(动作词)或"财报"(对象词)单独出现即触发误路由。双命中将"用户明确表达对某类对象执行某动作"作为高置信信号,是整套路由中确定性最高的一路。
#### 2.2 向量语义匹配(语义层 · 一次 embedding)
当动作意图无把握时,进入向量检索 `knowledge_base.search_capability`(`capability_router.py:_vector_match:270` → `knowledge_base.py:1538`)。
**检索与打分流程**:
1. 用百炼 `BailianEmbeddingFunction`(维度随 `config.EMBEDDING_MODEL` 自动探测)对用户输入做 embedding;
2. 在 ChromaDB 的 `CAPABILITY_COLLECTION` 中 `query`(`top_k=8`,由路由指定);
3. Chroma 返回余弦**距离**(∈[0,2]),转换为相似度:`score = 1.0 - distance / 2.0`(`knowledge_base.py:1579`);
4. **硬下限过滤**:`MIN_SCORE = 0.50`(`:1574`),低于此视为不相关直接丢弃——这只是候选池门槛,与路由阈值无关;
5. **变体去重**:一条示例可能展开为多个"变体问法"向量,按 `example_id` 去重、每示例仅保留最高分(`:1597`)——防止热门示例的多个变体挤占候选位、淹没其它方向;
6. 结果按分数降序返回。
**直接路由判定**:取 `best = results[0]`,若 `best.score ≥ VECTOR_DIRECT_THRESHOLD(0.80)`(`capability_router.py:24`、`:199`),则**直接路由到 `best.capability`,不调用 LLM**。
> **区分两个同名阈值**:
> - **向量直路由阈值 `0.80`**(`capability_router.py:24`):决定路由是否将请求提交至该能力;
> - **推荐答案直出阈值 `0.80`**(`engine.py:_RECOMMENDED_ANSWER_THRESHOLD:1259`):决定下游引擎是否直接输出预设回答、跳过 LLM 生成。
> 二者数值相同但作用阶段不同,彼此独立:路由可在 `0.80` 提交能力,而引擎仍可能因"仅命中单条参考、未达直出线"而进入 LLM 综合路径(见 2.3)。
**设计依据**:`top_k=8` 配合 `0.50` 下限的设定,使 LLM 兜底阶段既能获得充分的方向证据,又不会引入过多噪声;`0.50` 仅作为候选池准入门槛,真正的路由硬线为 `0.80`。这种"宽召回 + 硬阈值"的分层策略,使向量检索同时承担"直路由判据"与"LLM 证据来源"双重角色。
#### 2.3 直接命中后的示例问题收集(证据包)
路由经 ①或 ② 确定能力方向后,会再次检索示例库,将该方向上的参考素材打包进 `detail`,供下游引擎执行短路逻辑。核心方法 `_collect_recommended_answers`(`capability_router.py:88`)实现了**方向一致性过滤**:
- 仅保留**带 `recommended_answer`** 的候选;
- 仅保留 `score ≥ RECOMMENDED_ANSWER_MIN_SCORE(0.65)`(`:31`)的候选;
- 若 `allowed_caps` 已确定方向(动作单匹配 / 向量直路由 / LLM 已裁决),**只保留该方向的答案**,杜绝把 A 能力的预设答案注入 B 引擎;
- 按分数降序取前 `RECOMMENDED_ANSWER_TOP_N(3)`(`:32`)。
打包进 `detail` 的字段(`capability_router.py:155-168`、`:215-231`):
| 字段 | 含义 | 下游消费 |
|------|------|----------|
| `recommended_answer` | 单条高相似预设回答 | chat 引擎:若 `match_score ≥ 0.80` 则直出、不调用 LLM(降低调用成本);否则作为参考提供给 LLM |
| `recommended_answers` | 多条(≥2)本方向预设回答 | chat 引擎:全部交给 LLM 综合(用户可能多子问) |
| `example` | 命中的示例问法文本 | 日志与 trace 展示、作为参考上下文 |
| `recommended_sql` | 预设 SQL | data_query 引擎注入为参考(`engine.py:968`) |
| `recommended_hint` | 预设提示 | 注入用户输入,引导 NL2SQL(`engine.py:1160`) |
| `report_schema` | 预设子查询 schema | report 引擎作为子查询参考(`engine.py:275`) |
> **方向一致性的约束意义**:当动作意图多匹配或向量已确定方向后,若将 `knowledge` 的预设答案注入 `data_query` 引擎,会导致引擎获得错误的参考 SQL/回答,进而产生答非所问。`_collect_recommended_answers` 中的 `allowed_caps` 过滤(`:108`)确保路由确定方向后,所有随行证据均来自同一方向。
> **细节说明**:动作意图单匹配时,`detail["score"]` 初始为动作分 `0.85`(`:150`);若同时检索到本方向预设答案,则被改写为该示例的向量相似度(`:161`)。此 `score` 随后作为下游 chat 引擎判断是否直出预设回答(≥0.80)的依据——"动作拍板 + 向量证据"的融合在此自然完成,无需任何加权运算。
#### 2.4 与 LLM 的交互:讲解式 Prompt(兜底层 · 末位)
仅当 ①动作意图与 ②向量均无法判定时,路由才进入 `_llm_route` → `_llm_fallback`(`capability_router.py:308`)。该路径采用"**讲解式**"Prompt 设计:系统不直接将原始问题交由 LLM 从零判定,而是将前两轮自动分析所得的证据整理后一并提交,由 LLM 做出最终裁决。
Prompt 由五段构成(`_llm_fallback:399` 的 `system_prompt`):
1. **能力说明**(来自 DB):每个能力的名称、描述、示例、知识库范围、备注;`web_search` 额外标注"⚠️ 会产生额外费用,严格限制"(`capability_router.py:327`)。
2. **第一轮分析:动作意图**(逐词级披露):对 `knowledge/data_query/report/web_search` 四个能力,分别列出命中的动作词、对象词,并打标——`✅ 完全匹配` / `⚠️ 命中对象词缺动作词` / `⚠️ 命中动作词缺对象词`(`:347-356`);若仅命中对象词,额外给出 `action_summary` 提示"仍有参考意义"(`:361-370`)。
3. **第二轮分析:语义向量检索**:列出 `score ≥ VEC_REF_MIN_SCORE(0.60)` 的 Top-5 向量候选及相似度(`:373-391`),并明确告诉 LLM"最高分低于 0.80 直路由阈值,仅供参考"。
4. **对话历史上下文**:由引擎层传入 `history_context`(`engine.py:633-641`,含 `state.to_prompt_context()` 与历史查询摘要),支撑多轮消歧。
5. **LLM 任务与五条决策要点**(`:412-433`):
- `chat` 是默认兜底,普通问题不要强行分配特殊能力;
- 多匹配时(如"查阅公司财报")仔细分析偏向,举具体例子;无法判定可选 `uncertain:追问`;
- 参考向量检索的高相似历史方向;
- `web_search` 严格限制——只有明确说出"联网/上网查"才选,拿不准宁选 `chat`/`uncertain`;
- `uncertain` 格式为 `uncertain:追问问题`,系统原样展示引导用户。
> **讲解式 Prompt 的设计要点**:LLM 接收的是"前两轮自动分析的证据 + 待裁决问题",而非孤立的原始问题。其设计收益包括:(a) 将 LLM 的介入范围收敛至真正的模糊地带,常规请求不会到达该阶段;(b) 证据前置使 LLM 裁决更稳定、更少自由发挥;(c) 兜底阶段使用 `LLM_STEP_MODELS["capability_routing"]`(通常为轻量级模型),单步调用成本低。
#### 2.5 追问的识别及特殊路径
**追问(uncertain / clarify)**:
- LLM 兜底若返回 `uncertain:追问内容`(`capability_router.py:450`),路由解析出追问文本、记入 `call_info`,并返回 `("uncertain", detail)`;
- 引擎 `_execute_by_capability` 收到 `capability == "uncertain"` 时(`engine.py:1732`),构造一个**澄清事件**:给出三个标准选项(查知识库 / 查业务数据 / 随便聊聊),并把 `pending_clarification` 存入 `SessionStore`(按 `dialog_id` 键)(`:1734-1748`);
- 前端渲染选项,用户选择后带着 `capability_hint` 重新路由,从而把"模糊问题"收敛为明确能力。
**其它特殊路径**(在路由判定之前或之中短路,体现健壮性):
| 路径 | 触发 | 行为 | 位置 |
|------|------|------|------|
| 匿名 / 空输入 | `is_anonymous` 或空串 | 直接 `chat`,不路由 | `capability_router.py:125` |
| 前端锁定能力 | `capability` 参数合法 | 跳过路由,`method=locked` | `engine.py:653` |
| 图片输入 | 存在 `_image_b64` | 强制 `chat`(多模态对话),不走数据/知识库 | `engine.py:667` |
| `clarify` 能力 | 用户回应澄清 | 携带 `llm_question/options` 渲染澄清 | `engine.py:1758` |
> **概念边界(避免混淆)**:能力路由层的"追问"(`uncertain`/`clarify`)解决的是**"该交给哪个能力"的歧义**;而 NL2SQL 分析器里的 `new / continue / ambiguous` 多轮意图(见概述第十节及 `state.py`),解决的是**"同一能力内如何续问/补条件"**——两者机制不同、层级不同,不要把"路由追问"和"分析器多轮"混为一谈。
#### 2.6 设计取舍与踩坑小结
把上述演进中"踩过的坑"与"刻意的设计"汇总,正是路由智能"先进"之处:
1. **不加权混合**:三路独立硬判定,规避跨量纲分数相加的语义失真(docstring `capability_router.py:9`)。
2. **方向一致性**:`allowed_caps` 过滤(`:88`、`:108`),杜绝跨能力预设答案污染下游引擎。
3. **双 0.80 分离**:向量直路由 0.80 与推荐答案直出 0.80 各司其职,互不绑架。
4. **多匹配不放弃**:从"返回空"演进到"约束方向后交 LLM"(`:81-85` vs 旧 docstring),ambiguous 请求不再彻底失效。
5. **变体去重**:`example_id` 去重(`:1597`),防热门示例变体淹没其它方向。
6. **LLM 末位兜底 + 讲解式 Prompt**:约九成请求由规则/向量解决,省成本、降延迟、降幻觉;LLM 只在模糊地带做裁决。
7. **web_search 成本护栏**:限定词加分(`QUALIFIER_BONUS`)+ 严格 Prompt 规则,避免误触发付费联网。
8. **证据包随行**:路由不止"指路",还把 `recommended_sql/hint/report_schema/answer` 带给药引擎,让下游能短路——路由的价值贯穿到执行层。
---
### 四、统一 LLM 调度底座(双模路由 · 步骤模型 · KV 缓存降本)
统一的 LLM 调度底座收敛于 `code/misc/llm_client.py`,是横向贯穿全部引擎的调用基础设施。其设计目标是:**对上层屏蔽模型供应商与调用协议的差异,对下层提供按步骤的模型选择与成本优化手段**,使各引擎只需关注业务 Prompt 的构造,而不必关心"用什么 SDK、调哪个模型、如何降本"。底座由三套机制协同构成——双模自动路由、按步骤指定模型、百炼 ephemeral KV 缓存。
#### 4.0 总体架构
```mermaid
graph TD
APP[各引擎调用 llm.chat / chat_stream] --> R0{_trace_label 查步骤模型}
R0 -->|命中且非空| M1[步骤专属模型]
R0 -->|未命中或空| M2[默认模型 LLM_MODEL]
APP --> C0{enable_cache_control?}
C0 -->|是| CA["强制 OpenAI 兼容 + 原始 HTTP
_apply_cache_control 注入缓存标记"]
C0 -->|否| C1{_needs_openai_mode 判定}
C1 -->|白名单 / native 模式| N0["Native: dashscope.Generation.call"]
C1 -->|其余 / openai 模式 / qwen3.6-| O0["OpenAI 兼容端点
OpenAI SDK"]
CA --> O0
M1 --> N0
M1 --> O0
M2 --> N0
M2 --> O0
N0 --> OUT[统一返回文本 + 调用元数据
last_call_info / trace]
O0 --> OUT
```
三项设计前提:
1. **全局单例、上层零感知**。所有引擎共享同一个 `llm_client` 单例(`llm_client.py:748`),模型切换、协议选择、缓存策略均封装在底座内部;上层仅通过 `chat()` / `chat_stream()` 传入 `messages` 与可选 `_trace_label`、`enable_cache_control`,不感知底层差异。
2. **模型选择分两层**:默认模型(`LLM_MODEL`)与按步骤指定的覆盖模型(`LLM_STEP_MODELS`)。二者在调用入口统一解析(`llm_client.py:131`、`:159`),优先级为"显式 model 参数 > 步骤模型 > 默认模型"。
3. **缓存与协议耦合**:百炼 ephemeral KV 缓存要求 `cache_control` 标记经由 OpenAI 兼容协议透传,而 Native SDK 不转发该字段——因此启用缓存时底座强制走 OpenAI 兼容路径(见 4.3)。这是双模路由与缓存降本之间的一个关键约束。
#### 4.1 双模自动路由(协议无关)
底座同时支持两种调用协议,由 `_needs_openai_mode`(`llm_client.py:20`)按模型名自动裁决,亦可由 `LLM_API_MODE`(`config.py:37`)强制覆盖:
- **Native 模式**:`dashscope.Generation.call()`,对应白名单模型 `LLM_NATIVE_MODELS`(`config.py:41`,含 `qwen3-max` / `qwen-max` / `qwen-plus` / `qwen-turbo` / `qwen-coder` / `qwen-long`)。
- **OpenAI 兼容模式**:OpenAI SDK 指向百炼兼容端点 `DASHSCOPE_OPENAI_BASE`(`llm_client.py:17`,支持 `DASHSCOPE_OPENAI_BASE` 环境变量覆盖为工作空间专属端点)。
判定逻辑(`_needs_openai_mode`):
1. `LLM_API_MODE` 显式 `native` → 返回 False(强制 Native);显式 `openai` → 返回 True(强制 OpenAI);`auto`(默认)走自动裁决。
2. `auto` 下,模型名命中白名单任一关键词 → Native;否则默认 OpenAI(安全兜底)。
3. 特例:`qwen3.6-` 系列(`qwen3.6-flash/plus`)不支持 Native,强制 OpenAI(`llm_client.py:34`)。
**设计依据**:自动裁决而非硬编码协议的收益在于——当百炼新增或调整模型支持时,仅需维护 `LLM_NATIVE_MODELS` 白名单,业务代码与 Prompt 无需改动;"其余默认 OpenAI"的兜底策略则保证未预见模型不会因协议不匹配而整体失效。Native 与 OpenAI 两条实现(`_chat_native:226` / `_chat_openai:359`)对外接口一致,返回文本与调用元数据(`last_call_info`),使上层对协议无差别。
#### 4.2 按步骤指定模型(步骤模型)
`LLM_STEP_MODELS`(`config.py:52`)以 `_trace_label` 为键,将不同调用步骤映射到不同模型,实现轻重任务分流:
- 键(`_trace_label`)须与代码中 `chat()` 传入的标签一致,如 `sql_generate` / `sql_fix` / `intent_analysis` / `table_select` / `result_analyze` / `knowledge_qa` / `web_search` / `dialogue_understand` / `capability_routing` / `multimodal_chat` / `dashboard_plan`。
- 值为模型名;**空字符串表示回落到默认模型 `LLM_MODEL`**(`config.py:26`,默认 `qwen-plus-2025-12-01`,强模型)。
- 调用解析:`effective_model = model or config.LLM_STEP_MODELS.get(_trace_label) or self.model`(`llm_client.py:131`、`:159`)。
实际分流策略(以 `config.py:52` 当前配置为准):
| 步骤 | 模型配置 | 分流意图 |
|------|----------|----------|
| `sql_generate` / `sql_fix` | 空 → `LLM_MODEL`(强) | SQL 与 JSON 结构生成对精度要求高,使用默认强模型 |
| `table_select` / `intent_analysis` / `capability_routing` / `dialogue_understand` | 空 → `LLM_MODEL` | 路由类、选择类判断同样依赖强模型稳定性 |
| `result_analyze` | `qwen-flash-character-*` | 结果归纳总结,轻量、低延迟优先 |
| `knowledge_qa` | `qwen-flash-character-*` | 知识库问答综合,轻量模型足够 |
| `dashboard_plan` | 空 → `LLM_MODEL` | 看板方案为 JSON 输出,回落默认强模型以保证结构正确 |
**设计依据**:以 `_trace_label` 为路由键而非在每处散落模型名,使"哪一步用强模型、哪一步用轻模型"集中可配、可在不重启业务逻辑的前提下调优;空值回落机制保证未显式配置的步骤始终有可用模型,避免遗漏导致的调用失败。轻量步骤(结果分析、知识库 QA)固定到 flash 类模型,在不损质量的前提下降低单步成本与延迟。
#### 4.3 百炼 ephemeral KV 缓存降本
KV 缓存用于削减高频、System Prompt 前缀稳定的请求的重复计费。实现围绕百炼 `cache_control:{"type":"ephemeral"}` 标记展开。
**缓存标记的注入**(`_apply_cache_control`,`llm_client.py:92`):将最后一个 system message 的 content 由字符串转为数组格式,并在末个 text 块附加 `cache_control` 标记。底座提供 `chat_with_multi_system`(`llm_client.py:202`)将"纯静态规则"与"域 Schema"分条放置为多条 system message,使前缀保持稳定、可最大化缓存命中率。
**协议耦合与原始 HTTP 透传**(关键约束):OpenAI SDK 会在序列化时剥离 message content 中的 `cache_control` 未知字段,因此启用缓存时底座(a)强制走 OpenAI 兼容路径;(b)绕过 SDK,通过 `_make_raw_cache_request`(`llm_client.py:588`)发送原始 HTTP 请求,确保 `cache_control` 完整送达。Native 路径同时以顶层参数 `cache_control` 双保险传递(`:254`),但缓存主链路依赖 OpenAI 原始请求。
**调用入口分支**(`chat()`,`:133-145`):`enable_cache_control=True` 时先注入标记再走 OpenAI 原始请求;该分支为非流式路径,故 KV 缓存用于非流式、重输出的步骤(与流式对话路径分离)。
**命中检测与计量**:`_extract_cache_info`(`llm_client.py:542`)与 `_extract_cache_info_from_dict`(`:571`)从 `usage.prompt_tokens_details` 读取 `cached_tokens`(命中时 >0)与 `cache_creation_input_tokens`(首次创建)。命中信息写入 `last_call_info`,并在日志中以 `[KV缓存]` 标记与命中 token 数输出。
**实际启用点**:
| 启用位置 | 触发条件 | 说明 |
|----------|----------|------|
| NL2SQL 域 SQL 生成(`engines/nl2sql/engine.py:157-183`) | `DOMAIN_CACHE_CONTROL_ENABLED`(默认 True,`config.py:136`) | 单 system message,域 System Prompt 前缀 100% 一致,命中率高 |
| 意图分析(`engines/nl2sql/core/intent_analyzer.py:162`) | 硬编码 `enable_cache_control=True` | 业务地图(biz_map)为静态前缀,适合缓存 |
| Analyst 引擎(`engines/analyst/engine.py:454`) | `DOMAIN_CACHE_CONTROL_ENABLED` | 看板方案生成的域 Schema 前缀稳定 |
**成本模型**(`config.py:136` 注释):首次写入缓存有少量溢价,后续命中仅收原价 **10%**;在高频同域查询场景下大幅降本。
**预热机制**:`LLM_WARMUP_ENABLED`(默认 False,`config.py:164`)启用后,启动约 5 秒由后台守护线程 `_warmup_llm_cache`(`api/main.py:113`)对目标业务域执行一次简单查询,预先建立首条缓存,消除首请求的缓存创建延迟;预热失败不影响主服务。目标域可由 `LLM_WARMUP_DOMAINS` 指定,缺省自动选取首个含业务表的启用域。
**设计依据**:KV 缓存的降本效果依赖"前缀稳定"。系统已统一采用业务域路由(关键字匹配 → 域固定 Schema),使同域查询的 System Prompt 前缀完全一致,这正是缓存能稳定命中的结构性前提;`chat_with_multi_system` 的分条放置进一步强化了前缀稳定性。缓存与双模路由、流式/非流式路径的耦合(强制 OpenAI 原始请求、仅非流式)是经过权衡的工程取舍,确保标记不被 SDK 丢弃的同时不侵入流式对话路径。
#### 4.4 多模态与 OCR 扩展
底座对多模态输入提供统一入口,模型可独立配置:
- `VISION_MODEL`(`config.py:27`,默认 `qwen-vl-plus`):支撑图文对话,`chat_with_images`(`llm_client.py:176`)接受文字 + 多张 base64 图片,构造多模态 messages。
- `OCR_MODEL`(`config.py:30`,默认 `qwen-vl-ocr-2025-04-13`):独立路径处理 DOCX 内嵌图片的 OCR 文字识别,可与视觉模型分别配置以控制精度与成本。
- 路由层对图片输入强制走 `chat`(多模态对话),不进入数据/知识库路径(见设计分析第二章 2.5 特殊路径)。
#### 4.5 设计要点总结
底座的各项机制相互支撑,其设计要点归纳如下:
1. **协议无关与上层零感知**:双模自动路由(`_needs_openai_mode`)按白名单与特例裁决协议,全局单例封装差异,模型/供应商切换对业务引擎透明。
2. **步骤级模型分流**:以 `_trace_label` 为键的 `LLM_STEP_MODELS` 集中配置轻重任务所用模型,空值回落保证缺省可用,轻量步骤固定 flash 以降本提速。
3. **KV 缓存与架构前提耦合**:业务域路由带来的"前缀稳定"是缓存命中率的结构性保障;缓存强制走 OpenAI 原始请求以保全 `cache_control` 标记,命中仅收原价 10%。
4. **流式/非流式路径分离**:KV 缓存仅作用于非流式重输出步骤,避免 SDK 字段剥离与流式 token 透传相互干扰。
5. **可观测与预热**:缓存命中/创建、token 计量、首调用延迟均写入 `last_call_info` 与 trace;预热线程消除冷启动溢价。
6. **多模态可扩展**:视觉与 OCR 模型独立配置、统一入口,与文本调用路径共享同一底座。
---
### 五、检索与知识(知识库检索能力)
知识库检索引擎是一个多阶段 RAG Pipeline,由编排层 `KnowledgeEngine`(`code/engines/knowledge/engine.py`)与存储检索层 `KnowledgeBase`(`code/vector/knowledge_base.py`)协同构成。
**它要解决的企业级真命题**是:在企业**私域封闭、权限隔离、知识持续变更、文档形态繁杂(含扫描截图/表格)** 的严苛条件下,把「**查得全、答得准、信得过、管得住、建得快**」同时做到——这恰恰是通用 RAG 与"切chunk塞向量库"式朴素方案的工程天花板。多数自研检索系统止步于"能召回相似文本",而本系统把检索从"相似性匹配"升级为"**带结构、带证据、带边界、带权限的可信知识交付**"。
**技术上为何强大——八大相互咬合的制高点:**
| # | 制高点 | 技术内核 | 直击的痛点 / 相对朴素 RAG 的优势 |
|---|--------|----------|----------------------------------|
| 1 | **召回不偏科:三级混合检索** | 向量语义 + BM25 关键词 + 知识图谱关系,三路互补、归一化加权融合(`engine.py:163`、`knowledge_base.py` 双路召回) | 单路检索必有盲区:向量漏"精确术语"、BM25 漏"语义近义"、图谱补"结构化关系"。三路并用使召回既宽又互补,**高召回**不是靠堆 `top_k` 换来的噪声,而是结构性的互补。 |
| 2 | **超越向量:结构化知识图谱层** | 显式「域-实体-属性-关系-文档」五要素 + `entity_code` 编码命名空间(5.1 节) | 向量库"只有相似、没有关系"——无法做属性过滤、图遍历、实体级权限。图谱层以**可导航、可策展**的结构叠加于模糊向量层之上,使"理得清业务关系"成为可能,是检索能力成立的前提底座。 |
| 3 | **精准不噪声:Reranker 语义精排 + 双向容错** | `gte-rerank-v2` 对融合结果做语义重排(Step 4);向量失败降级 BM25、Reranker 失败回退混合分(5.4、5.11) | 粗排保召回、精排保精度,二者解耦。且**Pipeline 可降级、不中断**——任一节点失效都有安全回退,工程上"稳"而非"脆"。 |
| 4 | **答得准、信得过:反幻觉与可溯源闭环** | 句句带来源标注 + `【关键词引用】` 校验(5.6);前端点击定位目标 chunk、关键词标黄(5.10) | 溯源不是"附个文件名",而是把每句总结**回链到可定位、可比对**的原文与关键词——幻觉在用户侧可一眼识别,把反幻觉从"靠 Prompt 约束"落到"机制闭环"。 |
| 5 | **读得全:多格式摄取 + DOCX 内嵌图片 OCR** | `DocumentLoader` 支持 9 种格式并保留精细位置;DOCX 内嵌截图/扫描表格经 OCR(独立可配 `OCR_MODEL`)转文本、标记 `is_ocr` 禁止拆分(5.9) | 纯文本 RAG 对"图里的字"天然失明。把 OCR 前置到摄取阶段,使**图片中的文字也能进向量库被检索与溯源**,覆盖真实企业文档的高频盲区。 |
| 6 | **管得住:数智权限从架构层内建** | 粗排权限过滤 ① + 末位兜底过滤 ② 双层(`engine.py:163-188`/`:494-509`);实体级可见性(5.8) | 无权文档在检索阶段即被剔除,**根本不进 LLM 视野**;权限落到实体/文件粒度,而非事后过滤,安全可信。 |
| 7 | **诚实兜底:边界自知 + 原因透明** | `knowledge_fallback`(5.7):越界即声明"超出知识范围、不编造";结构化追加「权限提示(被过滤条数)+ 知识库提示(答案源自模型自身知识)」 | 通用方案常"答不上也硬编"。本系统**宁可说清"为什么答不上"也不答错**——契合企业知识封闭、合规要求高的场景,且行为可预期、可审计。 |
| 8 | **建得快:编码化知识树 + 程序化批量建树** | `entity_code` 命名空间 + 扁平存储解耦树路径;`generate_business_docs.py` 从业务系统一次性建骨架、可视化策展补连关系(5.1.4) | 海量知识库不靠逐文档手标,也不盲赖易错的文本自动抽取——**编码命名空间 + 程序化批量 + 可视化策展**四件套,在工程可控性与构建效率间取平衡。 |
八个制高点并非孤立:图谱(2)为混合检索(1)与权限(6)提供结构锚点,摄取(5)决定"进库的内容宽度",精排(3)决定"结果精度",溯源(4)与兜底(7)决定"交付的可信度",建树(8)决定"规模化的可行性"——它们共同把一次企业知识查询,从"模糊的文本相似度匹配"升级为"**结构可导航、证据可核对、边界可声明、权限可管控**"的确定性知识服务。
下文 5.0–5.11 逐层展开上述能力的设计与落地。
#### 5.0 总体流程
```mermaid
graph TD
Q[用户查询] --> S1["Step1 向量语义检索
vector_search top_k=10"]
Q --> S2["Step2 关键词检索
BM25 top_k=10"]
S1 --> S3["Step3 粗排融合
归一化 + 加权 0.4V/0.6K + 去重"]
S2 --> S3
S3 --> E1["富化 entity 信息"]
S3 --> P1["权限过滤 ①"]
P1 --> S35["Step3.5 知识图谱两步法扩展
θ1 种子 → θ3 预筛 → StepD 限域 → θ4 终判"]
S35 --> S4["Step4 Reranker 精排
gte-rerank-v2"]
S4 --> S5["Step5 LLM 回答
Mode A/B/C"]
S5 --> S6{"_is_kb_not_found?"}
S6 -->|否 有命中| OUT1["返回 含来源标注 + 关键词引用"]
S6 -->|是 未命中| OUT2["LLM 自身知识兜底
不联网"]
OUT1 --> P2["权限过滤 ②(末位兜底)"]
```
引擎对外提供同步入口 `search()` 与生成器入口 `search_iter()`(逐步 yield `phase` / `step` / `token` / `done` 事件,支撑前端流式渲染),二者共用同一套 Pipeline 逻辑(`engine.py:59`、`:532`)。
#### 5.1 知识图谱的引入动机与构建方法论(基础层)
检索 Pipeline 的"实体—关系—文档"扩展能力(5.3 节)并非凭空而来——它依赖一套**预先构建好的知识图谱**。本节先讲清系统"为什么还需要知识图谱""图谱长什么样""用什么方法论构建",以及"面对海量知识库时如何高效、低成本地构建"。这是检索能力能成立的前提,也是概述所提及"知识图谱方法论"在代码层面的真实落地。
##### 5.1.1 为什么引入知识图谱(传统向量知识库的盲区)
传统向量知识库把文档切成 chunk、各自嵌入成向量点。它擅长"语义相似度匹配",但存在结构性盲区:
1. **只有相似,没有关系**。向量空间里"项目管理"与"风险"只是距离近,却无法表达"项目风险管理**属于**项目管理域""财务部**负责**预算"这类**定向关系**;相似度把"共现"与"真关系"混为一谈。
2. **无法做关系遍历 / 属性过滤**。像"财务部负责的产品有哪些""哪些部门与项目交付相关"这类问题,本质是**图遍历或属性查询**,向量检索无能为力——它只能返回相似的文本片段,不能沿边跳转。
3. **无法天然承载权限可见性边界**。向量点没有"实体归属",难以做到"市场人员只看到客户关系图谱、技术人员只看到系统架构图谱"的实体级可见性(见 5.8 权限)。
4. **隐性关联被埋没**。文档散落各处,文档之间的业务关联(谁影响谁、谁归属谁)对人和模型都不可见,正是概述所述"知识资产沉睡"的根因。
知识图谱以**显式、可导航、可策展**的结构化层叠加在模糊的向量层之上,补上上述四类能力:
- **属性过滤问答**(Mode C,`engine.py:330-362`):直接从 `kg_entities.attributes` 取实体属性结果为主构建上下文,不依赖向量命中率——向量库做不到。
- **实体关系概览注入**(Mode B,`engine.py:923`):把"实体—关系"结构摘要前置进 prompt,让回答带业务关联脉络。
- **Step 3.5 图遍历扩展**(5.3 节):以种子实体为起点沿关系边横向召回片段,弥补纯语义检索对结构化关联的盲区。
- **实体级权限可见性**(5.8 节):图谱节点天然带域/实体归属,权限可落到实体粒度。
简言之:**向量库解决"找得到相似的文字",知识图谱解决"理得清业务的关系"**——二者互补,而非替代。
##### 5.1.2 知识图谱的结构:域 · 实体 · 属性 · 关系 · 文档
图谱由三张 MySQL 表构成(`code/dao/kg_dao.py`,全局单例 `kg_dao`),结构层次如下:
```mermaid
graph TD
ROOT["根节点 type=root
code=ROOT_BIZ / ROOT_REG"] --> DOM["域节点 type=domain
(如 人事 / 财务 / 项目)"]
DOM --> ENT["实体节点 type=entity
code=ROOT_BIZ_HR"]
ENT -->|attributes JSON| ATTR["属性
{负责人, 职责, 生效日期...}"]
ENT -->|kg_relations| REL["关系边
relation_type + description"]
ENT -->|kg_documents| DOC["关联文档
store/{doc_code}{ext}"]
REL --> ENT2["另一实体
(双向遍历)"]
```
| 结构要素 | 存储位置 | 关键字段 | 说明 |
|----------|----------|----------|------|
| **根 / 域(Domain)** | `kg_entities`(`type='root'/'domain'`) | `parent_id, name, code, description` | 树形组织顶层;`ROOT_BIZ`/`ROOT_REG` 区分业务/制度两大域(`generate_business_docs.py:66-82`) |
| **实体(Entity)** | `kg_entities`(`type='entity'`) | `code, name, description, attributes` | 业务概念节点,如"考勤管理""预算编制";`code` 是全局寻址键(`kg_dao.py:49-55`) |
| **属性(Attribute)** | `kg_entities.attributes`(JSON) | 任意键值对 | 实体附带的元数据,支撑 Mode C 属性过滤(`kg_dao.py:108-125` 写入 `attributes` 列) |
| **关系(Relation)** | `kg_relations` | `source_entity_code, target_entity_code, relation_type, description` | 实体间有向边;`get_entity_relations` 双向返回(`kg_dao.py:219-249, 469-478`) |
| **文档(Document)** | `kg_documents` | `entity_code, filename, relative_path, doc_code, ext` | 挂到实体的原始材料,按 `entity_code` 关联(`kg_dao.py:261-298`) |
**编码即地址**是这一结构的核心设计:`entity_code` 形如 `ROOT_BIZ_HR`(父码 + 段,自动生成于 `kg_dao.py:122-123`),文档 `doc_code` 形如 `ROOT_BIZ_HR_DOC0156`(`kg_dao.py:290`),二者把"树形语义"与"扁平物理存储"绑定在一起。检索时 `_extract_entity_info`(`engine.py:1332-1367`)优先用 `entity_code` 直接查缓存取得实体名/描述/属性,无需解析文件路径——这正是编码化带来的好处。
##### 5.1.3 构建方法论:编码化知识组织树 + 关系策展
系统以**组织驱动 + 关系策展**的编码化知识树构建图谱,具体由四步构成:
- **建树(组织驱动)**:管理员在 UI 或脚本中按业务域逐层建立 `根 → 域 → 实体` 的目录树(`/api/kg/directory` 系列接口,`knowledge_graph.py:267-347`)。每个节点本质是"一个业务概念容器"。
- **挂文档(编码关联)**:文档上传到某实体时,系统按其 `entity_code` 生成 `doc_code` 并扁平落库(`knowledge_graph.py:394-423`、`kg_dao.add_document`),文档与实体的绑定靠编码而非目录路径。
- **连关系(人工策展)**:实体间关系由管理员通过 `/api/kg/relation` 显式添加(`knowledge_graph.py:354-370`),即"图"的边是人工确认的业务关系,确保关联准确可信。
- **LLM 降摩擦(编码建议)**:为降低建树成本,新建节点时系统调用 LLM 推荐语义编码段(`_llm_suggest_segment`,`knowledge_graph.py:202-238`,步骤标签 `kg_code_suggest`),并备有一份业务关键词→缩写映射 `_BIZ_KEYWORDS`(`kg_dao.py:133-149`,如"人事→HR""财务→FIN")作为离线兜底。编码可由用户确认/修改(`editable=True`),兼顾自动化与可控性。
**设计依据**:组织驱动 + 编码化的方法更**可控、可审计、可解释**——企业知识本就由人按业务边界组织,让人类主导树的骨架、机器辅助编码与关系补全,使每个实体都有明确归属与权限边界。这也与第一章"知识资产沉睡"的痛点解法一致:把"人脑中的业务关联"显式固化成可导航的图。
##### 5.1.4 海量知识库下的高效构建方式
当企业文档量级巨大时,系统通过以下设计让图谱"建得快、管得住、扩得开":
1. **扁平存储解耦树形路径**(`kg_dao.py:291-292`):文档统一存于 `resource/store/{doc_code}{ext}`,与树形层级**物理解耦**。重命名/移动节点(`move_entity`)只改 MySQL 的 `parent_id`,**不搬文件**(`kg_dao.py:210-215`),避免了海量文档下的目录级联移动灾难。
2. **编码命名空间支持程序化批量建树**:因实体以 `entity_code` 寻址,批量构建可由脚本从业务系统直接生成。典型实现 `scripts/generate_business_docs.py`:从 MySQL 业务表(员工/管线/产品/项目)拉取数据 → 自动建 `ROOT_BIZ` 根、按部门建域、在域下建子实体、把生成的业务文档按 `doc_code` 注册进 `kg_documents` 并写盘(`generate_business_docs.py:66-200`)。企业只需"喂业务数据",图谱骨架与文档挂载一次性完成。
3. **可视化策展闭环**:`/api/kg/graph-data`(`kg_dao.get_graph_data`,`kg_dao.py:333-383`)输出 ECharts 力导向图所需的节点/边,前端 `KnowledgeGraphView` 直观展示实体关联网络,帮助管理员**发现隐藏联系**并补连关系——把"人工策展"变成可规模化的可视化运营,而非逐文件阅读。
4. **轻量统计与增量**:`get_kg_stats`(`kg_dao.py:316-331`)单 SQL 返回实体/关系/文档计数,不加载全量;实体缓存 `get_entity_cache`(`kg_dao.py:446-467`)以 `code→信息` 字典驻留内存,检索时 O(1) 取得实体属性,支撑海量图谱下的低延迟检索。
**小结**:系统对海量知识库的构建策略是"**编码命名空间 + 扁平存储 + 程序化批量骨架 + 可视化人工策展**"四件套,在工程可控性与构建效率之间取得平衡。
#### 5.2 三级混合检索
所谓"三级",指向量语义召回、关键词召回、Reranker 精排三层逐级收敛;其中前两层并行召回、经粗排融合后送第三层精排。
**5.1.1 向量语义检索(Step 1,`vector_search` → `_vector_search`)**
- 调用 ChromaDB `query()`,基于 HNSW 索引的余弦距离搜索;Embedding 由 `BailianEmbeddingFunction` 封装百炼 `text-embedding-v4`(`EMBEDDING_MODEL`,维度 `EMBEDDING_DIMENSIONS=1024`,`config.py:92-93`)。
- 距离转相似度:`score = 1.0 - (distance / 2.0)`(`knowledge_base.py:933`),Chroma 余弦距离 ∈[0,2] 映射为相似度 ∈[0,1]。
- 召回 `top_k=10`(`VECTOR_SEARCH_TOP_K`,`config.py:87`),返回 `{content, source, filename, score, page_num, entity_code, ...}`。
- 降级机制:向量检索失败或为空时自动降级为 BM25 关键词检索,保证召回不中断。
**5.1.2 关键词检索(Step 2,`keyword_search` → `_keyword_search`)**
采用 BM25 风格词频统计(非完整概率 BM25,简化实现),核心在 `_tokenize`(`knowledge_base.py:853`)与评分:
- **中文分词**:数字与中文间插空格防粘连;抽取长度 ≥2 的 token;对纯中文词提取全部 2-gram(如"考勤制度"→"考勤""勤制""制度"),取并集扩大召回面。
- **评分**:`score = Σ(token_count_in_doc × 2.0)`(`knowledge_base.py:830`)——每命中一次 +2 分,不做 IDF 加权(简化设计,契合企业文档术语稳定的特征)。
- **索引缓存**:`_rebuild_bm25_cache`(`knowledge_base.py:735`)从 ChromaDB 全量加载文档文本构建内存索引,仅在索引重建后刷新,避免每次检索重扫。
**5.1.3 粗排融合(Step 3,`merge_results` → `_merge_results`)**
加权归一化 + 内容去重(`knowledge_base.py:985`):
1. **归一化**:分别以向量结果、关键词结果的最大分为分母,`norm = raw / max`。
2. **加权融合**:向量 `norm × 0.4` + 关键词 `norm × 0.6`(`knowledge_base.py:1001`、`:1011`)。关键词权重高于向量,设计依据是**企业内部文档中精确术语匹配比语义相似更可靠**——术语写错一字即语义漂移,但精确串匹配才是强信号。
3. **去重**:以 `content[:100]`(前 100 字符)为唯一键,重复项保留较高分(`knowledge_base.py:998`)。
4. **截断**:按 `final_score` 降序取 `top_k`(默认 `RERANK_CANDIDATE_K=30`,`config.py:106`)。
融合后引擎层立即执行**实体富化**(`_enrich_sources_with_entity`)与**权限过滤 ①**(`engine.py:163-186`,见 5.8),使后续精排只在有权结果上运行,节省 Rerank API 费用。
#### 5.3 知识图谱两步法扩展(Step 3.5,独立于语义检索)
KG 扩展通过"实体—关系—文档"三层结构做图遍历,与向量/关键词检索互不依赖、互为补充。种子选择基于查询与实体描述的余弦相似度(`_graph_expand_search`,`engine.py:1485`):
| 阶段 | 计算 | 阈值/参数 | 作用 |
|------|------|-----------|------|
| θ1 种子选择 | `cosine(query, entity_desc)` | `SEED_FLOOR=0.35`、`SEED_TOPK=3`(`config.py:226-237`) | 选出与查询相关的实体作为扩展起点 |
| 邻居枚举 | `kg_dao.get_entity_relations()` 双向图 | 跳过同为种子的邻居 | 收集关系描述 |
| θ3 预筛 | `cosine(query, relation_desc)` | `THETA3_FLOOR=0.45` | 关系描述与查询语义偏离则丢弃该邻居,不进入检索 |
| Step D 限域检索 | `search_by_entity(entity, relation_desc)`(`knowledge_base.py:1083`) | `EXPAND_TOPK=3` | 用关系描述(非原 query)在指定实体文档内检索,更精准 |
| θ4 终判 | `cosine(query, chunk_emb)` | `THETA4_PASS=0.50` | 即便关系描述命中,chunk 仍须与原始 query 足够相关才入池,防关系描述偏离 |
通过 θ4 的 chunk 以 θ4 为 `score` 并入候选池,并标注 `_source="kg"`(原检索结果标注 `_source="query"`),去重时以 query 结果为优先(`engine.py:235-244`)。候选池合并硬上限 `EXPAND_MERGE_CAP=30`(`config.py:226-237`)。
**设计依据**:
- **独立管道**:KG 扩展不依赖向量检索是否命中,提供"相关实体→关系→片段"的横向扩展能力,弥补纯语义检索对实体间结构性关联的盲区。
- **θ4 双重校验**:用关系描述检索到的 chunk,仍须与原始 query 做余弦验证,防止"关系描述相关但 chunk 实际无关"的噪声入池。
- **实体上下文严格过滤**:`_build_entity_context_summary`(`engine.py:1798`)仅纳入"实际召回并送进 LLM 的 chunk"所涉实体,关系两端实体必须都属于本次召回集合,避免 θ1 种子命中但无对应 chunk 的实体被注入 prompt 而产生幻觉。
#### 5.4 Reranker 精排(Step 4,`rerank`)
- 调用百炼 `gte-rerank-v2`(`RERANK_MODEL`,`config.py:104`)对候选池(粗排结果 + KG 扩展结果)做语义精排,用 Reranker 的 `relevance_score` 替换原混合分(`knowledge_base.py:1020`)。
- **动态 top_k**:无 KG 扩展时取 `RERANK_TOP_N=8`;有 KG 扩展时适度上调(`rerank_top_k = min(8 + len(kg_expanded), EXPAND_MERGE_CAP)`,`engine.py:261-263`),给 KG 结果竞争机会。
- **降级**:Reranker 调用失败或结果空时,回退为按混合分降序取 top_k(`engine.py:272-281`),保证流程不崩。
- 开关由 `RERANK_ENABLED`(`config.py:103`)控制;关闭时走 Step 4 的"最终排序"分支。
#### 5.5 LLM 回答与三种模式(Step 5)
根据是否命中 KG 实体与是否属性过滤,自动选择三种回答模式:
- **Mode A 标准模式**:无 KG 命中,仅拼接检索结果的 source block 为 `kb_context`。
- **Mode B KG 增强模式**:有实体命中,在 `kb_context` 前插入 `_build_entity_context_summary()` 生成的实体关系概览(严格过滤后)。
- **Mode C 属性过滤模式**(如"财务部负责的"):直接从知识图谱数据库取实体属性结果为主构建上下文,向量检索结果仅作补充(`engine.py:330-362`),不依赖向量命中率。
System Prompt 由 `_build_kb_prompt`(`engine.py:1119`)构造,包含五部分:角色定义、来源标注规则(`(来源: 【域: xxx | 文件: YYY】)`)、关键词引用规则(`【关键词引用】` 段落)、业务域结构运用规则、参考文档内容。其不变前缀在前,利于第四章所述的 LLM KV 缓存。回答经 `knowledge_qa` 步骤模型调用(flash 轻模型,见第四章 4.2)。
#### 5.6 反幻觉来源校验(溯源)
这是 RAG 可信度的核心机制,在 Step 5 之后对回答做结构化校验(`engine.py:453-477`):
1. **解析关键词引用**:`_parse_keywords`(`engine.py:1238`)从回答末尾 `【关键词引用】` 段落解析出 `{filename: [keywords]}` 映射。
2. **真实性验证**:对每个关键词,校验其**确实出现在**对应来源的 `content` 中——`valid_kw = [k for k in kw if k in content]`(`engine.py:1047-1052`)。LLM 编造的、原文中不存在的引用词被剔除。
3. **来源收敛**:仅保留含 `key_quotes` 的来源作为最终 `sources`(`engine.py:1057-1059`),确保回链到用户的是"确有依据"的文档。
4. 回答经 `_strip_keywords` 去除引用段落(`engine.py:1270`),前端展示干净文本 + 可点击来源。
**设计依据**:强制"引用词必须能在原文片段中找到",从机制上抑制 LLM 编造来源或张冠李戴,是"反幻觉"在工程上的落地,而非仅依赖 Prompt 约束。
#### 5.7 结果充分性判定与兜底(`knowledge_fallback`,Step 6)
知识库并非全知全能:用户可能问到**大模型训练时间之外**的事件/数据,也可能因**文档权限限制**根本检索不到相关内容。系统因此不能"答不上就装作答上了",而是用 `knowledge_fallback` 智能判定并兜底提示。该逻辑存在于同步与流式两条路径(`engine.py:398-448` / `:982-1052`),行为完全一致。
##### 5.7.1 为什么需要兜底(两类典型失效)
| 失效场景 | 成因 | 系统应对 |
|----------|------|----------|
| **超知识范围** | 问题涉及大模型训练截止时间之后的事件、数据(如最新季度财报、刚发布的政策) | 让 LLM 自知边界,明确告知"超出知识范围、无法回答",而非编造 |
| **权限受限** | 用户无权访问命中的文档,检索阶段被数智权限过滤(`engine.py:163-188` / `:674-699`),导致实际可用结果为空 | 主动提示"有多少条文档因权限被过滤",引导联系管理员 |
两者最终都表现为"知识库未能提供答案",由统一的兜底分支承接。
##### 5.7.2 智能判定:何时触发兜底
触发条件(`engine.py:398` / `:982`):
```python
kb_not_found = self._is_kb_not_found(kb_answer)
if kb_not_found or not search_results: # 任一成立 → 进入兜底
... # knowledge_fallback
```
- `_is_kb_not_found`(`engine.py:1249`)只匹配**强信号**关键词("未找到相关内容""知识库中未找到""无法回答""无法提供"等)。历史上曾包含"未提及/抱歉"等弱信号,但模型基于文档客观陈述"文档未提及X"时也会误命中,导致本可保留的 KB 回答被反向误伤——故收紧为强信号,**确保仅在真正答不上时才兜底**(`engine.py:1251-1255` 注释)。
- `not search_results` 覆盖"检索阶段因权限过滤而完全归零"的情形——此时即便 KB 回答不含否定词,也应兜底并提示权限。
##### 5.7.3 兜底行为:边界声明 + 双提示
进入兜底后,系统向 LLM 发送 `knowledge_fallback` prompt(`engine.py:412-422` / `:1000-1010`),其中关键约束:
> 若你判断该问题超出了你的知识范围(例如所问事件、数据的时间在你掌握之外),请直接、简短地告知用户"**该问题超出了我的知识范围,我无法回答**",不要展开解释,更不要编造内容。
这一边界意识同时由全局系统前缀 `_GLOBAL_SYSTEM_PREFIX`(`llm_client.py:24-35`)兜底强化——前缀明确"自知知识边界:超出范围就简短告知、不编造"。因此当 LLM 判明问题越界时,会输出类似:
> 该问题超出了我的知识范围,我无法回答。
> ✅ 建议您:→ 访问巨潮资讯网(https://www.cninfo.com.cn)搜索"软通动力"(301236)筛选定期报告;→ 或查阅公司官网"投资者关系"栏目;→ 或通过证券行情软件查看公告。
(上例是 LLM 依据自身知识**自行生成**的建议文本,并非系统硬编码——系统只保证"超出范围就声明、不编造"的边界,具体建议由模型据常识给出。)
在 LLM 回答之外,系统**结构化追加两类提示**(`engine.py:429-441` / `:1029-1042`):
1. **💡 权限提示**(仅当有权限拦截时):`当前查询有 {_permission_blocked} 条知识库文档因权限限制被过滤,如需更多数据请联系管理员确认权限范围。`——`_permission_blocked` 来自粗排权限过滤的拦截计数(`before - len(filtered)`,`engine.py:178-182` / `:689-693`),把"为什么查不到"透明地告诉用户。
2. **💡 知识库提示**(仅当无检索结果时):`知识库中未检索到与问题直接相关的内容,以下回答基于大模型自身知识生成,建议查阅相关制度文件获取准确信息。`——明确标注答案来源是"模型自身知识"而非企业文档,提示用户交叉核实。
**设计依据**:兜底不是"默默用通用知识糊弄",而是**(a) 边界自知的诚实**——越界就明说、不编造;**(b) 原因透明**——权限过滤了多少条、答案来自模型还是文档都告知用户;**(c) 可预期、可审计**——不联网、不引入外网不可信内容,契合企业知识封闭、合规要求高的场景。这与 5.6 反幻觉机制一脉相承:宁可"答不上并说清为什么",也不"答错了"。
#### 5.8 资源自治与权限
- **索引构建**:增量构建按 `md5(mtime + size)` 指纹检测变更(新增/修改/删除/未变),批量写入每批 100 条;全量重建分阶段(本地分块 → 并发向量化 `EMBEDDING_MAX_WORKERS=5` → 写 ChromaDB),失败跳过不注册、下次增量补(`docs/knowledge_engine_architecture.md` 第 5 节)。分块 `CHUNK_SIZE=800`、`CHUNK_OVERLAP=80`(`config.py:89-90`),长段落按句边界拆分、OCR/表格块禁止拆分、末尾 80 字符重叠以避免边界语义断裂。
- **两层权限过滤**:
| 层级 | 时机 | 说明 |
|------|------|------|
| 权限过滤 ① | Step 3 粗排融合后(`engine.py:163-186`) | Rerank 之前过滤无权限文档,节省精排 API 费用 |
| 权限过滤 ② | Step 6 输出前(`engine.py:494-509`) | 图谱扩展的安全兜底,确保最终 `sources` 不含越权文档 |
判定基于 `entity_code → check_knowledge_access(user_id, domain, file)`;无域信息文档按公开处理,无用户上下文时跳过检查。
#### 5.9 文档摄取与向量化(多格式支持 + DOCX 内嵌图片 OCR)
知识要"进得了库、读得全内容",摄取层 `DocumentLoader`(`code/utils/document_loader.py`)做了大量工程投入,覆盖格式广度与图片文字提取两件事。
**多格式摄取**(`document_loader.py:23-33`):`SUPPORTED_EXTENSIONS` 支持 PDF / MD / TXT / CSV / DOCX / XLSX / PPTX 共 9 种扩展名;`load_file_spans`(`:74`)按格式分派到对应的 `_load_*_spans`,且**每个 span 都携带精细位置信息**(`page_num` / `section_path` / `char_start` / `char_end`),为下游溯源(5.10)提供定位基础。各格式要点:
| 格式 | 解析方式 | 位置/结构信息 |
|------|----------|---------------|
| PDF | `pdfplumber` 逐页逐块,按字号/加粗推断章节;失败回退 `PyPDF2`(`:142-213`) | `page_num` + `section_path` |
| Markdown | 按 `#` 标题层级拆分段落(`:217-254`) | `section_path`(章节路径) |
| TXT | 空行分段并记录字符偏移(`:256-286`) | `char_start` / `char_end` |
| CSV / XLSX | 每 20 行一个 span,含列头与行范围(`:290-322`、`:516-544`) | `sheet_name` / `row_start` / `row_end` |
| DOCX | 按文档真实顺序交错处理段落与表格,记录段落索引与字符偏移(`:406-512`) | `paragraph_index` / `char_start` / `char_end` |
| PPTX | 每张幻灯片一个 span,首段短标题视为章节(`:548-583`) | `slide_num` / `section_path` |
**DOCX 内嵌图片 OCR**(这是纯文本 RAG 做不到的关键能力):`_load_docx_spans` 在遍历正文后,用 `iter(ns_drawing)` 抓取 `drawing` 中的图片(`blip → embed → related_parts`),逐张调用 `_ocr_image`(`:361-402`):
- 调用百炼多模态 `MultiModalConversation`,模型取 `config.OCR_MODEL or config.VISION_MODEL`(`config.py:27-30`,OCR 可独立配置以控制精度与成本),Prompt 要求输出纯文本、表格用 `|` 分隔;
- 清洗 `_clean_ocr_text`(`:327-341`)去 ```html 包裹、去 "OCX/OCR/以下是识别结果" 等杂音前缀,并把 HTML 表格转为纯文本;
- OCR 结果作为 `style_name="OCR"` 的 span,标记 `is_ocr=True`,在分块时**禁止拆分**(`knowledge_base.py:305-307`,与 HTML 表格块同等待遇),独立成块避免图文语义割裂;同时记录 `char_start` / `char_end`(`:497-507`),使图片中的文字同样可溯源定位。
**设计依据**:把"读得懂图片里的字"前置到摄取阶段,使 DOCX 中的截图、扫描表格也能进入向量库被检索与溯源——这是传统纯文本 RAG 的盲区;OCR 块受保护 + 位置追踪,保证 5.10 的溯源能力对图片文字同样有效。
#### 5.10 溯源体验:可点击来源定位到 Chunk + 关键词标黄
检索只是手段,让用户**信得过、对得上**才是目的。系统在"每条总结都有依据、依据可定位可比对"上做了重点投入。
1. **后端保证"句句有依据"**:System Prompt 强制每条事实标注 `(来源: 【域: xxx | 文件: YYY】)`,并在回答末尾输出 `【关键词引用】` 段落(`_build_kb_prompt`,`engine.py:1119`);`_parse_keywords`(`engine.py:1238`)解析出 `{filename: [keywords]}`,并校验关键词**确实出现在**对应来源 `content` 中(`valid_kw = [k for k in kw if k in content]`,`engine.py:1047-1052`),仅保留 LLM 实际引用且验证通过的来源(带 `key_quotes`,`engine.py:494` / `:1086`)。
2. **来源携带定位字段**:每个 source 含 `filename / relative_path / page_num / char_start / char_end / key_quotes`(`engine.py:540` 返回结构;位置字段由 `knowledge_base.py:281-345` 的 chunk 偏移记录与 `document_loader` span 共同产生)。
3. **前端可点击定位**:`ChatMessageList.vue` 渲染来源列表,点击某来源即打开 `DocumentPreviewer.vue` 并传入该来源的 `key_quotes`(`:key-quotes="previewKeyQuotes"`,`ChatMessageList.vue:321`;点击处理 `:552-568`、`:686-713`),预览器按 `relative_path` + 位置偏移**直接滚动定位到目标 chunk**。
4. **关键词标黄**:`highlightSourceContent`(`ChatMessageList.vue:721`)优先用 LLM 提供的 `key_quotes` 在来源内容中高亮(回退到 n-gram 算法匹配);`DocumentPreviewer.vue` 同样接收 `keyQuotes` 在预览文档中高亮(`:76` / `:108` / `:443`),方便用户快速比对"这句话对应原文哪段、哪几个词"。
**设计依据**:溯源不是"附个文件名"了事,而是把 LLM 的每一句总结回链到**可定位、可比对**的原文片段与关键词——既让用户可信(点得开、对得上),也让幻觉在用户侧可一眼识别(标黄词若在原文找不到即存疑),是 5.6 反幻觉机制在交互层的闭环。
#### 5.11 设计要点总结
1. **三级混合、分层收敛**:向量语义 + 关键词精确 + Reranker 精排,由宽到窄逐级提升精度;关键词权重 0.6 高于向量 0.4,契合企业术语稳定特征。
2. **KG 扩展独立且与语义互补**:两步法(θ1→θ3→StepD→θ4)以"实体—关系—文档"图遍历横向扩展,θ4 双重校验防噪声,实体上下文严格过滤防幻觉。
3. **反幻觉落地于校验机制**:关键词引用必须能在原文片段中找到,来源收敛到确有依据的文档,而非仅依赖 Prompt 约束。
4. **可降级、不中断**:向量失败降级 BM25、Reranker 失败回退混合分、知识库未命中转 LLM 自身知识兜底,Pipeline 各节点均有容错。
5. **权限前置与末位双重兜底**:粗排后即过滤以省精排费用,输出前再兜底,兼顾效率与安全。
6. **资源自治理**:分块/重叠/不可拆块保护保证语义完整;增量指纹构建与失败跳过重试保证索引可维护;知识库查询不缓存(文档持续变化,命中率低且 TTL 管理复杂)。
7. **图谱先行、组织驱动**:知识图谱是检索的结构化前提(5.1 节)——以"域/实体/属性/关系/文档"五要素 + `entity_code` 编码命名空间构建,采用"编码化知识组织树 + 关系策展 + LLM 编码建议"的方法论,并以"扁平存储解耦路径 + 程序化批量建树 + 可视化策展"支撑海量知识库的高效构建。
8. **摄取广、读得全**:`DocumentLoader` 支持 9 种格式并保留精细位置信息;DOCX 内嵌图片经 OCR(独立可配 `OCR_MODEL`)转文本、标记 `is_ocr` 禁止拆分且可溯源,使截图/扫描表格也能进库被检索。
9. **溯源可定位、可比对**:后端强制句句带来源与 `key_quotes`、校验关键词真实存在;前端点击来源滚动定位到目标 chunk、关键词标黄,把反幻觉闭环延伸到用户交互层。
10. **兜底诚实、原因透明**:`knowledge_fallback`(5.7)在"答不上/检索被权限清空"时触发——LLM 自知边界、越界即声明"超出知识范围、不编造";并结构化追加「权限提示(被过滤条数)+ 知识库提示(答案源自模型自身知识)」,宁可说清"为什么答不上"也不答错,契合企业知识封闭场景。
---
### 六、数据查询引擎(NL2SQL):为什么它比朴素 NL2SQL 更强大
> 与「五、检索与知识」并列,本节是 NL2SQL 引擎的**设计分析级展开**。它的论证主线不是"我们支持自然语言查数据库",而是"**为什么这条企业级问数链路在工程与技术维度上更强**"——朴素 NL2SQL(一句话 → 一条 SQL)常被成本、精度、自愈、权限四处短板卡住;本系统把这四处短板逐一补成工程级能力。
**它要解决的企业级真命题**是:让不懂 SQL 的业务人员,用自然语言**准确、低成本、可自愈、且绝不出越权数据**地查到企业数据库里的答案。难点在于——①大量表下选表昂贵且易错;②LLM 生成的 SQL 常语法/语义出错;③企业数据带行/列级权限,不能"查什么给什么";④高频同类查询重复烧 token。本系统以"**域路由降本 + 两阶段选表 + SQL 自修正 + 架构层权限**"四根支柱应对,并把"结果可缓存、行为可观测"作为确定性底座。
**技术上为何强大——七大相互咬合的制高点:**
| # | 制高点 | 技术内核 | 直击的痛点 / 相对朴素 NL2SQL 的优势 |
|---|--------|----------|--------------------------------------|
| 1 | **域路由降本** | 关键字匹配 → 固定域完整 Prompt,同域前缀 100% 一致,百炼 ephemeral KV 缓存命中≈100%(`engine.py:134-208`、`config.py:130-136`) | 朴素方案每次查询都重做选表+生成,token 烧不停;本系统把"域级上下文"一次沉淀、KV 复用,高频查询成本仅原价 10%——**企业大规模问数的成本命门**。 |
| 2 | **两阶段选表 + 三条短路** | 向量粗排(`TABLE_VECTOR_THRESHOLD=5` 启用)→ LLM 精排;高置信时跳过 LLM(Top1≥0.88 / 候选≤7 / 断崖差≥0.25),粗排后 `top1*0.82` 截断噪声、Stage2 失败回退向量(`table_selector.py:408-530`) | 单路选表要么全靠 LLM(贵、慢)要么全靠向量(易错)。本系统"粗排保召回、精排保精度、短路砍成本"——**准且快**,且有多级回退不中断。 |
| 3 | **SQL 自修正闭环** | 执行失败 → 错误分类 → Schema 诊断 → 本地规则修正 + LLM 修正(合计 ≤3 轮,`SQL_FIX_MAX_ROUNDS=3`,`config.py:196`);安全网 `_auto_inject_enum_case_when` / `_deduplicate_select_columns`(`engine.py:658-705`、`194-195`) | 朴素方案"一次生成即定生死",SQL 写错就报错给用户。本系统**可诊断、可自愈**,语法/枚举/列名错误 3 轮内自修,一次成功率与用户满意度显著提升。 |
| 4 | **意图分析与多轮拆解** | `single/multi/ambiguous` 三分流(`intent_analyzer.py:104-176`);`multi` 拆 2-4 子查询并行,综合性查询(看板/总览)主动挖掘维度;`ambiguous` 反问澄清而非瞎答 | 朴素方案把"复杂多面问题"硬塞一条 SQL,必漏维度。本系统**先理清楚再查**,复杂查询拆并行、模糊查询先澄清,结果更完整、更可信。 |
| 5 | **数智权限架构层内建** | 表级 `get_allowed_data_tables` 剔除无权表(LLM 不知其存在);行/列级 `_apply_permission_to_schema` 逐行改写 Schema、注入 `perm_dept/perm_emp`、权限字段不进视野(`engine.py:1267-1335`);意图阶段约束个人级表"禁 GROUP BY 且关联表也不行"(约束传递性) | 传统 RBAC 只管菜单,管不住 AI 自由组合表。本系统把权限做成**生成链路的事前约束**——无权数据既不在视野也不在 SQL,从架构层堵死越权,而非事后过滤。 |
| 6 | **结果缓存 + 确定性生成** | 自然语言 → SQL+结果缓存 `TTL=300 / LRU 1000`(`config.py:208-214`,`code/cache/query_cache.py`),与"知识库不缓存"形成鲜明对比(DB 结果稳定、命中率高);Few-Shot 受 `FEW_SHOT_SCORE_THRESHOLD=0.65` 等约束保证生成确定 | 朴素方案同问反复烧钱。本系统对**稳定结果**做缓存降本;同时用约束注入边界(两阶段短路、Few-Shot 阈值)保证生成**确定且低延迟**,工程上"稳"。 |
| 7 | **可观测、可治理** | 全链路 Trace(`request_tracer`)落盘选表候选/得分、SQL 生成、修正每轮、权限注入点;全部阈值集中在 `config.py` 可调;表描述/Few-Shot 经 Web UI 维护并触发向量重建 | 企业级系统必须"查得清怎么决策的"。本系统每一环都可回溯、可调参、可运维,而非黑盒——这是可落地、可优化的前提。 |
七个制高点并非孤立:**域路由(1)与缓存(6)决定成本与确定性,两阶段选表(2)与意图分析(4)决定精度与完整性,SQL 自修正(3)决定健壮性,架构层权限(5)决定安全,可观测(7)把这一切变成可运维**。它们共同把"自然语言查数据库"从脆弱的 Demo 能力,升级为"准、快、稳、安全、可控"的企业级数据服务。
#### 6.0 总体流程
```mermaid
graph TD
Q[用户自然语言] --> D{get_domain 域路由}
D --> P[固定域 System Prompt
KV 缓存命中≈100%]
P --> I[intent_analyzer
single/multi/ambiguous]
I -->|multi| SP[拆 2-4 子查询并行]
I -->|ambiguous| CL[反问澄清]
I -->|single| G[生成 SQL]
SP --> G
G --> EX[执行 SQL]
EX -->|失败| FX["自修正循环 ≤3 轮
错误分类→诊断→本地/LLM 修正"]
FX --> EX
EX -->|成功| PERM["_apply_permission_to_schema
行/列级约束"]
PERM --> CACHE[(查询缓存 TTL=300)]
CACHE --> OUT[返回结果 + 过程步骤]
```
#### 6.1 业务域路由与 KV 缓存降本
`_generate_sql_domain`(`engine.py:134-208`)走"关键字匹配 → 固定域完整 System Prompt → 单次 LLM 调用"路径,**跳过动态选表与 Few-Shot 检索**。关键是:整个 system prompt 对同域查询 100% 固定(SQL 域规则 + 公共服务表 + 域业务表 + 域 Few-Shot),配合 `DOMAIN_CACHE_CONTROL_ENABLED=True`(`config.py:130-136`)的百炼 ephemeral KV 缓存,使同域前缀完全一致、命中率≈100%,高频查询成本仅原价 10%。
*设计依据*:企业问数高度集中在若干固定业务域(销售、财务、人力…),同域 Schema 短期内不变。把"域级上下文"沉淀为可缓存前缀,是把"每次都重算"变成"一次沉淀、反复复用"的成本杠杆——这是朴素 NL2SQL 没有的规模化经济性。
#### 6.2 两阶段表选择 + 三条短路(精准与效率的平衡)
`TableSelector.select_relevant_tables`(`table_selector.py:408-530`)实现"向量粗排 + LLM 精排"两阶段:
- **Stage 1 向量粗排**:仅当总表数 > `TABLE_VECTOR_THRESHOLD=5` 启用(`table_desc_store` 检索 Top-K 候选)。小表库直接发全表,避免无谓向量开销。
- **三条跳过 LLM 短路**(高置信即不调 LLM,省延迟省成本):
1. 候选表数 `≤ TABLE_DESC_MIN_FOR_LLM=7`;
2. Top1 得分 `≥ TABLE_DESC_SKIP_LLM_SCORE=0.88`(向量高度确信);
3. 分数断崖差 `≥ TABLE_DESC_SKIP_LLM_SCORE_GAP=0.25`(前 N 张与后段明显分层,`table_selector.py:492-507`)。
- **噪声剔除与回退**:粗排后做 `top1*0.82` 分数截断剔除低分噪声表(保留至少 `min_for_llm` 张防截过头,`table_selector.py:536-559`);LLM 精排失败时回退向量粗排 Top-N(`table_selector.py:614-639`)——**多级容错、不中断**。
*设计依据*:选表是 NL2SQL 最易错也最烧钱的环节。本系统用"粗排保召回、精排保精度、短路砍成本、回退保可用"四重设计,让**大多数高置信查询零 LLM 选表开销**,仅在确需判断时调用 LLM,兼顾准与快。批量子查询还有 `select_relevant_tables_batch` 一次为多个子查询选表,进一步省调用。
#### 6.3 SQL 生成安全网与自动修正循环
SQL 生成并非"一锤子买卖":
- **生成安全网**:解析 JSON 后自动 `_auto_inject_enum_case_when`(补全枚举字段 `CASE WHEN`,防 LLM 遗漏,`engine.py:194`)与 `_deduplicate_select_columns`(去重 SELECT 列,`engine.py:195`)——把常见 LLM 疏漏在生成即兜底。
- **自修正闭环**(`engine.py:658-705`):执行失败 → `_classify_sql_error` 错误分类 → `_diagnose_sql_error` Schema 诊断 → `_local_fix_sql` 本地规则修正 → 仍失败则 LLM 修正,循环上限 `SQL_FIX_MAX_ROUNDS=3`(`config.py:196`);每轮详情(应用规则、诊断、建议)经 `request_tracer` 落盘并输出到前端过程步骤。Few-Shot 相似示例(`FEW_SHOT_TOP_K=3`)注入辅助修正。
*设计依据*:LLM 生成的 SQL 难免语法/语义错。本系统把"生成—执行—报错—修正"做成**可诊断的自愈循环**而非一次性赌博,显著降低用户侧失败率;3 轮上限既给足修复机会又防无限重试。
#### 6.4 意图分析与多轮拆解(single / multi / ambiguous)
`IntentAnalyzer.analyze`(`intent_analyzer.py:104-176`)把查询分为三类:
- **single**:单维度/单问题,一次 SQL 回答;
- **multi**:多维度/综合性(看板/总览),拆 2-4 个自包含子查询(默认当年时间范围),由 Analyst 并行执行(见第七节);
- **ambiguous**:关键概念模糊(如"大区"指销售区域还是地理区域),返回澄清问题与 2-4 选项反问,而非瞎答。
Prompt 内嵌**前瞻性权限约束**(`:63-74`):个人级表禁止作 GROUP BY 维度,且"通过 JOIN 关联表间接引用也不行"——以约束传递性杜绝语义逃逸。
*设计依据*:把"复杂问题硬塞一条 SQL"必然漏维度。本系统**先理清意图结构再查**:复杂并行、模糊澄清,结果更完整可信;权限约束前置到意图层,与执行层行级注入形成双重保险。
#### 6.5 数智权限架构层内建(详见第八节)
权限不是事后过滤,而是**嵌入生成链路**:
- **表级**:`get_allowed_data_tables`(`smart_permission_service.py:393`)直接剔除无权表,LLM 根本不知其存在,无从生成越权 SQL;
- **行/列级**:`_apply_permission_to_schema`(`engine.py:1267-1335`)逐行改写域 Schema——删除禁止查询字段、为每张表追加 `WHERE perm_dept IN (...)` / `perm_emp = ?` 注解、权限字段 `perm_dept/perm_emp` 不出现在 Schema 中;
- **约束传递性**:个人级表不仅自身禁查,经 JOIN 间接引用其维度也被禁止(与 6.4 意图层约束一致)。
*设计依据*:传统 RBAC 管得住菜单、管不住 AI 自由组合表。本系统让**无权数据从视野消失、有权数据天然受限**,从架构层把越权路径堵死,是"AI 时代安全底座"在数据查询场景的落地。
#### 6.6 结果缓存与确定性生成
- **查询缓存**:`query_cache`(`code/cache/query_cache.py`,`QUERY_CACHE_ENABLED=True`、`TTL=300s`、`LRU 1000`,`config.py:208-214`)缓存"自然语言 → SQL + 结果"。**与知识库不缓存形成刻意对比**——DB 查询结果稳定、命中率高,缓存降本显著;而知识库文档持续变化,缓存命中率低且 TTL 管理复杂(见 5.11 第 6 点、架构文档第 10 节)。
- **确定性生成**:两阶段选表短路、Few-Shot 阈值(`FEW_SHOT_SCORE_THRESHOLD=0.65`、`FEW_SHOT_TOP_K=3`、`FEW_SHOT_MAX_INJECT=2`)等约束,使生成**确定且低延迟**,避免同问结果漂移。
*设计依据*:企业级问数要求"同样的问题得到稳定、可复现的答案"。缓存降本 + 约束注入确定性,二者共同把系统从"偶尔对的黑盒"变成"稳定可靠的服务"。
#### 6.7 设计要点总结
1. **域路由降本**:固定域 Prompt + 百炼 KV 缓存命中≈100%,高频查询成本仅原价 10%,是企业大规模问数的经济性命门。
2. **两阶段选表 + 短路**:粗排保召回、精排保精度、高置信短路砍成本、多级回退保可用——准且快。
3. **SQL 自修正闭环**:错误分类→诊断→本地/LLM 修正 ≤3 轮,加生成安全网,可诊断、可自愈。
4. **意图先行**:single/multi/ambiguous 三分流,复杂并行、模糊澄清,结果完整可信。
5. **权限架构层内建**:表级剔除 + 行/列级注入 + 约束传递性,越权 SQL 根本生成不出。
6. **缓存与确定性**:稳定结果缓存降本,约束注入保证生成确定性,与知识库不缓存形成对比。
7. **可观测可治理**:全链路 Trace 落盘每环、阈值集中可调、表描述/Few-Shot 经 UI 维护——不是黑盒。
---
## 快速上手
### 前置条件
- **Python 3.10+**
- **MySQL 8.0+**
- **Node.js 18+**
- **Windows / Linux / macOS**
### 启动命令
```bash
# 1. 创建虚拟环境并安装依赖
conda create -n rag-iss python=3.10 -y
conda activate rag-iss
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 2. 导入数据库
mysql -u root -p rag-iss < sql/db.sql
mysql -u root -p rag-iss < sql/006_capability_config.sql
mysql -u root -p rag-iss < sql/007_seed_capability_data.sql
# 3. 配置环境变量
# 编辑 .env 文件,填入阿里百炼 API Key 和 MySQL 连接信息
# 4. 启动后端(终端 1)
conda activate rag-iss
uvicorn code.api.main:app --host 0.0.0.0 --port 8000 --reload --reload-dir code
# 5. 启动前端(终端 2)
cd dialogue-frontend
npm install
npm run dev
```
### 访问地址
| 页面 | 地址 |
|------|------|
| 数智分析师对话页 | http://localhost:5173 |
| 管理后台 | http://localhost:5173/admin |
| Swagger API 文档 | http://localhost:8000/docs |
| 默认管理员 | `admin` / `666666` |
---
## 使用指南
### 数智分析师对话
访问 `http://localhost:5173`:
1. 在输入框输入问题,或点击快捷推荐问题卡片直接触发对话
2. 可通过「能力」按钮锁定特定能力(知识图谱 / 智能问数 / 综合报表 / 联网搜索)
3. 支持上传图片进行多模态对话
4. 报表模式下:报告文本实时流式输出 → 看板后台异步生成 → 就绪后自动展开
### 管理后台
访问 `http://localhost:5173/admin` 登录后:
- **系统管理**:用户(关联员工自动带出部门)、角色、菜单、部门、岗位、字典的完整 CRUD
- **员工管理**:员工信息 CRUD,部门树形选择器,修改 dept_id 自动同步到 sys_user
- **文档管理**:构建/重建知识库向量索引,查看文件变更状态
- **能力配置**:管理能力路由示例文本和变体,智能生成变体,重建向量索引
- **知识图谱**:实体/关系管理和图谱可视化
- **系统监控**:ChromaDB 健康检测、孤儿 segment/domain 清理
- **数智权限**:权限规则管理、角色配置
- **系统配置**:业务字段配置管理
### 首次使用流程
1. 配置 `.env` 文件中的阿里百炼 API Key 和 MySQL 连接信息
2. 执行 SQL 脚本导入数据库表结构和测试数据
3. 启动后端和前端
4. 登录管理后台,在「文档管理」中构建知识库
5. 在「能力配置」中重建能力路由示例库
6. 访问对话页开始使用
---
## 系统架构
系统采用分层 + 插件式能力引擎架构设计:
- **前端展示层**:Vue 3 + Element Plus 构建的对话界面和管理后台,含 ECharts 可视化看板
- **API 服务层**:FastAPI 提供 REST / SSE 接口,支撑实时流式交互
- **能力路由层**:智能路由 + 追问决策 —— LLM 作为决策器,综合动作词匹配、语义向量检索等多维信息,引导式选择最合适的能力引擎
- **插件式能力引擎层**:各能力引擎独立封装、可组合、可单独使用
| 能力引擎 | 核心功能 |
|----------|----------|
| **Knowledge** | 知识库 RAG 检索问答(向量+关键词混合检索) |
| **NL2SQL** | 自然语言转SQL、智能问数、会话意图引导+追问 |
| **Analyst/Report** | 多子查询拆解+并行执行、LLM 看板方案生成、权限注入 |
| **Knowledge Graph** | 知识图谱问答(实体关系检索) |
| **Web Search** | 联网搜索与结构化整理 |
| **Multi-modal** | 多模态理解(图片+文字)|
- **数据存储层**:MySQL 业务数据库 + ChromaDB 向量知识库
### 能力路由决策流程
```mermaid
graph LR
A[用户输入] --> B[动作词匹配]
A --> C[语义向量检索]
B --> D[LLM 路由决策器]
C --> D
D --> E[知识库问答]
D --> F[智能问数 NL2SQL]
D --> G[综合报表 Analyst]
D --> H[联网搜索]
D --> I[知识图谱]
D --> J[普通对话]
D --> K{不确定?} --> L[追问用户]
```
路由决策器基于多层信息(动作词命中、语义相似度、上下文历史)综合分析,当置信度不足时主动追问。
### NL2SQL 执行链路(含权限注入)
```mermaid
graph TB
subgraph 阶段1_意图分析
A1[用户查询] --> A2[会话意图判定 new/continue]
A2 --> A3[能力路由 → NL2SQL]
A3 --> A4[NL2SQL 引擎]
end
subgraph 阶段2_权限过滤
A4 --> B1[表级权限过滤
剔除无权限表]
B1 --> B2[行/列级权限注入
perm_dept / perm_emp / 被拒列]
B2 --> B3[Prompt 构建
system + user_msg]
end
subgraph 阶段3_执行
B3 --> C1[LLM 生成 SQL]
C1 --> C2[执行 SQL]
C2 --> C3[结果脱敏
剔除禁止列]
C3 --> C4[返回给用户]
end
```
### 报表分析权限注入链路
```mermaid
graph TB
subgraph plan_阶段
D1[用户: 销售综合看板] --> D2[意图分析 prompt
注入表格权限说明]
D2 --> D3[LLM 拆分子查询
禁止 products 作为分析维度]
end
subgraph exec_阶段
D3 --> E1[各子查询并行执行
权限注入 + 脱敏]
E1 --> E2[Summalize LLM]
E2 --> E3[看板方案生成
LLM 设计 charts]
E3 --> E4[前端可视化呈现]
end
```
---
## 数智权限体系
系统实现了**表级、行级、列级**三层数据权限控制,在 SQL 层面为 LLM 提供横向和纵向的双重权限管控。
### 三层权限架构
```mermaid
graph TB
subgraph 第1层_表级
A[用户请求] --> B[get_allowed_data_tables]
B --> C[匹配权限角色规则]
C --> D[剔除无权限表]
end
subgraph 第2层_行列级
D --> E[get_row_level_conditions]
E --> F[perm_dept IN ...
perm_emp = ?
被拒列列表]
F --> G[注入到 LLM prompt
system 示 user_msg]
end
subgraph 第3层_结果脱敏
G --> H[LLM 生成 SQL → 执行]
H --> I[扫描结果列
删除禁止列]
I --> J[返回脱敏后数据]
end
```
### 权限控制维度
| 维度 | 控制手段 | 实现方式 |
|------|----------|----------|
| **横向(表级)** | 过滤传给 LLM 的业务地图表集合 | 根据权限角色计算可访问的表集合 `get_allowed_data_tables()` |
| **纵向(行级)** | 动态注入 WHERE 条件 | `perm_dept IN (...)` / `perm_emp = ?` |
| **列级** | Prompt 约束 + 后处理扫描 | Prompt 中告知 LLM 禁止列 + SQL 执行后扫描剔除 |
### 权限注入链路(报表场景)
**阶段 1 — 意图分析(plan 阶段)**:在 `filtered_biz_map_text` 末尾追加权限说明,让 LLM 拆解子查询时就知道各表的权限级别,避免将个人级表(如 products)作为分析维度:
```
## 表格权限说明(子查询规划参考)
- pipeline: 部门级权限 → 只能看到本部门数据
- projects: 部门级权限 → 只能看到本部门数据
- products: ⛔ 个人级权限 → 禁止作为独立分析维度出现在子查询中
```
**阶段 2 — 子查询执行(exec 阶段)**:每次执行 SQL 前注入行级条件 + 禁止列,执行后脱敏。
### 权限合并规则
- **OR 策略**:用户拥有多个角色时,任一角色允许即可访问
- **Deny 优先**:Deny 规则始终优先于 Allow,确保安全性
- **懒加载 + 缓存**:首次查询时展开权限规则,结果缓存复用
---
## LLM 预热与 KV 缓存加速
针对 LLM 首次调用耗时较长的问题(如意图分析 12.4s),系统内置了 KV 缓存预热机制:
### 预热策略
- **后台线程预热**:服务启动后 5 秒,后台自动执行一次简单查询,建立百炼 KV 缓存
- **可配置开关**:开发环境下可通过 `LLM_WARMUP_ENABLED=False` 关闭预热,避免频繁重启导致的 Token 浪费
- **多域预热**:支持配置多个域代码按序预热,`LLM_WARMUP_DOMAINS=sales,marketing`
- **稳定 Prompt 优先**:系统 Prompt 部分(业务地图、权限规则)不变,大比例命中缓存
### 效果
| 阶段 | 无缓存 | 有缓存 |
|------|--------|--------|
| 意图分析(首次) | 12.4s | 12.4s(建立缓存) |
| 意图分析(后续) | 12.4s | 2-3s(命中缓存) |
---
## 多模型配置
支持为不同场景独立配置模型:
| 模型用途 | 配置项 | 典型模型 |
|----------|--------|----------|
| 通用对话/分析 | `LLM_MODEL` | `qwen-plus-2025-12-01` |
| 多模态视觉理解 | `VISION_MODEL` | `qwen-vl-plus` |
| 文档/图片 OCR | `OCR_MODEL` | `qwen-vl-max` |
复杂任务(意图分析、子查询拆解)使用强模型,简单任务(通用对话)使用轻模型,既保证效果又控制成本。
---
## 开发参考
### API 端点
| 前缀 | 模块 | 说明 |
|------|------|------|
| `/api/chat` | 对话 | SSE 流式对话、看板状态轮询 |
| `/api/sessions` | 会话 | 列表 / 创建 / 详情 / 删除 / 重命名 |
| `/api/upload` | 图片 | 上传(多张) / 删除 |
| `/api/knowledge` | 知识库 | 文档管理、统计、增量/全量构建、能力配置 CRUD |
| `/api/kg` | 知识图谱 | 实体/关系/文档 CRUD、图谱数据、目录树 |
| `/api/system` | 系统管理 | 登录/用户/角色/菜单/部门/岗位/字典/员工 完整 CRUD |
| `/api/system/employees` | 员工管理 | 员工分页 CRUD,部门树形选择,dept_id 同步到 sys_user |
| `/api/system/smart-permission` | 数智权限 | 权限规则管理、角色配置 |
| `/api/system/config` | 系统配置 | 业务字段配置管理 |
| `/api/dashboard` | 看板 | 看板方案生成 / 重设(LLM 驱动) |
| `/api/domain-def` | 数据域 | 域定义 CRUD、域表配置、域索引重建 |
| `/api/system/chroma` | 向量库监控 | ChromaDB 健康检测、孤儿 segment/domain 清理 |
Swagger 文档:`http://localhost:8000/docs`
### 数据库配置
```env
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=password
MYSQL_DATABASE=rag-iss
MYSQL_CHARSET=utf8mb4
```
**业务表:**
| 表名 | 说明 | 数据量 |
|------|------|--------|
| `employees` | 员工信息表 | 16 条 |
| `products` | 产品信息表 | 10 条 |
| `pipline` | 商机表 | 25 条 |
| `projects` | 项目表 | 20 条 |
| `projects_products` | 项目产品关联表 | 36 条 |
**系统管理表:** 兼容 RuoYi-Vue 架构,含 `sys_user`、`sys_role`、`sys_menu`、`sys_dept`、`sys_post`、`sys_dict_type`、`sys_dict_data`、`sys_role_menu`、`sys_user_role` 等。
**知识图谱表:** `kg_entities`(实体树)、`kg_relations`(实体关系)、`kg_documents`(实体关联文档)。
**能力配置表:** `capability_descriptions`(能力描述)、`capability_examples`(向量化示例)。
---
## 常见问题
### Q: 启动报 `Invalid API-key`?
检查 `.env` 中的 `DASHSCOPE_API_KEY`。
### Q: 知识库检索不准确?
调整 `CHUNK_SIZE`(默认 800)和 `CHUNK_OVERLAP`(默认 80),或重建向量索引。
### Q: 能力路由不匹配?
在管理后台「能力配置」页面管理示例文本和变体,点击「重建能力路由示例库」更新向量索引。
### Q: 如何重建知识库?
在「文档管理」页面先「清空知识库」,再「全量构建」。
### Q: 数据库维度变更?
系统启动时自动检测 Embedding 维度,变更时自动重建 collection。
### Q: 启动后页面空白?
检查前端 dev server 是否正常启动(`localhost:5173`),后端是否正常(`localhost:8000/docs` 可访问)。
### Q: LLM 调用太慢?
首次调用会建立 KV 缓存(约 12s),后续同类型查询仅 2-3s。开发环境下可通过 `.env` 设置 `LLM_WARMUP_ENABLED=False` 关闭预热。
### Q: 用户管理页面看不到数据?
检查浏览器控制台是否有网络错误。员工管理、用户管理页面需要 `/api/system/employees`、`/api/system/depts`、`/api/system/roles` 等多个接口同时正常返回。可在 Swagger 文档中逐个测试。
### Q: 报表中看不到产品维度的数据?
`products` 表配置为个人级权限,禁止作为独立分析维度。如果查询涉及该表维度,LLM 会自动用其他维度(部门/区域/项目状态)替代。
### Q: 修改员工部门后系统用户部门未更新?
系统会自动将 `employees.dept_id` 的变更同步到 `sys_user`(通过员工关联),无需手动修改。
### Q: 域向量库数量与数据库不一致?
系统管理 → 系统监控 → 页面会显示"孤儿域"数量和域名列表,支持一键清理。域定义更新时(重命名/启用/禁用)会自动同步向量库。
---
## License
MIT