跳转至

知识库助手前端接口设计方案

  • 版本:v1.0
  • 日期:2026-05-21
  • 适用项目:Tender_Documents
  • 目标界面:知识库侧栏助手、知识库下拉选择、检索问答、引用来源展示

1. 背景

当前知识库链路已经具备:

  1. 知识库 CRUD:/v1/knowledges
  2. 文件上传、解析、向量化状态:/v1/knowledges/{kb_id}/files
  3. 文件解析进度 SSE:/v1/knowledges/{kb_id}/files/{file_id}/parse-job/events
  4. 内部语义检索服务:KnowledgeSearchService.search

截图里的前端需要的是一层“知识库助手”能力,不只是裸检索:

  1. 顶部选择当前知识库。
  2. 初始欢迎态提示用户怎么问。
  3. 输入问题后流式返回检索结果或答案。
  4. 答案里带引用来源,支持前端展开文件名、页码、标题路径、原文片段。
  5. 需要明确空知识库、无命中、文件仍在解析等状态。

因此建议保留已有管理接口,在其上新增面向聊天 UI 的 assistant 接口。


2. 设计结论

推荐能力分两层:

  1. 知识库选择层:复用现有知识库列表接口,补充前端需要的 ready_file_countindexing_file_count 等聚合字段。
  2. 助手问答层:使用 POST /v1/knowledges/{kb_id}/assistant/chat/stream,内部复用检索服务完成 RAG 召回、LLM 总结和引用返回。

前端统一使用 SSE 流式接口,不再暴露裸检索和非流式问答 HTTP 接口。


3. 现有接口复用

3.1 获取知识库列表

GET /v1/knowledges?page=1&page_size=1000

用途:

  1. 顶部下拉框展示用户可选知识库。
  2. 页面初始化时选择最近使用或第一个可用知识库。

建议响应字段在现有 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 获取文件树

GET /v1/knowledges/tree

用途:

  1. 前端展示知识库文件状态。
  2. 判断是否有可检索文件。
  3. 引导用户上传文件或等待解析完成。

4. 新增助手接口

4.1 获取助手初始化信息

GET /v1/knowledges/{kb_id}/assistant/bootstrap

用途:

  1. 进入截图页面时获取当前知识库状态。
  2. 判断输入框是否可用。
  3. 返回问题长度限制、可检索状态和最近会话摘要。

欢迎文案、功能说明、示例问题和注意事项建议由前端固定维护。原因是这些内容不依赖用户数据,也不需要后端动态计算;前端固定可以减少接口字段和多端文案同步成本。后端只负责告诉前端当前知识库是否可用、能否输入、限制是多少。

响应:

{
  "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":"未在当前知识库中找到相关内容"}

前端处理规则:

  1. start 后创建一条 assistant 消息占位。
  2. retrieval 显示“正在检索知识库...”。
  3. references 先渲染引用卡片,可折叠。
  4. delta 逐字追加答案。
  5. done 结束 loading。
  6. error 保留用户问题,assistant 消息展示错误态。

5. 会话接口

如果只做截图里的单次问答,可以先不落会话;但从产品体验看,建议加会话,方便用户切换知识库后保留上下文。

5.1 会话列表

GET /v1/knowledges/{kb_id}/assistant/conversations?page=1&page_size=20

响应项:

