跳到主要内容

总览:PrivateGPT 是什么、检索在其中怎么转

30 秒导读: PrivateGPT 1.0 是一个开源、本地优先的 AI 应用 API 层,对外长得像 Anthropic 的 Messages API。它自己不跑模型——你把它指向任意 OpenAI 兼容的推理服务;它补上的是"应用层积木":文档摄取、带引用的检索、工具调用、流式 agent 循环。本库这一整套文档只盯其中一条线——RAG 检索:文件怎么变成可检索的东西,一次提问怎么把相关片段取回来、锚回原文、喂给模型流式作答。


1. 这是什么(零基础也能懂)

一句话定义: PrivateGPT 是一个本地部署的 AI 应用后端 API——你把本地模型接上它,它替你把"聊天 + 读文档 + 带引用检索 + 工具调用"这些后端积木做好,对外暴露成一套 Anthropic Messages API 兼容的接口。

它不是什么(这点最容易误解)。不负责跑模型。README.md:31 说得很直白:PrivateGPT 通过 OPENAI_API_BASE 连到任意 OpenAI 兼容推理服务(Ollama、llama.cpp、vLLM…),只要对方实现了 /v1/chat/completions/v1/models 就能用。

用一张分层图理解它站在哪:

你的 App / Agent / 工作流 / UI

PrivateGPT API ← 应用层积木(本项目)

OpenAI 兼容推理服务(Ollama / llama.cpp / vLLM…) ← 只管"把模型跑起来"

这张图直接来自 README.md:23-28。记住上下分工:下层回答"怎么把一个模型跑起来",PrivateGPT 回答"怎么在这个模型之上搭出有用的 AI 应用"。

它从哪来(一句话历史)。 PrivateGPT 2023 年出道时,只是一个"和你的文档离线聊天"的脚本,在 GitHub 上爆火、破 5 万星(README.md:196)。1.0 是把那个想法从头重写成一个正经的 API 层(README.md:200)——本文讲的就是这个 1.0。

给谁用 / 解决什么问题。 给要搭私有、不出本机的 AI 产品的开发者:你不想为了"和文档聊天"重新造一遍摄取、切块、向量检索、引用、工具循环这些后端;PrivateGPT 把它们标准化好,你在 API 之上写自己的应用即可。

它对外的形态(读了哪些文件得出的)。

你看到的入口是什么在哪
private-gpt serve 命令CLI 启动服务器pyproject.toml:26-27([project.scripts]private_gpt.cli.main:main)
一个 FastAPI 应用挂了 13 个路由的 HTTP APIprivate_gpt/launcher.py:100(create_app)、:198-210(include_router)
/v1/messages 等接口Anthropic 兼容的 Messages APIprivate_gpt/server/chat/chat_router.py:30
/ui 工作台自带的演示 UI(非核心产品)private_gpt/launcher.py:212(挂静态目录)
可选依赖分组按需装 LLM/嵌入/向量库/摄取/工具pyproject.toml:29-295

"按需装"是它的一个基调。 pyproject.toml 把能力切成一堆 optional 分组:llm-*(openai / anthropic / mistral)、embedding-*vectorstore-qdrantingest-*tool-*…… 装 core 这个 flavor(pyproject.toml:293-295)就等于一次拉齐"OpenAI 兼容模型 + Qdrant 向量库 + 全套摄取 + 工具"。换句话说:向量库默认走 Qdrant,摄取和工具是可插拔的。

用起来什么样(最小真实示例)。 启动后,一次带检索的提问就是一个标准 HTTP 请求(简化自 chat_router.py:30/v1/messages 与其 ChatBody):

# 示意,非源码:向本地 PrivateGPT 发一次流式提问
curl -N http://localhost:8080/v1/messages \
-H "content-type: application/json" \
-d '{
"model": "private-gpt",
"stream": true,
"messages": [{"role":"user","content":"我上传的合同里违约条款怎么说?"}],
"tools": [{"type":"semantic_search_v1","name":"semantic_search"}]
}'

