跳到主要内容

Open WebUI — 聊天补全与智能体编排架构与原理

30 秒导读: Open WebUI 是自托管的 AI 聊天前端 + 后端。它最有意思的不是界面,而是后端那条把"一条聊天消息"变成"会自己查资料、调工具、多轮迭代的智能体回答"的中间件管线。这份子库讲清楚这条管线:请求进来后先按你勾选的特性(记忆、联网、RAG、图像、工具)装配上下文,再路由到某个模型后端,最后在出口驱动一个"模型说要调工具 → 后端执行 → 结果回灌 → 再问模型"的循环。


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

一句话定义: Open WebUI 是一个自己部署、界面像 ChatGPT 的聊天应用,后端能把普通问答升级成智能体(会调工具、查网页、翻记忆的 AI)。

解决什么问题 / 给谁用: 假设你想在自己的服务器上用 Ollama 跑本地大模型,或接 OpenAI/兼容 API,但又想要"联网搜索、上传文档问答(RAG)、画图、让模型自己调工具"这些高级能力——Open WebUI 把这些开箱即用地拼好,还允许你用 Python 插件二次开发。它给的是运维/开发者,不是终端小白。

它能做什么(后端视角):

  • 把一条聊天请求按"勾选的特性"装配成带上下文的 payload(记忆、联网、RAG 文件、图像生成提示)。
  • 挂载四类来源的工具给模型:内置工具、用户写的 Python 函数工具、外部 OpenAPI 服务器、MCP 服务器。
  • 支持两种工具调用模式:模型原生 tool_calls,或后端用提示词模拟的"伪函数调用"。
  • 在出口驱动多轮智能体循环:模型要调工具 → 后端执行 → 结果塞回对话 → 再问模型,直到模型不再要工具。
  • 整条链路可被 pipe/filter 插件拦截:pipe 让你把任意 Python 函数注册成一个"模型",filter 让你在请求进/出时改写数据。

用起来什么样(对外就是 OpenAI 接口): 后端暴露的是标准的 POST /api/chat/completions(见 backend/open_webui/main.py:1666),body 和 OpenAI 一样是 {model, messages, stream, ...},只是多了 featurestool_idsfiles 等字段来打开那些高级能力。

一句话直觉: 把它想成一个**"聊天请求的流水线工厂"**——原料是一条消息,进厂后先经过"装配工位"(挂上下文和工具),送去"加工中心"(某个模型后端),出来再经过"质检/返工工位"(如果模型说要调工具,就返工再跑一遍),最后打包成流式响应发给你。

本节不谈代码细节;记住"入口装配 → 路由后端 → 出口循环"这三段即可。


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

2.1 主线:一条消息的三段旅程

怎么读下面这张图: 从上到下是一条 POST /api/chat/completions 请求的时间顺序。三个粗体大块就是三段旅程,中间的 generate_chat_completion 是"分叉路由器"。

HTTP POST /api/chat/completions main.py:1668 chat_completion()


┌─────────────────────────────────────────────────────────┐
│ ① 入口装配 process_chat_payload middleware.py:2311 │
│ • pipeline inlet + filter inlet(插件拦截改写) │
│ • 按 features 挂上下文:记忆/联网/图像/RAG │
│ • 解析 tool_ids → 装配四类工具 │
│ • 按 function_calling 分叉:native 挂 tools 数组; │
│ 非 native 现在就调好工具、把结果注入提示 │
└─────────────────────────────────────────────────────────┘
│ form_data(已装配好)

┌─────────────────────────────────────────────────────────┐
│ ② 路由后端 generate_chat_completion chat.py:152 │
│ ├─ model.pipe? → 插件函数当模型(functions.py) │
│ ├─ owned_by=ollama → /ollama/api/chat │
│ └─ 其它 → OpenAI 兼容后端 │
└─────────────────────────────────────────────────────────┘
│ 上游返回(通常是 SSE 流)

