什么是 ‘Tool Response Feedback’?当工具返回报错时,如何设计提示词引导 LLM 自行修正参数重试

各位同仁,各位技术爱好者,大家好!

今天,我将与大家深入探讨一个在大型语言模型(LLM)与外部工具交互领域至关重要的话题——“Tool Response Feedback”(工具响应反馈),以及如何巧妙地设计提示词,引导LLM在工具返回错误时,进行参数的自我修正并重试。在当今的AI应用中,LLM不再仅仅是文本生成器,它们正逐渐成为能够感知、规划并执行复杂任务的智能代理。而与外部工具的集成,正是赋予LLM这种能力的基石。然而,工具执行并非总是一帆风顺,错误在所难免。如何让LLM从错误中学习,进而自我修复,这正是我们今天要解决的核心问题。

一、理解“Tool Response Feedback”:智能代理的感知之眼

在LLM与工具交互的范式中,LLM首先根据用户指令和当前上下文,决定调用哪个工具以及传入哪些参数。这个过程我们称之为“工具调用规划”(Tool Calling Planning)。一旦LLM生成了工具调用指令,例如一个JSON对象或一个函数签名,这个指令会被发送给一个执行器(Executor),由它来实际调用外部API或执行本地代码。

“Tool Response Feedback”指的正是工具执行器将工具的运行结果,无论是成功的数据返回还是失败的错误信息,反馈给LLM的过程。这不仅仅是简单地将API响应抛给LLM,更关键的是,LLM需要能够理解这些响应,并据此调整其后续行为。

为什么Tool Response Feedback如此重要?

  1. 实现闭环控制: 没有反馈,LLM就像一个盲人摸象,无法得知其行动的实际效果。反馈机制是构建LLM代理的智能闭环控制系统的核心。
  2. 错误检测与诊断: 当工具执行失败时,反馈的错误信息是LLM诊断问题、理解失败原因的唯一线索。
  3. 自我修正与适应: 这是我们今天重点讨论的部分。通过理解错误反馈,LLM可以识别出参数问题、环境限制等,并尝试修正其规划,重新尝试。
  4. 增量学习与优化: 长远来看,LLM可以从大量的成功和失败反馈中“学习”更有效的工具使用策略,优化其参数选择和调用时机。
  5. 提升用户体验: 自动化错误处理和重试机制,可以减少用户在遇到问题时需要手动干预的次数,提升系统的鲁棒性和用户满意度。

简单来说,Tool Response Feedback是LLM从“工具调用者”升级为“工具使用者”的关键。它让LLM不仅仅是发出指令,更是能够理解指令执行结果,并据此调整策略的智能实体。

二、工具调用中常见的错误类型及其LLM认知挑战

在设计LLM的自我修正机制之前,我们必须先了解工具调用中可能出现的错误类型。这些错误各有特点,对LLM的理解和修正提出了不同的挑战。

错误类型 描述 示例 LLM认知挑战
参数验证错误 LLM提供的参数不符合工具的预期格式、类型、范围或枚举值。 date 格式错误 (如 2023-13-40)
quantity 为负数或非数字
city 不在允许的城市列表中
需要理解错误消息中的具体约束(格式、范围、枚举),并找到原始指令中对应参数进行修正。
业务逻辑错误 参数本身合法,但组合或业务场景不满足工具的业务规则。 – 预订的日期已满
– 购买商品库存不足
– 用户没有权限执行某操作
错误信息可能更抽象,需要LLM结合上下文理解业务规则,可能需要尝试其他参数组合或告知用户。
外部服务错误 工具依赖的第三方服务出现故障、超时或返回错误。 – 外部API 500 错误
– 数据库连接失败
– 网络超时
LLM通常无法直接解决这类问题,但可以尝试重试(如果错误是瞬时的),或告知用户/寻求帮助。对LLM而言,这是“黑盒”错误。
认证/授权错误 LLM或其代表的用户没有足够的权限调用该工具或访问特定资源。 – API Key 无效
– 用户未登录
– 访问被拒绝
LLM无法自行获取权限,需要告知用户或管理员。
环境配置错误 工具运行环境配置不正确,例如缺少必要的环境变量、文件路径错误等。 – 配置文件丢失
– 环境变量未设置
类似于外部服务错误,LLM通常无法直接处理,需要运维介入。
语义理解错误 (LLM侧) LLM虽然调用了正确的工具,但由于对用户意图理解偏差,导致选择的参数与用户真实需求不符,但工具执行成功。 用户想查“北京天气”,LLM误以为是“上海天气”并成功查询。 这种错误最难检测,因为工具返回成功。需要LLM在收到结果后,通过与用户意图的对比来判断是否需要进一步修正。这超出了本讲座的范围,但值得注意。

