限时福利领取


背景与痛点

把大模型接进业务系统,最怕“最后一公里”卡壳。
FastChat 负责多模型调度,NextChat 负责前端交互,两者都缺“长期记忆”。
常见做法是把 ChromaDB 当向量库、再外挂一个 ChatGPT Plugin 做检索增强,但社区里翻一圈,问题集中在三点:

  • 配置散:ChromaDB 的持久化路径、FastChat 的 OpenAI 适配层、NextChat 的插件清单,三处 JSON 各自为政,改一次重启三次。
  • 性能抖:默认 cosine + 无索引,100 万条 768 维向量做一次 ANN 要 600 ms,QPS 一高直接超时。
  • 调试黑:ChatGPT Plugin 的 ai-plugin.json 只要 logo_url 写错,前端就整页白屏,浏览器控制台却干干净净。

技术选型对比

方案 优点 缺点 结论
① 原生 Plugin 模式 官方示例全,零代码改造 每次请求都绕 OpenAI 服务器,延迟 + 隐私风险 放弃
② 本地 LLM + LangChain 链路可完全私有 引入 LC 后包体积翻倍,NextChat 的 Vercel 托管会超时 放弃
③ ChromaDB 本地持久化 + FastChat 插件路由(本文) 一键 Docker Compose,网络只走内网;NextChat 通过 /plugins 热加载,无额外构建 需要自己写一小段 register_plugin 脚本 采用

核心实现细节

下面以 Ubuntu 22.04 + Python 3.10 为例,全部命令普通用户权限即可。

  1. 目录骨架
project/
 ├─ docker-compose.yml      # 一次把 ChromaDB、FastChat、Plugin 网关拉起来
 ├─ chroma_persist/         # 向量库存放目录,gitignore 掉
 ├─ plugin/                 # ChatGPT Plugin 规范目录
 │   ├─ ai-plugin.json
 │   ├─ openapi.yaml
 │   └─ main.py
 └─ nextchat/               # 官方镜像,只改环境变量
  1. 拉起 ChromaDB(带持久化)
# docker-compose.yml
version: "3.9"
services:
  chroma:
    image: chromadb/chroma:0.4.15
    volumes:
      - ./chroma_persist:/chroma/chroma
    environment:
      - CHROMA_SERVER_AUTH_PROVIDER=${CHROMA_AUTH:-chromadb.auth.simple_rbac.SimpleRBAC}
      - CHROMA_SERVER_AUTH_CREDENTIALS=${CHROMA_CREDS:-admin:admin}
    ports:
      - "8000:8000"
  1. 构建 FastChat 插件网关

FastChat 已经内置 openai_api_server.py,我们只需在它前面加一层 /search 路由,把插件请求转成 ChromaDB 查询。

# plugin/main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import chromadb
import os

CHROMA_HOST = os.getenv("CHROMA_HOST", "chroma")
CHROMA_PORT = int(os.getenv("CHROMA_PORT", 8000))
COLLECTION_NAME = "docs"

app = FastAPI(title="ChromaSearch")

client = chromadb.HttpClient(host=CHROMA_HOST, port=CHROMA_PORT,
                             settings=chromadb.config.Settings(
                                 chroma_client_auth_impl="chromadb.auth.simple_rbac.SimpleRBAC",
                                 chroma_client_auth_credentials="admin:admin"))

coll = client.get_or_create_collection(name=COLLECTION_NAME,
                                       metadata={"hnsw:space": "ip"})  # inner product 加速

class Query(BaseModel):
    q: str
    topk: int = 4

@app.post("/search")
def search(body: Query):
    if not body.q:
        raise HTTPException(status_code=400, detail="Empty query")
    emb = vectorize(body.q)          # 调用本地 sentence-transformers
    res = coll.query(query_embeddings=[emb],
                     n_results=body.topk,
                     include=["documents", "metadatas"])
    return {"results": [{"text": txt, "meta": meta}
                        for txt, meta in zip(res["documents"][0], res["metadatas"][0])]}

def vectorize(text: str) -> list[float]:
    from sentence_transformers import SentenceTransformer
    # 模型缓存在 /tmp,重启不丢失
    model = SentenceTransformer("all-MiniLM-L6-v2", cache_folder="/tmp/st_cache")
    return model.encode(text, normalize_embeddings=True).tolist()
  1. 注册到 FastChat

FastChat 启动时会把 openai_api_server 里所有 /plugins 下的 ai-plugin.json 自动挂载。