响应是一串 SSE 事件(message_startcontent_block_delta …→ message_stop),模型边检索边把带引用的答案流回来。

一句话直觉。 把 PrivateGPT 想成**"本地版的 Anthropic API 后端"**:同样的 Messages 接口、同样的工具与引用概念,只是模型在你自己机器上、数据不出本机。

本节不碰任何底层实现;检索到底怎么转,下一节给全景,再往后各章拆开。


2. 顶层全景(它大概怎么转)

本节给两样东西:一张把"从文件到带引用回答"串起来的 ASCII 全景图,和一张"各部件干什么、在哪"的表。怎么读这张图:上半截是离线的"摄取/索引"(把文件变成可检索的东西),下半截是在线的"一次提问"(检索 + agent 循环 + 流式作答);两截通过中间的向量索引相接。

【离线:摄取 → 索引】
文件/内容 ──▶ 摄取 IngestComponent ──▶ 解析+切块成节点 ──▶ 写入 artifact 索引 / Qdrant
(/v1/artifacts) (树状节点) (VectorArtifactIndex)


┌──────────────────┐
│ 向量索引 (Qdrant) │
└──────────────────┘

【在线:一次提问 /v1/messages】 │ 语义检索
HTTP 请求 ──▶ chat_router ──▶ ChatAsyncFacade ──▶ ChatLoopEngine (agent 循环)

每一轮:调 LLM ──▶ 模型要用工具? ──是──▶ semantic_search 工具
│ │(取回带上下文的片段)
否 ▼
│ 结果 + SourceBlock 引用
▼ │
流式 SSE 事件(带 citations)◀────────┘

部件一句话职责(部件 | 干什么 | 在哪个文件)。

部件职责所在文件(符号)
CLI / launcher启动 FastAPI、挂路由、预热组件private_gpt/cli/main.pyprivate_gpt/launcher.py:100(create_app)
Messages 路由暴露 /v1/messages 等 Anthropic 兼容接口private_gpt/server/chat/chat_router.py:30(chat_messages)
Chat 门面(async)起流、处理断连取消、决定 SSE 还是整包返回private_gpt/server/chat_async/chat_async_facade.py:30(ChatAsyncFacadeService.chat)
Chat 服务构建 agent 循环引擎、包装成事件流private_gpt/server/chat/chat_service.py:164(_build_loop_engine)、:177(stream_chat)
Agent 循环引擎迭代式"调 LLM → 执行工具 → 再调"直到收敛,并把一切转成事件private_gpt/components/engines/chat_loop/chat_loop_engine.py:180(ChatLoopEngine)
语义检索工具把"检索"封成一个 LLM 可调用的工具private_gpt/components/tools/builders/semantic_search_builder.py:213(build_tool)
检索工作流查询改写(condense)+ 取回节点 + 组装带引用上下文private_gpt/components/workflows/retrieval/semantic_search.py:86(SemanticSearchWorkflow)
Artifact 索引节点的读写与索引结构(上层)private_gpt/artifact_index/base_artifact_index.py:51(BaseArtifactIndex)、vector_artifact_index.py:31(VectorArtifactIndex)
摄取组件把文件解析、切块成可检索节点private_gpt/components/ingest/ingest_component.py:46(IngestComponent)
向量库组件Qdrant 存取、构造带过滤的 retrieverprivate_gpt/components/vector_store/vector_store_component.py:28(VectorStoreComponent)、:111(get_retriever)
上下文栈把系统指令、文档、工具分层组装进 promptprivate_gpt/components/context/models/context_stack.py:16(ContextStack)
摄取路由/v1/artifacts 上传/列出/删除文档private_gpt/server/ingest/ingest_router.py:34(prefix="/v1/artifacts")

一个反直觉但关键的点:检索不是一个独立的"RAG 管线",而是 agent 循环里的一个工具。 模型在对话中自己决定要不要调 semantic_search(tool_names.py:4)。这就是"Agentic RAG"——第 04 章专门讲。


3. 主线走一遍(一次问答,从 HTTP 到 SSE)

