知识库助手前端接口设计方案¶
- 版本:v1.0
- 日期:2026-05-21
- 适用项目:
Tender_Documents - 目标界面:知识库侧栏助手、知识库下拉选择、检索问答、引用来源展示
1. 背景¶
当前知识库链路已经具备:
- 知识库 CRUD:
/v1/knowledges - 文件上传、解析、向量化状态:
/v1/knowledges/{kb_id}/files - 文件解析进度 SSE:
/v1/knowledges/{kb_id}/files/{file_id}/parse-job/events - 内部语义检索服务:
KnowledgeSearchService.search
截图里的前端需要的是一层“知识库助手”能力,不只是裸检索:
- 顶部选择当前知识库。
- 初始欢迎态提示用户怎么问。
- 输入问题后流式返回检索结果或答案。
- 答案里带引用来源,支持前端展开文件名、页码、标题路径、原文片段。
- 需要明确空知识库、无命中、文件仍在解析等状态。
因此建议保留已有管理接口,在其上新增面向聊天 UI 的 assistant 接口。
2. 设计结论¶
推荐能力分两层:
- 知识库选择层:复用现有知识库列表接口,补充前端需要的
ready_file_count、indexing_file_count等聚合字段。 - 助手问答层:使用
POST /v1/knowledges/{kb_id}/assistant/chat/stream,内部复用检索服务完成 RAG 召回、LLM 总结和引用返回。
前端统一使用 SSE 流式接口,不再暴露裸检索和非流式问答 HTTP 接口。
3. 现有接口复用¶
3.1 获取知识库列表¶
用途:
- 顶部下拉框展示用户可选知识库。
- 页面初始化时选择最近使用或第一个可用知识库。
建议响应字段在现有 KnowledgeResponse 基础上补充:
{
"id": "kb_uuid",
"name": "jweboy",
"description": null,
"remark": null,
"total_file_size": 123456,
"file_count": 12,
"ready_file_count": 10,
"indexing_file_count": 1,
"failed_file_count": 1,
"created_at": "2026-05-21T10:00:00+08:00",
"updated_at": "2026-05-21T10:00:00+08:00"
}
如果短期不改后端,前端可先用现有 total_file_size 和文件列表接口兜底。
3.2 获取文件树¶
用途:
- 前端展示知识库文件状态。
- 判断是否有可检索文件。
- 引导用户上传文件或等待解析完成。
4. 新增助手接口¶
4.1 获取助手初始化信息¶
用途:
- 进入截图页面时获取当前知识库状态。
- 判断输入框是否可用。
- 返回问题长度限制、可检索状态和最近会话摘要。
欢迎文案、功能说明、示例问题和注意事项建议由前端固定维护。原因是这些内容不依赖用户数据,也不需要后端动态计算;前端固定可以减少接口字段和多端文案同步成本。后端只负责告诉前端当前知识库是否可用、能否输入、限制是多少。
响应:
{
"code": 0,
"msg": "success",
"data": {
"knowledge": {
"id": "kb_uuid",
"name": "jweboy",
"file_count": 12,
"ready_file_count": 10,
"indexing_file_count": 1,
"failed_file_count": 1
},
"assistant_status": "ready",
"input_enabled": true,
"max_question_length": 200
}
}
assistant_status 建议枚举:
empty 知识库没有文件
indexing 有文件正在解析或向量化,可能只能检索部分内容
ready 至少存在一个可检索文件
failed 文件全部解析失败
disabled 当前用户无权限或功能未开通
4.2 知识库助手问答 SSE¶
POST /v1/knowledges/{kb_id}/assistant/chat/stream
Content-Type: application/json
{
"message": "检索与智慧城市相关的政策文件",
"top_k": 8,
"file_ids": null,
"conversation_id": "conversation_uuid",
"history_limit": 6,
"answer_mode": "grounded",
"use_query_rewrite": false,
"min_score": null
}
SSE 事件设计:
event: start
data: {"conversation_id":"...","message_id":"..."}
event: retrieval
data: {"status":"searching","query":"检索与智慧城市相关的政策文件"}
event: references
data: {"items":[{"ref_id":"ref_1","file_name":"政策文件.pdf","page_start":3,"page_end":4,"score":0.81}]}
event: delta
data: {"text":"根据知识库资料,"}
event: delta
data: {"text":"智慧城市相关政策主要包括..."}
event: related_questions
data: {"items":["是否需要按政策发布时间筛选?"]}
event: done
data: {"message_id":"...","finish_reason":"stop","usage":{"total_tokens":1500}}
event: error
data: {"code":"NO_RELEVANT_CONTEXT","message":"未在当前知识库中找到相关内容"}
前端处理规则:
start后创建一条 assistant 消息占位。retrieval显示“正在检索知识库...”。references先渲染引用卡片,可折叠。delta逐字追加答案。done结束 loading。error保留用户问题,assistant 消息展示错误态。
5. 会话接口¶
如果只做截图里的单次问答,可以先不落会话;但从产品体验看,建议加会话,方便用户切换知识库后保留上下文。
5.1 会话列表¶
响应项:
{
"id": "conversation_uuid",
"title": "智慧城市政策检索",
"message_count": 8,
"last_message_at": "2026-05-21T10:00:00+08:00",
"created_at": "2026-05-21T09:50:00+08:00"
}
5.2 会话消息¶
5.3 清空当前会话¶
短期实现可参考现有标书 AI 对话,把历史存 Redis;长期建议落 PostgreSQL,便于审计、引用追踪和用户历史恢复。
6. 后端处理流程¶
这里需要区分两类模型:
- embedding 模型:检索必须用。用户问题先通过
embedding_service.embed_texts([message])转成向量,再去 Qdrant/DashVector 做相似度召回。 - chat 模型:不是检索必须项,只在需要“总结成自然语言答案”时使用。如果前端只要资料命中和引用列表,可以走
references_only,不调用 chat 模型。
推荐默认模式是 grounded:先向量检索,再把命中的 chunk 作为上下文交给 chat 模型生成答案,同时返回引用。这样前端看到的是助手回答,不只是搜索结果。
sequenceDiagram
participant FE as Frontend
participant API as FastAPI
participant DB as PostgreSQL
participant V as Vector Store
participant LLM as Chat Model
FE->>API: POST assistant/chat/stream
API->>DB: 校验知识库归属与可检索文件状态
API-->>FE: event start
API-->>FE: event retrieval
API->>API: embedding 模型将问题转成 query vector
API->>V: 按 knowledge_id/file_ids/top_k 向量召回
API->>DB: 用 chunk_id 补齐文件名、页码、标题路径、正文
API-->>FE: event references
alt answer_mode = references_only
API-->>FE: event done
else answer_mode = grounded / creative
API->>LLM: 基于用户问题 + 命中 chunk 生成答案
LLM-->>API: stream delta
API-->>FE: event delta
end
API->>DB: 保存消息、引用、token usage
API-->>FE: event done
核心原则:
- 不允许模型声称引用了未命中的资料。
grounded模式下,如果无有效命中,直接返回无命中提示,不强行编答案。- 引用必须基于
KnowledgeSearchHitDTO,前端展示的quote从 chunk content 截断而来。 - 向量库只负责召回,权限、文件状态和引用详情以 PostgreSQL 为准。
- 不建议用 chat 模型直接“检索”。chat 模型可以做查询改写、答案生成和相关问题推荐,但不能替代向量召回与权限过滤。
7. RAG 模型链路详细设计¶
7.0 推荐模型配置¶
结合当前项目配置,知识库助手首版建议继续使用百炼 OpenAI 兼容接口:
| 用途 | 推荐模型 | 配置项 | 是否必须 | 说明 |
|---|---|---|---|---|
| 查询向量化 | text-embedding-v4 |
EMBEDDING_MODEL |
是 | 当前项目默认 embedding 模型,知识库入库和查询必须使用同一个模型,避免向量维度或语义空间不一致 |
| 答案生成 | qwen3.6-plus |
AI_CHAT_MODEL |
否 | 默认问答生成模型,适合 grounded 回答、引用组织、相关问题推荐 |
| 查询改写 | qwen3.6-plus |
AI_CHAT_MODEL |
否 | 首版不单独引入改写模型;只有 use_query_rewrite=true 时才调用 |
首版建议:
首版不设计兜底模型,也不新增知识库专用模型配置。模型不可用时直接返回明确错误,便于尽早暴露配置、账号权限或网关问题。
模型使用原则:
- 文件入库 embedding 和用户查询 embedding 必须使用同一个
EMBEDDING_MODEL。 - 切换
EMBEDDING_MODEL后,历史 chunk 必须重建向量索引,否则新旧向量不可混用。 references_only模式只使用text-embedding-v4,不调用qwen3.6-plus。grounded模式使用text-embedding-v4检索,再使用qwen3.6-plus生成答案。use_query_rewrite=true时,会先使用qwen3.6-plus改写 query,再用text-embedding-v4做检索。- 知识库助手首版统一关闭 thinking:embedding 检索本身没有 thinking 参数;chat 模型调用查询改写和答案生成时统一通过
settings.llm_extra_body(model)生成 provider 对应参数。
7.1 模型职责边界¶
| 环节 | 是否必须 | 使用模型 | 输入 | 输出 | 说明 |
|---|---|---|---|---|---|
| 权限与状态校验 | 是 | 不使用模型 | user_id、kb_id、file_ids |
可检索文件范围 | 必须先由 PostgreSQL 判定,不能交给模型 |
| 查询改写 | 否 | chat 模型 | 用户问题、少量历史问题 | search_query |
只改写检索词,不回答问题 |
| 向量化 | 是 | embedding 模型 | search_query 或原始 message |
query vector | 这是语义检索的必要步骤 |
| 向量召回 | 是 | 不使用 chat 模型 | query vector、knowledge_id、file_ids |
chunk id + score | Qdrant/DashVector 只按向量和 metadata 过滤 |
| 详情补齐 | 是 | 不使用模型 | chunk id | 文件名、页码、标题路径、正文 | 以 PostgreSQL 为准 |
| 答案生成 | 否 | chat 模型 | 用户问题、命中 chunk、引用编号 | answer | references_only 模式跳过 |
| 相关问题推荐 | 否 | chat 模型 | 用户问题、答案、引用摘要 | related questions | 可和答案生成合并一次调用 |
7.1.1 Thinking 设置¶
知识库助手首版不需要 thinking。
原因:
- 检索阶段不涉及 thinking:
text-embedding-v4只把文本转成向量,向量库只做相似度召回和 metadata 过滤。 - 查询改写是短任务:目标是得到稳定、简短的
search_query,打开 thinking 会增加延迟和输出不确定性。 - grounded 答案要可控:答案必须严格基于引用 chunk,打开 thinking 不会提升权限过滤或引用准确性,反而可能增加 token 成本。
- SSE 前端体验更稳定:关闭 thinking 后首 token 更快,也避免推理内容混入业务流。
chat 模型调用统一按 provider 生成参数:
DeepSeek 使用 {"thinking": {"type": "disabled"}},百炼/Qwen 使用 {"enable_thinking": false}。
适用范围:
use_query_rewrite=true的查询改写调用。answer_mode=grounded的答案生成调用。answer_mode=creative的扩展建议调用。
后续如果要做复杂多跳问答,可以单独增加实验开关,不作为首版默认行为。
7.2 默认请求链路¶
默认 answer_mode=grounded、use_query_rewrite=false:
1. 校验用户是否拥有 kb_id。
2. 校验 knowledge_files 中至少有 status=success 且 embedding_status=success 的文件。
3. 如果传了 file_ids,校验这些文件属于当前知识库且可检索。
4. search_query = message。
5. 调 embedding_service.embed_texts([search_query]) 得到 query vector。
6. 调 vector_store.search(query_vector, knowledge_id, top_k, file_ids)。
7. 按 chunk_id 回 PostgreSQL 查询 chunk 正文、文件名、页码、标题路径。
8. 过滤低分命中、空正文命中、已删除文件命中。
9. 如果无有效命中,返回 `NO_RELEVANT_CONTEXT`,不调用 chat 模型生成答案。
10. 把命中 chunk 编号为 `[1] [2] [3]`,构造 grounded prompt。
11. 调 chat 模型生成答案和 related_questions。
12. 保存用户消息、助手答案、references 快照和 usage。
7.3 查询改写策略¶
use_query_rewrite=true 时,chat 模型只做检索 query 改写,输出必须是短文本或 JSON,不允许回答问题。
适合打开的场景:
- 用户问题包含代词或上下文依赖,例如“这个政策有没有实施范围?”。
- 用户连续追问,需要结合最近几轮问题补全检索词。
- 用户问题过长,里面混有任务描述和检索目标。
不建议默认打开的原因:
- 增加一次 chat 模型调用,延迟和成本都会上升。
- 改写错误会降低召回质量。
- 知识库助手首版更需要稳定,直接用用户问题 embedding 更可控。
改写提示词原则:
你只负责把用户问题改写成适合知识库向量检索的查询语句。
不要回答问题。
不要编造专有名词、年份、文件名。
保留用户明确提到的关键词、年份、地区、行业、产品名。
输出 1 条中文查询语句,最长 80 字。
7.4 召回与过滤策略¶
推荐后端内部参数:
retrieval_top_k = min(request.top_k, 20)
candidate_top_k = retrieval_top_k * 2
min_score = request.min_score or settings.KNOWLEDGE_ASSISTANT_MIN_SCORE # 首版默认 0.5
max_context_chunks = 3
max_context_chars_per_chunk = 650
max_total_context_chars = 1950
max_tokens = 1024
temperature = 0.2
执行建议:
- 向量库先取
candidate_top_k,给后续过滤留余量。 - 必须带
knowledge_idmetadata filter。 - 如果请求带
file_ids,必须同时带 file metadata filter,并在 DB 再校验一遍。 - 低于
min_score的命中不进入答案生成上下文。 - 同一个文件连续 chunk 可以合并展示,但生成 prompt 里仍保留引用编号。
- 最终进入 chat 模型的 chunk 数不超过
max_context_chunks。
7.5 Grounded 答案生成约束¶
chat 模型生成答案时必须收到结构化上下文:
用户问题:
{message}
知识库引用:
[1] 文件:2023市场分析报告.pdf
页码:8-9
标题路径:第二章 / 市场规模
内容:...
[2] 文件:智慧城市政策.pdf
页码:3
标题路径:政策要求
内容:...
系统约束:
你是知识库问答助手。
只能基于给定“知识库引用”回答。
如果引用中没有答案,明确说明“当前知识库未检索到足够依据”。
不要编造文件名、页码、数据、政策条款。
涉及具体结论时,在句末标注引用编号,例如 [1]。
回答要简洁,优先给出可用于标书编写的要点。
返回结构建议由服务层整理,不强依赖模型输出完整 JSON:
answer来自 chat 模型流式文本。references来自检索命中,不从模型生成。related_questions可由同一次模型调用末尾生成,也可二期再做。usage来自模型 API 返回。
7.6 无命中与低置信度策略¶
无有效命中时:
references_only:返回空references和空answer,不报系统错误。grounded:返回NO_RELEVANT_CONTEXT,提示用户换关键词或上传相关文件。creative:可以给检索建议,但必须明确“未在知识库中找到依据”,不能包装成知识库结论。
低置信度命中时:
- 如果最高分低于阈值,按无命中处理。
- 如果命中数量少但分数合格,可以回答,但文案应保守,例如“根据当前检索到的资料...”。
- 前端可展示“找到 N 条可能相关引用”,避免让用户误以为覆盖完整。
7.7 和现有代码的对应关系¶
当前 app/services/knowledge/search_service.py 已经实现了核心检索路径:
KnowledgeSearchService.search
-> _ensure_access
-> _ensure_files_belong_to_knowledge
-> embedding_service.embed_texts
-> vector_store.search
-> PostgreSQL chunk/file 补齐
-> KnowledgeSearchResponseDTO
新增 assistant_service 应该复用这条路径,而不是重新实现向量库查询。建议只在 assistant_service 中增加:
- 知识库可用状态统计。
- 可选 query rewrite。
- 对
KnowledgeSearchResponseDTO.hits做阈值过滤、截断和引用编号。 - grounded prompt 构造。
- chat 模型流式调用。
- 会话、引用快照和 usage 保存。
8. 状态与错误码¶
建议业务错误码:
| code | 场景 | 前端文案建议 |
|---|---|---|
KNOWLEDGE_EMPTY |
当前知识库无文件 | 请先上传文档到知识库 |
KNOWLEDGE_INDEXING |
全部文件仍在解析或向量化 | 文档正在解析,请稍后再试 |
NO_RELEVANT_CONTEXT |
检索无命中 | 未在当前知识库中找到相关内容,请换个更具体的问题 |
FILE_NOT_READY |
指定 file_ids 中没有可检索文件 |
选择的文件尚未完成解析 |
ASSISTANT_QUOTA_EXCEEDED |
AI 调用额度不足 | 当前额度不足,请升级或稍后再试 |
ASSISTANT_MODEL_ERROR |
LLM 调用失败 | 知识库助手暂时不可用,请稍后重试 |
注意:项目当前失败响应大多是 HTTP 200 + code != 0,新接口应保持一致,SSE 中则通过 event:error 传同样结构。
9. 前端页面状态建议¶
8.1 初始化¶
- 调
GET /v1/knowledges?page=1&page_size=1000获取下拉列表。 - 选中最近使用知识库,localStorage key 建议:
knowledge_assistant:last_kb_id。 - 调
GET /v1/knowledges/{kb_id}/assistant/bootstrap获取知识库可用状态和输入状态。 - 欢迎文案、功能说明、示例问题、注意事项由前端固定渲染。
8.2 输入框¶
- 截图中前端限制 200 字即可。
- 后端
message最大仍保留 2000 字,兼容以后粘贴长问题。 empty/indexing/failed/disabled状态禁用发送按钮。
8.3 引用展示¶
每条答案下面展示“引用来源 N 条”:
点击文件名后续可接入:
- OSS 预览地址。
- 文件详情弹窗。
- 定位页码预览。
10. 数据表建议¶
短期可以不新增表,只用 Redis 保存会话;如果要做可恢复历史,建议新增:
9.1 knowledge_assistant_conversations¶
9.2 knowledge_assistant_messages¶
references_json 保存生成时实际使用的引用快照,避免后续文件删除或重新解析导致历史答案引用变动。
11. 分阶段落地¶
第一阶段:可用闭环¶
- 复用现有知识库列表、文件树和内部检索服务。
- 新增
assistant/bootstrap。 - 新增
assistant/chat/stream。 - 会话先用
conversation_id透传,可不提供历史列表。
第二阶段:体验完善¶
- 新增会话列表和消息历史。
- 保存引用快照。
- 补充知识库列表聚合字段。
- 增加“限定文件检索”。
第三阶段:质量优化¶
- 查询改写:把用户问题改写为更适合向量召回的 query。
- 混合检索:向量召回 + 关键词召回。
- rerank:对 top_k 结果二次排序。
- 引用去重:同文件相邻页合并展示。
12. 推荐接口清单¶
| 接口 | 方法 | 状态 | 用途 |
|---|---|---|---|
/v1/knowledges |
GET | 已有 | 知识库下拉 |
/v1/knowledges/tree |
GET | 已有 | 文件树和状态 |
/v1/knowledges/{kb_id}/assistant/bootstrap |
GET | 新增 | 页面初始化 |
/v1/knowledges/{kb_id}/assistant/chat/stream |
POST | 新增 | 流式问答 |
/v1/knowledges/{kb_id}/assistant/conversations |
GET | 新增二期 | 会话列表 |
/v1/knowledges/{kb_id}/assistant/conversations/{conversation_id}/messages |
GET | 新增二期 | 消息历史 |
/v1/knowledges/{kb_id}/assistant/conversations/{conversation_id} |
DELETE | 新增二期 | 删除/清空会话 |
13. 与现有代码的建议落点¶
建议新增文件:
app/routes/v1/knowledges/assistant.py
app/schemas/knowledge_assistant.py
app/services/knowledge/assistant_service.py
建议复用:
app/services/knowledge/search_service.py
app/services/ai_chat_service.py 中的流式响应组织方式
app/services/embedding_service.py
app/services/vector_store/*
app/models/knowledge.py
路由挂载:
# app/routes/v1/knowledges/__init__.py
from .assistant import router as assistant_router
router.include_router(assistant_router, prefix="/knowledges")
assistant_service 内部不要直接查 Qdrant/DashVector,应调用 knowledge_search_service.search(...),这样检索过滤、文件归属校验和返回结构能保持一致。