跳到主要内容

LibreChat — 架构与原理

30 秒导读: LibreChat 是一个自托管的 AI 聊天平台——你把它部署在自己的服务器上,就得到一个类 ChatGPT 的界面,但背后能接 Anthropic、OpenAI、Google、Bedrock,以及任何 OpenAI 兼容的模型。它真正的工程量不在界面,而在中间那层收敛:几十家风格各异的模型 API,被抽象成同一个叫 endpoint 的东西;每条聊天消息最终都走进一套 agent 集成层(工具调用、MCP、文件检索、会话记忆、流式输出),由一个外部的 langgraph 运行器 @librechat/agents 端到端跑完,再流式回传前端并落库。


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

一句话定义: LibreChat 是一个开源、多用户、可自托管的 AI 对话应用,一个界面统一接入所有主流大模型,并叠加 agent / 工具 / 检索 / 记忆等能力。

解决什么问题、给谁用。 想象你是一个团队的技术负责人:

  • 你不想让公司数据经过第三方 SaaS,想把聊天服务跑在自己机房
  • 你的人有的爱用 Claude、有的爱用 GPT、有的要接本地 Ollama——你不想为每家维护一套界面和权限。
  • 你还想让模型能调工具、查内部文档、记住用户偏好,而不只是干聊。

LibreChat 就是干这个的:一套后端 + 一套前端,把上面全部收进一个可自托管、带多用户鉴权的系统。

它能做什么(功能一览):

能力说明
多 provider 统一接入Anthropic / OpenAI / Azure / Google / Vertex / Bedrock + 任意 OpenAI 兼容 API
Agents无代码自定义助手,可挂工具、文件检索、代码执行、子 agent
工具与 MCP内置工具 + Model Context Protocol 服务器,给模型装"手脚"
RAG 文件问答上传文件、向量检索、把命中片段注入上下文
会话记忆跨轮次抽取并复用用户事实
流式与可恢复流SSE 实时输出,断线自动重连续传(Redis 支撑横向扩展)
多用户与权限OAuth2 / LDAP / 邮箱登录、角色/群组、额度与审计

用起来什么样。 前端本质是给后端发一个 POST。一条最小的聊天请求长这样(示意,非源码):

// POST /api/agents/chat/ —— 前端发起一次对话
{
"endpoint": "agents", // 走哪套接入:agents 是核心入口
"agent_id": "agent_xxx", // 用哪个 agent(带工具/指令/模型)
"conversationId": "conv_xxx",
"text": "帮我总结这份文档",
"files": [ /* 附件引用 */ ]
}

后端不会一次性返回一个大 JSON,而是开一条 SSE 流,把模型 token、工具调用步骤、用量统计一段段推回来。

一句话直觉。 把 LibreChat 想成机场的中央调度塔:几十家航空公司(provider)机型、跑道、通信协议各不相同;塔台(endpoint 抽象 + agent 层)用一套统一话术指挥所有飞机起降,旅客(用户)只跟塔台打交道,不需要知道每家航司的内部规矩。


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

怎么读下面这张图: 从上到下就是一条聊天消息的流向——前端发请求,经中间件闸门,进 AgentController,由 initializeClient 组装出 AgentClient,交给外部运行器 @librechat/agents 真正驱动模型与工具,结果沿 SSE 原路流回前端、同时落库 MongoDB。

┌────────────────────────────────────────────────────────┐
│ React 前端 (client/) —— 发 POST,收 SSE 流 │
└──────────────────────────┬─────────────────────────────┘
│ POST /api/agents/chat

┌────────────────────────────────────────────────────────┐
│ ① 闸门中间件链 │
│ 鉴权 · 封禁 · PII过滤 · 内容审核 · 权限 · │
│ buildEndpointOption(挑 provider 并解析会话) │
└──────────────────────────┬─────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│ ② AgentController (request.js) —— 主线编排/中止/落库 │
│ └─ initializeClient() 组装 AgentClient │
└──────────────────────────┬─────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│ ③ AgentClient —— 组消息 · 记忆 · 工具集 · 预算 │
│ └─ createRun() ← @librechat/agents (外部 langgraph)│
│ 工具调用循环 · 子 agent · 流式事件 │
└──────────────┬───────────────────────────┬─────────────┘
│ SSE 事件回流 │ saveMessage
▼ ▼
前端逐段渲染 MongoDB (data-schemas)