用一句自然语言提问("我上传的合同里违约条款怎么说?")把高层路径走一遍,只点部件、不进代码。想看某一步的机制,跟着链接跳到对应章。

  1. 请求进来。 POST /v1/messages 命中 chat_router.py:222(chat_messages)。它做两件事:用 ChatRequestMapper 把请求体 ChatBody 映射成内部的 ChatRequest,然后交给 chat 门面。

  2. 门面起流。 实际处理落在 chat_async_facade.py:30(ChatAsyncFacadeService.chat):它发起一个流、拿到事件生成器,并包一层"客户端断连就取消"的保护(:69,_cancellable_stream_generator)。请求里 stream=true 就走 SSE(:52,to_sse_stream),否则把事件折叠成一条完整消息返回(:59,fold)。

  3. 进入 agent 循环。 事件流最终由 chat_service.py:177(stream_chat)构建的 ChatLoopEngine 产出(chat_loop_engine.py:205,run)。循环每一轮(_run_iteration,:332)向 LLM 发起带工具的流式请求(:371,astream_chat_with_tools)。

  4. 模型决定检索。 若模型在这一轮要求调用 semantic_search 工具,引擎在流式过程中就地把工具调用派发执行(:834,_handle_tool_use)。这一步会触发检索工作流:必要时先把口语化问题改写成独立查询(condense),再到 Qdrant 取回相关节点,并组装成带 SourceBlock 引用的上下文(semantic_search.py:225,retrieve 步骤)。→ 机制见 0203

  5. 把工具结果喂回模型。 工具结果作为一条 tool 消息追加进对话,循环进入下一轮;模型这次带着检索到的原文,生成带引用的答案。收不到新的工具调用就收敛停止(:427)。→ 循环与工具装配见 0405

  6. 流式吐出。 整个过程以 Anthropic 风格的事件流出:message_startcontent_block_start/delta/stop →(工具块)→ message_deltamessage_stop(事件样例见 chat_router.py:136-147)。

一句话记住主线: 请求 → 门面起流 → agent 循环反复"调模型/按需检索" → 带引用的答案以 SSE 流回。


4. 阅读地图(01–05 建议顺序)

按"文件怎么进来 → 怎么取回来 → 怎么锚回原文 → 怎么挂进循环 → 怎么组进 prompt"的顺序读,是一条完整的检索主线。

顺序章节一句话讲什么
101-ingestion-tree-nodes.md摄取管线与树状节点模型:文件如何被解析、切块,变成可检索的 artifact 节点。
202-semantic-retrieval-tree-expansion.md语义检索与树扩展:一次查询如何做改写、向量召回,并沿节点树取回带上下文的片段。
303-citations.md引用(citations):检索到的片段如何被 SourceBlock 标记、把答案里的句子锚回原文。
404-agentic-rag-chat-loop.mdAgentic RAG:检索如何被封成 semantic_search 工具,挂进 ChatLoopEngine 的流式循环由模型自主调用。
505-context-stack-assembly.md上下文装配:检索结果、工具定义、系统指令如何分层进入最终 prompt。

建议先读本页建立心智模型,再从 01 顺读。急用时也可按上表直接跳章——每章开头都有自己的 30 秒导读。


5. 巧妙之处速览

这里只点名本项目在检索线上几处值得带走的设计,细节留给对应章:

  • 检索即工具,而非独立管线。 是否检索由模型在循环里自己决定(tool_names.py:4,SEMANTIC_SEARCH_TOOL_NAME),而不是每次强行 RAG。→ 04
  • 工具在流式中就地派发。 引擎不等整轮结束,一边解析工具调用的 JSON、一边启动执行任务(chat_loop_engine.py:651,_close_active_tool_block),降低"检索→作答"的延迟。→ 04
  • 检索前先改写查询(condense)。 有对话历史时,先把口语化追问改写成自洽的独立查询再去召回(semantic_search.py:196,condense 步骤),提升召回质量。→ 02
  • 检索结果自带来源块。 节点被取回时同时产出 SourceBlock(semantic_search.py:170,SourceBlock.from_nodes;定义在 private_gpt/events/models/_content_blocks.py:653),让答案能锚回原文。→ 03
  • 按需检索工作流缓存。 工具按 artifact 集合缓存对应的检索工作流(semantic_search_builder.py:235,_ensure_workflow),避免每次重建。→ 02

