Skip to main content

开发 (Development)

编写自定义工具包 (Toolkit)

工具包定义在单个 Python 文件中,包含带有元数据的顶级文档字符串 (docstring) 和一个 Tools 类。

顶级文档字符串示例

"""
title: 字符串反转
author: 您的名字
author_url: https://website.com
git_url: https://github.com/username/string-reverse.git
description: 此工具计算字符串的反转
required_open_webui_version: 0.4.0
requirements: langchain-openai, langgraph, ollama, langchain_ollama
version: 0.4.0
licence: MIT
"""

Tools 类

工具必须定义为名为 Tools 的类中的方法,该类可以包含可选的子类 ValvesUserValves,例如:

class Tools:
def __init__(self):
"""初始化工具。"""
self.valves = self.Valves()

class Valves(BaseModel):
api_key: str = Field("", description="在此输入您的 API 密钥")

def reverse_string(self, string: str) -> str:
"""
反转输入字符串。
:param string: 要反转的字符串
"""
# valves 的使用示例
if self.valves.api_key != "42":
return "API 密钥错误"
return string[::-1]

类型提示 (Type Hints)

每个工具都必须为参数提供类型提示。类型也可以是嵌套的,例如 queries_and_docs: list[tuple[str, int]]。这些类型提示用于生成发送给模型的 JSON 架构 (schema)。没有类型提示的工具在运行时的一致性会大大降低。

Valves 和 UserValves - (可选,但强烈建议使用)

Valves 和 UserValves 用于指定工具的可自定义设置,您可以在专门的 Valves & UserValves 页面 了解更多信息。

可选参数

以下是您的工具可以依赖的可选参数列表:

  • __event_emitter__:发出事件(见下一节)
  • __event_call__:与事件发射器相同,但可用于用户交互
  • __user__:包含用户信息的字典。它还在 __user__["valves"] 中包含 UserValves 对象。
  • __metadata__:包含聊天元数据的字典
  • __messages__:先前消息的列表
  • __files__:附加的文件
  • __model__:包含模型信息的字典
  • __oauth_token__:一个包含用户有效的、自动刷新的 OAuth 令牌有效负载的字典。这是访问用户令牌以进行身份验证 API 调用的全新、推荐且安全的方式。该字典通常包含 access_tokenid_token 和其他特定于提供商的数据。

有关 __oauth_token__ 以及如何配置将此令牌发送到工具的更多信息,请查看环境变量文档页面中的 OAuth 部分和 SSO 文档

只需将它们作为参数添加到工具类的任何方法中,就像上面示例中的 __user__ 一样。

在工具中使用 OAuth 令牌

在构建需要代表用户与外部 API 交互的工具时,您现在可以直接访问他们的 OAuth 令牌。这消除了对脆弱的 cookie 抓取的需要,并确保令牌始终有效。

