🔧 Tool 开发
工作区中的 Tools 会在您的服务器上执行任意 Python 代码。 请仅安装来自信任源的工具,在导入前仔细审核代码,并将工作区访问权限仅限于可信的管理员。授予用户创建或导入 Tools 的权限,等同于向其提供了服务器的终端 shell 访问权限。有关完整详情,请参阅 Plugin 安全警告。
编写自定义工具箱
工具箱(Toolkits)定义在单个 Python 文件中,其顶部配有一个用于声明元数据的文档字符串,内部包含一个 Tools 类。
Tool 里的方法通常应当定义为 async(异步),以确保与未来的 Open WebUI 版本兼容。后端正在逐步走向完全的异步执行,同步函数可能会阻塞执行,或在未来的 版本中导致各种问题。
顶级文档字符串示例
"""
title: String Inverse
author: Your Name
author_url: https://website.com
git_url: https://github.com/username/string-reverse.git
description: This tool calculates the inverse of a string
required_open_webui_version: 0.4.0
requirements: langchain-openai, langgraph, ollama, langchain_ollama
version: 0.4.0
licence: MIT
"""Tools 类
Tools 必须定义在一个名为 Tools 的类的方法中,内部可以包含可选的 Valves 和 UserValves 子类。示例如下:
class Tools:
def __init__(self):
"""初始化 Tool。"""
self.valves = self.Valves()
class Valves(BaseModel):
api_key: str = Field("", description="在此处填写您的 API 密钥")
async def reverse_string(self, string: str) -> str:
"""
反转输入的字符串。
:param string: 要反转的字符串
"""
# valves 配置项的使用示例
if self.valves.api_key != "42":
return "API 密钥错误"
return string[::-1]类型提示
每个 tool 的参数必须具有类型提示(type hints)。这些类型也可以是嵌套的,例如 queries_and_docs: list[tuple[str, int]]。这些类型提示会被用来生成发送给模型的 JSON schema 规范。没有任何类型提示的 tools 在模型调用时的一致性会大幅降低。
Valves 和 UserValves(可选,但强烈推荐)
Valves 和 UserValves 用于指定 Tool 的可自定义配置设置。您可以阅读专门的 Valves & UserValves 页面 获取更多信息。
可选参数
以下是您的 tools 可以依赖的可选注入参数列表:
__event_emitter__:用于发射事件(见下文事件发射器章节)。__event_call__:与事件发射器相同,但可用于收集用户交互反馈。服务器端对事件调用的超时时间可通过WEBSOCKET_EVENT_CALLER_TIMEOUT进行配置(默认值:300 秒)。__user__:包含用户信息的字典。它还在__user__["valves"]中包含了用户的UserValves对象。__metadata__:包含聊天元数据的字典。__messages__:历史消息列表。__files__:上传并附加的文件列表。__model__:包含模型信息的字典。__oauth_token__:包含用户有效的、能自动刷新的 OAuth 令牌 Payload 的字典。这是访问用户令牌以进行身份验证 API 调用的全新、推荐且最安全的方式。该字典通常包含access_token、id_token以及其他提供商特定的数据。
如需了解有关 __oauth_token__ 的更多信息以及如何配置该令牌以发送至 tools,请查阅 环境变量配置页面 中的 OAuth 章节以及 SSO 认证文档。
您只需将这些参数声明为您的 Tool 类中任何方法的参数即可,如上面示例中的 __user__。
在 Tool 中使用 OAuth 令牌
在构建需要代表用户与外部 API 进行交互的 tools 时,您现在可以直接获取他们的 OAuth 令牌。这避免了脆弱且容易出错的 Cookie 爬取,并能确保令牌始终有效。
示例:使用用户的 Access Token 调用外部 API 的 tool。
import httpx
from typing import Optional
class Tools:
# ... 其他类初始化代码 ...
async def get_user_profile_from_external_api(self, __oauth_token__: Optional[dict] = None) -> str:
"""
使用用户的 OAuth 访问令牌从安全的外部 API 获取用户个人资料数据。
:param __oauth_token__: 由 Open WebUI 注入,包含用户的 token 数据。
"""
if not __oauth_token__ or "access_token" not in __oauth_token__:
return "Error: 用户未通过 OAuth 进行身份验证,或 token 不可用。"
access_token = __oauth_token__["access_token"]
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
try:
async with httpx.AsyncClient() as client:
response = await client.get("https://api.my-service.com/v1/profile", headers=headers)
response.raise_for_status() # 对非 2xx 状态码抛出异常
return f"API Response: {response.json()}"
except httpx.HTTPStatusError as e:
return f"Error: 无法从 API 获取数据。状态码: {e.response.status_code}"
except Exception as e:
return f"发生未知错误: {e}"事件发射器 (Event Emitters)
事件发射器用于在聊天界面中动态添加额外的信息。与 Filter 的 outlet 类似,事件发射器能够在聊天中追加内容。不同于 Filter 的 outlet,它们不能剥离现有信息。此外,在 Tool 执行的任何阶段都可以随时激活事件发射器 。
Default 模式属于遗留模式,目前已不再受支持 — 详情请查阅 Tool 调用模式指南。请编写您的 tools,使其能在 Native (Agentic) 模式下正确工作,因为这是未来唯一受支持的模式。下方的事件发射器兼容性矩阵仅为了历史参考,以及为了尚未迁移的既有 tools 维护者保留了对 Default 模式行为的记录 — 但新的 tools 不应该再依赖仅在 Default 模式下可用的事件类型。如果您工具的交互设计从根本上需要仅在 Default 模式下运作的事件类型(如 message、chat:message:delta、chat:message、以及流式传输中途的 replace),请围绕 Native 模式兼容的事件(如 status、notification、citation、chat:message:files、confirmation、chat:message:follow_ups)重新设计交互体验,而不是强求用户将他们的模型切换到不推荐的遗留模式。
事件发射器的行为在两种函数调用模式之间存在明显差异。函数调用模式由 function_calling 参数控制:
- Native 模式 (Agentic 模式) (
function_calling = "native") — 唯一受支持的模式。使用模型的结构化工具调用(tool-call)API。事件发射器功能范围受限 — 请参考下方兼容性矩阵,以明确支持哪些事件类型。 - Default 模式 (
function_calling = "default") — 遗留的、基于 Prompt 注入的模式。虽支持全套事件发射器,但模式本身已不再推荐,新部署时不应当选择此模式。
有关完整的模式政策、模型要求和配置方法,请参阅 Tool 调用模式指南。简而言之:请使用 Native 模式;下方矩阵说明了哪些事件类型在此模式下可用。
函数调用模式配置
您可以在两处配置函数调用模式:
- 管理员级别:前往 管理员面板 > 设置 > 模型 > 模型特定设置 > 高级参数 > Function Calling(设为 "Default" 或 "Native")。
- 单次请求:在聊天控制项 > 高级参数中设置
params.function_calling = "native"或"default"。
如果模型似乎无法调用该工具,请确保已启用该工具(可以在模型页面上启用,也可以在聊天输入框旁的 + 号中启用)。
在编写自定义 tools 时,请注意当启用 Native 模式后,Open WebUI 也会提供内置的系统工具。有关内置工具、函数调用模式和模型要求的详细信息,请参阅 Tool 调用模式指南。
完整的事件类型兼容性矩阵
以下是各类事件类型在不同函数调用模式下的表现明细:
| 事件类型 | Default 模式表现 | Native 模式表现 | 状态 |
|---|---|---|---|
status | ✅ 完全支持 - 在 tool 执行期间更新状态历史 | ✅ 表现一致 - 追踪函数执行状态 | 兼容 (COMPATIBLE) |
message | ✅ 完全支持 - 在流式传输中追加增量内容 | ❌ 失效 - 会被 native 的完整生成快照覆盖 | 不兼容 (INCOMPATIBLE) |
chat:completion | ✅ 完全支持 - 处理流式响应和生成数据 | ⚠️ 受限 - 承载函数结果但可能覆盖 tool 里的更新 | 部分兼容 |
chat:message:delta | ✅ 完全支持 - 在执行期间流式输出 delta 增量内容 | ❌ 失效 - 内容会被 native 的完整生成快照覆盖 | 不兼容 (INCOMPATIBLE) |
chat:message | ✅ 完全支持 - 干净利落地替换整条消息内容 | ❌ 失效 - 会被后续的 native 生成快照覆盖 | 不兼容 (INCOMPATIBLE) |
replace | ✅ 完全支持 - 精确控制并替换内容 | ❌ 失效 - 替换后的内容会立刻被后续生成快照覆盖 | 不兼容 (INCOMPATIBLE) |
chat:message:files / files | ✅ 完全支持 - 处理消息中的文件附件 | ✅ 表现一致 - 处理来自函数输出的文件 | 兼容 (COMPATIBLE) |
chat:message:error | ✅ 完全支持 - 展示错误通知 | ✅ 表现一致 - 展示函数调用错误 | 兼容 (COMPATIBLE) |
chat:message:follow_ups | ✅ 完全支持 - 显示后续跟进问题建议 | ✅ 表现一致 - 展示函数生成的后续跟进问题 | 兼容 (COMPATIBLE) |
chat:title | ✅ 完全支持 - 动态更新聊天标题 | ✅ 表现一致 - 基于函数交互更新标题 | 兼容 (COMPATIBLE) |
chat:tags | ✅ 完全支持 - 修改聊天标签 | ✅ 表现一致 - 通过函数输出管理标签 | 兼容 (COMPATIBLE) |
chat:tasks:cancel | ✅ 完全支持 - 取消正在进行的任务 | ✅ 表现一致 - 取消 native 函数执行 | 兼容 (COMPATIBLE) |
citation / source | ✅ 完全支持 - 处理包含完整元数据的引文 | ✅ 表现一致 - 处理函数生成的引文 | 兼容 (COMPATIBLE) |
notification | ✅ 完全支持 - 显示气泡提示通知 (Toast) | ✅ 表现一致 - 展示函数执行通知 | 兼容 (COMPATIBLE) |
confirmation | ✅ 完全支持 - 请求用户确认操作 | ✅ 表现一致 - 确认函数执行 | 兼容 (COMPATIBLE) |
execute | ✅ 完全支持 - 动态执行代码 | ✅ 表现一致 - 运行函数生成的代码 | 兼容 (COMPATIBLE) |
input | ✅ 完全支持 - 呈现完整的 UI 请求用户输入 | ✅ 表现一致 - 为函数收集输入 | 兼容 (COMPATIBLE) |
为什么 Native 模式会破坏某些事件类型
在 Native 模式下,服务器会根据流式输出的模型内容构建内容块,并反复发射带有完整序列化内容快照的 "chat:completion" 事件。前端客户端会将这些快照视为权威内容,并完全替换消息内容,这实际上会直接抹除工具之前发射的任何更新事件(如 message、chat:message 或 replace)。
技术细节:
middleware.py将 tools 直接添加到 form data 中以供 native 模型处理。- 流式传输处理器通过
chat:completion事件反复发射完整的内容快照。 - 客户端的
chatCompletionEventHandler会将快照作为整块内容进行彻底替换:message.content = content。 - 这会导致 tool 发射的内容更新出现闪烁并随即消失。
最佳实践与建议
对于需要实时 UI 更新的 Tools(仅在 Default 模式下生效):
class Tools:
def __init__(self):
# 提示用户关于函数调用模式的要求
self.description = "该工具需要 Default 函数调用模式以获取完整的功能体验"
async def interactive_tool(self, prompt: str, __event_emitter__=None) -> str:
"""
⚠️ 该工具需要 function_calling = "default" 才能正确发射事件
"""
if not __event_emitter__:
return "事件发射器不可用 - 请确保已启用 Default 函数调用模式"
# 在 Default 模式下可以安全地使用 message 事件
await __event_emitter__({
"type": "message",
"data": {"content": "正在处理步骤 1..."}
})
# ... 工具的其余逻辑对于必须同时兼容两种模式的 Tools:
async def universal_tool(self, prompt: str, __event_emitter__=None, __metadata__=None) -> str:
"""
设计为同时兼容 Default 和 Native 函数调用模式的工具
"""
# 检测我们是否处于 native 模式(这是一种粗略的启发式判断)
is_native_mode = __metadata__ and __metadata__.get("params", {}).get("function_calling") == "native"
if __event_emitter__:
if is_native_mode:
# 在 native 模式下,仅使用兼容的事件类型
await __event_emitter__({
"type": "status",
"data": {"description": "正在 Native 模式下处理...", "done": False}
})
else:
# 在 default 模式下,可以使用全部事件功能
await __event_emitter__({
"type": "message",
"data": {"content": "正在处理中,完全的事件支持已启用..."}
})
# ... 此处为具体的 tool 逻辑
if __event_emitter__:
await __event_emitter__({
"type": "status",
"data": {"description": "处理成功完成", "done": True}
})
return "工具执行完毕"解决事件发射器问题
出现 Native 模式冲突的症状:
- 工具发送的 message 内容短暂显示后立刻消失。
- 工具执行期间内容发生闪烁。
message或replace事件似乎被完全忽略。status更新正常工作,但内容更新无法持久保留。
解决方案:
- 切换到 Default 模式:在模型设置中将
function_calling从"native"变更为"default"。 - 使用兼容的事件类型