LLM在处理这些错误时,最大的挑战在于:

  1. 符号接地问题: LLM理解的是文本,它需要将错误消息中的文本描述,准确地映射到它之前生成的具体参数及其意图上。
  2. 推理与规划: 仅仅知道错误类型不够,LLM还需要基于错误信息进行推理,找出修正策略,并重新规划工具调用。
  3. 上下文保持: 在多次重试和修正中,LLM必须始终保持对用户原始意图的理解,避免“跑偏”。

三、设计提示词引导LLM自我修正的原则与策略

现在,我们进入核心部分:如何设计提示词,让LLM能够有效地利用错误反馈进行自我修正。

3.1 核心设计原则

  1. 明确的错误处理指令: 在初始提示中就告知LLM,当工具返回错误时应该怎么做,而不是让它自己猜测。
  2. 结构化的错误反馈: 工具执行器返回的错误信息应当尽可能地结构化,便于LLM解析和理解。
  3. 提供修正指导: 提示词可以包含常见错误的修正建议或参数约束,帮助LLM更快地找到正确方向。
  4. 鼓励“思考”(Chain-of-Thought): 引导LLM在重试前先分析错误,解释其修正思路。
  5. 限定重试次数: 避免无限循环,浪费资源。

3.2 提示词设计的关键组成部分

一个用于自我修正的提示词通常包括以下几个部分:

  1. 系统角色定义: 明确LLM的身份和职责。
  2. 工具描述: 详细说明可用的工具、其功能、预期参数及其数据类型、约束、示例。
  3. 用户指令: 用户的原始请求。
  4. 工具调用历史: 记录LLM之前尝试调用的工具和参数。
  5. 工具执行结果(错误反馈): 这是最关键的部分,包含详细的错误信息。
  6. 自我修正指令: 明确要求LLM根据错误信息分析问题、修正参数并重新调用工具。

3.3 具体的提示词设计策略与代码示例

我们将通过一个具体的场景来演示:预订航班。

场景: 用户希望预订从北京到上海的航班,但可能提供了错误的日期格式、不支持的城市代码或错误的乘客数量。

工具定义 (示例 Python 函数):

import datetime

