基于FastAPI + Milvus + DeepSeek构建的医学知识问答系统

引言

在医疗健康领域,快速准确地获取信息至关重要。传统的信息检索方式往往依赖关键词匹配,难以理解用户自然语言提问背后的语义。近年来,随着大语言模型(LLM)和向量数据库技术的成熟,构建一个能够理解复杂医学问题、并从海量文档中精准召回相关知识的智能问答系统成为可能。

本文将结合一段完整的Python代码,详细介绍如何利用FastAPIMilvus (向量数据库)、智谱Embedding模型 以及DeepSeek大语言模型,搭建一个具有语义检索、混合排序(RRF)和父子文档检索能力的RAG(检索增强生成)系统。我们将逐层剖析代码的设计思路、技术选型与实现细节,并探讨其在实际部署中的考量。


一、系统架构与技术选型

1.1 整体流程

系统接收用户问题(question),依次执行以下步骤:

  1. 语义召回:使用Milvus向量数据库,基于稠密向量和稀疏向量(BM25)进行多路召回。
  2. 重排序(Rerank):采用RRF(Reciprocal Rank Fusion)算法融合多路召回结果,输出前10个最相关文档片段。
  3. 父子文档检索:针对PDF等长文档,利用ParentDocumentRetriever策略,先检索子片段(chunk),再返回其父文档(完整段落),补充更丰富的上下文。
  4. 提示构建与生成:将检索到的上下文与用户问题组装成提示(Prompt),调用DeepSeek模型生成最终答案。

1.2 技术栈

组件 选型 说明
Web框架 FastAPI + Uvicorn 轻量异步,支持高并发
向量数据库 Milvus (Lite模式) 支持稠密+稀疏混合检索,内置BM25函数
嵌入模型 智谱AI embedding-3 中文语义理解能力强
大语言模型 DeepSeek API 性价比高,推理能力强
文档分割 LangChain RecursiveCharacterTextSplitter 灵活控制chunk大小
检索策略 LangChain ParentDocumentRetriever 父子文档关联检索
跨域处理 CORS中间件 支持前端直接调用

二、环境准备与关键初始化

2.1 依赖库导入

python 复制代码
import os
import uvicorn
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
import json
import datetime
from zai import ZhipuAiClient
from langchain_milvus import Milvus, BM25BuiltInFunction
from model import ZhipuAIEmbeddings, create_deepseek_client, generate_deepseek_answer
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_classic.storage import InMemoryStore
from langchain_classic.retrievers.parent_document_retriever import ParentDocumentRetriever
  • zai 是智谱AI官方SDK,用于调用嵌入模型。
  • model 为自定义模块,封装了DeepSeek客户端创建和回答生成函数(在代码中未展示完整实现,但逻辑清晰)。

2.2 环境变量与API Key

python 复制代码
os.environ["TOKENIZERS_PARALLELISM"] = "false"
os.environ['DEEPSEEK_API_KEY'] = '***'
deepseek_api_key = os.getenv("DEEPSEEK_API_KEY")
client_embedding = ZhipuAiClient(api_key="***")
  • 关闭tokenizer并行警告,避免多进程冲突。
  • 实际部署时应使用环境变量或密钥管理服务,避免硬编码。

2.3 CORS配置

python 复制代码
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

允许所有域访问,便于前后端分离开发。


三、Milvus向量存储的构建

3.1 双路召回策略

代码中创建了两个Milvus实例:

  • milvus_vectorstore (URI = ./milvus_agent.db):用于通用知识库检索。
  • pdf_vectorstore (URI = ./pdf_agent.db):专门用于PDF长文档的父子检索。

两者均采用混合向量设计:

  • 稠密向量(dense) :由ZhipuAIEmbeddings生成,用于语义相似度计算。
  • 稀疏向量(sparse) :通过BM25BuiltInFunction()内置函数生成,用于关键词匹配。

索引参数:

python 复制代码
index_params=[
    {"metric_type": "IP", "index_type": "IVF_FLAT"},   # 稠密向量用内积
    {"metric_type": "BM25", "index_type": "SPARSE_INVERTED_INDEX"} # 稀疏向量用BM25
]

这种设计使得检索既能够捕捉语义相关性,又能保留精确关键词匹配能力,为后续RRF融合打下基础。

3.2 父子文档检索器

对于PDF等长文档,简单分块可能丢失上下文。ParentDocumentRetriever通过两个分割器解决:

  • child_splitterchunk_size=200,负责生成细粒度子片段,用于检索。
  • parent_splitterchunk_size=1000,负责生成更大的父段落,用于提供丰富上下文。

检索时,系统根据query匹配到子片段,然后从InMemoryStore中取出对应的父文档完整内容,从而既保证检索精度,又避免上下文截断。


四、核心API接口实现

4.1 请求处理

python 复制代码
@app.post("/")
async def chatbot(request: Request):
    json_post_raw = await request.json()
    json_post = json.dumps(json_post_raw)
    json_post_list = json.loads(json_post)
    query = json_post_list.get('question')

接收JSON格式的POST请求,提取question字段。

4.2 语义检索与重排序

python 复制代码
recall_rerank_milvus = milvus_vectorstore.similarity_search(
    query,
    k=10,
    ranker_type="rrf",
    ranker_params={"k": 100}
)
  • similarity_search方法集成了多路召回和重排序。
  • ranker_type="rrf":使用倒数排名融合算法,将稠密和稀疏检索结果合并排序,参数k=100控制平滑因子。
  • 返回前10个最相关文档。