各部件一句话职责:

部件干什么在哪(相对克隆根)
前端 SPA发起对话、渲染流式结果client/src/
路由与闸门挂载 /api/agents、跑鉴权/审核/权限中间件api/server/index.js:268api/server/routes/agents/chat.js
Endpoint 抽象endpoint 字段挑 provider 构建器、解析会话api/server/middleware/buildEndpointOption.js:21
AgentController主线编排:开流、监听中止、结束落库api/server/controllers/agents/request.js
initializeClient加载 agent、工具、模型,组装出客户端api/server/services/Endpoints/agents/initialize.js
AgentClient组消息、注入记忆、跑 run、聚合用量api/server/controllers/agents/client.js:91
@librechat/agents外部 langgraph 运行器,真正驱动"模型↔工具"循环npm 依赖(源码不在本仓)
工具 / MCP加载内置工具与 MCP 服务器工具api/server/services/ToolService.jspackages/api/src/mcp/MCPManager.ts
数据层Mongo 模型:会话、消息、agent、记忆等packages/data-schemas/src/models/
共享 SDK前后端共用的类型、端点、数据服务packages/data-provider/

主线走一遍(高层,不进代码):

  1. 前端 POST 一条消息到 /api/agents/chat(核心入口就是 agents 这套)。
  2. 一串中间件当闸门:登录了吗、被封了吗、内容合规吗、有权用这个 agent 吗;最后 buildEndpointOption 根据 endpoint 字段决定"这条消息用哪家 provider、按什么参数解析"。
  3. AgentController 接手,调 initializeClient 把 agent 定义、工具、模型凭证组装成一个 AgentClient,并开一条 SSE 流。
  4. AgentClient 组好消息(拼历史、注入记忆、附上文件上下文与工具集),调用外部 createRun 把控制权交给 @librechat/agents;后者跑"模型说话 → 要不要调工具 → 调完再喂回模型"的循环。
  5. 循环里产生的每个事件(token、工具步骤、用量)沿 SSE 实时回流前端;结束时把最终消息 saveMessage 落进 MongoDB。

关键认知:几乎所有对话都从 agents 这个 endpoint 走。 即使你只想直连 OpenAI,LibreChat 也是用 agent 层去包裹 provider——所以"agent 集成层"不是可选功能,而是主干。


3. 阅读地图(建议顺序)

这套文档按由浅入深排。想快速建立心智模型就读 §1–§2;想读懂某个子系统再下钻对应章节。

顺序章节一句话讲什么谁该读
0总览:这是什么 · 全景图 · 阅读地图本页:是什么、怎么转、去哪读所有人先读
1主线:一条聊天消息的一生从 POST 到 SSE 落库,端到端追一遍请求想懂整体流程
2Endpoint 抽象:把几十家 provider 收敛成一套接口EModelEndpointbuildOptions、provider 映射想加/改 provider
3Agent 编排与流式:AgentClient 深入chatCompletioncreateRun、子 agent、用量统计想懂 agent 内核
4工具与 MCP:给模型装手脚工具加载、MCP 连接管理、工具调用回调想扩展工具
5上下文工程:文件/RAG、会话记忆与 token 预算buildMessagesuseMemory、文件注入与剪枝想调上下文行为
6支撑层:数据模型、共享 SDK 与前端骨架Mongo 模型、data-provider、React 前端想懂持久化与前端

这个仓库长什么样(monorepo 布局):

LibreChat/
├── api/ 后端 Express 服务(遗留 JS,主入口)
│ ├── server/ 路由 · 中间件 · 控制器 · 服务
│ └── app/clients/ BaseClient 等基类
├── client/ 前端 React SPA
└── packages/ 新代码归口(TypeScript)
├── api/ 后端新逻辑(endpoints / mcp / tools / memory …)
├── data-provider/ 前后端共享:类型 · 端点 · 数据服务
├── data-schemas/ Mongo 模型与 schema
└── client/ 前端共享工具

一个约定值得记住:老代码在 api/(JS),新逻辑一律进 packages/api/(TypeScript),api/ 只做薄壳去调它。所以你在 client.js 里看到一大串从 @librechat/api import 的函数(createRunbuildToolSetinitializeAgent…),那些实现体都在 packages/api/


4. 巧妙之处(可借鉴的技术)

