08 Chroma 持久化 + FastAPI 封装

Chroma 持久化 + FastAPI 封装

安装依赖

bash 复制代码
cd D:\yunyanshijie\skill_test\python_study
.venv\Scripts\activate
pip install chromadb langchain-chroma

Chroma 持久化

为什么要换?

InMemoryVectorStore Chroma
存储位置 进程内存 磁盘 chroma_db/
重启后 索引丢失,需重新 Embedding 直接加载,秒级启动
适用场景 学习、调试 接近真实部署

核心 API

py 复制代码
from langchain_chroma import Chroma
def build_vectorstore(chunks, embeddings, chroma_dir: Path):
    chroma_dir.mkdir(parents=True, exist_ok=True)

    if any(chroma_dir.iterdir()):          # 目录非空 → 已有索引,库已存在,直接加载
        print("从磁盘加载向量库...")
        return Chroma(
            persist_directory=str(chroma_dir),
            embedding_function=embeddings,
            collection_name="knowledge",
        )
    else:                                   # 首次 → 建库(文档切分后)
        print("首次运行,正在建索引...")
        return Chroma.from_documents(
            documents=chunks,
            embedding=embeddings,
            persist_directory=str(chroma_dir),
            collection_name="knowledge",
        )

FastAPI 封装

架构

#mermaid-svg-mgw4WWbKdobAgero{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mgw4WWbKdobAgero .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mgw4WWbKdobAgero .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mgw4WWbKdobAgero .error-icon{fill:#552222;}#mermaid-svg-mgw4WWbKdobAgero .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mgw4WWbKdobAgero .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mgw4WWbKdobAgero .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mgw4WWbKdobAgero .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mgw4WWbKdobAgero .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mgw4WWbKdobAgero .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mgw4WWbKdobAgero .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mgw4WWbKdobAgero .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mgw4WWbKdobAgero .marker.cross{stroke:#333333;}#mermaid-svg-mgw4WWbKdobAgero svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mgw4WWbKdobAgero p{margin:0;}#mermaid-svg-mgw4WWbKdobAgero .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mgw4WWbKdobAgero .cluster-label text{fill:#333;}#mermaid-svg-mgw4WWbKdobAgero .cluster-label span{color:#333;}#mermaid-svg-mgw4WWbKdobAgero .cluster-label span p{background-color:transparent;}#mermaid-svg-mgw4WWbKdobAgero .label text,#mermaid-svg-mgw4WWbKdobAgero span{fill:#333;color:#333;}#mermaid-svg-mgw4WWbKdobAgero .node rect,#mermaid-svg-mgw4WWbKdobAgero .node circle,#mermaid-svg-mgw4WWbKdobAgero .node ellipse,#mermaid-svg-mgw4WWbKdobAgero .node polygon,#mermaid-svg-mgw4WWbKdobAgero .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mgw4WWbKdobAgero .rough-node .label text,#mermaid-svg-mgw4WWbKdobAgero .node .label text,#mermaid-svg-mgw4WWbKdobAgero .image-shape .label,#mermaid-svg-mgw4WWbKdobAgero .icon-shape .label{text-anchor:middle;}#mermaid-svg-mgw4WWbKdobAgero .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mgw4WWbKdobAgero .rough-node .label,#mermaid-svg-mgw4WWbKdobAgero .node .label,#mermaid-svg-mgw4WWbKdobAgero .image-shape .label,#mermaid-svg-mgw4WWbKdobAgero .icon-shape .label{text-align:center;}#mermaid-svg-mgw4WWbKdobAgero .node.clickable{cursor:pointer;}#mermaid-svg-mgw4WWbKdobAgero .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mgw4WWbKdobAgero .arrowheadPath{fill:#333333;}#mermaid-svg-mgw4WWbKdobAgero .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mgw4WWbKdobAgero .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mgw4WWbKdobAgero .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mgw4WWbKdobAgero .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mgw4WWbKdobAgero .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mgw4WWbKdobAgero .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mgw4WWbKdobAgero .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mgw4WWbKdobAgero .cluster text{fill:#333;}#mermaid-svg-mgw4WWbKdobAgero .cluster span{color:#333;}#mermaid-svg-mgw4WWbKdobAgero div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mgw4WWbKdobAgero .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mgw4WWbKdobAgero rect.text{fill:none;stroke-width:0;}#mermaid-svg-mgw4WWbKdobAgero .icon-shape,#mermaid-svg-mgw4WWbKdobAgero .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mgw4WWbKdobAgero .icon-shape p,#mermaid-svg-mgw4WWbKdobAgero .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mgw4WWbKdobAgero .icon-shape .label rect,#mermaid-svg-mgw4WWbKdobAgero .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mgw4WWbKdobAgero .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mgw4WWbKdobAgero .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mgw4WWbKdobAgero :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} POST /chat
startup 一次
Client
FastAPI
agent_core
chroma_db
LangGraph
DeepSeek