{
  "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 会话消息

GET /v1/knowledges/{kb_id}/assistant/conversations/{conversation_id}/messages?page=1&page_size=50

5.3 清空当前会话

DELETE /v1/knowledges/{kb_id}/assistant/conversations/{conversation_id}

短期实现可参考现有标书 AI 对话,把历史存 Redis;长期建议落 PostgreSQL,便于审计、引用追踪和用户历史恢复。


6. 后端处理流程

这里需要区分两类模型:

  1. embedding 模型:检索必须用。用户问题先通过 embedding_service.embed_texts([message]) 转成向量,再去 Qdrant/DashVector 做相似度召回。
  2. 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

核心原则:

  1. 不允许模型声称引用了未命中的资料。
  2. grounded 模式下,如果无有效命中,直接返回无命中提示,不强行编答案。
  3. 引用必须基于 KnowledgeSearchHitDTO,前端展示的 quote 从 chunk content 截断而来。
  4. 向量库只负责召回,权限、文件状态和引用详情以 PostgreSQL 为准。
  5. 不建议用 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_MODEL=text-embedding-v4
AI_CHAT_MODEL=qwen3.6-plus

首版不设计兜底模型,也不新增知识库专用模型配置。模型不可用时直接返回明确错误,便于尽早暴露配置、账号权限或网关问题。

模型使用原则:

  1. 文件入库 embedding 和用户查询 embedding 必须使用同一个 EMBEDDING_MODEL
  2. 切换 EMBEDDING_MODEL 后,历史 chunk 必须重建向量索引,否则新旧向量不可混用。
  3. references_only 模式只使用 text-embedding-v4,不调用 qwen3.6-plus
  4. grounded 模式使用 text-embedding-v4 检索,再使用 qwen3.6-plus 生成答案。
  5. use_query_rewrite=true 时,会先使用 qwen3.6-plus 改写 query,再用 text-embedding-v4 做检索。
  6. 知识库助手首版统一关闭 thinking:embedding 检索本身没有 thinking 参数;chat 模型调用查询改写和答案生成时统一通过 settings.llm_extra_body(model) 生成 provider 对应参数。

7.1 模型职责边界

环节 是否必须 使用模型 输入 输出 说明
权限与状态校验 不使用模型 user_idkb_idfile_ids 可检索文件范围 必须先由 PostgreSQL 判定,不能交给模型
查询改写 chat 模型 用户问题、少量历史问题 search_query 只改写检索词,不回答问题
向量化 embedding 模型 search_query 或原始 message query vector 这是语义检索的必要步骤
向量召回 不使用 chat 模型 query vector、knowledge_idfile_ids chunk id + score Qdrant/DashVector 只按向量和 metadata 过滤
详情补齐 不使用模型 chunk id 文件名、页码、标题路径、正文 以 PostgreSQL 为准
答案生成 chat 模型 用户问题、命中 chunk、引用编号 answer references_only 模式跳过
相关问题推荐 chat 模型 用户问题、答案、引用摘要 related questions 可和答案生成合并一次调用

7.1.1 Thinking 设置

知识库助手首版不需要 thinking。

原因:

  1. 检索阶段不涉及 thinkingtext-embedding-v4 只把文本转成向量,向量库只做相似度召回和 metadata 过滤。
  2. 查询改写是短任务:目标是得到稳定、简短的 search_query,打开 thinking 会增加延迟和输出不确定性。
  3. grounded 答案要可控:答案必须严格基于引用 chunk,打开 thinking 不会提升权限过滤或引用准确性,反而可能增加 token 成本。
  4. SSE 前端体验更稳定:关闭 thinking 后首 token 更快,也避免推理内容混入业务流。

chat 模型调用统一按 provider 生成参数:

extra_body = settings.llm_extra_body(model)

DeepSeek 使用 {"thinking": {"type": "disabled"}},百炼/Qwen 使用 {"enable_thinking": false}

适用范围:

  1. use_query_rewrite=true 的查询改写调用。
  2. answer_mode=grounded 的答案生成调用。
  3. answer_mode=creative 的扩展建议调用。

后续如果要做复杂多跳问答,可以单独增加实验开关,不作为首版默认行为。

7.2 默认请求链路

默认 answer_mode=groundeduse_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,不允许回答问题。

适合打开的场景:

  1. 用户问题包含代词或上下文依赖,例如“这个政策有没有实施范围?”。
  2. 用户连续追问,需要结合最近几轮问题补全检索词。
  3. 用户问题过长,里面混有任务描述和检索目标。

不建议默认打开的原因:

  1. 增加一次 chat 模型调用,延迟和成本都会上升。
  2. 改写错误会降低召回质量。
  3. 知识库助手首版更需要稳定,直接用用户问题 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

执行建议:

  1. 向量库先取 candidate_top_k,给后续过滤留余量。
  2. 必须带 knowledge_id metadata filter。
  3. 如果请求带 file_ids,必须同时带 file metadata filter,并在 DB 再校验一遍。
  4. 低于 min_score 的命中不进入答案生成上下文。
  5. 同一个文件连续 chunk 可以合并展示,但生成 prompt 里仍保留引用编号。
  6. 最终进入 chat 模型的 chunk 数不超过 max_context_chunks

7.5 Grounded 答案生成约束

chat 模型生成答案时必须收到结构化上下文:

用户问题:
{message}

知识库引用:
[1] 文件:2023市场分析报告.pdf
    页码:8-9
    标题路径:第二章 / 市场规模
    内容:...

[2] 文件:智慧城市政策.pdf
    页码:3
    标题路径:政策要求
    内容:...

系统约束:

你是知识库问答助手。
只能基于给定“知识库引用”回答。
如果引用中没有答案,明确说明“当前知识库未检索到足够依据”。
不要编造文件名、页码、数据、政策条款。
涉及具体结论时,在句末标注引用编号,例如 [1]。
回答要简洁,优先给出可用于标书编写的要点。

返回结构建议由服务层整理,不强依赖模型输出完整 JSON:

  1. answer 来自 chat 模型流式文本。
  2. references 来自检索命中,不从模型生成。
  3. related_questions 可由同一次模型调用末尾生成,也可二期再做。
  4. usage 来自模型 API 返回。

7.6 无命中与低置信度策略

无有效命中时:

  1. references_only:返回空 references 和空 answer,不报系统错误。
  2. grounded:返回 NO_RELEVANT_CONTEXT,提示用户换关键词或上传相关文件。
  3. creative:可以给检索建议,但必须明确“未在知识库中找到依据”,不能包装成知识库结论。

低置信度命中时:

  1. 如果最高分低于阈值,按无命中处理。
  2. 如果命中数量少但分数合格,可以回答,但文案应保守,例如“根据当前检索到的资料...”。
  3. 前端可展示“找到 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 中增加:

  1. 知识库可用状态统计。
  2. 可选 query rewrite。
  3. KnowledgeSearchResponseDTO.hits 做阈值过滤、截断和引用编号。
  4. grounded prompt 构造。
  5. chat 模型流式调用。
  6. 会话、引用快照和 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 初始化

  1. GET /v1/knowledges?page=1&page_size=1000 获取下拉列表。
  2. 选中最近使用知识库,localStorage key 建议:knowledge_assistant:last_kb_id
  3. GET /v1/knowledges/{kb_id}/assistant/bootstrap 获取知识库可用状态和输入状态。
  4. 欢迎文案、功能说明、示例问题、注意事项由前端固定渲染。

8.2 输入框

  1. 截图中前端限制 200 字即可。
  2. 后端 message 最大仍保留 2000 字,兼容以后粘贴长问题。
  3. empty/indexing/failed/disabled 状态禁用发送按钮。

8.3 引用展示

每条答案下面展示“引用来源 N 条”:

[1] 2023市场分析报告.pdf · 第 8-9 页 · 第二章 / 市场规模
    原文片段...

点击文件名后续可接入:

  1. OSS 预览地址。
  2. 文件详情弹窗。
  3. 定位页码预览。

10. 数据表建议

短期可以不新增表,只用 Redis 保存会话;如果要做可恢复历史,建议新增:

9.1 knowledge_assistant_conversations

id
user_id
knowledge_id
title
message_count
last_message_at
created_at
updated_at
is_deleted

9.2 knowledge_assistant_messages

id
conversation_id
role              user / assistant
content
references_json
usage_json
created_at

references_json 保存生成时实际使用的引用快照,避免后续文件删除或重新解析导致历史答案引用变动。


11. 分阶段落地

第一阶段:可用闭环

  1. 复用现有知识库列表、文件树和内部检索服务。
  2. 新增 assistant/bootstrap
  3. 新增 assistant/chat/stream
  4. 会话先用 conversation_id 透传,可不提供历史列表。

第二阶段:体验完善

  1. 新增会话列表和消息历史。
  2. 保存引用快照。
  3. 补充知识库列表聚合字段。
  4. 增加“限定文件检索”。

第三阶段:质量优化

  1. 查询改写:把用户问题改写为更适合向量召回的 query。
  2. 混合检索:向量召回 + 关键词召回。
  3. rerank:对 top_k 结果二次排序。
  4. 引用去重:同文件相邻页合并展示。

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(...),这样检索过滤、文件归属校验和返回结构能保持一致。