Skip to content

第十二阶段:Docker 安装与 FastAPI 问答接口 ​

进入项目 4:分布式 RAG 服务。第一步:把项目 1 的知识库问答做成 HTTP 接口。

一、为什么要做成接口 ​

之前的问答脚本只能在终端里运行,只有你自己能用。做成接口以后,任何程序都能通过 HTTP 请求来调用它:网页、手机 App、其他服务,用什么语言写的都可以。

这也是后面装进 Docker、做成分布式服务的前提:先把功能变成一个服务,才谈得上打包、扩容、排队。

二、安装 Docker Desktop ​

下一阶段要用 Docker,这一阶段先装好:

  1. 从官网下载 Docker Desktop for Windows,安装时选 Per-user installation,后端用 WSL 2;
  2. 启动后,窗口左下角显示 Engine running,说明引擎已经在运行;
  3. 在终端里验证:
bash
docker run hello-world

看到 Hello from Docker! 就说明装好了。

三、安装 FastAPI ​

bash
pip install fastapi uvicorn

FastAPI 是写接口的框架,uvicorn 是运行接口的服务器。

四、代码 12_api.py ​

提示

model_name 里的“这里填你的 Embedding 模型名”是占位文字。LM Studio 只加载了一个 Embedding 模型时,照这样写也能运行;想写规范一点,可以换成 LM Studio 里显示的模型名。

python
import os
from dotenv import load_dotenv
from fastapi import FastAPI
from pydantic import BaseModel
from openai import OpenAI
from llama_index.core import StorageContext, load_index_from_storage, Settings
from llama_index.embeddings.openai_like import OpenAILikeEmbedding

load_dotenv()

# 启动时只加载一次索引(复用项目 1 的 kb_storage),不要每次请求都加载
Settings.embed_model = OpenAILikeEmbedding(
    model_name="这里填你的 Embedding 模型名",
    api_base="http://localhost:1234/v1", api_key="<redacted>")

index = load_index_from_storage(StorageContext.from_defaults(persist_dir="kb_storage"))
retriever = index.as_retriever(similarity_top_k=3)
llm = OpenAI(api_key=<redacted>("LLM_API_KEY"), base_url=os.getenv("LLM_BASE_URL"))

app = FastAPI(title="知识库问答服务")

# 请求体格式:{"question": "..."},FastAPI 会自动校验
class AskRequest(BaseModel):
    question: str

@app.get("/health")
def health():
    return {"status": "ok"}

@app.post("/ask")
def ask(req: AskRequest):
    nodes = retriever.retrieve(req.question)
    context = "\n\n".join(n.get_content() for n in nodes)
    resp = llm.chat.completions.create(
        model=os.getenv("LLM_MODEL"),
        messages=[
            {"role": "system", "content": "根据资料回答,资料里没有就说不知道。\n资料:\n" + context},
            {"role": "user", "content": req.question},
        ])
    return {"answer": resp.choices[0].message.content,
            "sources": [round(n.score, 3) for n in nodes]}

代码讲解:

  • 启动时只加载一次索引:load_index_from_storage 写在函数外面,服务启动时执行一次。如果写进 /ask 里面,每个请求都要重新加载,会非常慢。
  • pydantic 的 BaseModel:AskRequest 规定请求体必须是 {"question": "..."} 的形式。FastAPI 会自动校验,格式不对就直接返回 422 错误,不用自己写判断。
  • 两个接口:
    • GET /health:健康检查,返回 {"status": "ok"}。以后 Docker 和 K8s 会靠它判断服务是否还活着。
    • POST /ask:检索、拼资料、调用 LLM,返回回答和每条资料的相似度分数(sources)。

五、启动 ​

bash
uvicorn 12_api:app --port 8000

12_api:app 的意思是:12_api.py 文件里名为 app 的对象。

文件名以数字开头报错怎么办:Python 的模块名一般不应该以数字开头。如果 uvicorn 报导入错误,把文件改成字母开头的名字,比如 api12.py,再运行 uvicorn api12:app --port 8000。

六、用 /docs 测试 ​

FastAPI 会自动生成测试页面。打开 http://localhost:8000/docs,展开 POST /ask,点 Try it out,填入问题后点 Execute。

实测结果:

  • 返回 200;
  • 正确回答了折叠键盘的蓝牙配对步骤;
  • sources 是 [0.643, 0.563, 0.418],即检索到的 3 段资料的相似度分数,第一段最相关。

七、.gitignore ​

运行后会生成 __pycache__/(Python 的缓存文件),已经加入 .gitignore,不会提交。

下一阶段预告 ​

第十三阶段:把这个服务装进 Docker 容器,并用 Qdrant 向量数据库替换本地的索引文件 kb_storage。

👉 下一阶段:第十三阶段:Qdrant 向量数据库