本文从最简单的时间工具入手,分析Tools相关的代码。
一、安装工具
git clone https://github.com/open-webui/openapi-servers
cd openapi-servers# 进入时间工具目录
cd servers/timepip install -r requirements.txt
# 启动服务
uvicorn main:app --host 0.0.0.0 --reload #缺省使用8000端口
二、配置
以admin登录webui,配置->工具,增加安装完成的工具地址:
在聊天窗口出现安装的工具:
在对话高级设置,设置函数调用(Function Calling)设置为原生。
三、代码分析
1)主要流程
在交互过程中,工具调用相关流程如下图所示:
2)入口参数
http://{ip:port}/api/chat/completions入口参数如下,与前述对比其中增加了tool_servers,其中包含了所有工具的说明。
{
"stream": true,
"model": "deepseek-r1:1.5b",
"messages": [
{
"role": "user",
"content": "请告诉现在东京的时间"
}
],
"params": {},
"tool_servers": [
{
"url": "http://192.168.21.201:8000",
"openapi": {
"openapi": "3.1.0",
"info": {
"title": "Secure Time Utilities API",
"description": "Provides secure UTC/local time retrieval, formatting, timezone conversion, and comparison.",
"version": "1.0.0"
},
"paths": {
"/get_current_utc_time": {
"get": {
"summary": "Current UTC time",
"description": "Returns the current time in UTC in ISO format.",
"operationId": "get_current_utc_get_current_utc_time_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
}
}
},
"/get_current_local_time": {
"get": {
"summary": "Current Local Time",
"description": "Returns the current time in local timezone in ISO format.",
"operationId": "get_current_local_get_current_local_time_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
}
}
},
"/format_time": {
"post": {
"summary": "Format current time",
"description": "Return the current time formatted for a specific timezone and format.",
"operationId": "format_current_time_format_time_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/FormatTimeInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/convert_time": {
"post": {
"summary": "Convert between timezones",
"description": "Convert a timestamp from one timezone to another.",
"operationId": "convert_time_convert_time_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConvertTimeInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/elapsed_time": {
"post": {
"summary": "Time elapsed between timestamps",
"description": "Calculate the difference between two timestamps in chosen units.",
"operationId": "elapsed_time_elapsed_time_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ElapsedTimeInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/parse_timestamp": {
"post": {
"summary": "Parse and normalize timestamps",
"description": "Parse human-friendly input timestamp and return standardized UTC ISO time.",
"operationId": "parse_timestamp_parse_timestamp_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ParseTimestampInput"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/list_time_zones": {
"get": {
"summary": "All valid time zones",
"description": "Return a list of all valid IANA time zones.",
"operationId": "list_time_zones_list_time_zones_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
}
}
}
},
"components": {
"schemas": {
"ConvertTimeInput": {
"properties": {
"timestamp": {
"type": "string",
"title": "Timestamp",
"description": "ISO 8601 formatted time string (e.g., 2024-01-01T12:00:00Z)"
},
"from_tz": {
"type": "string",
"title": "From Tz",
"description": "Original IANA time zone of input (e.g. UTC or Europe/Berlin)"
},
"to_tz": {
"type": "string",
"title": "To Tz",
"description": "Target IANA time zone to convert to"
}
},
"type": "object",
"required": [
"timestamp",
"from_tz",
"to_tz"
],
"title": "ConvertTimeInput"
},
"ElapsedTimeInput": {
"properties": {
"start": {
"type": "string",
"title": "Start",
"description": "Start timestamp in ISO 8601 format"
},
"end": {
"type": "string",
"title": "End",
"description": "End timestamp in ISO 8601 format"
},
"units": {
"type": "string",
"enum": [
"seconds",
"minutes",
"hours",
"days"
],
"title": "Units",
"description": "Unit for elapsed time",
"default": "seconds"
}
},
"type": "object",
"required": [
"start",
"end"
],
"title": "ElapsedTimeInput"
},
"FormatTimeInput": {
"properties": {
"format": {
"type": "string",
"title": "Format",
"description": "Python strftime format string",
"default": "%Y-%m-%d %H:%M:%S"
},
"timezone": {
"type": "string",
"title": "Timezone",
"description": "IANA timezone name (e.g., UTC, America/New_York)",
"default": "UTC"
}
},
"type": "object",
"title": "FormatTimeInput"
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
},
"ParseTimestampInput": {
"properties": {
"timestamp": {
"type": "string",
"title": "Timestamp",
"description": "Flexible input timestamp string (e.g., 2024-06-01 12:00 PM)"
},
"timezone": {
"type": "string",
"title": "Timezone",
"description": "Assumed timezone if none is specified in input",
"default": "UTC"
}
},
"type": "object",
"required": [
"timestamp"
],
"title": "ParseTimestampInput"
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
}
}
}
},
"info": {
"title": "Secure Time Utilities API",
"description": "Provides secure UTC/local time retrieval, formatting, timezone conversion, and comparison.",
"version": "1.0.0"
},
"specs": [
{
"type": "function",
"name": "get_current_utc_get_current_utc_time_get",
"description": "Returns the current time in UTC in ISO format.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
},
{
"type": "function",
"name": "get_current_local_get_current_local_time_get",
"description": "Returns the current time in local timezone in ISO format.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
},
{
"type": "function",
"name": "format_current_time_format_time_post",
"description": "Return the current time formatted for a specific timezone and format.",
"parameters": {
"type": "object",
"properties": {
"format": {
"type": "string",
"description": "Python strftime format string"
},
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g., UTC, America/New_York)"
}
},
"required": []
}
},
{
"type": "function",
"name": "convert_time_convert_time_post",
"description": "Convert a timestamp from one timezone to another.",
"parameters": {
"type": "object",
"properties": {
"timestamp": {
"type": "string",
"description": "ISO 8601 formatted time string (e.g., 2024-01-01T12:00:00Z)"
},
"from_tz": {
"type": "string",
"description": "Original IANA time zone of input (e.e.g. UTC or Europe/Berlin)"
},
"to_tz": {
"type": "string",
"description": "Target IANA time zone to convert to"
}
},
"required": [
"timestamp",
"from_tz",
"to_tz"
]
}
},
{
"type": "function",
"name": "elapsed_time_elapsed_time_post",
"description": "Calculate the difference between two timestamps in chosen units.",
"parameters": {
"type": "object",
"properties": {
"start": {
"type": "string",
"description": "Start timestamp in ISO 8601 format"
},
"end": {
"type": "string",
"description": "End timestamp in ISO 8601 format"
},
"units": {
"type": "string",
"description": "Unit for elapsed time"
}
},
"required": [
"start",
"end"
]
}
},
{
"type": "function",
"name": "parse_timestamp_parse_timestamp_post",
"description": "Parse human-friendly input timestamp and return standardized UTC ISO time.",
"parameters": {
"type": "object",
"properties": {
"timestamp": {
"type": "string",
"description": "Flexible input timestamp string (e.g., 2024-06-01 12:00 PM)"
},
"timezone": {
"type": "string",
"description": "Assumed timezone if none is specified in input"
}
},
"required": [
"timestamp"
]
}
},
{
"type": "function",
"name": "list_time_zones_list_time_zones_get",
"description": "Return a list of all valid IANA time zones.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
]
}
],
"features": {
"image_generation": false,
"code_interpreter": false,
"web_search": false,
"memory": false
},
"variables": {
"{{USER_NAME}}": "acaluis",
"{{USER_LOCATION}}": "Unknown",
"{{CURRENT_DATETIME}}": "2025-08-19 18:06:37",
"{{CURRENT_DATE}}": "2025-08-19",
"{{CURRENT_TIME}}": "18:06:37",
"{{CURRENT_WEEKDAY}}": "Tuesday",
"{{CURRENT_TIMEZONE}}": "Etc/GMT-8",
"{{USER_LANGUAGE}}": "zh-CN"
},
"model_item": {
"id": "deepseek-r1:1.5b",
"name": "deepseek-r1:1.5b",
"object": "model",
"created": 1755597385,
"owned_by": "ollama",
"ollama": {
"name": "deepseek-r1:1.5b",
"model": "deepseek-r1:1.5b",
"modified_at": "2025-08-17T04:50:08.766430912Z",
"size": 1117322768,
"digest": "e0979632db5a88d1a53884cb2a941772d10ff5d055aabaa6801c4e36f3a6c2d7",
"details": {
"parent_model": "",
"format": "gguf",
"family": "qwen2",
"families": [
"qwen2"
],
"parameter_size": "1.8B",
"quantization_level": "Q4_K_M"
},
"connection_type": "local",
"urls": [
0
]
},
"connection_type": "local",
"tags": [],
"actions": [],
"filters": []
},
"session_id": "R-JB6cdCyrSZ-GRcAAJc",
"chat_id": "f9ad2990-5ad1-44fc-b3ea-c5cfee936588",
"id": "d85123d0-276b-4796-afd0-f203a8606ecf",
"background_tasks": {
"title_generation": true,
"tags_generation": true,
"follow_up_generation": true
}
}
3)代码分析
在chat_completion方法中,在metadata中设置{function_calling:native},一般情况下不设置。
@app.post("/api/chat/completions")
async def chat_completion(
request: Request,
form_data: dict,
user=Depends(get_verified_user),
):try:
if not model_item.get("direct", False): #使用ollama作为后台时,走该分支
model_id = form_data.get("model", None)
if model_id not in request.app.state.MODELS:
raise Exception("Model not found")model = request.app.state.MODELS[model_id]
#如果使用ollama中的标准模型model_info为空
model_info = Models.get_model_by_id(model_id)# Check if user has access to the model
if not BYPASS_MODEL_ACCESS_CONTROL and user.role == "user":
try:
check_model_access(user, model)
except Exception as e:
raise e
else:
model = model_item
model_info = Nonerequest.state.direct = True
request.state.model = modelmetadata = {
"user_id": user.id,
……
**( #一般情况,请求中的params为空,并且model_info也为空,所以走else分支
{"function_calling": "native"}
if form_data.get("params", {}).get("function_calling") == "native"
or (
model_info
and model_info.params.model_dump().get("function_calling")
== "native"
)
else {}#非native
),
}……
在process_chat_payload处理function_calling,相关代码如下:
async def process_chat_payload(request, form_data, user, metadata, model):
……tools_dict = {}
if tool_ids: #当前仅配置了一个Tool,故tool_ids为空
tools_dict = get_tools(
request,
tool_ids,
user,
{
**extra_params,
"__model__": models[task_model_id],
"__messages__": form_data["messages"],
"__files__": metadata.get("files", []),
},
)if tool_servers:
for tool_server in tool_servers:
tool_specs = tool_server.pop("specs", [])for tool in tool_specs:
tools_dict[tool["name"]] = {
"spec": tool,
"direct": True,
"server": tool_server,
}if tools_dict:
#一般情况,前面chat_completion方法中并未设置function_calling:native,所以走else
if metadata.get("function_calling") == "native":
# If the function calling is native, then call the tools function calling handler
metadata["tools"] = tools_dict
form_data["tools"] = [
{"type": "function", "function": tool.get("spec", {})}
for tool in tools_dict.values()
]
else:#走本分支,调用大模型获取function_calling结果
try:
form_data, flags = await chat_completion_tools_handler(
request, form_data, extra_params, user, models, tools_dict
)
sources.extend(flags.get("sources", []))except Exception as e:
log.exception(e)
# 仅处理知识库上下文列表,与调用工具获取的列表无关,后继代码省略
if len(sources) > 0:
context_string = ""
citation_idx_map = {}
for source in sources:
is_tool_result = source.get("tool_result", False)
if "document" in source and not is_tool_result:
……
#如果没有查询过向量库则context_string为空
context_string = context_string.strip()
prompt = get_last_user_message(form_data["messages"])if prompt is None:
raise Exception("No user message found")
if context_string == "":#如果未查询向量库或未查询到,则输出日志
if request.app.state.config.RELEVANCE_THRESHOLD == 0:
log.debug(
f"With a 0 relevancy threshold for RAG, the context cannot be empty"
)
else:#如果有上下文查询结果,则需要用系统所带的RAG模版组装请求消息。不再详解
# Workaround for Ollama 2.0+ system prompt issue
# TODO: replace with add_or_update_system_message
if model.get("owned_by") == "ollama":
form_data["messages"] = prepend_to_first_user_message_content(
rag_template(
request.app.state.config.RAG_TEMPLATE, context_string, prompt
),
form_data["messages"],
)
else:
form_data["messages"] = add_or_update_system_message(
rag_template(
request.app.state.config.RAG_TEMPLATE, context_string, prompt
),
form_data["messages"],
)
……
以下重点分析chat_completion_tools_handler方法。
async def chat_completion_tools_handler(
request: Request, body: dict, extra_params: dict, user: UserModel, models, tools
) -> tuple[dict, dict]:
async def get_content_from_response(response) -> Optional[str]:
content = None
if hasattr(response, "body_iterator"):
async for chunk in response.body_iterator:
data = json.loads(chunk.decode("utf-8"))
content = data["choices"][0]["message"]["content"]# Cleanup any remaining background tasks if necessary
if response.background is not None:
await response.background()
else:
content = response["choices"][0]["message"]["content"]
return content
'''
get_tools_function_calling_payload方法负责组装发送的ollama的function_calling请求,示例如begin-end之间内容。
---------------------------------begin--------------------------------------------------------------------------------
{
"model": "qwen:0.5b",
"messages": [
{
"role": "system",
"content": "Available Tools: [{\"type\": \"function\", \"name\": \"get_current_utc_get_current_utc_time_get\", \"description\": \"Returns the current time in UTC in ISO format.\", \"parameters\": {\"type\": \"object\", \"properties\": {}, \"required\": []}}, {\"type\": \"function\", \"name\": \"get_current_local_get_current_local_time_get\", \"description\": \"Returns the current time in local timezone in ISO format.\", \"parameters\": {\"type\": \"object\", \"properties\": {}, \"required\": []}}, {\"type\": \"function\", \"name\": \"format_current_time_format_time_post\", \"description\": \"Return the current time formatted for a specific timezone and format.\", \"parameters\": {\"type\": \"object\", \"properties\": {\"format\": {\"type\": \"string\", \"description\": \"Python strftime format string\"}, \"timezone\": {\"type\": \"string\", \"description\": \"IANA timezone name (e.g., UTC, America/New_York)\"}}, \"required\": []}}, {\"type\": \"function\", \"name\": \"convert_time_convert_time_post\", \"description\": \"Convert a timestamp from one timezone to another.\", \"parameters\": {\"type\": \"object\", \"properties\": {\"timestamp\": {\"type\": \"string\", \"description\": \"ISO 8601 formatted time string (e.g., 2024-01-01T12:00:00Z)\"}, \"from_tz\": {\"type\": \"string\", \"description\": \"Original IANA time zone of input (e.g. UTC or Europe/Berlin)\"}, \"to_tz\": {\"type\": \"string\", \"description\": \"Target IANA time zone to convert to\"}}, \"required\": [\"timestamp\", \"from_tz\", \"to_tz\"]}}, {\"type\": \"function\", \"name\": \"elapsed_time_elapsed_time_post\", \"description\": \"Calculate the difference between two timestamps in chosen units.\", \"parameters\": {\"type\": \"object\", \"properties\": {\"start\": {\"type\": \"string\", \"description\": \"Start timestamp in ISO 8601 format\"}, \"end\": {\"type\": \"string\", \"description\": \"End timestamp in ISO 8601 format\"}, \"units\": {\"type\": \"string\", \"description\": \"Unit for elapsed time\"}}, \"required\": [\"start\", \"end\"]}}, {\"type\": \"function\", \"name\": \"parse_timestamp_parse_timestamp_post\", \"description\": \"Parse human-friendly input timestamp and return standardized UTC ISO time.\", \"parameters\": {\"type\": \"object\", \"properties\": {\"timestamp\": {\"type\": \"string\", \"description\": \"Flexible input timestamp string (e.g., 2024-06-01 12:00 PM)\"}, \"timezone\": {\"type\": \"string\", \"description\": \"Assumed timezone if none is specified in input\"}}, \"required\": [\"timestamp\"]}}, {\"type\": \"function\", \"name\": \"list_time_zones_list_time_zones_get\", \"description\": \"Return a list of all valid IANA time zones.\", \"parameters\": {\"type\": \"object\", \"properties\": {}, \"required\": []}}]\n\nYour task is to choose and return the correct tool(s) from the list of available tools based on the query. Follow these guidelines:\n\n- Return only the JSON object, without any additional text or explanation.\n\n- If no tools match the query, return an empty array: \n {\n \"tool_calls\": []\n }\n\n- If one or more tools match the query, construct a JSON response containing a \"tool_calls\" array with objects that include:\n - \"name\": The tool's name.\n - \"parameters\": A dictionary of required parameters and their corresponding values.\n\nThe format for the JSON response is strictly:\n{\n \"tool_calls\": [\n {\"name\": \"toolName1\", \"parameters\": {\"key1\": \"value1\"}},\n {\"name\": \"toolName2\", \"parameters\": {\"key2\": \"value2\"}}\n ]\n}"
},
{
"role": "user",
"content": "Query: History:\nUSER: \"\"\"\u8bf7\u544a\u8bc9\u6211\u5f53\u524d\u5927\u962a\u7684\u65f6\u95f4\"\"\"\nQuery: \u8bf7\u544a\u8bc9\u6211\u5f53\u524d\u5927\u962a\u7684\u65f6\u95f4"
}
],
"stream": false"metadata": {"ftask"f:"function_calling"}
}----------------------------------------------------end---------------------------------------------------------------
'''
def get_tools_function_calling_payload(messages, task_model_id, content):
#从请求表单中提取用户提问
user_message = get_last_user_message(messages)
history = "\n".join(
f"{message['role'].upper()}: \"\"\"{message['content']}\"\"\""
for message in messages[::-1][:4] #请求表单中messages列表倒序排列后取前4个
)#先在history 前增加History:,再拼接Query:用户问题
prompt = f"History:\n{history}\nQuery: {user_message}"
return {
"model": task_model_id,
"messages": [
{"role": "system", "content": content},
{"role": "user", "content": f"Query: {prompt}"},
],
"stream": False,
"metadata": {"task": str(TASKS.FUNCTION_CALLING)},
}event_caller = extra_params["__event_call__"]
metadata = extra_params["__metadata__"]#确定执行function_calling任务的模型,实际为用户聊天时选择的模型
task_model_id = get_task_model_id(
body["model"],
request.app.state.config.TASK_MODEL,
request.app.state.config.TASK_MODEL_EXTERNAL,
models,
)skip_files = False
sources = []specs = [tool["spec"] for tool in tools.values()]
'''
specs数据如下:
[
{
"type": "function",
"name": "get_current_utc_get_current_utc_time_get",
"description": "Returns the current time in UTC in ISO format.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
},……
]
'''
tools_specs = json.dumps(specs)
if request.app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE != "":
template = request.app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
else: #未配置工具函数模板时,使用缺省的模板
template = DEFAULT_TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE#用tool_spces内容替换模板中的{TOOL}
tools_function_calling_prompt = tools_function_calling_generation_template(
template, tools_specs
)#组织发送到ollama的请求,具体见上面的函数定义部分
payload = get_tools_function_calling_payload(
body["messages"], task_model_id, tools_function_calling_prompt
)try:
#调用大模型获取需要调用的工具信息
response = await generate_chat_completion(request, form_data=payload, user=user)
log.debug(f"{response=}")
content = await get_content_from_response(response)
log.debug(f"{content=}")'''
以下是一个无参函数示例时cotent的示例内容
{
"tool_calls": [
{
"name": "get_current_local",
"parameters": {}
}
]
}'''
if not content:
return body, {}try:
content = content[content.find("{") : content.rfind("}") + 1]
if not content:
raise Exception("No JSON object found in the response")result = json.loads(content)
#该方法根据function_calling调用结果进行后继的调用处理,需要重点分析
async def tool_call_handler(tool_call):
nonlocal skip_fileslog.debug(f"{tool_call=}")
'''
获取函数名和函数参数。
防错处理:如果大模型返回的函数名字,不在本请求所提供的工具列表中,则
返回请求表单+{}
'''
tool_function_name = tool_call.get("name", None)
if tool_function_name not in tools:
return body, {}tool_function_params = tool_call.get("parameters", {})
try:
tool = tools[tool_function_name]spec = tool.get("spec", {})
allowed_params = (#工具定义时允许的参数列表
spec.get("parameters", {}).get("properties", {}).keys()
)
tool_function_params = {#实际的参数必须在工具允许的参数列表中,否则丢弃
k: v
for k, v in tool_function_params.items()
if k in allowed_params
}if tool.get("direct", False): #如果是外部服务函数,则本分支
'''
通过websocket发送请求到前端,前端走API调用,并返回结果。
结果为列表,比如:
[
{"local_time":"2025-08-20T12:09:16.773972"}
]
'''
tool_result = await event_caller(
{
"type": "execute:tool",
"data": {
"id": str(uuid4()),
"name": tool_function_name,
"params": tool_function_params,
"server": tool.get("server", {}),
"session_id": metadata.get("session_id", None),
},
}
)
else: #如果是本地代码中的函数,则直接调用函数
tool_function = tool["callable"]
tool_result = await tool_function(**tool_function_params)except Exception as e:
tool_result = str(e)'''
以下代码针对function_calling涉及引用文件时的处理,此时列表中的元素为
data:开头的字符串,支架到tool_result_files列表中,并从源列表删除
'''
tool_result_files = []
if isinstance(tool_result, list):
for item in tool_result:
# check if string
if isinstance(item, str) and item.startswith("data:"):
tool_result_files.append(item)
tool_result.remove(item)if isinstance(tool_result, dict) or isinstance(tool_result, list):#转换为JSON串
tool_result = json.dumps(tool_result, indent=2)if isinstance(tool_result, str):#因前面以把tool_result转换为字符串,进入本分支
tool = tools[tool_function_name]
tool_id = tool.get("tool_id", "")tool_name = (
f"{tool_id}/{tool_function_name}"
if tool_id
else f"{tool_function_name}"
)
'''把类似如下数据追加到sources列表中:
{
"source":{"name": "TOOL:get_current_local_get_current_local_time_get"
},
"document": [
{
"local_time": "2025-08-20T11:54:16.180931"
}
],
"metadata": [
{
"source": "TOOL:get_current_local_get_current_local_time_get",
"parameters": {}
}
],"tool_result": True
}'''
sources.append(
{
"source": {
"name": (f"TOOL:{tool_name}"),
},
"document": [tool_result],
"metadata": [
{
"source": (f"TOOL:{tool_name}"),
"parameters": tool_function_params,
}
],
}
)
'''把function_calling相关结果拼接后追加到用户请求表单的messages中,比
如一个对话中拼接后的messages:
[
{
"role": "user",
"content": "请告诉我当前大阪的时间"
},
{
"role": "assistant",
"content": "\n根据工具返回的示例数据,当前大阪的本地时间是 **2025年8月20日 11:54:16**。请注意,此时间是示例数据,实际当前时间可能不同。若需真实时间,请结合实时数据更新。"
},
{
"role": "user",
"content": "请告诉我当前的时间\n\nTool `get_current_local_get_current_local_time_get` Output: {\n \"local_time\": \"2025-08-20T11:59:16.404818\"\n}"
}
]'''
body["messages"] = add_or_update_user_message(
f"\nTool `{tool_name}` Output: {tool_result}",
body["messages"],
)if (
tools[tool_function_name]
.get("metadata", {})
.get("file_handler", False)
):
skip_files = True'''
如果function_calling返回的tool_calls列表不为空,则迭代调用tool_call_handler,
否则直接调用tool_call_handler
'''
if result.get("tool_calls"):
for tool_call in result.get("tool_calls"):
await tool_call_handler(tool_call)
else:
await tool_call_handler(result)except Exception as e:
log.debug(f"Error: {e}")
content = None
except Exception as e:
log.debug(f"Error: {e}")
content = Nonelog.debug(f"tool_contexts: {sources}")
if skip_files and "files" in body.get("metadata", {}):
del body["metadata"]["files"]return body, {"sources": sources}