大模型会提供一个叫Tool Calls(也有的叫Function Calls)的功能,看起来很神奇,实际上就是LLM根据对话内容提取出需要使用的工具及其参数,再将其以结构化的形式返回给客户端,客户端通过响应的内容判断是否存在“使用工具”这个操作,如果存在,则从中获取所需使用的“工具名”及“参数”来使用工具。
看以下python写的一个示例。
import json
import os
import sys
from openai import OpenAI
API_KEY = "sk-xxx"
client = OpenAI(api_key=API_KEY, base_url="https://api.deepseek.com")
ESC = "\033"
COLOR_FAIL = ESC + "[31m"
COLOR_SUCCESS = ESC + "[32m"
MSG_FAIL = "fail"
MSG_SUCCESS = "success"
RESET = ESC + "[0m"
def pretty_msg(msg, color=COLOR_FAIL):
"""美化提示"""
colors = {MSG_FAIL: COLOR_FAIL, MSG_SUCCESS: COLOR_SUCCESS}
color = COLOR_FAIL if colors.get(color) is None else colors.get(color)
return f"{color}{msg}{RESET}"
def code(name):
"""执行代码生成任务"""
try:
print(COLOR_FAIL + "+" * 50 + "\n" + COLOR_SUCCESS, sep="", end="")
os.system('ls -l ~')
print(COLOR_FAIL + "-" * 50 + RESET, sep="")
return "用 {} 写一个简单的字符串反转函数".format(name)
except Exception as e:
print(pretty_msg("内部执行错误: {}".format(str(e))))
sys.exit(1)
def safe_chat_completion(**kwargs):
"""安全的 API 调用包装器"""
try:
return client.chat.completions.create(**kwargs)
except Exception as e:
print(pretty_msg("调用失败: {}".format(str(e))))
sys.exit(1)
def main(content):
"""主函数"""
print("正在处理请求...")
content = "如果你从这段话'{}'中获取到编程语言的名称,就调用 code 函数,并将编程语言的名称作为 name 参数。".format(content)
# 第一次调用:检测是否需要调用工具
response = safe_chat_completion(
model="deepseek-chat",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": content},
],
tools=[
{
"type": "function",
"function": {
"name": "code",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
}
},
"required": ["name"]
}
},
}
],
tool_choice="auto"
)
message = response.choices[0].message
# 输出第一次响应
if message.content:
print('first:', message.content)
else:
print('first: (无文本内容)')
# 处理工具调用
if hasattr(message, 'tool_calls') and message.tool_calls:
for tool_call in message.tool_calls:
if tool_call.function.name == "code":
try:
arguments = json.loads(tool_call.function.arguments)
name = arguments.get("name", "未知语言")
result = code(name)
# 第二次调用:带工具结果的流式响应
second_response = safe_chat_completion(
model="deepseek-chat",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": content},
message, # 模型的 tool call 响应
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
}
],
stream=True
)
print("\nsecond:")
for chunk in second_response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
print() # 换行
except json.JSONDecodeError:
print(pretty_msg("错误:无法解析工具调用参数"))
sys.exit(1)
except KeyError as e:
print(pretty_msg("错误:缺少必需参数 {}".format(str(e))))
sys.exit(1)
except Exception as e:
print(pretty_msg("执行错误 {}".format(str(e))))
sys.exit(1)
else:
if message.content:
print('first:', message.content)
else:
print("未检测到工具调用")
if __name__ == "__main__":
try:
content = "你知道吗?python 是世界最好的语言。"
main(content)
except KeyboardInterrupt:
print(pretty_msg("用户中断执行"))
sys.exit(1)
except Exception as e:
print(pretty_msg("未预期的错误:{}".format(str(e))))
sys.exit(1)
以上代码会从让LLM从用户的对话中判断是否存在编程语言的名称,如果存在,则返回Tool Calls响应,客户端针对该响应进行调用code工具,再将code工具的返回内容进行再次对话。
可以看到code其实就是一个普通的python函数,code里面当然可以做其它“神奇”的操作,如打开浏览器做爬虫等。关键点就是第一次请求将code函数作为工具传给了大模型,这时候就需要大模型自行判断是否返回一个工具调用的响应了。如果返回工具调用响应,客户端则可以根据响应使用相应的工具,再根据工具的处理结果进行后续与大模型交互等操作。