大模型通过tool_calls调用外部方法


import json
import os
from openai import OpenAI
from dotenv import load_dotenv

# 加载 .env 文件
load_dotenv()
client = OpenAI(
                # 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:api_key="sk-xxx",
                api_key=os.getenv("DASHSCOPE_API_KEY"),
                base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
            )
messages = [
        # {'role': 'system', 'content': ''},
        {'role': 'user', 'content': '我的手上有10个苹果  又买了3个  还剩多少个?'}
        ]

tools =  [
            {
                "type": "function",
                "function": {
                    "name": "sum",
                    "description": "使列表中n个数字相加",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "query": {
                                "description": "列表",
                                "type": "array",
                                "items": {
                                    "type": "number"
                                }
                            }
                        },
                        "required": ["query"]
                    }
                }
            }
        ]
def call_model_with_tool_response(messages):
    # 继续与模型对话
    response = client.chat.completions.create(
        model="qwen-plus",
        messages=messages,
        temperature=0, max_tokens=1024, top_p=1, frequency_penalty=0, presence_penalty=0,
        tools=tools
    )
    return response
message = call_model_with_tool_response(messages).choices[0].message
print(message)
#定义一个方法使调用工具回参

while message.tool_calls and message.tool_calls[0].function is not None:
    arguments = json.loads(message.tool_calls[0].function.arguments)["query"]
    name = message.tool_calls[0].function.name
    sumReturn  = sum(arguments)
    print(name)
    print(arguments)
    if name == "sum":
        sumReturn =  sum(arguments)
        messages.append(message)
        messages.append({
            'tool_call_id': message.tool_calls[0].id,
            'role': 'tool',
            'name': name,
            'content': str(sumReturn)
        })
    response = call_model_with_tool_response(messages)
    message = response.choices[0].message
    print(message)

调用tool_calls的步骤实际上是访问了两次大模型

1.大模型根据用户定义的tools识别出本次要用的tool 并在message内部返回本次调用的tool信息

2. 用户把大模型给的tool运行出来

3.用户把tool结果和tool_call_id   附加大模型第一次返回的message信息在第二次发送给大模型

        第三条需要特别注意不然不会被大模型识别为二次调用:

        抛出异常:openai.BadRequestError: Error code: 400 - {'error': {'code': 'invalid_parameter_error', 'param': None, 'message': '<400> InternalError.Algo.InvalidParameter: messages with role "tool" must be a response to a preceeding message with "tool_calls".', 'type': 'invalid_request_error'}, 'id': 'chatcmpl-c6979e5a-22b5-92ea-80ad-b18e820b279f', 'request_id': 'c6979e5a-22b5-92ea-80ad-b18e820b279f'}
4.如果需要更多的工具调用 例如  func1->func2->func3...->funN   大模型会逐步返回,真实与大模型发生N+1次交互

