企业知识库问答系统 #
前后端分离的企业知识库助手:React 19 + Ant Design 6 + FastAPI + LangChain / LangGraph + PostgreSQL(pgvector + pg_jieba)。
系统定位:带权限、带记忆、带引用、可护栏 的企业 FAQ / 制度 / 工单助手。模型负责理解与组织语言;检索、部门 ACL、审计、危险操作与审批由后端代码保证。
| 模块 | 技术栈 |
|---|---|
| 前端 | React 19、TypeScript、Vite 8、Ant Design 6、Ant Design X、TanStack Query、React Router 7 |
| 后端 | FastAPI、SQLAlchemy 2、LangGraph(PostgresSaver + PostgresStore)、DashScope 嵌入/重排、DeepSeek 对话 |
| 数据 | 混合检索(向量 + BM25/jieba → RRF → 重排)、异步文档摄入、部门 ACL、审计事件 |
详细需求见 企业知识库需求文档.md,分步计划见 企业知识库开发计划.md(Step 01~28 已实现)。
目录 #
系统能力一览 #
| 能力 | 说明 | 前端入口 | 后端要点 |
|---|---|---|---|
| 知识问答(RAG) | 混合检索 + 带 [n] 引用生成;库外问题固定拒答 |
/chat |
thread_turn / rag.py |
| 多轮会话 | thread_id 隔离;PostgresSaver 跨刷新续聊 |
/chat/:threadId |
threads + checkpointer |
| SSE 流式 | token / tool / citation / done / error | 问答页默认流式 | messages:stream,见 docs/sse.md |
| Agent 工具 | 查单、计算、建工单、记笔记、发信等 | 问答页「工具步骤」开关 | agent_tools.py |
| 业务工作台 | 按角色聚合待办:待分配工单、SLA 超时、待我审批、待复审文档、未读通知;工单行可直达详情 | / |
workbench |
| 工单全生命周期 | 状态机、指派、优先级、SLA、回复与内部备注、事件时间线、满意度 | /tickets |
tickets + ticket_workflow |
| 订单查询 | 真实订单表;客服按单号/客户检索,员工只看本人,可退余额直接驱动退款 | /orders |
orders |
| 长期记忆 | 用户档案 + 笔记;跨会话注入 prompt | /me/profile |
PostgresStore |
| HITL 发信审批 | send_email 打断 → 审批后 resume |
/approvals |
approvals + threads/resume |
| 退款审批流 | 金额分级(阈值可在页面配置)、驳回/改额必填意见、按订单校验可退余额 | /approvals/flows |
approval_flows + approval_policy |
| 账号与权限 | 密码哈希、首登强制改密、失败锁定、启停用、登录审计 | /admin/users |
users + security |
| 通知体系 | 站内通知 + 邮件出站台账;工单流转与审批结果自动通知当事人 | 顶栏铃铛、/kb/ops |
notifications + mailer + outbox_worker |
| 知识库治理 | 责任人、审阅状态机、生效/失效区间、复审周期、治理审计;仅生效文档参与检索 | /kb/documents |
documents + kb_governance |
| 运维看板 | Agentic RAG / 多 Agent 开关、费用统计、发信台账、TTL 清扫 | /kb/ops |
admin/settings、usage/summary |
| 评测门禁 | 黄金集 32 条、越权/拒答/引用指标 | — | kb-golden-report |
| TTL 合规 | Store 过期清扫 + 审计证明 | — | ttl_sweeper、kb-sweep-ttl |
架构概览 #
一轮问答路由逻辑(thread_turn):
- 命中「查单 / 计算 / 建工单 / 发信 / 记住…」等意图 → Agent 工具链
- 开启多 Agent 且非强制单 Agent → 路由器 + 无历史子专家
- 默认 → 固定 RAG(预检索 → 生成 → 拒答句)
- RAG 无检索命中 → 回退 Agent(保留多轮记忆,不预检索)
流式与非流式共用同一套逻辑,最终 answer / refused / citations 一致。
目录结构 #
langrag/
├── backend/ # FastAPI 后端
│ ├── app/
│ │ ├── api/routes/ # auth、me、users、org、workbench、notifications、
│ │ │ # documents、tickets、orders、approvals、admin…
│ │ ├── services/ # RAG、Agent、工单状态机、订单、通知、治理、发信
│ │ ├── cli/ # kb-serve、kb-user-passwd、kb-golden-report…
│ │ └── db/ # SQLAlchemy 模型、迁移、checkpointer / store
│ ├── migrations/ # 001~012:扩展、种子、会话、审批、IAM、工单、
│ │ # 治理、订单、通知、存量回填
│ ├── tests/ # pytest(含黄金集 golden_v0)
│ ├── scripts/ # export_openapi.py、smoke_productized.py
│ └── openapi.json
├── frontend/ # React SPA(见 frontend/README.md)
│ └── src/
│ ├── api/ # REST 客户端 + OpenAPI 类型
│ ├── pages/ # Home、Chat、Tickets、Orders、Approvals、
│ │ # Documents、OpsDashboard、Users、Profile、Login
│ ├── components/ # 通知铃铛、工单时间线、工作台卡片…
│ └── layouts/AppLayout.tsx
├── deploy/
│ ├── nginx.conf.example # 生产反代(含 SSE)
│ └── backup_pg.ps1 # PostgreSQL 备份脚本
├── docs/ # SSE 契约、MVP 清单、生产 runbook
├── docker/ # pg-search 镜像(pgvector + pg_jieba)
├── .github/workflows/ci.yml
├── docker-compose.yml
└── .env.example前置条件 #
| 工具 | 版本建议 | 用途 |
|---|---|---|
| Docker Desktop | 最新稳定版 | PostgreSQL(pg-search 镜像) |
| Python + uv | 3.13+ | 后端与 CLI |
| Node.js + npm | 20+ | 前端构建与开发 |
| API Key | — | DeepSeek(对话)、通义 DashScope(嵌入/重排) |
快速开始 #
以下命令均在 PowerShell 下执行;路径以仓库根目录 D:\forever\docs\langrag 为例。
第 1 步:配置环境变量 #
Copy-Item .env.example .env
# 编辑 .env,至少填写:
# DEEPSEEK_API_KEY
# DASHSCOPE_API_KEY
# DATABASE_URL(默认与 docker-compose 一致即可)必填项缺失时,后端启动会 ValidationError 并拒绝启动(避免运行中静默 401)。
第 2 步:启动数据库 #
docker compose up --build -d
docker compose ps期望看到 enterprise-kb-postgres 状态为 healthy,端口 5432:5432。
国内 Docker Hub 超时:项目已默认使用 DaoCloud 镜像源构建基础镜像。若仍失败,见 常见问题 — Docker 拉取失败。
第 3 步:数据库迁移 #
Set-Location backend
uv sync
uv run python -m app.db.migrate up
# 等价:uv run kb-migrate up回滚最近一次迁移:uv run python -m app.db.migrate down
第 4 步:启动后端 #
Set-Location backend
uv run kb-serve等价命令:
# 热重载模式下 uvicorn 会自己切好事件循环,可直接用
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000| 检查 | 地址 / 命令 |
|---|---|
| 存活探针 | http://127.0.0.1:8000/api/v1/healthz |
| 就绪探针 | http://127.0.0.1:8000/api/v1/readyz |
| Swagger | http://127.0.0.1:8000/docs |
启动时自动完成:数据库校验 → 嵌入维度校验 → 账号口令引导 → 审批阈值加载 → LangGraph checkpointer/store → 异步摄入 worker → TTL 定时清扫 → 邮件出站投递 worker。
Windows 单进程启动:
kb-serve --no-reload会自动切到SelectorEventLoop。若你绕过kb-serve直接跑uvicorn app.main:app --no-reload,uvicorn 在 Windows 单进程下会用ProactorEventLoop,psycopg 的异步连接池不兼容,服务会一直无法就绪。
第 5 步:启动前端 #
新开一个终端:
Set-Location frontend
npm install
npm run dev浏览器打开:http://localhost:5173
开发环境下,Vite 将 /api 代理到 http://127.0.0.1:8000(见 frontend/vite.config.ts)。
第 6 步:登录 #
使用 测试账号 登录,例如知识管理员 `kbadmin@example.com/Kb@Init2026`。首次登录会被要求先改密码,改完即可使用各功能模块。
如何使用系统(用户手册) #
1. 登录与工作台(/) #
- 访问 http://localhost:5173 ,未登录会跳转
/login,并记住原来要去的路径(分享的工单 / 订单链接登录后能回来)。 - 输入邮箱与密码(初始口令见 测试账号)。登录页会展示当前的密码强度要求(来自公开接口
GET /auth/password-policy)。开发环境登录页提供快捷填充邮箱(密码仍需手输)。 - 首次登录必须改密:带
must_change_password的账号在登录页当场改密,改完才进系统。若绕过登录页,除「我的档案」外都会被拦回档案页的「账号安全」表单。 - 连续输错
AUTH_MAX_FAILED_ATTEMPTS(默认 5)次会锁定AUTH_LOCK_MINUTES(默认 15)分钟,接口返回 403 并说明剩余锁定时间;解锁需等待或由系统管理员在/admin/users手动解锁。账号停用同样是 403;邮箱或密码错误是 401。 - 登录后进入 工作台,按角色聚合当天要干的事(卡片数字随工单 / 审批 / 治理写操作自动刷新):
| 角色 | 工作台看到什么 |
|---|---|
客服 support |
待分配工单、我处理的工单、SLA 已超时、我提交的待批退款 |
审批人 approver |
我提交的进行中工单、等我补充信息、待我审批(发信 HITL)、审批中的退款 |
普通员工 employee |
我提交的进行中工单、等我补充信息、我提交的待批退款 |
知识管理员 kb_admin |
客服那组工单待办 + 入库失败 / 待处理 / 待复审 / 无责任人文档 |
系统管理员 sys_admin |
客服工单待办 + 待我审批 + 审批中的退款 + 知识库治理待办 |
访客 visitor |
无业务待办,仅问答入口 |
工作台还会展示需要关注的工单(点击行直接打开 /tickets?ticket= 详情抽屉)、最近会话、近 7 天解决量与平均满意度。系统健康检查(pgvector / pg_jieba / 嵌入维度 / 模型 ping)在运维看板,工作台只放业务待办。
- 侧栏按角色分组显示:工作台、智能问答、服务与审批(工单 / 订单查询 / 退款申请 / 待我审批)、知识库运营、系统管理、个人。顶栏「修改密码」进入档案页账号安全表单。
- 顶栏铃铛每 30 秒拉一次未读站内通知,点击条目直接跳到对应工单或审批。
2. 知识问答(/chat、/chat/:threadId) #
问答页是系统的核心入口,支持 SSE 流式 与 多轮会话。
基本操作 #
| 操作 | 说明 |
|---|---|
| 新建会话 | 左侧「新建」或直接在空会话中发送首条消息(自动创建 thread_id) |
| 切换会话 | 点击左侧列表项;URL 变为 /chat/{threadId},刷新可恢复 |
| 发送问题 | 输入框 Enter 发送,Shift+Enter 换行 |
| 仅看最终答案 | 工具栏开关:关闭 SSE,改用 POST .../messages 一次性返回 |
| 工具步骤 | 展开每轮 Assistant 消息下的检索/工具调用摘要 |
期望行为 #
| 提问类型 | 示例 | 期望 |
|---|---|---|
| FAQ(库内有文档) | 「多少天内可以无理由退货?」 | 流式回答 + 引用 [1] + 引用卡片 |
| 库外问题 | 「公司食堂几点开饭?」 | 拒答:「根据现有资料无法回答该问题」 |
| 查物流 | 「查 A1001 订单状态」 | Agent 调用 lookup_order(客服可见;员工无此工具) |
| 计算 | 「算一下 12 加 8」 | Agent 调用 calculate |
| 建工单 | 「我要投诉发货太慢,帮我建工单」 | Agent 调用 create_ticket,可在工单页查看 |
| 长期偏好 | 「我住杭州,回答短一点」 | 回合结束后写入档案;新开会话仍记得杭州 |
| 发信(客服) | 「给 zhang@example.com 发一封…」 | HITL 打断,问答页显示「待人工审批」,需去审批工作台处理 |
引用与拒答 #
- 回答中的
[1]、[2]与下方 引用卡片 对应,含来源文件名、页码等。 refused=true时前端以警告样式展示,便于与正常回答区分。
错误提示 #
| 情况 | 前端提示 |
|---|---|
| 429 限流 | 「请求过于频繁,请稍后再试」 |
| 503 模型不可用 | 「模型服务暂时不可用,请稍后再试」 |
| 401 | 自动跳转登录页 |
3. 工单(/tickets) #
权限:访客不可用(后端返回 403);员工仅看本人工单;客服 / 知识管理员 / 系统管理员可看全部并可流转。
待办视图 #
页面顶部按角色给出待办页签,URL 带 ?scope= 可直接分享:
| scope | 客服看到 | 员工看到 |
|---|---|---|
unassigned |
待分配(仅客服 / 管理员;员工带此链接会被纠正为 mine) |
— |
assigned_to_me |
我处理的未结单(仅客服 / 管理员) | — |
mine |
我提交的 | 我的工单 |
open |
未结单(工作台「SLA 已超时」会带 ?scope=open&sla=breached) |
我可见的未结单 |
default |
全部工单 | 我可见的工单 |
还支持按状态(多选)、类别、优先级、紧急度、关键词(工单号 / 标题 / 摘要)筛选,以及「仅看 SLA 已超时」。详情抽屉用 ?ticket=<id>,可直接分享。
状态机 #
工单号形如 TK-001234,状态流转由后端强制校验,非法流转返回 409 并列出当前可选项:
| 当前状态 | 可流转到 |
|---|---|
待受理 open |
已指派、处理中、已取消 |
已指派 assigned |
处理中、待用户回复、已解决、已取消 |
处理中 in_progress |
待用户回复、已解决、已取消 |
待用户回复 pending_user |
处理中、已解决、已取消 |
已解决 resolved |
已关闭、处理中(重新打开) |
已关闭 closed |
处理中(重新打开) |
已取消 cancelled |
终态 |
优先级与 SLA #
紧急度自动映射优先级(高→P1、中→P2、低→P3),下单即按优先级算出 sla_due_at;列表与详情用标签区分 正常 / 即将到期 / 已超时,超时工单会进入客服工作台的「SLA 已超时」待办。
交互 #
| 功能 | 说明 |
|---|---|
| 新建 · 手工 | 类别、紧急度、标题、摘要、订单号、优先级、受理部门 |
| 新建 · 自然语言 | 粘贴投诉原文,由模型抽取字段后入库 |
| 认领 / 指派 | 客服可指派给自己或其他客服,状态自动进入「已指派」 |
| 回复 | 提单人与客服都能回复;客服回复后自动流转,用户回复会把「待用户回复」拉回「处理中」 |
| 内部备注 | 仅客服可写可见,提单人看不到 |
| 时间线 | 建单、指派、状态变更、回复、评价全部留痕 |
| 满意度 | 仅提单人可对已解决工单评分,评分后自动关闭 |
| 订单联动 | 详情里的订单号可点,直接跳到订单查询页并展开该单 |
问答 Agent 建的单与手工建单写入同一张表,在工单页刷新即可看到。工单被指派、有新回复、被解决或重新打开时,当事人会收到站内通知(配置了发信服务时同时发邮件)。
3.1 订单查询(/orders) #
权限:访客不可用;客服 / 知识管理员 / 系统管理员可查全部客户,其余角色只看与本人账号关联的订单。没有系统账号的外部客户(如种子数据里的「李四」)只能由客服代客查单。
| 功能 | 说明 |
|---|---|
| 检索 | 订单号 / 客户名 / 收件人 / 快递单号关键词 + 订单状态筛选;URL 带 ?keyword= |
| 详情 | 金额(应付 / 实付 / 已退 / 可退余额)、物流、收货信息、商品明细;?order= 直达抽屉 |
| 关联工单 | 一键跳到工单列表并按订单号搜索 |
| 发起退款 | 可退余额 > 0 时跳到 /approvals/flows?order= 并预填订单号 |
问答里的 lookup_order 工具查的就是这张表(仅客服及以上角色装载该工具),不再是写死的示例数据。
4. 我的档案(/me/profile) #
管理账号信息与 跨会话长期记忆(PostgresStore):
| 区块 | 说明 |
|---|---|
| 账号信息 | 姓名、邮箱、角色、岗位、主部门、所属部门(只读,改动走用户管理) |
| 账号安全 | 用当前密码换新密码;策略与登录页相同。改密后当前会话仍有效 |
| 档案 | 称呼、城市、回答风格等;可手动 PATCH 更正,新开会话按此作答 |
| 笔记 | 过敏、设备习惯等陈述句;支持增删;可选 TTL |
Agent 工具 remember_note / recall_notes 与页面数据同源。问答中口头告知的偏好会在回合结束后由 after_agent 从 Human 消息抽取并合并档案。
5. 审批工作台(/approvals) #
权限:approver、support、kb_admin、sys_admin 均可打开此页。工作台「待我审批」卡片目前只给 approver 与 sys_admin;客服 / 知识管理员从侧栏「待我审批」进入。
- 客服在问答中触发
send_email→ 进入 待审批 列表。 - 审批人查看 明文收件人(不对 email 做 PII 脱敏,避免无法发信)。
- 批准 → 调用
POST /threads/{id}/resume继续 Agent 图;驳回 → 不发送。 interrupt_id与thread_id不匹配时返回 409(防串会话)。
6. 退款审批流(/approvals/flows) #
权限:员工及以上可提交并查看自己提交的申请;approver / support / kb_admin / sys_admin 可审批。
金额分级(确定性节点,非模型决策) #
阈值存在数据库里,系统管理员可直接在页面上改,改完立即对新申请生效,并记录修改人与修改时间:
| 金额(元) | 路径 |
|---|---|
< 自动通过上限(默认 200) |
自动通过 → 打款 |
自动通过上限 ~ 两级门槛(默认 200~1999) |
一级审批(默认「经理」) |
≥ 两级门槛(默认 2000) |
两级审批:经理 → 总监 |
≥ 风控上限(默认 5000) |
风控直接拒绝 |
一级 / 二级的职位名也可配置,页面与时间线上的文案会跟着变。
提交与校验 #
- 必须挂在一张真实工单上(下拉给出本人的退款类工单)。
- 可选关联订单号;填了订单号时后端会按
orders.refundable_amount校验,超过可退余额直接 400 拒绝并说明差额。 - 必须填申请原因(≥2 字)。
- 表单会实时预览这笔金额会走哪一档。
审批 #
页面展示 Steps 可视化与中文审批日志;支持批准、改额、驳回:
- 驳回与改额必须填审批意见,意见会落进审批日志与申请人的通知里。
- 批准可选填意见。
- 打款仅在批准后的独立
payout节点执行。 - 审批出结果时申请人收到站内通知。
7. 知识库治理(/kb/documents) #
权限:kb_admin、sys_admin。
摄入 #
| 功能 | 说明 |
|---|---|
| 上传 | 支持 txt / md / pdf / docx / csv / xlsx;multipart 提交 |
| 异步摄入 | 返回 job_id(HTTP 202);后台 worker 执行加载→切分→嵌入 |
| 进度 | 轮询 GET /ingest-jobs/{id};页面展示状态与质量摘要 |
| 重索引 | 单文档 POST ...:reindex;批量 reindex-batch |
| 驳回 | 质量失败文档可 POST ...:reject(归档并删向量) |
上传参数:space_id(如 space_faq)、dept(如 public)、title。上传人默认成为该文档的责任人。
治理 #
文档不再是「上传即永久有效」,而是带责任人与有效期的知识资产:
| 维度 | 说明 |
|---|---|
| 责任人 | 出问题找谁;无责任人文档会进知识管理员的工作台待办 |
| 审阅状态 | 草稿 → 审阅中 → 已发布 → 已下线,状态机强制校验;下线必须填原因 |
| 生效区间 | effective_from / effective_to;只有「已发布且在生效期内」的文档参与检索,过期文档自动退出问答 |
| 复审周期 | 按天设置,到期前 7 天标「即将到期」,逾期标「已逾期」并进待办 |
| 版本号 | 每次「复审通过」递增 |
| 治理审计 | 谁在什么时候把文档改成了什么,全部留在 document_reviews |
列表支持按审阅状态、责任人(含「无责任人」)、复审到期情况、生效状态筛选,URL 可分享。
8. 用户与权限(/admin/users) #
权限:sys_admin。
| 功能 | 说明 |
|---|---|
| 列表 | 按角色、状态、部门、关键词(姓名 / 邮箱 / 账号 / 岗位)筛选 |
| 建号 | 邮箱、显示名、角色、岗位、部门;可留空由系统生成强口令 |
| 改角色 / 岗位 / 部门 | 改动即时生效(令牌最长 AUTH_STATUS_CACHE_SECONDS 后失效) |
| 停用 / 启用 | 停用后令牌很快失效,无法继续调用接口 |
| 重置密码 | 生成一次性口令并要求对方首登改密;口令只在弹窗里显示一次 |
| 解锁 | 清掉失败计数与锁定时间 |
| 登录审计 | 成功 / 失败记录,含失败原因(口令错误、账号停用、账号锁定…)与来源 IP |
命令行也可改密:uv run kb-user-passwd --email <email> --generate(默认要求下次登录改密)。
9. 运维看板(/kb/ops) #
权限:kb_admin、sys_admin。
| 功能 | 说明 |
|---|---|
| 系统状态 | healthz / readyz:pgvector、pg_jieba、嵌入维度、模型 ping |
| Agentic RAG 开关 | 默认关;开启后「再查一遍」等多跳意图才升 Agent |
| 多 Agent 开关 | 默认关;开启后知识问答走路由器(模型调用次数显著增加) |
| 费用统计 | 基于 audit_events 的相对调用量(24h 等窗口) |
| 多 Agent 对比 | 单 Agent ≈2 次 vs 双子 Agent ≈6 次模型调用说明 |
| 摄入质量 | 文档状态分布与失败文档明细 |
| 通知发信台账 | 每封通知邮件的收件人、主题、状态(待投递 / 已发送 / 投递失败 / 仅记录)、重试次数与失败原因;可手动重投 |
| TTL 清扫 | 查看/手动触发 sweep_ttl 状态 |
发信说明:业务事务只把邮件写进 app.email_outbox,真正投递由后台 worker 按 NOTIFY_OUTBOX_INTERVAL_SECONDS 轮询完成 —— 邮件服务挂了不会拖慢工单和审批接口。未配置 SMTP_HOST 时邮件只留台账并标记「仅记录」,不会真的发出去。
角色与权限 #
系统内置 6 种角色(见 app.users.role):
| 角色 | 说明 | 问答 | 工单 | 订单 | 退款申请 | 审批 | 知识库 | 账号管理 |
|---|---|---|---|---|---|---|---|---|
visitor |
访客 | ✅ FAQ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
employee |
普通员工 | ✅ | 本人 | 本人 | ✅ 提交 | ❌ | ❌ | ❌ |
support |
客服 | ✅ | 全部 + 流转 | 全部 | ✅ 提交 | ✅ HITL + 退款 | ❌ | ❌ |
kb_admin |
知识管理员 | ✅ | 全部 + 流转 | 全部 | ✅ 提交 | ✅ | ✅ 读写 + 治理 | ❌ |
approver |
审批人 | ✅ | 本人 | 本人 | ✅ 提交 | ✅ | ❌ | ❌ |
sys_admin |
系统管理员 | ✅ | 全部 + 流转 | 全部 | ✅ 提交 | ✅ + 改阈值 | ✅ | ✅ |
部门 ACL:检索结果按 JWT 中的 depts 过滤(含 public)。例如 HR 文档仅 hr 部门用户可见;非 HR 用户问「年假有几天」应 0 命中(黄金集门禁校验)。
账号状态校验:每次请求都会带缓存地核对数据库里的账号状态与角色。账号被停用、锁定,或角色被改动时,旧令牌最长在 AUTH_STATUS_CACHE_SECONDS(默认 30 秒)后失效,不必等 JWT 过期。
前端路由守卫(RoleRoute、KbAdminRoute)仅为体验优化;真实权限以后端为准。
测试账号 #
密码不再写在代码里。首次启动时,AUTH_BOOTSTRAP_PASSWORDS=true(默认)会给所有还没有密码哈希的账号写入 AUTH_INITIAL_PASSWORD(默认 Kb@Init2026)并打上「必须改密」标记,首次登录后会被强制要求改密。
| 邮箱 | 显示名 | 角色 | 典型用途 |
|---|---|---|---|
| `visitor@example.com` | 访客 | visitor | 仅 FAQ,无工单无订单 |
| `zhang@example.com` | 张三 | employee | 自助提单、查本人订单、提退款申请 |
| `support@example.com` | 客服小李 | support | 认领工单、查全部订单、发信 HITL |
| `kbadmin@example.com` | 知识管理员 | kb_admin | 上传文档、文档治理、运维看板 |
| `approver@example.com` | 审批人王五 | approver | 退款审批 |
| `admin@example.com` | 系统管理员 | sys_admin | 全权限、账号管理、改审批阈值 |
生产环境务必把 AUTH_BOOTSTRAP_PASSWORDS 设为 false,改用 uv run kb-user-passwd --email <email> --generate 逐个下发口令。
种子数据还包含:
- 知识空间
space_faq(公开)、space_hr(人事)、space_support(客服) - 演示订单
A1001~A1005,覆盖已发货 / 运输中 / 已签收 / 退款中 / 已取消,其中A1001~A1003属于张三,A1004/A1005属于没有系统账号的外部客户「李四」(只能客服代查)
环境变量 #
复制 .env.example 为 .env。下表列出常用项;完整注释见 .env.example。
必填 #
| 变量 | 说明 |
|---|---|
DATABASE_URL |
PostgreSQL 连接串,默认 postgresql://kb:kb_dev_password@127.0.0.1:5432/enterprise_kb |
DEEPSEEK_API_KEY |
DeepSeek 对话 API Key |
DASHSCOPE_API_KEY |
通义嵌入 / 重排 API Key |
EMBEDDING_DIMS |
须与 embed_query('x') 实测一致,默认 1024 |
应用与鉴权 #
| 变量 | 默认 | 说明 |
|---|---|---|
APP_SECRET_KEY |
— | JWT 签名密钥(生产务必更换) |
CORS_ORIGINS |
localhost:5173 | 逗号分隔 |
JWT_EXPIRE_MINUTES |
720 | Token 有效期(分钟) |
AUTH_INITIAL_PASSWORD |
Kb@Init2026 | 引导 / 管理员建号时的初始口令,登录后强制修改(旧名 DEV_AUTH_PASSWORD 仍兼容) |
AUTH_BOOTSTRAP_PASSWORDS |
true | 启动时给无密码账号写初始口令;生产设为 false |
AUTH_MAX_FAILED_ATTEMPTS |
5 | 连续失败几次锁定账号 |
AUTH_LOCK_MINUTES |
15 | 锁定时长(分钟) |
AUTH_STATUS_CACHE_SECONDS |
30 | 账号停用 / 锁定 / 改角色后旧令牌失效的最长延迟 |
PASSWORD_MIN_LENGTH |
10 | 最短密码长度 |
PASSWORD_MIN_CHAR_CLASSES |
3 | 大写 / 小写 / 数字 / 符号至少满足几类 |
PASSWORD_HASH_ITERATIONS |
600000 | PBKDF2-HMAC-SHA256 迭代次数 |
通知与发信 #
| 变量 | 默认 | 说明 |
|---|---|---|
NOTIFY_EMAIL_ENABLED |
true | 业务通知是否同时入邮件出站台账;站内通知始终写入 |
NOTIFY_OUTBOX_INTERVAL_SECONDS |
30 | 出站台账的后台投递间隔;<=0 表示只入账不自动投递 |
SMTP_HOST |
空 | 留空时邮件只留台账并标记「仅记录」,不真发 |
SMTP_PORT |
587 | SMTP 端口 |
SMTP_USER / SMTP_PASSWORD |
空 | SMTP 凭据;留空则不做登录 |
SMTP_FROM |
no-reply@example.com | 发件人 |
SMTP_STARTTLS |
true | 是否 STARTTLS |
SMTP_TIMEOUT |
10 | 单次投递超时(秒) |
模型 #
| 变量 | 默认 | 说明 |
|---|---|---|
CHAT_MODEL_ID |
deepseek:deepseek-v4-flash | 主对话模型 |
CHAT_FALLBACK_MODEL_ID |
空 | 备用对话模型;为空则 fallback 到固定拒答句 |
EMBEDDING_MODEL |
text-embedding-v4 | 嵌入模型 |
RERANK_MODEL |
gte-rerank-v2 | 重排模型 |
MODEL_TIMEOUT |
60 | 单次模型调用超时(秒) |
MODEL_MAX_RETRIES |
6 | 瞬态错误重试上限 |
READYZ_MODEL_PING |
true | readyz 是否 ping 模型 |
文档与检索 #
| 变量 | 默认 | 说明 |
|---|---|---|
CHUNK_SIZE |
600 | 必须显式配置,禁止依赖 4000 默认值 |
CHUNK_OVERLAP |
80 | 切分重叠 |
SEARCH_FETCH_K |
5 | 初筛条数 |
SEARCH_TOP_K |
3 | 最终返回条数 |
SEARCH_RRF_C |
60 | 向量与 BM25 的 RRF 融合常数 |
RERANK_ENABLED |
true | 是否重排 |
MAX_UPLOAD_BYTES |
32MiB | 单文件上传上限 |
REQUEST_QUOTA_PER_USER_PER_MINUTE |
120 | 每用户每分钟请求配额 |
功能开关 #
| 变量 | 默认 | 说明 |
|---|---|---|
AGENTIC_RAG_ENABLED |
false | 多跳 Agentic RAG(可被运维看板 DB 覆盖) |
MULTI_AGENT_ENABLED |
false | 多专家路由器 |
MCP_ENABLED |
false | 挂载外部 MCP 工单工具 |
MCP_TICKET_URL |
http://127.0.0.1:8100/mcp | MCP HTTP 端点 |
HITL_ENABLED |
true | 发信等人机协同 |
STORE_TTL_ENABLED |
true | Store 条目 TTL 与定时清扫 |
KB_GOVERNANCE_FILTER_ENABLED |
true | 检索仅召回已发布且在生效期内的文档 |
观测(可选) #
| 变量 | 说明 |
|---|---|
LANGSMITH_TRACING |
开启 LangSmith 追踪 |
LANGSMITH_API_KEY |
LangSmith API Key |
LANGSMITH_PROJECT |
项目名,默认 enterprise-kb |
后端 API 参考 #
所有业务接口前缀:/api/v1。除 healthz、readyz、auth/login、auth/password-policy 外均需 Header:
Authorization: Bearer <access_token>健康检查 #
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /healthz |
进程存活 |
| GET | /readyz |
DB、pgvector、pg_jieba、维度、model_ping、TTL 状态 |
鉴权与用户 #
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /auth/login |
{ "email", "password" } → JWT + must_change_password;锁定 / 停用返回 403,口令错误返回 401 |
| GET | /auth/password-policy |
密码强度要求(免鉴权,供登录页展示) |
| GET | /me |
当前用户(role、depts、岗位、主部门、是否需改密) |
| POST | /me/password |
用旧密码换新密码 |
| GET/PATCH | /me/profile |
长期档案 |
| GET/POST/DELETE | /me/notes |
笔记 CRUD |
| GET | /org/departments |
部门树(供表单下拉) |
| GET | /org/agents |
可指派的客服列表 |
工作台与通知 #
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /workbench |
按角色聚合的业务待办、关注工单、最近会话、近 7 天指标、未读通知数 |
| GET | /notifications |
站内通知(?unread & limit & offset) |
| POST | /notifications/read |
标记已读({ "all": true } 或 { "ids": [...] }) |
账号管理(sys_admin) #
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /admin/users |
列表(?role & status & dept & keyword) |
| GET | /admin/users/{id} |
账号详情 |
| POST | /admin/users |
建号,可由系统生成强口令 |
| PATCH | /admin/users/{id} |
改显示名 / 角色 / 岗位 / 部门 / 状态 |
| POST | /admin/users/{id}:reset-password |
重置为一次性口令并要求改密 |
| POST | /admin/users/{id}:unlock |
解锁账号 |
| GET | /admin/login-events |
登录审计(成功与失败原因) |
知识库与摄入 #
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /documents |
上传(202 + job_id),上传人默认成为责任人 |
| GET | /documents |
列表(部门 ACL + 治理筛选:review_state / owner / review_due / effective) |
| GET | /documents/{id} |
详情(含治理字段与派生状态) |
| DELETE | /documents/{id} |
删除 |
| POST | /documents/{id}:reindex |
重索引 |
| POST | /documents/reindex-batch |
批量重建 |
| GET | /ingest-jobs/{id} |
摄入进度 |
| GET | /quality/summary |
质量汇总 |
| GET | /quality/documents |
失败文档列表 |
| GET | /quality/documents/{id} |
单文档质量明细 |
| POST | /documents/{id}:reject |
驳回归档 |
| PATCH | /documents/{id}/governance |
改责任人 / 生效区间 / 复审周期 / 标签 |
| POST | /documents/{id}/review-state |
审阅状态流转(下线必须填原因) |
| POST | /documents/{id}/reviewed |
记一次复审通过并推进版本号 |
| GET | /documents/{id}/reviews |
治理审计流水 |
| GET | /documents/governance/meta |
状态机与可选责任人(供前端渲染) |
| GET | /documents/governance/stats |
治理健康度(待复审、已过期、无责任人…) |
检索与问答 #
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /search |
检索调试(kb_admin,含分数) |
| POST | /ask |
单次 RAG(非会话) |
| POST | /ask:stream |
单次 RAG 流式 |
多轮会话 #
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /threads |
创建会话 |
| GET | /threads |
当前用户会话列表 |
| GET | /threads/{id} |
会话详情 + 消息视图 |
| GET | /threads/{id}/state |
会话图状态(含 HITL 打断) |
| POST | /threads/{id}/messages |
非流式发消息 |
| POST | /threads/{id}/messages:stream |
SSE 流式(见 docs/sse.md) |
| POST | /threads/{id}/resume |
HITL 恢复(审批人用) |
工单 #
| 方法 | 路径 | 说明 | ||||
|---|---|---|---|---|---|---|
| GET | /tickets/meta |
状态机、优先级、渠道、SLA 时长等枚举(前端据此渲染按钮) | ||||
| GET | /tickets/stats |
工单指标(待分配、指派给我、超时、待我回复…) | ||||
| GET | /tickets |
列表(`?scope=default\ | mine\ | assigned_to_me\ | unassigned\ | open&status(多选) &category&priority&urgency&keyword&sla&dept` & 排序 & 分页) |
| GET | /tickets/{id} |
详情(含回复、内部备注仅客服可见、事件时间线、可执行流转) | ||||
| POST | /tickets |
结构化或 { "text": "..." } 自然语言建单 |
||||
| PATCH | /tickets/{id} |
流转状态 / 改优先级 / 指派 / 换受理部门(仅客服与管理员;非法流转 409) | ||||
| POST | /tickets/{id}/comments |
回复或内部备注 | ||||
| POST | /tickets/{id}/rating |
提单人评分(1~5),评分后自动关闭 |
订单 #
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /orders |
列表(?keyword & status & customer_id);非客服角色只返回本人订单,访客 403 |
| GET | /orders/{order_no} |
详情(金额、可退余额、物流、商品明细);越权与不存在同样返回 404 |
审批 #
| 方法 | 路径 | 说明 | |
|---|---|---|---|
| GET | /approvals |
HITL 待办列表(?status 可查历史,支持分页) |
|
| POST | /approvals/{id}:decide |
`{ "decision": "approve" \ | "reject", "message" }`;驳回必须填意见 |
| GET/POST | /approval-flows |
退款审批流;POST 需 ticket_id + amount + reason,可选 order_no(会校验可退余额) |
|
| GET | /approval-flows/{ticket_id} |
单笔审批流详情 | |
| GET | /approval-flows/rules |
当前分流阈值与说明 | |
| PATCH | /approval-flows/rules |
改阈值与审批职位名(仅 sys_admin) | |
| POST | /approval-flows/{ticket_id}:decide |
审批决策;reject / edit 必须填 message |
管理与审计 #
| 方法 | 路径 | 说明 |
|---|---|---|
| GET/PATCH | /admin/settings |
Agentic / 多 Agent 开关 |
| GET | /usage/summary |
费用统计 |
| GET | /usage/multi-agent-compare |
多 Agent 调用对比 |
| GET | /admin/email-outbox |
发信台账(收件人、主题、状态、重试次数、失败原因) |
| POST | /admin/email-outbox/retry |
手动重投仍在队列中的邮件 |
| GET/POST | /admin/ttl-sweep |
TTL 清扫状态 / 手动触发 |
| GET | /audit-events |
按 thread 查审计(sys_admin 可看 PII 原文) |
Agent 工具一览 #
| 工具 | 角色限制 | 说明 |
|---|---|---|
search_knowledge |
全部 | 检索企业知识库(仅召回已发布且在生效期内的文档) |
lookup_order |
support+ | 查真实订单表;非客服角色不装载该工具 |
calculate |
全部 | 四则运算(除零返回错误文案) |
create_ticket |
全部 | 建工单,返回 TK-xxxxxx 工单号并自动算 SLA;访客建的单写入库,但访客不能打开工单页 |
remember_note / recall_notes |
全部 | 长期笔记 |
send_email |
support+ | HITL 打断后发信,落邮件出站台账 |
MCP 启用后额外挂载 ticket_* 前缀工具(需另开 kb-mcp-ticket)。
CLI 工具 #
在 backend/ 目录下执行:
| 命令 | 说明 |
|---|---|
uv run kb-serve |
启动 API(--port / --no-reload);Windows 单进程会切到 SelectorEventLoop |
uv run kb-migrate up |
执行迁移(也可用 uv run python -m app.db.migrate up / down) |
uv run kb-user-passwd --email <email> |
设置 / 重置账号口令(--generate 生成强口令并打印;默认要求下次登录改密,--no-force-change 可关闭) |
uv run python scripts/smoke_productized.py |
业务联调冒烟:登录 → 工作台 → 订单 → 工单 → 审批 → 通知 → 越权(需服务已启动) |
uv run kb-ingest-one <file> |
同步摄入单文件 |
uv run kb-rag-ask "问题" |
命令行 RAG(--stream 流式) |
uv run kb-mcp-ticket |
示例 MCP 工单服务(:8100) |
uv run kb-golden-report --fail-on-gate |
黄金集评测报表 |
uv run kb-langsmith-dataset |
同步 LangSmith 数据集 |
uv run kb-sweep-ttl |
手动 TTL 清扫 |
API 调用示例(PowerShell) #
# 登录
$token = (Invoke-RestMethod http://127.0.0.1:8000/api/v1/auth/login `
-Method POST -ContentType 'application/json' `
-Body '{"email":"kbadmin@example.com","password":"Kb@Init2026"}').access_token
$headers = @{ Authorization = "Bearer $token" }
# 上传文档
curl.exe -X POST http://127.0.0.1:8000/api/v1/documents `
-H "Authorization: Bearer $token" `
-F "file=@backend/tests/fixtures/refund.md" `
-F "space_id=space_faq" `
-F "dept=public" `
-F "title=退换货政策"
# 创建会话并提问
$thread = Invoke-RestMethod http://127.0.0.1:8000/api/v1/threads `
-Method POST -Headers $headers -ContentType 'application/json' -Body '{}'
Invoke-RestMethod "http://127.0.0.1:8000/api/v1/threads/$($thread.thread_id)/messages" `
-Method POST -Headers $headers -ContentType 'application/json' `
-Body '{"content":"多少天内可以无理由退货?"}'测试与 CI #
后端 #
Set-Location backend
uv run pytest -q
# 黄金集(需 PostgreSQL)
uv run pytest tests/test_golden_v0.py tests/test_eval_gates.py -q
uv run kb-golden-report --out artifacts/golden_report.json --fail-on-gate前端 #
Set-Location frontend
npm run lint
npm run build
npm run openapi:types # 生成 src/api/schema.d.tsGitHub Actions:.github/workflows/ci.yml(pytest + golden 门禁 + 前端 build)。
生产部署 #
构建前端 #
Set-Location frontend
npm run build
# 产物 → frontend/dist/运行后端 #
Set-Location backend
# 生产建议使用 gunicorn + uvicorn worker;Windows 单进程请用 kb-serve,
# 不要直接 `uvicorn ... --no-reload`(ProactorEventLoop 与 psycopg 不兼容)
uv run kb-serve --no-reload --host 0.0.0.0 --port 8000Nginx 同域 #
- 静态资源:
/→dist/ - API:
/api/→ 后端 - SSE:必须
proxy_buffering off、proxy_read_timeout足够长
运维 #
| 项 | 文档 / 脚本 |
|---|---|
| 备份 | deploy/backup_pg.ps1 |
| 监控、TTL、日志 | docs/PRODUCTION.md |
| 密钥轮换 | docs/KEY_ROTATION.md |
| 上线验收 | docs/MVP_DEPLOYMENT_CHECKLIST.md |
| 渗透抽检 | docs/PENTEST_CHECKLIST.md |
生产环境请更换 APP_SECRET_KEY、数据库密码与 API Key;把 AUTH_BOOTSTRAP_PASSWORDS 设为 false,不要用统一初始口令当生产认证。
相关文档 #
| 文档 | 说明 |
|---|---|
| frontend/README.md | 前端路由、脚本、环境变量 |
| backend/README.md | 后端模块速查 |
| docs/sse.md | SSE 流式事件契约(冻结) |
| docs/MVP_SCENARIOS.md | 演示场景 S1 / S2 / S5 |
| docs/RELEASE_GATES.md | 发版评测门禁 |
| 企业知识库需求文档.md | 产品需求规格 |
| 企业知识库开发计划.md | 分步开发计划 |
常见问题 #
后端启动报 ValidationError #
检查项目根目录 .env 是否包含 DATABASE_URL、DEEPSEEK_API_KEY、DASHSCOPE_API_KEY。
数据库未就绪 / readyz 503 #
docker compose ps # 确认 healthy
uv run python -m app.db.migrate up前端无法访问 API #
- 开发:确认后端在 8000 端口,且 Vite dev server 已启动(代理仅 dev 生效)。
- 生产:配置 Nginx 或设置
VITE_API_BASE_URL为完整 API 前缀。
Docker Hub 拉取失败 #
国内无法访问 auth.docker.io 时,项目 已默认 使用 DaoCloud 基础镜像:
docker compose up --build -d若仍失败,在 Docker Desktop → Settings → Docker Engine 添加:
{
"registry-mirrors": ["https://docker.m.daocloud.io"]
}保存并 Restart Docker 后重试。海外环境可改用官方镜像:
docker compose build --build-arg PGVECTOR_IMAGE=pgvector/pgvector:pg165432 端口被占用 #
修改 docker-compose.yml 端口映射(如 5433:5432),并同步更新 .env 中 DATABASE_URL。
问答显示「模型超时」但模型正常 #
若 PostgreSQL 短暂不可用,旧版本可能将审计写入失败误报为模型超时;当前版本已降级处理。请同时检查 readyz 与数据库连接。
Windows 下 kb-serve --no-reload 一直无法就绪 #
psycopg 异步连接池不能跑在 Windows 默认的 ProactorEventLoop 上。请用 uv run kb-serve --no-reload(会切到 SelectorEventLoop),不要绕过它直接 uvicorn app.main:app --no-reload。带 --reload 的开发模式不受影响。
员工无法查单 / 发信 #
符合设计:仅 support、kb_admin、sys_admin 拥有 lookup_order / send_email 工具。访客访问 /orders、/tickets 会得到 403。
演示速查(5 分钟) #
- S1 知识问答:`zhang@example.com` → 问「多少天内可以无理由退货?」→ 有引用;问「食堂几点开饭?」→ 拒答。
- S2 知识库:`kbadmin@example.com
→/kb/documents上传refund.md` → 等待 succeeded → 问答验证;可改责任人 / 生效期,过期文档不再被检索。 - S5 Agent:`support@example.com` → 问「查 A1001」「算 12+8」「帮我建工单投诉物流」→ 工具步骤可见;员工账号问查单会提示需要客服权限。
- HITL:`support@example.com
→ 问答请求发信 →approver@example.com→/approvals` 批准。 - 工单生命周期:员工提单 → 客服认领 / 回复 / 内部备注 → 员工在顶栏看到通知 → 解决后评分关闭。
- 订单与退款:
/orders打开A1001→ 发起退款(超额会被可退余额拦截)→ 审批人在/approvals/flows处理。
联调冒烟(需后端已启动):uv run python scripts/smoke_productized.py。
更多场景见 docs/MVP_SCENARIOS.md。