class FlightBookingTool:
    def __init__(self):
        self.supported_cities = ["北京", "上海", "广州", "深圳", "成都"]

    def book_flight(self, origin: str, destination: str, date: str, passengers: int) -> dict:
        """
        预订航班的工具。
        参数:
        - origin (str): 出发城市,必须是中国支持的城市名称。
        - destination (str): 目的城市,必须是中国支持的城市名称。
        - date (str): 航班日期,格式为 YYYY-MM-DD。日期必须在未来。
        - passengers (int): 乘客数量,必须是正整数,且最多不能超过5人。
        """
        errors = []

        # 1. 城市验证
        if origin not in self.supported_cities:
            errors.append(f"出发城市 '{origin}' 不支持。支持的城市有:{', '.join(self.supported_cities)}")
        if destination not in self.supported_cities:
            errors.append(f"目的城市 '{destination}' 不支持。支持的城市有:{', '.join(self.supported_cities)}")
        if origin == destination:
            errors.append("出发城市和目的城市不能相同。")

        # 2. 日期格式验证
        try:
            flight_date = datetime.datetime.strptime(date, "%Y-%m-%d").date()
            if flight_date < datetime.date.today():
                errors.append(f"航班日期 '{date}' 不能是过去或今天。请提供未来的日期。")
        except ValueError:
            errors.append(f"日期格式错误。请使用 YYYY-MM-DD 格式,例如: {datetime.date.today() + datetime.timedelta(days=7)}")

        # 3. 乘客数量验证
        if not isinstance(passengers, int) or passengers <= 0:
            errors.append(f"乘客数量 '{passengers}' 必须是正整数。")
        elif passengers > 5:
            errors.append(f"乘客数量 '{passengers}' 不能超过5人。")

        if errors:
            return {"status": "error", "message": "参数校验失败。", "details": errors}
        else:
            # 模拟成功预订
            return {
                "status": "success",
                "message": "航班预订成功。",
                "booking_details": {
                    "origin": origin,
                    "destination": destination,
                    "date": date,
                    "passengers": passengers,
                    "booking_id": "FL" + str(hash(f"{origin}-{destination}-{date}-{passengers}"))[:8],
                },
            }

# 实例化工具
flight_tool = FlightBookingTool()

模拟LLM代理的函数 (简化版):

为了演示,我们模拟一个LLM代理与工具的交互循环。

import json

# 模拟LLM的思考和工具调用生成
def mock_llm_generate_tool_call(prompt: str) -> dict:
    """
    模拟LLM根据prompt生成工具调用(JSON格式)。
    如果LLM决定不调用工具,它将返回一个文本响应。
    """
    print(f"n--- LLM Input Prompt ---n{prompt}n------------------------")

    # 在实际应用中,这里会调用真正的LLM API
    # 为了演示,我们根据prompt内容模拟LLM的行为

    # 第一次尝试:假设LLM会尝试调用book_flight
    if "book_flight" in prompt and "Tool Call History" not in prompt:
        # 模拟LLM第一次可能犯的错
        if "北京" in prompt and "上海" in prompt and "明天" in prompt and "3个人" in prompt:
            return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "北京",
                    "destination": "上海",
                    "date": "明天",  # 错误格式
                    "passengers": 3
                }
            }
        elif "洛杉矶" in prompt: # 模拟LLM提供不支持的城市
             return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "洛杉矶",
                    "destination": "上海",
                    "date": "2024-12-25",
                    "passengers": 2
                }
            }
        elif "超过5个人" in prompt: # 模拟LLM提供过多乘客
             return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "北京",
                    "destination": "上海",
                    "date": "2024-12-25",
                    "passengers": 6
                }
            }
        elif "今天" in prompt: # 模拟LLM提供过去日期
             return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "北京",
                    "destination": "上海",
                    "date": datetime.date.today().strftime("%Y-%m-%d"),
                    "passengers": 1
                }
            }

    # 第二次及以后尝试:假设LLM会根据反馈修正
    if "Tool Execution Error" in prompt:
        # 假设LLM根据错误信息进行修正
        if "日期格式错误" in prompt and "明天" in prompt:
            print("LLM: 检测到日期格式错误,将 '明天' 修正为 '2024-12-25'。")
            return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "北京",
                    "destination": "上海",
                    "date": "2024-12-25", # 修正后的日期
                    "passengers": 3
                }
            }
        elif "出发城市 '洛杉矶' 不支持" in prompt:
            print("LLM: 检测到出发城市不支持,将 '洛杉矶' 修正为 '北京'。")
            return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "北京",
                    "destination": "上海",
                    "date": "2024-12-25",
                    "passengers": 2
                }
            }
        elif "乘客数量 '6' 不能超过5人" in prompt:
            print("LLM: 检测到乘客数量超限,将 '6' 修正为 '5'。")
            return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "北京",
                    "destination": "上海",
                    "date": "2024-12-25",
                    "passengers": 5
                }
            }
        elif "航班日期不能是过去或今天" in prompt:
            print("LLM: 检测到日期是过去或今天,将日期修正为 '2024-12-25'。")
            return {
                "tool_name": "book_flight",
                "parameters": {
                    "origin": "北京",
                    "destination": "上海",
                    "date": "2024-12-25", # 修正为未来日期
                    "passengers": 1
                }
            }

    # 如果LLM无法修正,或者没有工具调用,则返回文本
    return "抱歉,我无法完成您的请求,请检查信息或稍后再试。"