<think>我们正在处理一个技术错误信息:"Invalid parameter messages with role tool must be a response to preceding message with tool_calls"。这个错误通常发生在使用OpenAI的API(特别是Chat Completions API)时,当我们尝试构建消息列表(messages list)时,消息的角色(role)使用不正确。 ### 错误原因分析 在OpenAI的API中,消息列表中的每个消息都有一个角色,常见的角色有: - `system`:系统消息,用于设定对话的背景或行为。 - `user`:用户消息,代表用户输入。 - `assistant`:助手消息,代表模型的回复。 - `tool`:工具消息,代表工具调用的结果。 错误信息明确指出:`messages with role 'tool' must be a response to a preceding message with 'tool_calls'`。这意味着: 1. 当我们使用`tool`角色的消息时,它必须紧接在一个包含`tool_calls`的`assistant`消息之后。 2. 换句话说,模型(assistant)必须先发送一个包含`tool_calls`的消息(表示它要调用工具),然后我们才能用`tool`角色消息来提供工具调用的结果。 ### 错误场景 假设我们构建的消息列表如下: ```json [ {"role": "user", "content": "今天北京的天气怎么样?"}, {"role": "tool", "content": "北京今天晴,气温25℃"} // 错误:前面没有assistant的tool_calls ] ``` 这样就会触发上述错误,因为`tool`消息前面没有对应的`assistant`消息(包含`tool_calls`)。 ### 正确流程 正确的消息序列应该是: 1. `user`消息:用户提问。 2. `assistant`消息:模型决定调用工具,因此返回一个包含`tool_calls`的响应(而不是普通文本)。该消息中会包含一个或多个工具调用请求。 3. `tool`消息:开发者根据工具调用的结果,构造一个或多个`tool`消息,每个消息对应一个工具调用结果,并包含一个`tool_call_id`(由步骤2中的`tool_calls`提供)和`content`(工具调用的结果)。 4. 然后,我们可以将之前的消息(包括步骤1、2、3)再次发送给API,让模型根据工具调用的结果生成最终回复。 ### 示例代码 下面是一个模拟正确流程的伪代码: ```python # 第一步:用户消息 messages = [ {"role": "user", "content": "今天北京的天气怎么样?"} ] # 第二步:调用API,模型返回工具调用请求 response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=messages, tools=[...] # 定义可用的工具列表 ) # 假设模型返回了工具调用 assistant_message = response.choices[0].message messages.append(assistant_message) # 这个assistant_message包含tool_calls # 第三步:执行工具调用,并构造tool消息 tool_calls = assistant_message.tool_calls for call in tool_calls: # 根据call中的参数调用工具 result = call_tool(call.function.name, call.function.arguments) # 添加tool消息 messages.append({ "role": "tool", "name": call.function.name, # 可选,但推荐 "content": result, "tool_call_id": call.id # 非常重要,对应之前的tool_calls中的id }) # 第四步:再次调用API,让模型根据工具结果生成回复 second_response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=messages ) final_reply = second_response.choices[0].message.content ``` ### 常见错误及解决方案 1. **错误地放置`tool`消息**:确保`tool`消息紧跟在包含`tool_calls`的`assistant`消息之后,并且中间没有其他消息。 2. **缺少`tool_call_id`**:每个`tool`消息必须包含一个`tool_call_id`,该值来自之前`assistant`消息中`tool_calls`数组里每个调用的`id`。 3. **多个工具调用的处理**:如果有多个工具调用,需要为每个调用生成一个`tool`消息(每个消息对应一个`tool_call_id`)。 4. **在第一次调用时未提供工具定义**:如果希望模型调用工具,在第一次调用时必须通过`tools`参数提供工具定义(函数列表)。 ### 总结 该错误是由于消息列表中`tool`消息出现的位置不正确或缺少必要的关联信息(如`tool_call_id`)导致的。请检查消息列表的顺序,确保每个`tool`消息都紧跟在对应的`assistant`(包含`tool_calls`)消息之后,并且每个`tool`消息都包含正确的`tool_call_id`。 ### 相关问题 1. 如何定义OpenAI API中的工具(函数)? 2. 如何处理多个工具调用? 3. 如何调试OpenAI API的消息序列? 4. 如果工具调用失败,应该如何在消息中表示?</think>### 错误分析:`messages with role 'tool' must be a response to a preceding message with 'tool_calls'` 这个错误发生在使用 OpenAI API 的函数调用(Function Calling)功能时,消息序列的结构不符合规范。根本原因是 **`tool` 角色的消息缺少前置的 `tool_calls` 消息**。 #### 错误原理 1. **消息角色规范**: - `assistant` 消息需包含 `tool_calls` 字段(指示要调用的函数) - `tool` 消息必须作为 `tool_calls` 的响应(提供函数执行结果) - 两者必须**严格成对出现**,顺序不可颠倒或缺失 2. **错误场景**: - 直接发送了 `role: "tool"` 的消息,但前一条消息不是包含 `tool_calls` 的 `assistant` 消息 - 在对话历史中遗漏了模型的函数调用请求 - 手动构造消息时顺序错误 --- ### 解决方案 #### 步骤 1:检查消息序列结构 正确的消息序列结构应为: ```python [ # 用户输入 {"role": "user", "content": "查询北京天气"}, # 模型返回函数调用请求 (必须包含 tool_calls) { "role": "assistant", "content": null, "tool_calls": [ # 关键字段 { "id": "call_abc123", "type": "function", "function": { "name": "get_current_weather", "arguments": '{"location":"Beijing"}' } } ] }, # 工具执行结果 (必须响应 tool_calls) { "role": "tool", "content": "{\"temperature\": 22}", # 函数返回结果 "tool_call_id": "call_abc123" # 匹配前文的 id } ] ``` #### 步骤 2:修复代码逻辑 1. **确保触发函数调用**: - 在 API 请求中需包含 `tools` 参数定义可用函数 ```python response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": "北京天气如何?"}], tools=[{ # 必须声明可用函数 "type": "function", "function": { "name": "get_current_weather", "description": "获取当前天气", "parameters": {...} } }] ) ``` 2. **正确处理模型响应**: ```python # 检查是否包含 tool_calls if response.choices[0].message.tool_calls: tool_call = response.choices[0].message.tool_calls[0] # 执行函数 if tool_call.function.name == "get_current_weather": result = get_weather(tool_call.function.arguments) # 添加 tool 消息时必须包含 tool_call_id messages.append({ "role": "tool", "content": result, "tool_call_id": tool_call.id # 关键匹配项 }) ``` #### 步骤 3:常见错误排查 1. **消息顺序错误**: ```python # 错误示例:tool 消息出现在 assistant 之前 messages = [ {"role": "tool", ...}, # ❌ 非法位置 {"role": "assistant", ...} ] ``` 2. **缺失 tool_call_id**: ```python # 错误示例 { "role": "tool", "content": "22°C" # ❌ 缺少 tool_call_id } ``` 3. **未处理中间消息**: - 若进行多轮对话,必须完整传递所有历史消息,包括中间的 `assistant/tool_calls` 和 `tool` 消息 --- ### 完整修复示例 ```python import openai messages = [{"role": "user", "content": "北京今天气温多少?"}] # 首次请求(触发函数调用) response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=messages, tools=[...] # 函数定义 ) # 添加 assistant 消息(含 tool_calls) assistant_msg = response.choices[0].message messages.append(assistant_msg) # 必须加入消息序列 if assistant_msg.tool_calls: # 处理每个函数调用 for tool_call in assistant_msg.tool_calls: # 执行函数获取结果 result = execute_function(tool_call) # 添加 tool 消息(携带匹配的 tool_call_id) messages.append({ "role": "tool", "content": result, "tool_call_id": tool_call.id # 关键匹配项 }) # 二次请求获取最终回答 final_response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=messages # 此时包含完整序列 ) print(final_response.choices[0].message.content) ``` --- ### 相关问题 1. 如何定义 OpenAI API 的函数调用(Function Calling)参数? 2. 函数调用过程中如何正确处理 JSON 参数? 3. 多轮对话中如何维护消息序列的正确性? 4. 如何调试 `tool_calls` 未触发的常见原因? [^1]: 参考 OpenAI API 文档:Function calling 规范
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值