OpenAI Chat Completion 集成¶
延续 基础用法 中的简单数学示例,本文介绍如何将 ToolRegistry 与 OpenAI Chat Completion API 配合使用。需要注意的是,你可以通过 OpenAI 客户端连接任何提供 OpenAI 兼容 API 的服务商。本指南以 DeepSeek 为例进行演示。
设置 ToolRegistry¶
我们创建一个包含两个数学函数的工具注册表:
from toolregistry import ToolRegistry
registry = ToolRegistry()
@registry.register
def add(a: float, b: float) -> float:
"""Add two numbers together."""
return a + b
@registry.register
def subtract(a: float, b: float) -> float:
"""Subtract the second number from the first."""
return a - b
导出工具 Schema¶
以上两种写法均会返回符合 OpenAI Chat Completion API 要求的工具 Schema。格式化后的 JSON 输出如下:
[
{
"type": "function",
"function": {
"name": "add",
"description": "Add two numbers together.",
"parameters": {
"properties": {
"a": {
"title": "A",
"type": "number"
},
"b": {
"title": "B",
"type": "number"
}
},
"required": [
"a",
"b"
],
"title": "addParameters",
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "subtract",
"description": "Subtract the second number from the first.",
"parameters": {
"properties": {
"a": {
"title": "A",
"type": "number"
},
"b": {
"title": "B",
"type": "number"
}
},
"required": [
"a",
"b"
],
"title": "subtractParameters",
"type": "object"
}
}
}
]
使用工具 Schema 发送查询¶
将工具 JSON Schema 提供给 OpenAI 客户端的 Chat Completion 接口:
import os
from dotenv import load_dotenv
from openai import OpenAI
# Load environment variables from the .env file
load_dotenv()
# Configure the OpenAI client
client = OpenAI(
api_key=os.getenv("API_KEY", "your-api-key"),
base_url=os.getenv("BASE_URL", "https://api.deepseek.com/"),
)
messages = [
{
"role": "user",
"content": "I have 15 chestnuts. Joe ate 3. How many chestnuts do I have left?",
}
]
# Send the chat completion request
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
tools=schemas, # this is where we feed in the schema
tool_choice="auto",
)
提取工具调用¶
如果模型(LLM)决定使用工具,它会在响应消息中返回 tool_calls:
函数调用的输出示例:
[ChatCompletionMessageToolCall(id='call_egkg4evbb19d8012bex83v8a', function=Function(arguments='{"a":15,"b":3}', name='subtract'), type='function', index=0)]
tool_calls 对象是一个 ChatCompletionMessageToolCall 的 List。以下属性尤为重要:
id:在将结果反馈给 LLM 时需要用到function:核心字段,包含目标函数的参数和名称index:在非流式模式下用处不大,但在流式输出时,它是拼接完整 tool_calls 信息的关键
执行工具调用¶
使用 ToolRegistry,你可以轻松处理所有 tool_calls 的执行结果。注意,有时 LLM 可能会同时调用多个工具。
注册表返回的工具执行结果是一个 Python 字典,键为 tool_call_id,值为对应的结果:
将结果反馈给 LLM¶
执行完工具调用后,我们还需要将结果告知 LLM,它才能回答原始问题。
为了在后续与 LLM 的交互中保持工具调用的上下文,我们需要重建两部分信息:
- 助手决定发起
tool_calls的消息 - 实际的
tool_calls执行结果
# Construct assistant messages with results
assistant_tool_messages = registry.build_tool_call_messages(
tool_calls, tool_responses, api_format="openai-chat"
) # you can leave out api_format, it defaults to "openai-chat"
[
{
"content": null,
"role": "assistant",
"tool_calls": [
{
"id": "call_wAcYzTLh37jfrCmihEv7x4FC",
"function": {
"arguments": "{\"a\":15,\"b\":3}",
"name": "subtract"
},
"type": "function"
}
]
},
{
"role": "tool",
"tool_call_id": "call_wAcYzTLh37jfrCmihEv7x4FC",
"content": "12"
}
]
然后将这些重建的消息追加到之前发送给 LLM 的消息列表中。
messages.extend(assistant_tool_messages)
# Send the results back to the model
second_response = client.chat.completions.create(
model="deepseek-chat", messages=messages
)
# Print final response
print(second_response.choices[0].message.content)
最终结果与注意事项¶
LLM 在处理完工具执行结果后,会返回最终答案:
重要的实现说明¶
实现时应处理连续函数调用的情况,因为对话可能需要多轮工具调用,每次 LLM 的响应都可能触发新的工具调用。
错误处理至关重要:在执行工具调用前,始终需要验证参数的合法性(ToolRegistry 已自动完成此操作)。需要处理工具可能失败或返回错误的情况,并考虑为长时间运行的操作添加超时机制。
状态管理涉及维护完整的对话历史(包括所有工具调用及其响应)、跟踪工具执行顺序以便调试,以及考虑对话状态的持久化。
性能方面需要注意减少不必要的工具调用、在适当时缓存频繁的工具响应结果,以及监控和优化工具执行时间。
完整 Python 脚本¶
以下是本示例使用的完整脚本。
import json
import os
from dotenv import load_dotenv
from openai import OpenAI
from toolregistry import ToolRegistry
# Load environment variables from .env file
load_dotenv()
model_name = os.getenv("MODEL", "deepseek-chat")
stream = os.getenv("STREAM", "True").lower() == "true"
registry = ToolRegistry()
@registry.register
def add(a: float, b: float) -> float:
"""Add two numbers together."""
return a + b
@registry.register
def subtract(a: float, b: float) -> float:
"""Subtract the second number from the first."""
return a - b
# Set up OpenAI client
client = OpenAI(
api_key=os.getenv("API_KEY", "your-api-key"),
base_url=os.getenv("BASE_URL", "https://api.deepseek.com/"),
)
messages = [
{
"role": "user",
"content": "I have 15 chestnuts. Joe ate 3. How many chestnuts do I have left?",
}
]
# Make the chat completion request
response = client.chat.completions.create(
model=model_name,
messages=messages,
tools=registry.get_schemas(api_format="openai-chat"),
tool_choice="auto",
)
# Handle tool calls using ToolRegistry
if response.choices[0].message.tool_calls:
tool_calls = response.choices[0].message.tool_calls
print(tool_calls)
# Execute tool calls
tool_responses = registry.execute_tool_calls(tool_calls)
print(tool_responses)
# Construct assistant messages with results
assistant_tool_messages = registry.build_tool_call_messages(
tool_calls, tool_responses, api_format="openai-chat"
)
print(json.dumps(assistant_tool_messages, indent=2))
# Send the results back to the model
messages.extend(assistant_tool_messages)
second_response = client.chat.completions.create(
model=model_name, messages=messages
)
# Print final response
print(second_response.choices[0].message.content)