这几处是读完能带走的"精华",每条先说妙在哪,再给锚点。

① 用一个 endpoint 字段收敛所有 provider。 前端不关心底层是 Claude 还是 GPT,只发 endpoint: "agents";后端一个 buildFunction 查表就把请求路由到对应的构建器,再由 agent 层统一驱动。加一家新 provider = 加一个 endpoint 配置 + 一个 provider 映射,主干不动。依据:api/server/middleware/buildEndpointOption.js:21(buildFunction)、packages/data-provider/src/schemas.ts:18(EModelEndpoint)、:31(Providers,注释明说"Mirrors @librechat/agents providers")。

② 把"跑 agent"外包给独立包 @librechat/agents LibreChat 本体不实现"模型↔工具"的 langgraph 循环,而是把它抽成同团队的独立 npm 包,主仓只负责组装输入、接住流式事件、落库计费client.js 顶部从 @librechat/agents 一次 import 进 createRun / formatAgentMessages / Providers 等。依据:api/server/controllers/agents/client.js:56:1378(createRun)。

③ 可恢复流(resumable streams)。 生成任务由 GenerationJobManager 管理,SSE 断了能重连、重放漏掉的 chunk 再续传;单机到 Redis 横向扩展都适用。这让"刷新页面/切设备不丢回复"成为默认行为。依据:api/server/routes/agents/index.js:63(GET /chat/stream/:streamId,"replay support")。

④ agent 层内建记忆与 token 预算。 AgentClient 在组消息时并行拉起会话记忆、把命中的用户事实注入指令,同时对超长上下文做剪枝、用 EMA 校准 token 计数——上下文工程被做成主线的一部分,而非外挂。依据:api/server/controllers/agents/client.js:292(buildMessages)、:628(useMemory)。

⑤ 单向依赖的分层 monorepo。 data-provider(纯共享类型)在最底层,前后端都依赖它;data-schemas 依赖它;packages/api 再依赖上两者;老 api/ 只做薄壳。类型有单一真相来源,前后端不会各写一份。依据:仓库 package.jsonworkspaces + 各包 package.json 依赖方向。


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

给人和 agent 的跳转表。认准符号名(函数/类/常量)——上游更新后行号会漂,符号名通常还在,可直接 grep

主题文件路径(相对克隆根)关键符号
路由挂载 /api/agentsapi/server/index.js:268app.use('/api/agents', routes.agents)
agents 路由树 · 流/中止端点api/server/routes/agents/index.js:63,333chatRouter/chat/stream/:streamId
聊天中间件链与入口api/server/routes/agents/chat.jscontrollerbuildEndpointOptioninitializeClient
主线编排控制器api/server/controllers/agents/request.jsAgentController(module.exports)
Endpoint→provider 分发api/server/middleware/buildEndpointOption.js:21buildFunctionbuildEndpointOption
Endpoint 枚举与 provider 映射packages/data-provider/src/schemas.ts:18,31EModelEndpointProviders
组装 AgentClientapi/server/services/Endpoints/agents/initialize.jsinitializeClientAgentClient
Agent 客户端内核api/server/controllers/agents/client.js:91class AgentClient extends BaseClient
跑一次补全api/server/controllers/agents/client.js:1141chatCompletion
创建 run(外部运行器)api/server/controllers/agents/client.js:1378createRun(from @librechat/agents)
组消息 / 文件上下文api/server/controllers/agents/client.js:292buildMessages
会话记忆api/server/controllers/agents/client.js:628,815useMemoryrunMemory
客户端基类api/app/clients/BaseClient.jsBaseClient
工具加载api/server/services/ToolService.jsloadAgentToolsloadToolsForExecution
MCP 连接管理packages/api/src/mcp/MCPManager.tsMCPManager
Mongo 模型集合packages/data-schemas/src/models/agent.tsconvo.tsmessage.tsmemory.ts
前端 SPA 入口client/src/main.jsxclient/src/App.jsxApp

溯源说明: 以上引用均 as-of sourceCommit 9e74cc0e(仓库 ✨ v0.8.7 (#13907))。@librechat/agents 是独立 npm 包,其 langgraph 运行细节不在本仓源码内,本页只描述 LibreChat 侧对它的调用契约(createRun 及回传事件);其内部实现标记为本仓外,不在此下断言。