Open WebUI函数Function的调用步骤
基于上文《Open WebUI开发高级接口参数应用》的知识,本文对Function的定义和调用做更详细的笔记。
Open WebUI的Function调用流程可以分为以下几个步骤:
- 模型判断是否需要调用函数
- 模型生成函数调用指令
- 后端执行函数
- 函数返回结果
- 模型整合结果,生成最终回答
在实际开发中,需要注意以下几点: - 模型判断是否需要调用函数的依据是函数注册信息和用户输入。 - 模型生成的函数调用指令需要符合Open WebUI的格式要求,包括函数名、参数名和参数值。 - 后端根据模型生成的函数调用指令,调用注册的函数,并将函数返回的结果整合到最终回答中。
对于上文中涉及到的function
call,我们对其调用步骤做拆解,这部分代码确实涉及多个环节(模型生成调用指令、后端执行函数、结果反馈),容易让人混淆。核心问题在于:function_call不是预先定义的“函数”,而是模型根据用户输入和已注册的工具函数,动态生成的“调用指令”。下面重新拆解流程,用更清晰的步骤和注释说明:
1 先明确核心前提
在模型调用工具函数前,必须先在Open
WebUI中完成函数注册(这是模型能“知道”有这个函数的前提),步骤如下:
1.
准备函数定义文件(如前面的weather_api.json,描述get_weather函数的参数、功能、后端地址);
2. 登录Open WebUI管理员账号 →
进入Admin Panel → Tools → Functions →
上传weather_api.json,完成注册。
此时,模型已“认识”get_weather函数,知道它能查天气、需要city参数、调用地址是http://localhost:5000/get_weather。
2 重新拆解“模型调用函数”的完整流程(带清晰代码注释)
以“用户问‘上海天气’→ 模型调用get_weather→
返回结果”为例,分4步拆解:
2.1 步骤1:用户提问,触发模型判断是否需要调用函数
用户输入“上海的天气如何?”,代码向Open
WebUI发送请求,模型会分析:“这个问题需要实时天气数据,我已注册了get_weather函数,应该调用它”。
1 | |
2.2
步骤2:模型生成function_call指令(核心!)
模型分析用户问题后,会生成一个“调用指令”(即function_call),告诉后端:“我需要调用get_weather函数,参数是city=上海”。
此时model_resp的结构类似这样(模型动态生成的):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 模型返回的响应示例(model_resp)
{
"id": "模型生成的响应ID",
"messages": [
{
"id": "msg_abc123",
"role": "assistant", # 模型角色:助手
"content": "", # 此时无直接回答,因为需要先调用工具
"function_call": { # 核心:模型生成的函数调用指令
"name": "get_weather", # 要调用的函数名(必须与注册的一致)
"parameters": {"city": "上海"} # 函数参数
},
"parentId": user_msg_id, # 关联用户的问题
"childrenIds": []
}
]
}
代码中提取function_call的逻辑: 1
2
3
4
5
6
7# 2. 提取模型生成的函数调用指令
function_call = model_resp["messages"][0].get("function_call")
if not function_call:
print("模型未生成函数调用,直接回答:", model_resp["messages"][0]["content"])
else:
print(f"模型决定调用函数:{function_call['name']},参数:{function_call['parameters']}")
# 输出:模型决定调用函数:get_weather,参数:{'city': '上海'}
2.3 步骤3:后端执行函数(调用实际的天气服务)
拿到function_call指令后,后端需要:
- 确认调用的函数是get_weather;
- 提取参数city=上海;
-
发送请求到weather_api.json中定义的后端地址(http://localhost:5000/get_weather)。
假设我们已用Flask搭建了天气服务后端(代码见前文),此时调用它:
1
2
3
4
5
6
7
8
9
10
11
12
13# 3. 执行函数:调用天气服务后端
if function_call["name"] == "get_weather":
# 提取参数
city = function_call["parameters"]["city"]
# 调用天气服务(地址来自注册的weather_api.json)
weather_backend_url = "http://localhost:5000/get_weather"
weather_response = requests.post(
weather_backend_url,
json={"city": city} # 传递参数
)
# 获取天气数据(假设返回:{"temperature": 25, "condition": "晴"})
weather_data = weather_response.json()
print(f"天气服务返回结果:{weather_data}")
2.4 步骤4:将函数结果返回给模型,生成最终回答
模型本身不会直接处理天气数据,需要将weather_data返回给模型,让它整理成自然语言回答:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34# 4. 将函数结果返回给模型,生成最终回答
function_result_msg = {
"id": uuid.uuid4().hex, # 函数结果消息的ID
"role": "function", # 角色:工具返回结果
"content": str(weather_data), # 传递天气数据(转为字符串)
"parentId": model_resp["messages"][0]["id"], # 关联模型的调用指令
"childrenIds": []
}
# 构造新的请求,包含历史消息+函数结果
final_payload = {
"model": "llama-3.2",
"messages": [
# 历史:用户的问题
{
"id": user_msg_id,
"role": "user",
"content": user_input,
"parentId": None,
"childrenIds": [model_resp["messages"][0]["id"]]
},
# 历史:模型的调用指令
model_resp["messages"][0],
# 新增:函数返回的结果
function_result_msg
],
"stream": False
}
# 发送请求,获取模型整理后的回答
final_response = requests.post(API_URL, json=final_payload, headers=headers)
final_answer = final_response.json()["messages"][0]["content"]
print(f"模型最终回答:{final_answer}")
# 输出:上海当前天气为晴,气温25℃。
3 为什么之前的代码看起来“混乱”?
因为它把4个步骤的代码合并了,没有明确拆分。核心逻辑其实是:
用户提问 → 模型生成调用指令(function_call)→ 后端执行函数 → 结果返回模型 → 模型生成回答
其中,function_call是模型动态生成的指令,不是预先定义的函数,它的作用是“告诉后端该调用哪个工具、传什么参数”。
4 一句话总结
- 先在Open WebUI注册函数(模型才能“认识”它);
- 用户提问时,模型判断需要调用函数,生成
function_call指令;
- 后端按指令调用实际服务,拿到结果;
- 结果返回模型,模型整理成自然语言回答。
这样拆解后,每个步骤的目的就清晰了,代码逻辑也更容易理解。