def execute_tool_call(tool_call: dict) -> dict:
    """
    执行模拟的工具调用。
    """
    tool_name = tool_call.get("tool_name")
    parameters = tool_call.get("parameters")

    if tool_name == "book_flight":
        return flight_tool.book_flight(**parameters)
    else:
        return {"status": "error", "message": f"未知工具: {tool_name}"}

def run_llm_agent(user_query: str, max_retries: int = 3) -> str:
    """
    模拟LLM代理与工具交互的完整流程,包括错误修正。
    """
    chat_history = []
    tool_call_history = []

    # 初始系统提示和工具描述
    system_prompt = f"""
你是一个航班预订助手,能够帮助用户预订航班。
请根据用户的请求,使用下方提供的工具。

可用工具:
```json
{{
  "name": "book_flight",
  "description": "{flight_tool.book_flight.__doc__.strip()}",
  "parameters": {{
    "type": "object",
    "properties": {{
      "origin": {{"type": "string", "description": "出发城市,例如:北京"}},
      "destination": {{"type": "string", "description": "目的城市,例如:上海"}},
      "date": {{"type": "string", "description": "航班日期,YYYY-MM-DD 格式,例如:2024-12-25"}},
      "passengers": {{"type": "integer", "description": "乘客数量,正整数,最多5人"}}
    }},
    "required": ["origin", "destination", "date", "passengers"]
  }}
}}

你必须严格按照上述工具的参数要求来生成调用。
如果工具返回错误,你必须分析错误信息,理解问题所在,然后修正参数并重新调用工具。
在重试之前,请在你的“思考”中说明你为什么修正了参数以及如何修正的。
如果多次尝试后仍无法成功,请告知用户。

当前日期: {datetime.date.today().strftime("%Y-%m-%d")}
"""

current_prompt = f"{system_prompt}nn用户请求: {user_query}"

for attempt in range(max_retries):
    print(f"n--- Attempt {attempt + 1}/{max_retries} ---")

    # LLM生成工具调用或文本响应
    llm_response = mock_llm_generate_tool_call(current_prompt)

    if isinstance(llm_response, dict) and "tool_name" in llm_response:
        tool_call = llm_response
        print(f"LLM决定调用工具: {tool_call['tool_name']},参数: {json.dumps(tool_call['parameters'], ensure_ascii=False)}")

        tool_call_history.append({"attempt": attempt + 1, "tool_call": tool_call})

        # 执行工具
        tool_result = execute_tool_call(tool_call)
        print(f"工具执行结果: {json.dumps(tool_result, ensure_ascii=False)}")

        if tool_result["status"] == "success":
            return f"航班预订成功!预订详情: {json.dumps(tool_result['booking_details'], ensure_ascii=False)}"
        else:
            # 工具执行失败,需要反馈给LLM进行修正
            error_message = json.dumps(tool_result, ensure_ascii=False)

            # 构建包含错误反馈的新的提示词
            feedback_prompt = f"""

{system_prompt}

用户请求: {user_query}

Tool Call History:
{json.dumps(tool_call_history, indent=2, ensure_ascii=False)}

Tool Execution Error (from last attempt):

{error_message}

请分析上述错误信息,理解为什么工具调用失败了。
在你的“思考”中说明你将如何修正参数,然后生成一个新的工具调用。
"""
current_prompt = feedback_prompt
print("LLM收到错误反馈,准备重试…")
else:

LLM生成了文本响应,或者无法生成工具调用

        return llm_response