4.3 PDF文档补充召回

python 复制代码
retrieved_docs = parent_retriever.invoke(query)
if retrieved_docs and len(retrieved_docs) >= 1:
    res = retrieved_docs[0].page_content
    context = context + "\n" + res

从PDF向量库中检索,取第一个父文档内容追加到上下文,增加信息覆盖面。

4.4 构建提示词

python 复制代码
SYSTEM_PROMPT = """
System: 你是一个非常得力的医学助手, 你可以通过从数据库中检索出的信息找到问题的答案.
"""
USER_PROMPT = f"""
User: 利用介于<context>和</context>之间的从数据库中检索出的信息来回答问题, 具体的问题介于<question>和</question>之间. 如果提供的信息为空, 则按照你的经验知识来给出尽可能严谨准确的回答, 不知道的时候坦诚的承认不了解, 不要编造不真实的信息.
<context>
{context}
</context>
<question>
{query}
</question>
"""
  • 明确角色(医学助手)。
  • 规定回答依据(检索信息优先,空则依赖自身知识,且禁止捏造)。
  • 使用标记符包裹上下文和问题,便于模型理解结构。

4.5 调用DeepSeek生成答案

python 复制代码
response = generate_deepseek_answer(
    client_llm,
    SYSTEM_PROMPT + USER_PROMPT.format(context, query)
)

generate_deepseek_answer为自定义函数,内部调用DeepSeek API(如deepseek-chat模型),返回文本回答。

4.6 返回结果

python 复制代码
now = datetime.datetime.now()
time = now.strftime("%Y-%m-%d %H:%M:%S")
answer = {
    "response": response,
    "status": 200,
    "time": time
}
return answer

附带状态码和时间戳,便于前端追踪。


五、关键设计亮点

5.1 混合检索与RRF融合

单靠稠密向量容易遗漏关键词信息,单靠BM25则无法理解同义词。两者结合并使用RRF算法,能够平衡召回率和精准度,尤其适合医学领域术语严谨、同义表达多样的特点。

5.2 父子文档策略

医学文档(如指南、文献)往往段落较长,直接分割成小chunk会丢失逻辑完整性。通过父子检索,检索到匹配的子句后,返回其所属的完整段落,让LLM拥有更充分的上下文,提升回答质量。

5.3 灵活的提示设计

提示中加入了**"若上下文为空则依靠自身知识"的指令,增强了系统对未知问题的容错性,同时通过"不知道时坦诚承认"**来抑制幻觉,符合医学场景对准确性的高要求。

5.4 异步与轻量化部署

使用FastAPI的异步特性,支持高并发请求。Milvus采用本地文件模式(./milvus_agent.db),无需单独部署服务,降低了运维复杂度,适合中小规模应用。


六、部署与运行

6.1 启动服务

python 复制代码
if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8103, workers=1)

执行脚本后,服务监听8103端口,可通过http://localhost:8103访问。

6.2 数据准备

  • 需提前将知识文档(如医学教材、指南)分割并嵌入到Milvus中(代码未展示写入逻辑,但需预先执行)。
  • PDF文档需先解析并存入pdf_vectorstore对应的集合。

6.3 测试示例

bash 复制代码
curl -X POST http://localhost:8103/ -H "Content-Type: application/json" -d '{"question":"高血压的常见并发症有哪些?"}'

返回JSON包含response字段。


七、未来优化方向

  1. 多轮对话:当前为单轮问答,可引入记忆模块实现上下文连贯。
  2. 文档动态更新:结合Milvus的upsert能力,实现知识库的实时增量更新。
  3. 细粒度重排序:可引入Cross-Encoder模型对RRF后的top-k结果进行二次精排,进一步提高相关度。
  4. 监控与日志:接入日志系统,记录检索效果和用户反馈,用于持续调优。
  5. 安全性:API Key应通过环境变量或密钥服务管理,避免代码泄露。

结语

本文详细解析了一段基于FastAPI、Milvus和DeepSeek的医学问答系统代码。通过混合检索、父子文档策略和精心设计的提示词,系统在准确性和上下文完整性上达到了较好的平衡。该架构不仅适用于医学领域,也可迁移到法律、金融等需要精确知识检索的场景。希望本文能为读者在构建自己的RAG系统时提供参考与启发。

如果你对代码中的某些部分有疑问,或希望了解更多实现细节,欢迎在评论区交流讨论!

相关推荐
卷无止境6 小时前
FastAPI 实现 SSO,从协议原理到生产级落地
后端·python·fastapi
卷无止境6 小时前
FastAPI 与 RustFS 集成指南
后端·python·fastapi
Jackyzhe6 小时前
向量库不再囤数据:Milvus 3.0 零拷贝直读数据湖
milvus
会编程的吕洞宾11 小时前
Spring AI 2.0 接 Milvus 做混合检索:RAG 召回率翻倍的实战方案
人工智能·spring·milvus
大模型丫丫1 天前
FastAPI 入门指南:从零开始构建高性能 Python API
开发语言·python·fastapi
卷无止境1 天前
Python的contextlib与 FastAPI 中的上下文管理
后端·python·fastapi
卷无止境1 天前
FastAPI Events 深度解析与工程实践
后端·python·fastapi
tryCbest2 天前
搭建企业知识库之Milvus 向量数据库
数据库·milvus
小月土星2 天前
构建 AI 应用的后端,最主流的选择就是 Python + FastAPI。
后端·fastapi