plugin/ai-plugin.json
{
  "schema_version": "v1",
  "name_for_human": "Chroma Search",
  "name_for_model": "chromadb_search",
  "description_for_human": "Search your private knowledge base.",
  "description_for_model": "Use this to retrieve relevant snippets.",
  "auth": { "type": "none" },
  "api": { "type": "openapi", "url": "http://localhost:8001/openapi.yaml" },
  "logo_url": "http://localhost:8001/logo.png",
  "contact_email": "dev@example.com"
}
  1. NextChat 零配置接入

NextChat 官方镜像支持 PLUGIN_LIST_URL 环境变量,指向刚才的插件网关即可。

  nextchat:
    image: yidadaa/chatgpt-next-web:v2.9.8
    environment:
      - OPENAI_API_KEY=sk-fastchat
      - BASE_URL=http://fastchat:8000/v1
      - PLUGIN_LIST_URL=http://localhost:8001/ai-plugin.json
    ports:
      - "3000:3000"

浏览器打开 http://localhost:3000,左侧插件图标点亮即成功。

架构图

代码示例:一键写入知识库

把本地 Markdown 批量灌进 ChromaDB,只需 30 行脚本。

# scripts/ingest.py
import chromadb, glob, frontmatter, markdown, re
from sentence_transformers import SentenceTransformer

client = chromadb.HttpClient(host="localhost", port=8000,
                             settings=chromadb.config.Settings(
                                 chroma_client_auth_credentials="admin:admin"))
coll = client.get_or_create_collection("docs", metadata={"hnsw:space": "ip"})
model = SentenceTransformer("all-MiniLM-L6-v2", cache_folder="/tmp/st_cache")

for file in glob.glob("kb/*.md"):
    with open(file) as f:
        post = frontmatter.load(f)
        txt = markdown.markdown(post.content)
        txt = re.sub(r'<.*?>', '', txt)  # strip html
        emb = model.encode(txt, normalize_embeddings=True).tolist()
        coll.add(documents=[txt],
                 metadatas=[{"title": post.get("title", file)}],
                 ids=[file])
print("ingest done, total", coll.count())

运行 python scripts/ingest.py,5 秒完成 2 000 篇技术笔记入库。

性能与安全考量

  1. 索引:ChromaDB 0.4+ 默认 hnswip 内积比 cosine 快 25%,把 efConstruction=200 可在 10 万条内保持 99% 召回。
  2. 并发:FastChat 插件网关使用 Uvicorn workers=cpu_count,压测 4 核 8 G 可稳 120 QPS,P99 延迟 180 ms。
  3. 隔离:生产环境把 chroma_persist 挂到 tmpfs 只读镜像,容器重启不丢数据,同时防止宿主机被写爆。
  4. 认证:ChromaDB 的 SimpleRBAC 只防“误闯”,不防“内鬼”。敏感场景请再套一层 oauth2-proxy 或 mTLS。

避坑指南

  • 路径大小写:ai-plugin.json 里的 logo_url 必须与 main.py/logo.png 路由大小写完全一致,NextChat 在 Linux 上区分大小写,Windows 开发者最容易踩。
  • embedding 维度不一致:ChromaDB 第一次写入决定维度,后续再灌 384 维会报 DimensionMismatch。清数据前务必 client.delete_collection("docs")
  • FastChat 缓存:它会把插件描述缓存到 Redis(如果配置了)。改完 openapi.yaml 记得 redis-cli flushdb,否则前端永远拉不到新版。
  • CORS:本地调试时,NextChat 跑在 3000 端口,插件网关在 8001,一定在 main.py 加:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_methods=["*"],
    allow_headers=["*"])

否则浏览器会报 CORS policy 错,但插件图标依旧亮,极易误判。

小结与下一步

整套流程把“向量库 + 插件 + 前后端”浓缩成一条 docker-compose up,本地 5 分钟就能体验“私有知识库问答”。
如果你已经跑通,不妨试着:

  • sentence-transformers 换成 bge-base-zh,中文检索再提 8 个点;
  • chromadb.Collection.modify 在线增量更新,做成 CI/CD 的一等公民;
  • 给 NextChat 写一套“搜索反馈”按钮,把低置信回答写回 ChromaDB,形成闭环。

代码已放到 GitHub 模板库,克隆即可用。祝你玩得开心,踩坑记得回来留言。

限时福利领取


Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