6. 边界(本库只讲这一条线)

本库聚焦 rag-retrieval: 摄取 → 索引 → 语义检索 → 把检索挂进 agent 循环 → 上下文装配 → 引用。以下能力虽然同在这个仓库、也挂在同一个 agent 循环上,但本套文档只点名、不展开:

只点名、不展开在哪(供好奇者自查)
代码执行 / bash / 文本编辑器工具private_gpt/components/tools/tool_names.py:14-20components/code_execution/
Skills(技能加载/卸载)private_gpt/server/skills/components/skills/
MCP 连接器private_gpt/server/mcp/pyproject.toml:229(tool-mcp)
数据库查询 / 表格分析(tabular)components/workflows/tabular/pyproject.toml:233(tool-tabular)
Web search / web fetch 工具components/tools/processors/(WebSearchProcessor 等)
Celery 异步任务队列private_gpt/celery/pyproject.toml:204(queue-celery)

这些都通过同一套工具处理器管线(components/tools/tool_pipeline.py:38,ToolPipeline)接入循环,所以理解了 04 章的"检索即工具",就能推断它们的接入方式——只是它们的领域逻辑不在本库范围内。


7. 代码地图(导航索引)

一张跳转表:想读源码从这里进。优先用符号名 grep(行号会随上游漂移,符号名相对稳定)。

主题文件路径符号名
CLI 入口pyproject.toml:26 / private_gpt/cli/main.py[project.scripts] private-gpt / main
建 FastAPI、挂路由private_gpt/launcher.py:100create_app
预热核心组件private_gpt/launcher.py:66eager_loading
Messages 接口private_gpt/server/chat/chat_router.py:222chat_messages
Chat 门面(起流/取消)private_gpt/server/chat_async/chat_async_facade.py:30ChatAsyncFacadeService.chat
构建 agent 循环private_gpt/server/chat/chat_service.py:164ChatService._build_loop_engine
Agent 循环引擎private_gpt/components/engines/chat_loop/chat_loop_engine.py:180ChatLoopEngine
一轮迭代(调 LLM+工具)private_gpt/components/engines/chat_loop/chat_loop_engine.py:332_run_iteration
工具派发执行private_gpt/components/engines/chat_loop/chat_loop_engine.py:834_handle_tool_use
语义检索工具构建private_gpt/components/tools/builders/semantic_search_builder.py:213SemanticSearchToolBuilder.build_tool
检索工作流(agentic 路径)private_gpt/components/workflows/retrieval/semantic_search.py:86SemanticSearchWorkflow
检索工具处理器private_gpt/components/tools/processors/semantic_search_processor.py:19SemanticSearchProcessor
检索服务(02 章主线 / REST 入口)private_gpt/server/primitives/semantic_search_service.py:94SemanticSearchService.retrieve_semantic_relevant_expand_nodes
工具名常量private_gpt/components/tools/tool_names.py:4SEMANTIC_SEARCH_TOOL_NAME
Artifact 索引(上层)private_gpt/artifact_index/base_artifact_index.py:51BaseArtifactIndex
向量 artifact 索引private_gpt/artifact_index/vector_artifact_index.py:31VectorArtifactIndex
摄取组件private_gpt/components/ingest/ingest_component.py:46IngestComponent
向量库组件 / retrieverprivate_gpt/components/vector_store/vector_store_component.py:111VectorStoreComponent.get_retriever
上下文栈private_gpt/components/context/models/context_stack.py:16ContextStack
来源块(引用)private_gpt/events/models/_content_blocks.py:653SourceBlock
摄取路由private_gpt/server/ingest/ingest_router.py:34ingest_router (prefix=/v1/artifacts)

各机制的深入走读见 0105;本页只负责给心智模型与路由,不与后续章节的机制细节重叠。