重点:向量库和 Graph 在 服务启动时初始化一次,不是每个请求重建。

生命周期(lifespan)

FastAPI 推荐用 lifespan,在 startup 里完成初始化:

py 复制代码
from contextlib import asynccontextmanager

graph = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    global graph # 在函数里要赋值模块级变量时必须写,否则 Python 会当成函数局部变量
    graph = init_graph()   # 内部调 build_vectorstore + build_graph
    yield # 关服务   → yield 之后的代码再跑(清理)
    # shutdown 清理

app = FastAPI(lifespan=lifespan)

接口设计

py 复制代码
class ChatRequest(BaseModel):
    message: str
    thread_id: str = "default"

class ChatResponse(BaseModel):
    reply: str
    sources: list[str] = [] # 这是 RAG 的 溯源(citation):不只给答案,还告诉用户「依据哪些文档」。
路由 方法 作用
/health GET 健康检查,返回 {"status": "ok", "chunks": N}
/chat POST 发送消息,返回回答 + 引用来源

/chat 核心逻辑

py 复制代码
@app.post("/chat", response_model=ChatResponse)
def chat(req: ChatRequest):
    config = {"configurable": {"thread_id": req.thread_id}}
    result = graph.invoke(
        {"messages": [HumanMessage(content=req.message)], "context": ""},
        config=config,
    )
    reply = result["messages"][-1].content
    sources = result.get("sources", [])   # 见下方「溯源」
    return ChatResponse(reply=reply, sources=sources)

检索溯源

在 AgentState 里加一个字段:

py 复制代码
class AgentState(TypedDict):
    messages: Annotated[list, add_messages]
    context: str
    sources: list[str]   # 新增

retrieve 节点里:

py 复制代码
sources = [str(doc.metadata.get("source", "unknown")) for doc in results]
return {"context": context, "sources": sources}
相关推荐
余槐i2 天前
拆解 Agent 核心原理|从零动手实现简易 AI 智能体(三)
人工智能·python·fastapi·ai agent
醇氧3 天前
Git 合并冲突与`__pycache__` 的恩怨情仇
python·numpy·fastapi
甜到心里的蛋糕3 天前
如何用企业微信实现让微信收到通知实时通知
服务器·python·微信小程序·fastapi
basketball6163 天前
Python FastAPI 介绍以及常用方法
python·fastapi·vllm·ai infra
troy1284 天前
Codex 安全盲区:代码漏洞生成实测
windows·python·ci/cd·pycharm·django·github·fastapi
打工仔折腾 AI5 天前
FastAPI 从本机到生产服务器:Nginx+Gunicorn+Uvicorn 完整部署实录
人工智能·后端·python·nginx·fastapi·gunicorn
西安栈上月明软件科技6 天前
从 Linux 0.01 到 AI 开源:星图邻的开源实践
人工智能·自然语言处理·架构·开源·fastapi
troy1286 天前
Python 基础语法(九):Django/Flask/FastAPI 三大 Web 框架详细对比解析
python·jupyter·django·flask·github·fastapi
刚子编程7 天前
Ubuntu 22.04 Docker 从零部署全栈项目实录:Next.js + FastAPI + PostgreSQL 一次跑通
fastapi·nextjs·docker部署·全栈开发·ubuntu22.04
CSharp精选营8 天前
Ubuntu 22.04 Docker 从零部署全栈项目实录:Next.js + FastAPI + PostgreSQL 一次跑通
fastapi·nextjs·docker部署·全栈开发·ubuntu22.04