return "抱歉,经过多次尝试,我仍无法成功预订航班。请检查您的请求或稍后再试。"

— 演示 —

print("n===== 演示1: 日期格式错误 =====")
user_request_1 = "帮我预订一张明天从北京到上海的机票,3个人。"
print(run_llm_agent(user_request_1))

print("n===== 演示2: 不支持的出发城市 =====")
user_request_2 = "我想预订一张从洛杉矶到上海的机票,2个人,日期是2024年12月25日。"
print(run_llm_agent(user_request_2))

print("n===== 演示3: 乘客数量超限 =====")
user_request_3 = "帮我预订一张从北京到上海的机票,6个人,日期是2024年12月25日。"
print(run_llm_agent(user_request_3))

print("n===== 演示4: 过去或今天日期 =====")
user_request_4 = "帮我预订一张从北京到上海的机票,1个人,日期是今天。"
print(run_llm_agent(user_request_4))


#### 3.3.1 提示词核心结构分析

让我们分解`run_llm_agent`函数中构建的提示词:

1.  **系统角色与通用指令:**
你是一个航班预订助手,能够帮助用户预订航班。
请根据用户的请求,使用下方提供的工具。
...
你必须严格按照上述工具的参数要求来生成调用。
如果工具返回错误,你必须分析错误信息,理解问题所在,然后修正参数并重新调用工具。
在重试之前,请在你的“思考”中说明你为什么修正了参数以及如何修正的。
如果多次尝试后仍无法成功,请告知用户。
```
这部分定义了LLM的身份,强调了工具调用的准确性,并**明确地指示了错误处理的流程和要求LLM进行“思考”**。
  1. 详细的工具描述 (JSON Schema):

    {
      "name": "book_flight",
      "description": "预订航班的工具。参数: ...",
      "parameters": {
        "type": "object",
        "properties": {
          "origin": {"type": "string", "description": "出发城市,例如:北京"},
          "destination": {"type": "string", "description": "目的城市,例如:上海"},
          "date": {"type": "string", "description": "航班日期,YYYY-MM-DD 格式,例如:2024-12-25"},
          "passengers": {"type": "integer", "description": "乘客数量,正整数,最多5人"}
        },
        "required": ["origin", "destination", "date", "passengers"]
      }
    }

    提供工具的完整功能签名和参数的JSON Schema。这是LLM理解工具及其约束的关键。详细的description字段尤其重要,它告诉LLM每个参数的语义和一些基本规则(如日期格式、乘客数量限制)。

  2. 用户请求:

    用户请求: 帮我预订一张明天从北京到上海的机票,3个人。

    保留原始用户意图,确保LLM在修正过程中不会偏离。

  3. 工具调用历史 (用于上下文和避免重复错误):

    Tool Call History:
    [
      {
        "attempt": 1,
        "tool_call": {
          "tool_name": "book_flight",
          "parameters": {
            "origin": "北京",
            "destination": "上海",
            "date": "明天",
            "passengers": 3
          }
        }
      }
    ]

    这一部分至关重要。它提供了一个审计追踪,让LLM知道它之前尝试了什么。在更复杂的场景中,这可以帮助LLM避免重复相同的错误,甚至可以用于实现更高级的“禁止列表”策略(即,如果某个参数组合多次失败,就不要再尝试了)。

  4. 结构化的工具执行错误反馈:

    Tool Execution Error (from last attempt):
    ```json
    {
      "status": "error",
      "message": "参数校验失败。",
      "details": [
        "日期格式错误。请使用 YYYY-MM-DD 格式,例如: 2024-12-25"
      ]
    }

    这是自我修正的“燃料”。我们确保工具的错误返回是JSON格式,包含statusmessagedetailsdetails字段尤其重要,它包含了具体的错误原因和明确的修正建议(如“请使用 YYYY-MM-DD 格式”)。LLM可以更容易地解析和理解这种结构化信息,而不是一段模糊的自由文本。

  5. 明确的修正指令:

    请分析上述错误信息,理解为什么工具调用失败了。
    在你的“思考”中说明你将如何修正参数,然后生成一个新的工具调用。

    这再次强化了LLM的行动指令,并鼓励Chain-of-Thought推理,使其在修正前先“思考”再“行动”。

