企业知识库问答系统 #

前后端分离的企业知识库助手: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

架构概览 #

flowchart TB subgraph Browser["浏览器 React SPA"] Login["/login 首登改密"] Home["/ 业务工作台"] Chat["/chat SSE"] Tickets["/tickets 工单"] Orders["/orders 订单"] Approvals["/approvals 审批"] KB["/kb/documents 治理"] Ops["/kb/ops 运维"] Users["/admin/users"] end subgraph API["FastAPI /api/v1"] Auth["auth + JWT + 账号状态"] WB["workbench + notifications"] Threads["threads + ask"] BizAPI["tickets / orders / approval-flows"] Docs["documents + ingest"] Agent["LangGraph Agent"] RAG["RAG LCEL 链"] end subgraph Workers["后台 worker"] Ingest["异步摄入"] TTL["Store TTL 清扫"] Outbox["邮件出站投递"] end subgraph PG["PostgreSQL pg-search"] Biz["documents / tickets / orders / notifications / email_outbox"] Vec["pgvector 向量"] BM25["pg_jieba BM25"] CP["checkpoints + store"] end subgraph Ext["外部服务"] DS["DeepSeek 对话"] Dash["DashScope 嵌入/重排"] SMTP["SMTP(可选)"] end Browser -->|Bearer JWT| API WB --> Biz BizAPI --> Biz Threads --> Agent Threads --> RAG Agent --> CP RAG --> Vec RAG --> BM25 Docs --> Biz Docs --> Ingest Outbox --> SMTP Agent --> DS RAG --> DS Vec --> Dash

一轮问答路由逻辑(thread_turn):

  1. 命中「查单 / 计算 / 建工单 / 发信 / 记住…」等意图 → Agent 工具链
  2. 开启多 Agent 且非强制单 Agent → 路由器 + 无历史子专家
  3. 默认 → 固定 RAG(预检索 → 生成 → 拒答句)
  4. 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. 登录与工作台(/) #

  1. 访问 http://localhost:5173 ,未登录会跳转 /login,并记住原来要去的路径(分享的工单 / 订单链接登录后能回来)。
  2. 输入邮箱与密码(初始口令见 测试账号)。登录页会展示当前的密码强度要求(来自公开接口 GET /auth/password-policy)。开发环境登录页提供快捷填充邮箱(密码仍需手输)。
  3. 首次登录必须改密:带 must_change_password 的账号在登录页当场改密,改完才进系统。若绕过登录页,除「我的档案」外都会被拦回档案页的「账号安全」表单。
  4. 连续输错 AUTH_MAX_FAILED_ATTEMPTS(默认 5)次会锁定 AUTH_LOCK_MINUTES(默认 15)分钟,接口返回 403 并说明剩余锁定时间;解锁需等待或由系统管理员在 /admin/users 手动解锁。账号停用同样是 403;邮箱或密码错误是 401。
  5. 登录后进入 工作台,按角色聚合当天要干的事(卡片数字随工单 / 审批 / 治理写操作自动刷新):
角色 工作台看到什么
客服 support 待分配工单、我处理的工单、SLA 已超时、我提交的待批退款
审批人 approver 我提交的进行中工单、等我补充信息、待我审批(发信 HITL)、审批中的退款
普通员工 employee 我提交的进行中工单、等我补充信息、我提交的待批退款
知识管理员 kb_admin 客服那组工单待办 + 入库失败 / 待处理 / 待复审 / 无责任人文档
系统管理员 sys_admin 客服工单待办 + 待我审批 + 审批中的退款 + 知识库治理待办
访客 visitor 无业务待办,仅问答入口

工作台还会展示需要关注的工单(点击行直接打开 /tickets?ticket= 详情抽屉)、最近会话、近 7 天解决量与平均满意度。系统健康检查(pgvector / pg_jieba / 嵌入维度 / 模型 ping)在运维看板,工作台只放业务待办。

  1. 侧栏按角色分组显示:工作台、智能问答、服务与审批(工单 / 订单查询 / 退款申请 / 待我审批)、知识库运营、系统管理、个人。顶栏「修改密码」进入档案页账号安全表单。
  2. 顶栏铃铛每 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 打断,问答页显示「待人工审批」,需去审批工作台处理

引用与拒答 #

错误提示 #

情况 前端提示
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;客服 / 知识管理员从侧栏「待我审批」进入。

  1. 客服在问答中触发 send_email → 进入 待审批 列表。
  2. 审批人查看 明文收件人(不对 email 做 PII 脱敏,避免无法发信)。
  3. 批准 → 调用 POST /threads/{id}/resume 继续 Agent 图;驳回 → 不发送。
  4. interrupt_id 与 thread_id 不匹配时返回 409(防串会话)。

6. 退款审批流(/approvals/flows) #

权限:员工及以上可提交并查看自己提交的申请;approver / support / kb_admin / sys_admin 可审批。

金额分级(确定性节点,非模型决策) #

阈值存在数据库里,系统管理员可直接在页面上改,改完立即对新申请生效,并记录修改人与修改时间:

金额(元) 路径
< 自动通过上限(默认 200) 自动通过 → 打款
自动通过上限 ~ 两级门槛(默认 200~1999) 一级审批(默认「经理」)
≥ 两级门槛(默认 2000) 两级审批:经理 → 总监
≥ 风控上限(默认 5000) 风控直接拒绝

一级 / 二级的职位名也可配置,页面与时间线上的文案会跟着变。

提交与校验 #

审批 #

页面展示 Steps 可视化与中文审批日志;支持批准、改额、驳回:

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 逐个下发口令。

种子数据还包含:

环境变量 #

复制 .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.ts

GitHub 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 8000

Nginx 同域 #

参考 deploy/nginx.conf.example:

运维 #

项 文档 / 脚本
备份 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 #

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:pg16

5432 端口被占用 #

修改 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 分钟) #

  1. S1 知识问答:`zhang@example.com` → 问「多少天内可以无理由退货?」→ 有引用;问「食堂几点开饭?」→ 拒答。
  2. S2 知识库:`kbadmin@example.com→/kb/documents上传refund.md` → 等待 succeeded → 问答验证;可改责任人 / 生效期,过期文档不再被检索。
  3. S5 Agent:`support@example.com` → 问「查 A1001」「算 12+8」「帮我建工单投诉物流」→ 工具步骤可见;员工账号问查单会提示需要客服权限。
  4. HITL:`support@example.com→ 问答请求发信 →approver@example.com→/approvals` 批准。
  5. 工单生命周期:员工提单 → 客服认领 / 回复 / 内部备注 → 员工在顶栏看到通知 → 解决后评分关闭。
  6. 订单与退款:/orders 打开 A1001 → 发起退款(超额会被可退余额拦截)→ 审批人在 /approvals/flows 处理。

联调冒烟(需后端已启动):uv run python scripts/smoke_productized.py。

更多场景见 docs/MVP_SCENARIOS.md。