# yuncheng-agent-qa
**Repository Path**: liochaoo/yuncheng-agent-qa
## Basic Information
- **Project Name**: yuncheng-agent-qa
- **Description**: 基于 Spring AI + 阿里云百炼 + Qdrant 向量库的企业级知识问答系统
- **Primary Language**: Java
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 7
- **Forks**: 5
- **Created**: 2026-04-11
- **Last Updated**: 2026-04-29
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# AI 智能助手 - 基于 RAG 的知识问答系统
基于 Spring AI + 阿里云百炼 + Qdrant 向量库的企业级知识问答系统
🌐 云程低代码平台官网 |
🤖 在线体验智能客服
---
RAG 核心处理流程
---
## 目录
- [项目简介](#项目简介)
- [技术架构](#技术架构)
- [功能特性](#功能特性)
- [快速开始](#快速开始)
- [系统配置](#系统配置)
- [使用指南](#使用指南)
- [接口文档](#接口文档)
- [系统截图](#系统截图)
- [RAG 核心处理流程](#rag-核心处理流程)
---
## 项目简介
本项目是一个基于 **RAG(检索增强生成)** 技术的企业级 AI 知识问答系统,是 [云程低代码平台](http://www.yunchengxc.com/) 官网智能客服系统的开源版本。系统支持从语雀文档或本地 Markdown 文件构建知识库,通过向量检索 + 大语言模型生成精准回答。
> 想先体验效果?点击访问 [云程官网智能客服](https://www.yunbangong100.com:31190/agent/) 在线体验
### 核心能力
- **智能问答**:基于知识库的智能问答,支持多轮对话
- **文档管理**:支持语雀文档和本地 Markdown 文档导入
- **向量检索**:混合检索(稠密向量 + 稀疏向量 + 重排序)
- **权限控制**:管理员专属的数据管理功能
- **流式响应**:SSE 流式输出,实时展示 AI 回答
---
## 技术架构
### 后端技术栈
| 技术 | 版本 | 说明 |
|------|------|------|
| Java | 21 | 编程语言 |
| Spring Boot | 3.5.11 | 基础框架 |
| Spring AI | 1.1.2 | AI 框架 |
| Spring AI Alibaba | 1.1.2.2 | 阿里云 AI 适配 |
| MyBatis-Plus | 3.5.16 | ORM 框架 |
| MySQL | 8.x | 关系型数据库 |
| Qdrant | 1.x | 向量数据库 |
| JWT | 4.4.0 | 身份认证 |
### 前端技术栈
| 技术 | 版本 | 说明 |
|------|------|------|
| Vue | 3.5.30 | 前端框架 |
| TypeScript | 5.9.3 | 类型语言 |
| Element Plus | 2.13.6 | UI 组件库 |
| Vite | 8.0.1 | 构建工具 |
| Pinia | 3.0.4 | 状态管理 |
### 系统架构图
系统架构图
---
## 功能特性
### 用户端功能
- **密码登录**:支持账号密码 + 滑块验证码登录
- **体验登录**:一键体验账号登录(无需密码)
- **智能对话**:流式 AI 回答,支持多轮对话
- **历史会话**:查看历史对话记录,支持会话切换
- **个人资料**:查看剩余问答次数、修改密码
### 管理端功能(仅管理员)
- **加载语雀文档**:从语雀知识库批量导入文档
- **加载本地文档**:从本地 Markdown 目录导入文档
- **同步向量库**:将审核通过的知识片段同步到向量库
- **清空向量库**:清空所有向量数据(危险操作)
---
## 快速开始
### 环境要求
- JDK 21+
- Node.js 18+
- MySQL 8.0+
- Docker(用于运行 Qdrant)
### 1. 克隆项目
```bash
git clone https://github.com/your-repo/yuncheng-agent-qa.git
cd yuncheng-agent-qa
```
### 2. 启动 Qdrant 向量库
使用 Docker 快速启动:
```bash
docker run -d \
--name qdrant \
-p 6333:6333 \
-p 6334:6334 \
-v $(pwd)/qdrant_storage:/qdrant/storage \
qdrant/qdrant:latest
```
参数说明:
- `-p 6333:6333`:HTTP API 端口
- `-p 6334:6334`:gRPC API 端口
- `-v`:持久化存储目录
### 3. 初始化数据库
```bash
# 1. 创建数据库
mysql -u root -p -e "CREATE DATABASE qa CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 2. 执行建表脚本
mysql -u root -p qa < db/create_table.sql
```
### 4. 配置环境变量
创建 `backend/.env` 文件或在系统中设置以下环境变量:
```bash
# MySQL 配置
export MYSQL_HOST=127.0.0.1
export MYSQL_PORT=3306
export MYSQL_DB=qa
export MYSQL_USERNAME=root
export MYSQL_PASSWORD=your_password
# 阿里云百炼配置(必需)
export DASHSCOPE_API_KEY=sk-your-api-key
# Qdrant 配置
export QDRANT_HOST=127.0.0.1
export QDRANT_PORT=6334
export QDRANT_API_KEY=your-qdrant-api-key # 如设置了API Key
# JWT 密钥(建议修改)
export JWT_SECRET=your-jwt-secret-key
# 语雀配置(可选,如需导入语雀文档)
export YUQUE_AUTH_TOKEN=your-yuque-token
export YUQUE_REPO_NAMESPACE=your-namespace/your-repo
# 本地文档目录(可选,如需导入本地文档)
export LOCAL_MARKDOWN_DIR=/path/to/your/docs
```
### 5. 启动后端服务
```bash
cd backend
mvn clean install
mvn spring-boot:run
```
后端服务默认运行在 `http://localhost:9090/agent`
### 6. 启动前端服务
```bash
cd frontend
npm install
npm run dev
```
前端服务默认运行在 `http://localhost:3000/agent`
### 7. 访问系统
打开浏览器访问 `http://localhost:3000/agent`
---
## 系统配置
### 默认账号
| 账号类型 | 手机号 | 密码 | 说明 |
|---------|--------|------|------|
| 管理员 | 19999999999 | 123456 | 拥有文档管理、向量库操作权限 |
| 体验账号 | 18888888888 | 无需密码 | 一键登录体验 |
> **注意**:管理员手机号在 `application-dev.yml` 的 `qa.admin.phones` 中配置。
### 问答次数配置
每个账号的问答次数存储在 `qa_user` 表的 `res_available` 字段中,可通过 SQL 修改:
```sql
-- 增加指定用户的问答次数
UPDATE qa_user SET res_available = res_available + 100 WHERE user_phone = '19999999999';
```
### 系统提示词配置
系统提示词定义了 AI 助手的行为规范,可在以下文件中修改:
| 提示词类型 | 文件路径 | 说明 |
|-----------|---------|------|
| RAG 系统提示词 | `backend/src/main/java/com/yuncheng/agent/qa/config/agent/ReactAgentConfig.java` | 定义 AI 回答的核心规则、检索策略、兜底话术 |
| 问题重写提示词 | `backend/src/main/java/com/yuncheng/agent/qa/config/agent/ReactAgentConfig.java` | 定义问题重写专家的行为 |
| 产品信息 | `backend/src/main/resources/information/yuncheng-info.md` | 产品基本介绍,可替换为自己的产品信息 |
---
## 使用指南
### 接入语雀文档
1. **获取语雀 Token**
- 登录语雀,进入「设置」→「开发者设置」→「个人访问令牌」
- 创建新 Token,复制保存
2. **配置环境变量**
```bash
export YUQUE_AUTH_TOKEN=your-token
export YUQUE_REPO_NAMESPACE=your-namespace/your-repo
```
3. **加载文档**
- 使用管理员账号登录系统
- 点击右上角头像 → 「加载语雀文档」
- 等待处理完成
4. **审核并同步**
```sql
-- 将所有知识片段标记为审核通过
UPDATE qa_pair SET check_status = 'CHECKED' WHERE check_status = 'UNCHECKED';
```
- 点击「同步向量库」完成向量化
### 接入本地 Markdown 文档
1. **准备文档**
- 确保文档为 `.md` 格式
- 文档需包含一级标题(`# 标题`)
- 建议按功能模块组织文档
2. **配置目录**
```bash
export LOCAL_MARKDOWN_DIR=/path/to/your/markdown/files
```
3. **加载文档**
- 使用管理员账号登录
- 点击「加载本地文档」
- 系统自动处理目录下所有 `.md` 文件
4. **审核并同步**(同上)
### 文档处理流程
文档处理流程
### 重要 SQL 语句
```sql
-- 查看所有待审核的知识片段
SELECT * FROM qa_pair WHERE check_status = 'UNCHECKED';
-- 批量审核通过
UPDATE qa_pair SET check_status = 'CHECKED' WHERE check_status = 'UNCHECKED';
-- 查看向量同步状态
SELECT
check_status,
vector_status,
COUNT(*) as count
FROM qa_pair
GROUP BY check_status, vector_status;
-- 查看用户剩余次数
SELECT user_phone, nickname, res_available, res_used FROM qa_user;
```
---
## 接口文档
### 主要 Controller 接口
#### 1. 用户模块 (UserController)
| 接口 | 方法 | 说明 |
|------|------|------|
| `POST /api/user/login` | 密码登录 | 账号密码 + 滑块验证码 |
| `POST /api/user/login/demo` | 体验登录 | 无需密码,一键登录 |
| `GET /api/user/profile` | 获取用户信息 | 包含剩余次数等 |
| `POST /api/user/password` | 修改密码 | 需登录 |
#### 2. 问答模块 (QaController)
| 接口 | 方法 | 说明 |
|------|------|------|
| `POST /api/qa/chat` | 流式对话 | SSE 流式返回 |
| `GET /api/qa/sessions` | 获取会话列表 | 当前用户的所有会话 |
| `GET /api/qa/history/{chatId}` | 获取对话历史 | 指定会话的详细对话 |
#### 3. 文档模块 (DocumentController) - 仅管理员
| 接口 | 方法 | 说明 |
|------|------|------|
| `POST /api/document/load/yuque` | 加载语雀文档 | 从语雀批量导入 |
| `POST /api/document/load/local` | 加载本地文档 | 从本地 Markdown 导入 |
#### 4. 向量模块 (VectorController) - 仅管理员
| 接口 | 方法 | 说明 |
|------|------|------|
| `POST /api/vector/sync` | 同步向量库 | 将审核通过的数据向量化 |
| `POST /api/vector/clear` | 清空向量库 | 危险操作,谨慎使用 |
#### 5. 验证码模块 (CaptchaController)
| 接口 | 方法 | 说明 |
|------|------|------|
| `POST /api/captcha/get` | 获取滑块验证码 | 返回滑块图片 |
| `POST /api/captcha/check` | 验证滑块 | 返回验证结果 |
---
## 系统截图
登录页面
智能对话页面
管理员功能菜单
---
## 项目结构
```
yuncheng-agent-qa/
├── backend/ # 后端项目
│ ├── src/main/java/com/yuncheng/agent/qa/
│ │ ├── config/ # 配置类
│ │ │ ├── agent/ # AI Agent 配置
│ │ │ └── QaProperties.java # 业务配置属性
│ │ ├── controller/ # 控制器层
│ │ │ ├── CaptchaController.java
│ │ │ ├── DocumentController.java
│ │ │ ├── QaController.java
│ │ │ ├── UserController.java
│ │ │ └── VectorController.java
│ │ ├── service/ # 服务层
│ │ ├── mapper/ # 数据访问层
│ │ ├── model/ # 数据模型
│ │ └── tools/ # 工具类
│ ├── src/main/resources/
│ │ ├── application-dev.yml # 开发环境配置
│ │ └── information/
│ │ └── yuncheng-info.md # 产品信息(可替换)
│ └── pom.xml
├── frontend/ # 前端项目
│ ├── src/
│ │ ├── api/ # API 接口封装
│ │ ├── components/ # 组件
│ │ ├── stores/ # Pinia 状态管理
│ │ └── views/ # 页面视图
│ ├── package.json
│ └── vite.config.ts
├── db/
│ └── create_table.sql # 数据库建表脚本
├── doc/
│ └── images/ # 文档图片
├── README.md
└── .gitignore
```
---
## RAG 核心处理流程
本系统采用企业级 RAG(检索增强生成)架构,以下详细介绍文档处理到问答的完整流程。
### 一、文档切片规则
#### 1.1 清洗规则
文档加载后首先进行清洗处理:
| 清理项 | 处理方式 |
|--------|----------|
| HTML 标签 | 使用 Jsoup 提取纯文本 |
| Markdown 图片 | `` 完全移除 |
| HTML 注释 | `` 移除 |
| 代码块 | 保护后恢复(占位符替换) |
| 行内代码 | 保护后恢复 |
| 连续空行 | 3 个及以上合并为 2 个 |
| 行尾空格 | 逐行去除 |
#### 1.2 切片策略
清洗后的文档采用三级递进切片策略:
| 层级 | 触发条件 | 处理方式 |
|------|----------|----------|
| **一级标题** | 文档存在 `# ` 标题 | 按 H1 切分,保留上下文路径 |
| **二级标题** | H1 段落 > 800 字符 | 按 `## ` 继续切分 |
| **段落拆分** | H2 段落 > 800 字符 | 按双换行分段 |
| **强制拆分** | 单段落 > 800 字符 | 按句子边界强制切分 |
#### 1.3 字数控制
- **最大块大小**: 800 字符
- **拆分优先级**: 优先在句子边界(。!?.!?)处截断
- **边界容差**: 不超过最大大小的 20%(160 字符)
### 二、信息提取规则
#### 2.1 标题选择优先级
1. 格式化后的 H1/H2 标题
2. 目录路径(如:功能模块 > 子模块)
3. 默认名称:"未命名知识片段"
#### 2.2 关键词提取
**权重分配**(多维度打分):
| 来源 | 权重 | 说明 |
|------|------|------|
| 标题 | 20 | 业务短语候选 |
| 目录路径 | 14 | 业务短语候选 |
| 正文 | 4 | 业务短语候选 |
| 标题 | 12 | 结构化候选(驼峰、连字符等)|
| 目录路径 | 8 | 结构化候选 |
| 正文 | 1 | 文本候选 |
**过滤规则**:
- 长度 2-20 字符
- 排除纯数字
- 排除停用词("的"、"了"、"和"等)
- 排除通用词("如下图所示"、"我们"等)
- 英文词需命中白名单
**输出**: 默认提取 6 个关键词,逗号分隔
#### 2.3 摘要提取
**长度控制**:
- 最大长度:120 字符
- 最小长度:40 字符
**生成策略**:
1. 优先从正文提取前 120 字符
2. 在标点符号(。;!?.)处智能截断
3. 格式:`{标题} - {摘要}`
4. 兜底:取正文前 40-120 字符
### 三、存储规则
#### 3.1 数据表结构 (`qa_pair`)
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `id` | varchar(36) | 主键(UUID)|
| `source_id` | varchar(36) | 关联文档源 ID |
| `feature_title` | text | 功能点标题 |
| `feature_keyword` | text | 功能点关键字(逗号分隔)|
| `feature_summary` | text | 功能点摘要 |
| `feature_content` | text | 功能点完整内容 |
| `check_status` | varchar(32) | 审核状态:UNCHECKED/CHECKED/REJECTED |
| `vector_status` | varchar(32) | 向量化状态:UNVECTORIZED/VECTORIZED/FAILED |
| `original_link` | text | 原始文档链接 |
| `vector_point_id` | varchar(255) | Qdrant 向量点 ID |
#### 3.2 状态流转
```
文档加载 → UNCHECKED/UNVECTORIZED
↓
审核通过 → CHECKED/UNVECTORIZED
↓
同步向量库 → CHECKED/VECTORIZED
```
### 四、向量化规则
#### 4.1 向量模型
- **模型**: `text-embedding-v3`(阿里云百炼)
- **批次大小**: 20 条/批次
#### 4.2 正文组织格式
```
[TITLE]: {feature_title}
[KEYWORDS]: {feature_keyword}
[SUMMARY]: {feature_summary}
[CONTENT]: {feature_content}
```
#### 4.3 元数据结构
| 字段 | 存储位置 | 用途 |
|------|----------|------|
| pairId | metadata | 关联数据库记录 |
| featureTitle | metadata | 稀疏检索字段 |
| featureKeywords | metadata | 稀疏检索字段 |
| featureSummary | metadata | 稀疏检索字段 |
| originalLink | metadata | 参考链接 |
**说明**: 完整结构化文本存储在 `Document.text` 中用于稠密向量编码
### 五、问答策略
#### 5.1 问题重写
**触发条件**: 存在历史问答记录时触发
**重写规则**:
- 话题判断:区分历史追问 vs 全新话题
- 指代消解:补全"它、这个、那个"等指代
- 术语标准化:使用平台标准功能术语
- 禁止扩写:绝不添加用户问题中不存在的信息
**示例**:
- 原问题:"这个怎么配置?"
- 重写后:"表单设计器的字段校验规则如何配置?"
#### 5.2 关键字拆分策略
**系统强制规则**:
- 每次检索只能是 **1 个核心功能点**,长度 ≤ 12 字
- 多要点问题必须先拆分,逐轮分别检索
- 严禁:长句、多词堆砌(如"表单+流程+配置")、宽泛词
#### 5.3 多轮检索流程
```
用户提问
↓
问题重写(基于历史上下文)
↓
问题拆分(多要点拆分为单点)
↓
逐轮检索(每轮检索一个单点,最多 10 轮)
↓
结果聚合(合并多轮检索结果)
↓
答案生成
```
### 六、检索规则(四阶段流水线)
| 阶段 | 名称 | 处理逻辑 | TopK |
|------|------|----------|------|
| **1** | 稠密召回 | 对 Document.text 做向量相似度搜索 | 24 |
| **2** | 稀疏召回 | 对 doc_content + title + keywords + summary 做文本匹配 | 48 |
| **3** | 融合打分 | BM25 词法打分 + 字段加权,归一化融合 dense + sparse 分数 | 12 |
| **4** | 重排序 | 基于 gte-rerank 模型,融合分数与重排分数加权混合 | - |
#### 6.1 稠密召回配置
```yaml
dense-recall-top-k: 24
similarity-threshold: 0.7
```
#### 6.2 稀疏召回配置
```yaml
sparse-recall-top-k: 48
sparse-scroll-batch-size: 128
min-query-term-length: 2
max-query-terms: 8
```
**查询分词**: 使用 Jieba 分词,限制最多 8 个词,最小词长 2 字符
#### 6.3 融合打分规则
**字段权重**:
```yaml
title-field-weight: 3.5 # 标题权重最高
keyword-field-weight: 2.8 # 关键词次之
summary-field-weight: 1.8 # 摘要再次
content-field-weight: 1.0 # 内容基础权重
```
**精确匹配加权**:
```yaml
exact-phrase-boost: 1.8 # 摘要/内容完整匹配加分
title-exact-match-boost: 1.4 # 标题完整匹配加分
keyword-exact-match-boost: 1.2 # 关键词完整匹配加分
```
**融合权重**:
```yaml
dense-weight: 0.45 # 稠密向量分数权重
sparse-weight: 0.55 # 稀疏检索分数权重
```
**融合公式**:
```
finalScore = 0.45 × normalizedDenseScore + 0.55 × normalizedSparseScore
```
#### 6.4 重排序规则
```yaml
rerank-enabled: true
rerank-model: gte-rerank
rerank-max-text-length: 1800
rerank-blend-weight: 0.7
```
**混合公式**:
```
finalScore = 0.7 × rerankScore + 0.3 × fusionScore
```
### 七、给到大模型的内容
检索结果按以下格式组织后提供给大模型:
```
--- Result 1 ---
Title: {功能点标题}
Keywords: {关键词}
Summary: {摘要}
Content: {内容前 1200 字符}
Score: {最终相关性分数}
Reference: {原始文档链接}
--- Result 2 ---
...
```
**系统提示词约束**:
- 必须基于检索结果回答,严禁编造
- 优先使用高 Score 的结果
- 必须标注参考来源(Reference)
- 无法回答时给出兜底话术
---
## 常见问题
### Q1: 如何修改系统提示词?
编辑 `backend/src/main/java/com/yuncheng/agent/qa/config/agent/ReactAgentConfig.java` 文件中的 `RAG_SYSTEM_PROMPT` 常量。
### Q2: 如何接入自己的语雀知识库?
1. 在语雀创建个人访问令牌
2. 配置 `YUQUE_AUTH_TOKEN` 和 `YUQUE_REPO_NAMESPACE` 环境变量
3. 重启后端服务
4. 使用管理员账号点击「加载语雀文档」
### Q3: 为什么加载文档后问答没有反应?
需要执行两步操作:
1. 在数据库中将 `qa_pair` 表的 `check_status` 更新为 `CHECKED`
2. 点击「同步向量库」按钮
### Q4: 如何增加用户的问答次数?
执行 SQL:
```sql
UPDATE qa_user SET res_available = 100 WHERE user_phone = '用户手机号';
```
### Q5: 向量库连接失败怎么办?
1. 检查 Qdrant 是否已启动:`docker ps | grep qdrant`
2. 检查 `QDRANT_HOST` 和 `QDRANT_PORT` 配置是否正确
3. 检查防火墙是否放行对应端口
---
## 许可证
本项目采用 [MIT License](LICENSE) 开源协议。
---
## 联系方式
如有问题或建议,欢迎提交 Issue 或 Pull Request。
---
Made with ❤️ by Yuncheng