┌─────────────────────────────────────────────────────────┐
│ ③ 出口循环 process_chat_response middleware.py:5259 │
│ • 流式转发 token 给前端 │
│ • 若模型发了 tool_calls → 智能体循环: │
│ 执行工具 → 结果回灌进 messages → │
│ 再 generate_chat_completion → 重复 │
│ • filter outlet(插件拦截最终输出) │
└─────────────────────────────────────────────────────────┘


流式响应 SSE → 前端逐字渲染

编排代码就在 process_chat() 里,三段依次调用:main.py:2023(入口)→ main.py:2025(路由,别名 chat_completion_handler)→ main.py:2044(出口)。

2.2 部件一句话职责

部件干什么在哪(文件:符号)
HTTP 入口接收 OpenAI 风格请求、准备 metadatamain.py:1668 chat_completion
入口装配按特性挂上下文、装配工具、按模式分叉middleware.py:2311 process_chat_payload
路由分发按模型类型分流到 pipe/ollama/openaichat.py:152 generate_chat_completion
出口处理转发流、驱动智能体工具循环、跑 outletmiddleware.py:5259 process_chat_response
工具装配加载并合并四类工具来源tools.py:221 get_tools / tools.py:430 get_builtin_tools
非 native 工具用任务模型+提示词模拟函数调用middleware.py:1246 chat_completion_tools_handler
插件-pipe把 Python 函数当成一个"模型"functions.py:147 generate_function_chat_completion
插件-filterinlet/outlet/stream 三点拦截filter.py:66 process_filter_functions

2.3 一句话点透那个"关键开关"

整条链路的行为被一个参数劈成两半:metadata['params']['function_calling'] 是不是 'native'

  • native(模型自主调工具): 入口强行注入记忆/联网/RAG,而是把它们做成工具挂到 form_data['tools'],让模型自己决定要不要调;真正的执行在出口循环里做(middleware.py:28462869)。
  • 非 native(后端替模型调): 入口就用一个任务模型把工具调用"算出来"、当场执行、把结果注入到提示词里,模型只负责最后总结(middleware.py:28791246)。

这条分叉在下面每一章都会反复出现,是理解本项目的钥匙。


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

五章由浅入深,顺着"一条请求的旅程"排列。第一次读按顺序;想直接看某个机制可跳读。

  1. 01-request-lifecycle.md — 一条聊天请求的生命周期:从 HTTP 入口到流式出口。先建立"三段旅程"的整体骨架,搞清 process_chatgenerate_chat_completionprocess_chat_response 各自的边界与交接。先读这章。

  2. 02-context-assembly.md — 入口装配:把"特性"变成智能体能力(记忆 / 联网 / RAG / 图像)。讲 process_chat_payload 如何按 features 逐项挂上下文,以及 native/非 native 在这一步就开始的分叉(middleware.py:25492611)。

  3. 03-tools-system.md — 工具系统:四类工具来源 + 两种函数调用模式。讲清楚 builtin / Python 函数 / OpenAPI 服务器(server:)/ MCP(server:mcp:)如何统一成一个 tools_dict,以及 native 数组模式 vs 非 native 提示模式的实现差异。

  4. 04-agentic-loop.md — 智能体循环与出口处理:原生工具调用如何迭代。讲 process_chat_response 里那个 while tool_calls 循环——执行、回灌、再调用,以及 CHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS 的触顶保护。

  5. 05-plugins-pipes-filters.md — 插件框架:pipe 自定义模型与 filter 拦截函数。讲 pipe 如何把函数注册成模型、filter 的 inlet/outlet/stream 三个拦截点与优先级排序。

想快速判断"我该读哪章":要理解整体流程读 01;要改上下文/RAG/联网读 02;要接外部工具/MCP读 03;要调多轮智能体行为读 04;要写插件读 05。


4. 巧妙之处(可带走的精华)

