企业智能助手
来源地址:https://gitee.com/zhu_xin/ai-serve-desk
架构总览
一、项目定位
一个面向企业内部的 LLM 智能中台:
| 能力 | 说明 |
|---|---|
| 知识问答 | 基于知识库的 RAG 检索,回答 WiFi、VPN、规章制度等问题 |
| 文档同步 | 接收飞书/钉钉文档变更 Webhook,向量库自动增删 (更新中) |
二、技术栈全景
## 后端 ┌─────────────────────────────────────────────┐ │ Python 3.11+ + venv 虚拟环境 │ ├─────────────────────────────────────────────┤ │ FastAPI 0.115.6 + sse-starlette 2.2.1 ← Web框架 + SSE流式输出 │ │ Uvicorn[standard] 0.34.0 ← ASGI服务器 │ │ python-multipart 0.0.20 ← 文件上传解析 │ ├─────────────────────────────────────────────┤ │ LangChain 1.2.12 + LangGraph 1.1.10 ← AI编排与Agent工作流 │ │ langchain-openai 1.2.2 ← OpenAI LLM + Embedding │ │ langchain-community 0.4.2 ← 向量库与工具扩展 │ ├─────────────────────────────────────────────┤ │ PostgreSQL 16 + pgvector 0.3.6 ← 向量数据库 │ │ psycopg[binary,pool] 3.2.3 ← PG异步驱动 │ │ SQLAlchemy[asyncio] 2.0.36 + SQLModel >=0.0.22 ← ORM模型 │ │ Alembic 1.14.0 ← 数据库版本迁移 │ ├─────────────────────────────────────────────┤ │ pydantic >=2.10.4,<3.0 + pydantic-settings >=2.10.1,<3.0 ← 数据校验与配置 │ │ python-dotenv 1.0.1 ← 环境变量加载 │ ├─────────────────────────────────────────────┤ │ pymupdf 1.28.2 + python-docx 1.1.2 + tiktoken 0.8.0 ← 文档解析 & Token统计 │ ├─────────────────────────────────────────────┤ │ tenacity 9.0.0 + httpx 0.28.1 + orjson 3.10.12 ← 重试、异步HTTP、高性能JSON │ ├─────────────────────────────────────────────┤ │ python-jose[cryptography] + bcrypt >=4.0.0 ← JWT鉴权、密码加密 │ │ redis >=5.0.0 ← 缓存/会话/令牌存储 │ │ fastapi-bizresponse v0.1.1 + rbac-core v0.1.0 ← 统一响应 & RBAC权限内核 │ ├─────────────────────────────────────────────┤ │ pytest + pytest-asyncio + ruff 0.8.4 ← 单元测试 & 代码格式化检查 │ └─────────────────────────────────────────────┘ ## 前端 ┌─────────────────────────────────────────────┐ │ Node.js + Vite + TypeScript 6.0 + Vue3 3.5 │ ├─────────────────────────────────────────────┤ │ Vue 3.5.41 ← 前端核心框架(Composition API)│ │ Vue Router 5.3.1 ← 路由管理、权限路由守卫 │ │ Pinia 4.0.3 ← 全局状态管理 │ ├─────────────────────────────────────────────┤ │ Ant Design Vue 4.2.6 ← UI组件库 │ │ @ant-design/icons-vue ← 图标库 │ │ @duxiaoma/adm-vue3 ← 后台管理基础封装 │ │ @duxiaoma/design-rbac ← RBAC权限组件 │ ├─────────────────────────────────────────────┤ │ Axios ^1.20.0 ← HTTP请求(支持SSE流式) │ │ Less 4.9.0 ← CSS预处理器 │ ├─────────────────────────────────────────────┤ │ Vite 8.2.2 ← 构建&开发服务 │ │ Vue-tsc 3.3.11 ← TS类型校验 │ ├─────────────────────────────────────────────┤ │ ESLint 10 + TS ESLint 插件 + eslint-plugin-vue ← 代码规范 │ │ @vitejs/plugin-vue ← Vite Vue编译插件 │ │ @types/node 24.13.3 ← Node类型声明 │ └─────────────────────────────────────────────┘三、目录架构(已按职责分层)
ai-serve-desk/ ├── backend/ ├── frontend/ ├── docs/四、核心请求流转(以一次对话为例)
用户发送:"公司WiFi怎么连?" │ ▼ ┌─────────────────────────────┐ │ POST /chat/stream │ │ SSE 流式接口(FastAPI) │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ chat_service(请求级会话) │ ← 1. 获取/创建会话(无则新建,标题取前30字) │ │ ← 2. 用户消息落库 │ │ ← 3. 建一条空的 AI 占位消息(status=STREAMING) └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ job_manager(ChatJob) │ ← 4. 创建后台 asyncio.Task 跑生成 │ │ 已存在则直接订阅(重试 / 多标签页 / 重复提交) └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ chat_runner(独立DB会话) │ ← 5. 从 DB 重建历史为 LangChain Message │ │ 排除 AI 占位;用户问题已在历史末尾,不重复传 └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ LangGraph 状态机 │ │ │ │ intent_classifier ──? LLM 只返回 JSON │ │ (10s超时,不开思考;失败默认 general) │ ▼ │ │ route_by_intent 按 intent 分流 │ │ │ │ ├─ knowledge │ │ │ ▼ │ │ │ rag_retrieve ──? knowledge_service.search │ │ │ (top_k=5, 相似度≥0.25, 可见性过滤) │ │ ▼ │ 检索失败降级为空结果,不阻断 │ │ rag_agent ──? 格式化知识片段(标注来源/相似度) │ │ │ 流式生成,60s超时 │ │ │ │ │ └─ general │ │ ▼ │ │ general_chat_agent ─? 直接流式闲聊,30s超时 │ │ │ │ ▼ │ │ END │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ chat_runner / chat_service │ ← 6. 正文约每 2s 节流增量落库(崩溃也保住内容) │ │ ← 7. 图结束:完整回复 + 思考落库,status=COMPLETE └─────────────────────────────┘ │ ▼ SSE 流式推送(前端逐句看到打字效果) ├── event: start (conversation_id) ├── event: user_message (用户消息ID) ├── event: resync (全量快照,断线重连时整体替换) ├── event: intent (knowledge / general) ├── event: knowledge (Top3 参考片段,仅 knowledge 分支,先于答案到达) ├── event: thinking (思考 delta,开启深度思考时才有) ├── event: message (正文 delta,逐句推送) └── event: done (complete)五、数据模型设计亮点
| # | 亮点 | 项目中的落地 | 设计价值 |
|---|---|---|---|
| 1 | CheckConstraint 替代数据库 ENUM | 所有状态 / 角色字段(users.status、messages.role/status、files.file_type等)统一用VARCHAR(20)+CheckConstraint,约束值由 PythonEnum动态拼接生成 | 枚举值增删无需ALTER TYPE,Alembic 迁移友好;Python Enum 与 DB 约束单一数据源,不漂移 |
| 2 | UUID 主键 + 数据库层生成 | 全部主键为pg.UUID,server_default=gen_random_uuid() | 全局唯一、分布式友好,不暴露业务量级与自增规律,对外暴露 ID 更安全 |
| 3 | 深度利用 PostgreSQL 原生能力 | 半结构化字段一律用JSONB(generation_config、token_usage、error_info、tool_calls.arguments/result、chunk.meta);时间统一TIMESTAMP(timezone=True) | 免建大量稀疏列,结构灵活且可查询 / 可索引;带时区时间戳杜绝时区错乱 |
| 4 | 向量检索内嵌数据库(pgvector) | file_chunks.embedding = Vector(维度从配置读取),并建ivfflat索引(vector_cosine_ops,lists=100) | 召回与业务数据在同一库完成,少维护一套向量库;ANN 索引保障大规模向量检索性能 |
| 5 | 消息版本树:自引用 + 主路径标记 | messages.parent_id自引用外键,重新生成 / 编辑形成兄弟分叉;is_active标记当前展示路径,action_type记录来源(initial/continue/edit/regenerate) | 完整保留历史版本,切换版本只改标记不删数据,是「重新生成 / 历史版本」功能的经典建模 |
| 6 | 面向真实查询路径的索引设计 | 部分索引WHERE is_pinned=true/false(置顶 / 普通会话分开排序)、WHERE is_active=true(消息主路径)、WHERE parent_id IS NOT NULL;复合索引(conversation_id, created_at) | 索引体积更小、命中更精准;每条索引都对应一个具体列表 / 排序场景,而非泛泛建索引 |
| 7 | 冗余缓存字段 + 触发器维护 | conversations.message_count、last_message_at由数据库触发器自动维护 | 会话列表免COUNT(*)/MAX()聚合,读多写少场景用空间换时间,列表渲染更快 |
| 8 | 文件两级模型 + 父子分块 | files(元数据 /extracted_text/ 可见性)与file_chunks(分块文本 / 向量)分离;file_chunks.parent_id自引用支持父子块;embedding可空 | 元数据与向化解耦,适配「上传→切分→嵌入」异步流水线;父子块支持小块召回、大块喂上下文 |
| 9 | 工具调用独立建模、全程可审计 | tool_calls独立成表,JSONB 存入参 / 结果 +result_summary卡片文本,含pending→running→success/error状态机和executed_at/completed_at | AI 行为可追溯、可观测;前端可渲染工具过程卡片 |
| 10 | 用约束在 DB 层兜底数据一致性 | chk_tool_calls_time_order:completed_at >= executed_at;枚举 Check 约束;多字段唯一约束(username/email/phone) | 非法数据在写入时即被拒绝,不依赖应用层自觉 |
| 11 | 权限 / 可见性下沉到数据层 | files.visibility(PRIVATE/PUBLIC)+ 可见性索引,RAG 检索在 SQL 层按用户过滤;用户表不再存角色,RBAC 解耦 | 越权检索在数据层就被挡住;权限模型与业务模型互不污染 |
| 12 | 级联策略按关系强弱精细区分 | 强归属用CASCADE(user→conversation→message、file→chunk);弱关联用SET NULL(files.message_id);message_share_items.message_id故意不加外键 | 归属数据随主删除、可恢复数据保留;分享链接不因消息物理删除而崩溃(容忍悬空引用) |
| 13 | 流式生成状态可持久化、可恢复 | messages.status状态机(pending/streaming/complete/stopped/error)+error_info,并记录thinking_content、thinking_duration_ms、model_name、token_usage | 支撑断线重连回放、断点续写、停止 / 重试;同时为成本核算与问题排查留全数据 |
| 14 | 分享主从模型 + 复合主键 | message_shares(短码share_code唯一)+message_share_items明细,明细以(share_id, message_id)复合主键 +display_order | 同时支持整会话分享与多消息勾选分享,结构简洁、天然防重复明细 |
| 15 | 统一审计规范与自文档化 | 全表统一created_at/updated_at(server_default+onupdate双保险);表、列、约束全部带comment;类型精挑(BIGINT存字节、Text存长文本、VARCHAR限长) | 数据字典即代码,新人可直接读库理解;默认值在 DB 层也成立,裸写 SQL 不出错 |
实现步骤
- 🚀 Step 1:环境初始化与项目骨架
- 📁 Step 2:数据库模型 + Alembic 迁移
- 🔐 Step 3:身份认证与权限路由
- 🧠 Step 4:RAG知识库检索与文档同步
- 📁 Step 5:LangGraph状态机与意图路由
- 🔧 Step 6:FastAPI路由层与SSE流式联调
- 🚀 Step 7:会话管理和分享管理
- 🗺️ Step 8:系统概览
- 📜 Step 9:工具调用日志
项目部署 - 完整使用步骤
请提前安装好下面全部软件,否则项目无法启动
- Python 3.11(不能低于 3.11,不要使用 3.12 + 版本,容易依赖报错)
- Node.js >=18.x(LTS 版本,推荐 18.20,不要使用 23 + 最新版)
- pnpm 包管理器
- PostgreSQL 16 数据库,必须安装 pgvector 向量扩展插件
- Redis(用于会话缓存、登录鉴权)
- 可访问的兼容 OpenAI 协议的 LLM 大模型 API、Embedding 向量模型 API
Windows 提示:PostgreSQL 安装完成后,需要单独编译安装 pgvector 插件;Linux 参考官方 pgvector 安装文档。
1. 拉取当前项目
gitclone https://gitee.com/zhu_xin/ai-serve-desk.gitcdai-serve-desk/backend(后端项目)2. 创建虚拟环境
参考文档:🚀 Step 1:环境初始化与项目骨架
# Windows / Mac /Linuxpython-mvenv .venv# Windows激活虚拟环境.venv\Scripts\activate# Mac/Linux激活虚拟环境source.venv/bin/activate✅ 激活成功后命令行前面会出现
(.venv)标识,后续 pip 命令全部在虚拟环境内执行
3. 安装项目依赖
pipinstall-rrequirements.txt如果国内下载慢,可以加上清华源:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
4. 配置 .env 环境变量
- 在 backend 目录新建
.env文件(注意是点开头,Windows 会看不到,关闭文件隐藏扩展名),.env示例模板:在 .env.example。 - 安装 PostgreSQL,自行部署;参考文档:🚀 Step 1:环境初始化与项目骨架.md#17-验证骨架
- 生成 SECRET_KEY
python-c"import secrets; print(secrets.token_urlsafe(32))"- 向量模型:当前使用千问向量模型,部署时请评估兼容性
- 安装 Redis;接入文档:Step3-身份认证与权限路由.md#35-接入redis
5. 执行 Alembic 数据库迁移
5.1 我的.alembic 内容要记得删除哟!或者你更直接!
alembic upgradehead5.2 如果你删除想直接自己弄。
alembic init .alembic直接查看 配置 Alembic 迁移。
大概会弄两个文件迁移,一个创建表,一个触发器!
6. 运行
python-mapp.main打开 http://127.0.0.1:8000/docs
使用注册接口(留的,后续会删除使用执行脚本的方式),注册一个用户,用来当超级管理员!复制用户id
7. 设置超级管理员
打开 scripts/init_rbac_admin.py
created=awaitrbac.grant_superuser("2954f755-4c8f-41c7-bae4-228c9d56a842")# 换成开始的用户id执行脚本(backend 目录,虚拟环境激活状态)
python-mscripts.init_rbac_admin控制台输出:超级管理员授权已新增/超级管理员权限已存在代表成功。
8. 设置 RBAC 权限系列一次性操作数据库
运行方式(必须在 backend 目录执行)
# 模拟运行(不写库,先看输出).venv/Scripts/python.exe scripts/migrate_rbac_data.py --dry-run# 真实写入数据库.venv/Scripts/python.exe scripts/migrate_rbac_data.py# 如果已经alembic建好rbac表.venv/Scripts/python.exe scripts/migrate_rbac_data.py --no-create-tables执行成功后的效果:
========== RBAC 数据迁移结果 ========== 资源行为 新增 1 / 跳过 6 / 对齐显示名 1 资源目录 新增 3 / 跳过 4 资源节点 新增 6 / 跳过 7 节点-行为绑定 新增 18 / 跳过 20 部门角色 新增 9 / 跳过 0 角色-资源授权 新增 107 / 跳过 0 ======================================9. 启动运行
python-mapp.main后端接口文档地址:http://127.0.0.1:8000/docs
💡 后端端口默认 8000;0.0.0.0 代表允许局域网其他机器访问;127.0.0.1 仅本机访问。
项目部署 - 前端
1. 拉取项目
已经一体 clone 整个仓库,无需重复 git clone
cdai-serve-desk/frontend2. 安装依赖
记得安装pnpm、Node >= 18.x
查看版本命令:node -v、pnpm -v
pnpminstall❗禁止混用 npm install 安装前端依赖,会出现依赖版本异常。
如果国内安装慢:pnpm config set registry https://registry.npmmirror.com
3. 本地开发运行
npmrun dev# 或者 pnpm dev❗常见部署问题排查
- pgvector 报错:vector 类型不存在
解决方案:PostgreSQL 执行
CREATE EXTENSION IF NOT EXISTS vector;
- pnpm install 报错
Node 版本过高 / 过低;删除 node_modules、pnpm-lock.yaml,重新 pnpm install
- RBAC 页面看不到菜单,提示无权限
确认已经执行
migrate_rbac_data.py;确认用户已经授予超级管理员;确认 alembic 迁移全部执行成功
- 聊天接口报错大模型调用失败
检查.env 里面 LLM 地址、API_KEY,确认网络可以访问大模型接口;检查模型名称是否正确
- 知识库上传文档,向量化失败
检查 Embedding 配置;确认向量维度和数据库定义匹配;检查大模型接口是否可以正常调用
- SSE 会话流式不输出消息
确认后端服务正常启动;检查前端 VITE_API_BASE_URL 地址;检查防火墙没有拦截 8000 端口