3.3.2 自我修正的LLM内部机制(模拟)

mock_llm_generate_tool_call函数中,我们模拟了LLM的“思考”过程:

  1. 错误识别: LLM接收到包含Tool Execution Error的提示后,会解析JSON格式的错误信息。
  2. 定位问题参数: 根据details中的描述,LLM识别出是date参数出了问题,因为它收到了“日期格式错误”的提示。
  3. 理解修正建议: 错误信息明确指出了“请使用 YYYY-MM-DD 格式”。
  4. 执行修正: LLM会回顾原始请求“明天”,并结合当前日期和建议格式,将“明天”转换为一个合法的未来日期,例如2024-12-25
  5. 生成新的工具调用: LLM用修正后的参数生成一个新的book_flight工具调用。

这种“思考-修正-重试”的循环是LLM实现自我修正的关键。

四、高级考量与优化

4.1 语义错误(Semantic Errors)的处理

前面提到的“语义理解错误”是最难处理的。当LLM成功调用了工具,但结果与用户的真实意图不符时,工具返回的是成功信息,没有显式错误。

处理策略:

  • 结果确认: 在工具调用成功后,LLM可以尝试向用户总结结果,并询问用户是否满意。例如:“您预订了从北京到上海的航班,日期是2024年12月25日,乘客3人。请问这是您想要的吗?”
  • 多模态或多工具交叉验证: 结合其他工具或信息源对结果进行验证。例如,预订机票后,再查询一下该航班的信息,看是否与用户描述一致。
  • 用户反馈循环: 在生产环境中,收集用户对LLM行为的反馈,可以用于微调LLM或改进其规划逻辑。

这部分通常需要更复杂的代理设计,涉及到对用户意图的持续跟踪和主动的用户交互。

4.2 重试策略与资源管理

  • 指数退避(Exponential Backoff): 在多次重试之间增加等待时间,尤其适用于外部服务暂时性故障。
  • 错误类型特定的重试: 对于参数验证错误,LLM应该立即尝试修正并重试;对于外部服务错误,可以尝试几次后放弃或转人工。
  • 成本考量: 每次LLM调用都会产生费用。限制max_retries至关重要,避免无限重试导致成本激增。
  • 缓存与记忆: LLM可以记住哪些参数组合是无效的,避免在后续会话中重复尝试。

4.3 动态工具描述与参数建议

  • API文档作为上下文: 将更详细的API文档作为LLM的上下文提供,特别是在参数描述中难以完全表达的复杂逻辑。
  • 上下文相关的参数建议: 工具可以根据LLM提供的部分参数,动态地提供下一步的建议。例如,如果LLM只提供了出发城市,工具可以反馈支持的目的城市列表。
  • Enum或List参数: 对于有明确枚举值的参数,可以在工具描述中直接列出,如"enum": ["北京", "上海", "广州"],方便LLM选择。

4.4 可观察性与调试

  • 详细日志: 记录LLM的每次思考、工具调用、工具响应和重试过程,方便开发者调试和优化。
  • 可视化工具: 开发可视化界面来展示LLM的决策路径和工具交互链,有助于理解复杂行为。

五、展望与思考

我们今天探讨的Tool Response Feedback与LLM的自我修正能力,是构建真正智能、鲁棒的AI代理不可或缺的一环。它将LLM从简单的指令执行者提升为具备学习和适应能力的智能体。通过精心设计的提示词,我们能够赋予LLM在面对工具错误时,分析、诊断并自我修复的能力,这极大地增强了LLM在现实世界复杂任务中的应用潜力。随着LLM能力的不断提升和工具生态的日益完善,我们有理由相信,未来的AI代理将能够更加自主、高效地与各种工具进行交互,为人类带来前所未有的智能体验。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注