每条先白话点出"妙在哪",再给 file:line。细节在对应章里展开。

  • 一个布尔开关切换两种智能体范式。 function_calling == 'native' 决定"模型自主调工具"还是"后端替它调好再注入提示"。同一套特性(记忆/联网/RAG/图像),native 下变成工具、非 native 下变成被强灌的上下文——用一个判断复用了两条完全不同的实现路径(middleware.py:2566257125762584)。

  • 四类异构工具被抹平成同一个字典。 内置函数、用户 Python 工具、远程 OpenAPI 服务器、MCP 服务器,来源天差地别,却都被规整成 {name: {'spec', 'callable', ...}} 的统一形状塞进 tools_dict,出口循环因此不必关心工具来自哪里(middleware.py:277728192846;tools.py:221430)。

  • 工具参数解析先试 ast.literal_eval 再退回 json.loads 因为很多模型吐的"JSON"其实不合法(单引号、尾逗号),先用 Python 字面量解析器兜一层,更宽容(middleware.py:4583)。

  • 智能体循环有硬上限,而非无限递归。 出口的 while tool_callsCHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS(默认 256,可设 -1 解除)封顶,触顶写一条错误消息而不是卡死(middleware.py:45344913;env.py:888)。

  • "caller 自带 tools 就整段跳过服务端工具解析。" 当外部 API 客户端在 body 里直接给了 OpenAI 风格 tools 数组,后端识别为"你自己管工具",跳过全部四类工具装配,原样透传(middleware.py:2710if not payload_tools)。

  • RAG 注入可被"回滚"以避免重复。 出口在多轮循环里会把消息还原到注入 RAG 模板之前的原始状态再重新应用,防止每轮都把检索上下文叠一遍(middleware.py:28962902 存快照,47604768 还原)。

  • pipe 让"一个函数"就是"一个模型"。 用户写的 pipe 函数被 get_function_models 枚举成模型列表,选它时 generate_chat_completion 直接分流到 generate_function_chat_completion 执行该函数——模型和插件在这里是同一个抽象(functions.py:71147286)。


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

grep 符号名比行号抗漂移(上游更新后行号会变,符号名通常还在)。

主题文件路径符号名
HTTP 入口路由backend/open_webui/main.pychat_completion
三段编排backend/open_webui/main.pyprocess_chat(内联于 chat_completion)
入口装配backend/open_webui/utils/middleware.pyprocess_chat_payload
记忆特性backend/open_webui/utils/middleware.pychat_memory_handler
联网特性backend/open_webui/utils/middleware.pychat_web_search_handler
图像特性backend/open_webui/utils/middleware.pychat_image_generation_handler
非 native 工具调用backend/open_webui/utils/middleware.pychat_completion_tools_handler
RAG/文件上下文backend/open_webui/utils/middleware.pychat_completion_files_handler
路由分发backend/open_webui/utils/chat.pygenerate_chat_completion
出口处理入口backend/open_webui/utils/middleware.pyprocess_chat_response
智能体工具循环backend/open_webui/utils/middleware.pystreaming_chat_response_handler(内 while tool_calls)
工具结果处理backend/open_webui/utils/middleware.pyprocess_tool_result
加载 DB/Python 工具backend/open_webui/utils/tools.pyget_tools
内置工具集合backend/open_webui/utils/tools.pyget_builtin_tools
OpenAPI→工具 specbackend/open_webui/utils/tools.pyconvert_openapi_to_tool_payload
调用 OpenAPI 服务器backend/open_webui/utils/tools.pyexecute_tool_server
连接 MCP 服务器backend/open_webui/utils/middleware.pyconnect_mcp_server
pipe 当模型执行backend/open_webui/functions.pygenerate_function_chat_completion
pipe 模型枚举backend/open_webui/functions.pyget_function_models
filter 三点拦截backend/open_webui/utils/filter.pyprocess_filter_functions
filter 排序/启用backend/open_webui/utils/filter.pyget_sorted_filter_ids
循环上限配置backend/open_webui/env.pyCHAT_RESPONSE_MAX_TOOL_CALL_ITERATIONS