Tool Use 2026-06-08

大模型函数调用的调试技巧

Tool Use Function Calling 调试 LLM

背景

函数调用(Function Calling)是大模型与外部系统交互的桥梁。通过它,模型可以把自然语言请求转换成结构化的工具调用参数。然而在实际开发中,我遇到最多的问题不是“模型不愿调用工具”,而是“调用参数总是差一点”:字段类型错误、缺少必填项、JSON 解析失败,甚至出现无限循环调用。

这篇笔记记录了我常用的调试思路和定位方法,希望能帮助自己少踩坑,也提高排错效率。

核心思路

调试函数调用时,最重要的是把“黑盒调用”变成“可观测链路”。我通常会从三个层面入手:Prompt 层面、模型响应层面、工具执行层面。

  • 1 Prompt 层面:检查工具描述是否清晰、参数命名是否语义明确、是否给出了调用示例。描述越贴近模型训练时见过的模式,输出越稳定。
  • 2 响应层面:打印模型原始输出,确认它真的生成了函数调用,而不是普通文本。很多框架会自动解析失败,但原始输出能暴露真正的问题。
  • 3 执行层面:在工具函数入口处记录参数和返回值,确认问题发生在“参数解析”还是“工具执行”。

示例

我曾经遇到模型反复调用 search_document 工具,却始终不进入总结阶段。后来发现,是因为我在 Prompt 里没有明确告诉它“找到足够信息后应该停止搜索并给出最终答案”。

# 改进前的工具描述
{
  "name": "search_document",
  "description": "在文档中搜索相关信息。"
}

# 改进后的工具描述
{
  "name": "search_document",
  "description": "当用户问题无法直接用已有信息回答时,在文档中搜索一次相关信息。搜索次数不要超过 3 次。"
}

另一个常见问题是参数类型不匹配。比如某个字段定义为 {"type": "integer"},但模型偶尔会传字符串。我的做法是在调用前做一层参数校验和类型转换,并把转换失败的信息反馈给模型,让它重试。

# 参数校验与反馈示例
def invoke_tool(name, args):
    schema = tool_schemas[name]
    try:
        validate(args, schema)
        return execute_tool(name, args)
    except ValidationError as e:
        return f"参数校验失败:{e.message},请根据工具定义重新生成参数。"

总结

函数调用的稳定性很大程度上取决于“边界是否清晰”。工具描述要具体、参数约束要明确、执行结果要有反馈。调试时,务必保留原始响应和调用链路日志,这是快速定位问题的关键。

接下来我打算为工具调用加上统一的 trace id 和结构化日志,方便在复杂 Agent 场景中追踪每一步的输入输出。