示例: 使用用户的访问令牌调用外部 API 的工具。

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 注入,包含用户的令牌数据。
"""
if not __oauth_token__ or "access_token" not in __oauth_token__:
return "错误:用户未通过 OAuth 身份验证或令牌不可用。"

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() # 对错误的响应状态码抛出异常
return f"API 响应: {response.json()}"
except httpx.HTTPStatusError as e:
return f"错误:无法从 API 获取数据。状态码: {e.response.status_code}"
except Exception as e:
return f"发生了意外错误: {e}"

事件发射器 (Event Emitters)

事件发射器用于向聊天界面添加额外信息。与过滤器出口 (Filter Outlets) 类似,事件发射器能够向聊天追加内容。与过滤器出口不同的是,它们不能删除信息。此外,发射器可以在工具运行的任何阶段被激活。

⚠️ 至关重要:函数调用模式的兼容性

事件发射器的行为会根据您的函数调用模式而显著不同。函数调用模式由 function_calling 参数控制:

  • 默认模式 (Default Mode):使用传统的函数调用方法,具有更广泛的模型兼容性
  • 原生模式 (Native Mode):利用模型内置的工具调用能力以减少延迟

在使用事件发射器之前,您必须了解这些关键限制:

  • 默认模式 (function_calling = "default"):完全支持事件发射器,所有事件类型均按预期工作
  • 原生模式 (function_calling = "native"):有限的事件发射器支持 - 许多事件类型无法正常工作,因为原生函数调用绕过了 Open WebUI 的自定义工具处理管道

何时使用每种模式:

  • 当您需要完整的事件发射器功能、复杂的工具交互或实时 UI 更新时,使用默认模式
  • 当您需要更低的延迟和基础的工具调用而无需复杂的 UI 交互时,使用原生模式

函数调用模式配置

您可以在两个地方配置函数调用模式:

  1. 模型设置:前往模型页面 → 高级参数 → 函数调用 (Function Calling)(设置为“默认”或“原生”)
  2. 基于单个请求:在您的请求中设置 params.function_calling = "native""default"

如果模型似乎无法调用该工具,请确保已启用该工具(通过模型页面或聊天输入字段旁边的 + 号)。

完整的事件类型兼容性矩阵

以下是各事件类型在不同函数调用模式下的行为综合分解:

事件类型默认模式功能原生模式功能状态
status✅ 完全支持 - 在工具执行期间更新状态历史记录完全相同 - 追踪函数执行状态兼容
message✅ 完全支持 - 在流式传输期间追加增量内容失效 - 会被原生生成的快照覆盖不兼容
chat:completion✅ 完全支持 - 处理流式响应和完成数据⚠️ 受限 - 携带函数结果,但可能覆盖工具更新部分兼容
chat:message:delta✅ 完全支持 - 在执行期间流式传输增量内容失效 - 内容会被原生函数快照替换不兼容
chat:message✅ 完全支持 - 干净地替换整个消息内容失效 - 会被后续的原生生成内容覆盖不兼容
replace✅ 完全支持 - 精确控制替换内容失效 - 替换的内容会立即被覆盖不兼容
chat:message:files / files✅ 完全支持 - 处理消息中的文件附件完全相同 - 从函数输出中处理文件兼容
chat:message:error✅ 完全支持 - 显示错误通知完全相同 - 显示函数调用错误兼容
chat:message:follow_ups✅ 完全支持 - 显示后续建议完全相同 - 显示函数生成的后续操作兼容
chat:title✅ 完全支持 - 动态更新聊天标题完全相同 - 根据函数交互更新标题兼容
chat:tags✅ 完全支持 - 修改聊天标签完全相同 - 从函数输出中管理标签兼容
chat:tasks:cancel✅ 完全支持 - 取消进行中的任务完全相同 - 取消原生函数执行兼容
citation / source✅ 完全支持 - 处理带有完整元数据的引用完全相同 - 处理函数生成的引用兼容
notification✅ 完全支持 - 显示 toast 通知完全相同 - 显示函数执行通知兼容
confirmation✅ 完全支持 - 请求用户确认完全相同 - 确认函数执行兼容
execute✅ 完全支持 - 动态执行代码完全相同 - 运行函数生成的代码兼容
input✅ 完全支持 - 请求带有完整 UI 的用户输入完全相同 - 为函数收集输入兼容

为什么原生模式会导致某些事件类型失效

原生模式下,服务器根据流式模型输出构建内容块,并反复发出带有完整序列化内容快照的 "chat:completion" 事件。客户端将这些快照视为权威内容并完全替换消息内容,从而有效地覆盖了工具先前发出的任何更新,如 messagechat:messagereplace 事件。

技术细节:

  • middleware.py 将工具直接添加到表单数据中,以便原生模型处理
  • 流式处理程序通过 chat:completion 事件发出重复的内容快照
  • 客户端的 chatCompletionEventHandler 将快照视为完整替换:message.content = content
  • 这导致工具发出的内容更新会出现闪烁并消失

最佳实践与建议

对于需要实时 UI 更新的工具:

class Tools:
def __init__(self):
# 添加关于函数调用模式要求的说明
self.description = "此工具需要‘默认’函数调用模式以实现完整功能"

async def interactive_tool(self, prompt: str, __event_emitter__=None) -> str:
"""
⚠️ 此工具需要 function_calling = "default" 以确保正常的事件发射
"""
if not __event_emitter__:
return "事件发射器不可用 - 请确保已启用‘默认’函数调用模式"

# 在默认模式下可以安全使用消息事件
await __event_emitter__({
"type": "message",
"data": {"content": "正在处理步骤 1..."}
})
# ... 工具逻辑的其余部分

对于必须在两种模式下都能运行的工具:

    async def universal_tool(self, query: str, __event_emitter__=None) -> str:
"""
可在两种模式下运行,但在原生模式下 UI 更新有限
"""
if __event_emitter__:
await __event_emitter__({
"type": "status",
"data": {"description": "正在获取数据...", "done": False}
})

# 执行操作...
result = "操作成功完成"

if __event_emitter__:
await __event_emitter__({
"type": "status",
"data": {"description": result, "done": True}
})

return result