☰
AI Agent开发核心:大模型工具调用(Function Calling)实战
2026/10/8 4:43:16 网站建设 项目流程

AI Agent开发绕不开的核心环节,就是把大模型从“只会聊天”变成“能干活”。我这次要分享的是AI Agent系列教程的第三篇,围绕大模型接口与工具调用(Function Calling)展开。上一篇讲了Agent的骨架和规划能力,这次会深入到底层:怎么让模型稳定地调用外部工具,怎么处理接口返回的结构化数据,以及我在实际项目中踩过的那些坑。

这篇内容适合两类人:一是已经跑通了大模型API调用,想给Agent加上“手脚”的开发者;二是想理解工具调用原理,为后面做多Agent协作、复杂工作流打基础的读者。我会从接口协议、工具定义、调用循环、参数校验、异常处理这一条线讲下来,全程配可运行的代码和实测数据,保证你照着敲就能跑出一个会调用工具的Agent。

1. 为什么工具调用是Agent的“手脚”

很多人对Agent有误解,以为它就是个大模型包装壳,用户问一句,模型答一句。真正的Agent是要去执行任务的,比如查天气、订机票、操作数据库、调内部系统接口。可大模型本身只有“大脑”,没有“手脚”,它不联网、不读库、不执行代码,所有动作都停留在“生成文本”这个层面。

工具调用(Function Calling / Tool Use)就是给模型装上手和脚的关键机制。它的思路很直接:事先告诉模型有哪些工具可用、每个工具长什么样(参数结构)、什么时候该用哪个工具。模型在生成回复时,如果判断任务需要调用工具,就会输出一个结构化的“工具调用请求”,里面包含工具名称和参数。你的程序拦截到这个请求,去真实执行对应的函数,再把执行结果回传给模型,模型基于结果组织最终回复。

这一来一回,就是Agent的核心工作循环。我在第一版Agent里就是只调大模型API,发现它连“查天气”这种基础功能都做不到,只会胡编一个天气给你。接入工具调用之后,整个系统的能力边界立刻就不一样了。

1.1 工具调用和普通API调用的本质区别

普通API调用是你写死逻辑:定义一个函数,传参,拿结果。工具的调用则带上了“智能决策”的味道——模型自己根据上下文判断该调哪个工具、该传什么参数。这个判断是基于模型对语义的理解,所以同一个问题换个说法,模型也能选出合适的工具。

打个比方,普通API调用像你用遥控器按按钮,按哪个亮哪个,全程你自己控制;工具调用像你给助手描述需求,助手自己去决定开电视还是开空调。这个“自主决策”的过程,就是Agent智能体区别于普通程序的核心点。

当然,这种自主性也带来了不确定性,你必须做好兜底方案,后文我会详细展开。

1.2 工具调用在Agent架构中的位置

一个完整的Agent架构,通常包含这几层:

  • 感知层:接收用户输入,可能是文本、语音、图片
  • 规划层:拆解任务,决定先做什么后做什么
  • 工具层:提供可调用的外部能力,比如API、代码执行器、数据库查询
  • 记忆层:保存对话历史、任务状态、长期偏好
  • 执行层:把规划转化为具体的工具调用,拿到结果继续决策

工具层和执行层之间,靠的就是大模型接口中的Function Calling机制。规划层决定“要做什么”,工具层决定“能做什么”,Function Calling就是这两者之间的桥梁。你得把工具定义写得够清晰,模型的规划能力才能落地。

2. 大模型接口的技术底座

聊工具调用之前,得先把大模型接口的基础说清楚。现在市面上主流的大模型厂商,API风格基本都向OpenAI的接口规范靠拢,这已经成为事实上的行业标准。你用熟一家,切别的厂商就是改改base_url和密钥的事。

我这边用OpenAI格式的接口来讲解,因为它的文档最全、生态最成熟,而且很多开源模型(比如Qwen系列、GLM系列)也都提供了兼容OpenAI格式的调用方式。

2.1 接口协议基础:认证、请求和响应

大模型接口的核心就是一个HTTP POST请求,访问一个/chat/completions或类似的端点。请求体是一个JSON,里面主要包含这几个字段:

  • model:模型名称,不同厂商模型名不一样,得在文档里确认
  • messages:对话消息列表,每条消息包含role和content
  • temperature:温度参数,控制随机性,数值越大越发散
  • max_tokens:最大输出token数

认证方式几乎清一色是Bearer Token,也就是在请求头里放一个Authorization: Bearer sk-xxxx这样的密钥。大多数服务商还提供了OpenAI SDK包,你只需要设置api_key和base_url两个环境变量就能开始调。

一个最简的接口请求长这样:

from openai import OpenAI client = OpenAI( api_key="你的密钥", base_url="你的接口地址" ) response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是一个智能助手。"}, {"role": "user", "content": "今天天气怎么样?"} ] ) print(response.choices[0].message.content)

这里要注意,虽然很多库会兜底填默认值,但建议你显式设置好每个参数,尤其是temperature,不同任务的合适值差异很大。比如代码生成我倾向用0.1左右,创意写作才会用到0.7以上。

2.2 从聊天补全到结构化输出

普通的聊天接口返回的是纯文本,模型自由发挥的空间比较大。但Agent场景下,程序需要的是能直接解析执行的结构化指令——比如“调用名称为get_weather的工具,参数是{city: 北京}”。让模型用自然语言表达这层意思再让程序去解析,既容易出错又低效。

这时候就需要结构化输出能力。Function Calling的基础版本就是让模型输出一个JSON格式的工具调用指令,程序拿到这个JSON直接反序列化成对象,立刻就能执行。更进一步的还有JSON Mode,可以强制模型只输出合法的JSON,适合那些需要严格结构化数据的场景。

接口层面对应的是tools参数和tool_choice参数:

  • tools:系统支持的工具列表,每个工具包含名称、描述、参数Schema
  • tool_choice:控制模型何时使用工具,有“自动”“必选某个工具”“禁用工具”这几个选项

理解了这两个参数,工具调用的大门就打开了。

3. 工具定义是写给模型看的说明书

工具定义的质量,直接决定了模型能不能做出正确的调用决策。这步最容易被新手忽略,却又恰恰是整个工具调用链路上最重要的一环。我的经验是,花在写工具定义上的时间,至少是写函数本体时间的1到2倍。

工具定义的标准格式是这样的:

{ "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的当前天气信息,包括温度、湿度、风力等", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,如:北京、上海、广州" } }, "required": ["city"] } } }

看到没有,这里面有一个容易忽视的字段叫description。这可是模型判段要不要调用这个工具的重要依据。描述写得像“获取天气信息”这种笼统的话,模型很容易和别的工具搞混。你得写清楚“什么情况下用这个工具”“它接收什么参数”“返回什么数据”。

3.1 参数Schema的正确书写姿势

参数Schema用的是JSON Schema规范,这其实就是一套描述“数据长什么样”的标准语言。初学者最容易犯的错是把参数写得过于粗糙,导致模型要么不会填、要么乱填。

几个关键要求:

  • 每个参数必须有type,包括嵌套对象里的属性也要标注
  • 每个参数必须有description,这直接影响模型填参的准确性
  • required数组必须准确列出必填字段,漏了会导致调用时缺参数
  • 枚举值用enum列出来,模型会严格遵守,这能磨掉很多异常情况
  • 嵌套对象要完整展开结构,不要只写一个"object"就完事

比如你要设计一个获取天气的工具,city参数你加一句“城市名称,如北京、上海,不要加‘市’字”,模型就知道传参时别带后缀。这种细节,实测能让参数准确率提升好几个百分点。

3.2 工具粒度与命名规范

工具不是越少越好,也不是越多越好。工具粒度的拿捏,我总结下来是“独立、内聚、语义清晰”这三个词。

  • 独立:每个工具只做一件事,不要把“查天气然后订机票”塞进一个工具里
  • 内聚:强相关的操作放一起,比如“获取用户信息”和“更新用户信息”可以合成一个用户服务,但保留为两个独立tool
  • 语义清晰:命名要用动词开头,比如get_weather、send_email,不要用weather或util1这种模糊名字

工具数量上,我建议一次对话最多传10到20个工具定义。太多了模型会挑花眼,响应延迟也会明显上升。如果工具数量很大,可以先做一层分类,或者写一个“路由工具”作为入口,让模型先选分类再选具体工具。

3.3 我常用的工具定义模板

不同业务场景的工具定义大同小异,我给你一个能直接改着用的模板:

tools = [ { "type": "function", "function": { "name": "execute_sql", "description": "执行只读SQL查询,用于从业务数据库获取数据,仅支持SELECT语句", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "完整的SQL查询语句,必须以SELECT开头" }, "limit": { "type": "integer", "description": "返回结果的最大行数,默认10,最大100", "default": 10 } }, "required": ["query"] } } }, { "type": "function", "function": { "name": "get_current_time", "description": "获取当前的日期和时间,精确到秒,常用于需要计算时间的场景", "parameters": { "type": "object", "properties": {} } } } ]

这个模板里我特意展示了一个无参数工具get_current_time。很多开发者不知道,无参工具也是常用场景,properties设成空对象{}就行。模型会知道这类工具不需要传任何参数。

4. 深度拆解工具调用的运行流程

现在我们正式进入工具调用的核心环节。理解了整个调用循环,你才能自己动手写一个Agent框架,而不是只会调现成的库。

4.1 工具调用的完整循环

一个标准的工具调用周期,分为下面的步骤:

  1. 用户发送请求,附带系统Prompt和工具列表
  2. 模型判断需要调用工具,返回一个tool_calls结构
  3. 程序解析tool_calls,拿到工具名和参数
  4. 程序执行对应函数,拿到执行结果
  5. 程序把工具执行结果作为一条新的消息传给模型
  6. 模型基于工具的返回结果,生成最终回复给用户

这个过程可以用下面这段代码完整演示出来:

import json from datetime import datetime # 模拟执行工具 def get_weather(city: str) -> str: # 这里接真实API,我做了一个模拟返回 return json.dumps({"city": city, "weather": "晴", "temperature": 24}, ensure_ascii=False) def get_current_time() -> str: return json.dumps({"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}) # 工具注册表,实际项目中建议用装饰器自动收集 tools_map = { "get_weather": get_weather, "get_current_time": get_current_time } # 第一次请求 messages = [ {"role": "system", "content": "你是一个智能助手,可以帮用户查询天气和时间。"}, {"role": "user", "content": "北京现在天气怎么样?"} ] response = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=tools, tool_choice="auto" ) assistant_message = response.choices[0].message print(assistant_message)

跑完这段代码,你会发现assistant_message.tool_calls参数里装着模型决定要调用的工具信息。它的结构大致是:

ToolCall(id='call_abc123', function=Function(arguments='{"city":"北京"}', name='get_weather'), type='function')

拿到这个结构,接下来就要通过遍历工具调用、执行函数、组装新消息这几个步骤完成整个循环。

4.2 工具执行与结果回传

工具调用的结果回传过程,是新手最容易写错的地方。正确的做法是把工具执行结果放进一条role=tool的消息里,并用tool_call_id把这条消息和刚才的工具调用关联起来。

代码继续写下去:

# 组装工具消息 tool_messages = [] for tool_call in assistant_message.tool_calls: func_name = tool_call.function.name func_args = json.loads(tool_call.function.arguments) func_result = tools_map[func_name](**func_args) tool_messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": func_result }) # 把助手消息和工具消息都追加到历史里 messages.append(assistant_message) messages.extend(tool_messages) # 第二次请求,让模型看到工具结果后组织最终回复 final_response = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=tools ) print(final_response.choices[0].message.content)

注意几个细节:

  • assistant_message必须整个追加进messages,不能只把content加进去,否则后面的对话会丢失工具调用的上下文
  • tool_call_id必须和之前返回的id一模一样,这是消息关联的凭证
  • 工具执行结果建议用JSON字符串返回,结构化数据比自然语言好解析

4.3 多次工具调用的场景处理

一个复杂任务可能触发多次工具调用。比如用户问“北京天气如何,顺便告诉我今天几号”,模型可能会同时发起两个工具调用。OpenAI格式的接口支持一次返回多个tool_calls,你的程序要循环处理,把每个工具的结果都组织成独立的tool消息。

更复杂的情况是链式调用:第一次工具调用的结果,模型看了之后又需要调用另一个工具。比如用户问“北京和上海哪个更冷”,模型先调get_weather(北京)、get_weather(上海),拿到结果后可能觉得需要额外查一下风力,又调get_weather_wind(北京)。

实现链式调用,就是把这套循环包进一个while里,直到模型不再返回tool_calls为止:

while True: response = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=tools ) assistant_message = response.choices[0].message messages.append(assistant_message) # 没有工具调用了,说明可以返回最终结果 if not assistant_message.tool_calls: return assistant_message.content for tool_call in assistant_message.tool_calls: # 执行工具,组织tool消息 ...

这个while True就是Agent执行循环的雏形。等到后面加上了任务规划、自我反思,它就是Agent的主干框架了。

5. 实操:从零搭建一个能调用工具的Agent

理论部分讲了不少,现在我把一套完整的实操过程走下来。我们会搭建一个能在命令行里对话的Agent,它支持查询天气和当前时间,并且能处理多轮对话。

5.1 环境准备与依赖安装

第一步,确保环境里装好了Python 3.10以上版本,然后安装OpenAI SDK:

pip install openai

注意,新版OpenAI SDK(1.x版本)的接口和旧版0.x差异很大,很多老教程的代码直接跑不通。我建议直接用最新的:

pip install --upgrade openai

装好后设置环境变量:

export OPENAI_API_KEY="你的密钥" export OPENAI_BASE_URL="你的接口地址"

如果你用的是OpenAI官方服务,OPENAI_BASE_URL可以不设。但你要是接了国内厂商的兼容接口或者本地部署的模型服务(比如vLLM、Ollama),这个就必须设对了。很多开发者卡在这一步,建议先在别的API调试工具里确认接口地址能通,再来做开发。

5.2 Agent类的设计与实现

我习惯把Agent封装成一个类,核心结构如下:

class ToolAgent: def __init__(self, model="gpt-4o-mini", tools=None): self.client = OpenAI() self.model = model self.tools = tools or [] self.tools_map = {} def register_tool(self, name, func, tool_def): self.tools.append(tool_def) self.tools_map[name] = func def run(self, user_input, system_prompt="你是智能助手。"): messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_input} ] while True: response = self.client.chat.completions.create( model=self.model, messages=messages, tools=self.tools, tool_choice="auto" ) assistant_message = response.choices[0].message messages.append(assistant_message) if not assistant_message.tool_calls: return assistant_message.content for tool_call in assistant_message.tool_calls: func = self.tools_map.get(tool_call.function.name) if not func: # 工具不存在,返回错误信息给模型 messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": "错误:不存在的工具" }) continue try: args = json.loads(tool_call.function.arguments) result = func(**args) result_str = json.dumps(result, ensure_ascii=False) except Exception as e: result_str = f"工具执行失败:{str(e)}" messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result_str })

这个类里有两个地方是实际项目中一定要做到的:

  • 工具执行必须做异常捕获,不能让一个工具挂了整个Agent就崩溃
  • 工具不存在或者执行失败,要以错误信息的形式回传给模型,让模型知道“这个事没办成”,它才能做出下一步决策

5.3 实际运行效果与参数调节

注册两个工具进去,跑一下实际效果:

agent = ToolAgent() agent.register_tool( name="get_weather", func=get_weather, tool_def={ "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的当前天气信息", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"] } } } ) agent.register_tool( name="get_current_time", func=get_current_time, tool_def={ "type": "function", "function": { "name": "get_current_time", "description": "获取当前的日期和时间", "parameters": {"type": "object", "properties": {}} } } ) print(agent.run("北京现在天气怎么样?")) print(agent.run("现在几点了?"))

实测跑下来,模型能准确选出对应工具并正确填空。这里有一个参数调节经验:temperature默认是1.0,但在工具调用场景下建议调到0.2左右,太高容易让模型在参数上“自由发挥”,导致参数格式不标准。

5.4 上下文管理与记忆增强

上面这个Agent有个致命缺陷:每轮对话只传了本轮用户输入,没有保留历史。实际项目里,用户可能来回好几轮,跳到第5轮还在问第3轮提到的某个信息。所以我们必须把messages历史维护起来。

简单方案是把run方法里的messages抽成类成员,每轮结束把整个messages存进去:

class ToolAgent: def __init__(self, ...): self.messages = [] self.max_history = 20 # 限制历史轮数,防token爆炸 def run(self, user_input): self.messages.append({"role": "user", "content": user_input}) # 历史太久远的消息裁剪掉 if len(self.messages) > self.max_history * 2: self.messages = [self.messages[0]] + self.messages[-self.max_history * 2:] while True: # 同上,使用self.messages ...

注意裁剪策略有讲究:system消息要保留,裁剪从用户消息开始裁。这步看着不起眼,实际做长对话时没有它,token消耗会直线上升,而且模型容易“遗忘”最初的指令。

6. 常见问题与排查技巧实录

工具调用看着简单,实际跑起来坑比想象中多。我整理了几个高频问题,每个都是从实际项目里捞出来的,有问题的现象、排查路径和最终解法。

6.1 模型返回空参数或者缺少必填字段

现象:模型发起了工具调用,但arguments是{}或缺少必填字段。解析时直接KeyError,程序报错。

排查思路:

  1. 检查工具定义的description是否写得足够清楚,特别是参数部分
  2. 检查required数组有没有正确列出所有必填参数
  3. 看看是不是模型版本太老,不支持复杂的参数Schema

我遇到过一种情况:参数是嵌套对象,模型老是只填外层不填内层。解决方法是把参数打平,降低嵌套深度,模型填参的准确率立刻上来了。

6.2 模型重复调用同一个工具导致死循环

现象:Agent卡在一个while True里出不来,反复调同一个工具,而且返回的结果一模一样。

排查思路:

  1. 检查工具执行结果是否每次都一样,如果是,考虑加入随机性或缓存标记
  2. 检查工具出错后回传的错误信息,如果错误信息太笼统,模型无法判断下一步该怎么修正,就会反复尝试同样的操作

我的解法是给错误信息加上足够多的上下文。比如“执行失败,城市参数city的值无效,可尝试查询city的备选名”,模型看了才能调整策略,而不是原地打转。

同时,建议在循环外层加一个最大迭代次数(比如5次),超过就强制退出并返回提示。这是最保险的防线。

MAX_ITERATIONS = 5 current_iteration = 0 while True: if current_iteration >= MAX_ITERATIONS: return "已经进行了多次工具调用仍未完成任务,请检查并重试。" current_iteration += 1 ...

6.3 工具调用结果格式不一致

现象:有些工具返回纯文本,有些返回JSON字符串,有些返回Python对象。模型在组织最终回答时,经常被冗杂的格式搞得晕头转向。

我的建议是:所有工具统一返回JSON字符串。这个约定能减少模型的理解负担,也方便调试。工具内部就算业务逻辑复杂,最后返回前也做一次json.dumps。

注意,JSON里不要放太长字段值。比如数据库查询返回1000行数据,塞给模型之前要先做截断或者聚合,否则一次调用的token消耗就是灾难。

6.4 工具错误处理不当导致“崩溃链”

现象:工具内部抛了异常,程序没捕获,整个Agent进程崩了。

排查思路:看异常是哪个工具抛出来的,给那个工具补充异常捕获,并让上层兜底。

我建议在工具函数内部主动捕获并返回错误信息,不要让它抛出去。同时在上层的工具调用循环里也再包一层try-except,双层防护。实践下来,这种“错误隔离”让Agent的稳定产出提高了不少。

6.5 工具定义过多导致模型“选择困难症”

现象:工具列表从10个增加到30个以后,模型选择工具的准确率肉眼可见地下降,还经常调错。

排查思路:工具太多,模型的注意力被分散了。这不能怪模型,这是attention机制的物理限制。

解法是分级路由:先让模型调用一个“总入口工具”,它根据用户意图去加载下一级的子工具列表。这样每一层暴露给模型的工具数量控制在10个左右,准确率就稳住了。

6.6 兼容性问题:不同模型对Function Calling的支持差异

这个点很现实。OpenAI的GPT-4系列支持完整Function Calling,但很多开源模型或者小参数的模型,要么支持不完整,要么输出格式不稳定。我实测过几个模型,表现差异很大:

模型Function Calling支持参数准确性稳定性
GPT-4o完整支持高稳定
GPT-4o-mini完整支持中高稳定
Qwen2.5-72B支持高较稳定
Qwen2.5-7B基本支持中偶有格式错误
Llama-3.1-8B部分支持低不稳定

结论是:生产环境优先选支持完整Function Calling的模型,小模型可以做测试验证,但不要贸然上生产。如果必须用小模型,只能走提示词解析原生输出的老路,自己做JSON解析和校验。

7. 工具调用的进阶玩法与思考

基础的工具调用跑通之后,这个机制能玩出的花样远超你的想象。我这里分享几个我实测过的进阶方向,每一个都能显著扩展Agent的能力边界。

7.1 多工具协作:让Agent自己编排工作流

前面讲了单工具和简单链式调用,但真实业务很少这么简单。比如用户问“本季度销售数据和去年同期对比怎么样”,Agent需要先调query_sales_data再调query_sales_last_year,可能还要调calculate_growth_rate,最终组织成报告。

多工具协作的关键在于工具之间的“数据契约”。上一个工具的输出,能不能作为下一个工具的输入,得在设计工具时就想清楚。我常用的做法是:让每个工具返回统一的JSON结构,比如都包含status和data两个字段,这样模型面对不同工具的结果时,处理逻辑是一致的。

7.2 并行调用:同时抓多个数据源

OpenAI接口支持一次返回多个工具调用,这意味着Agent可以先并行拉取几个独立的数据源,再把结果汇合。

比如问“给我推荐北京和上海适合周末去的餐厅”,模型可以同时调search_restaurants(city=北京)和search_restaurants(city=上海),然后再汇总。这比串行调用快了一倍,用户体验提升明显。

值得注意的细节是被并行调用的工具彼此要确实独立,如果B依赖A的输出,就不能并行,得排队队调。

7.3 自定义工具的常见类型

实际项目里,工具的类型比想象中丰富。除了最常见的HTTP API封装,还有下面这些:

  • 代码解释器:execute_python_code,让Agent能动态写代码解决计算问题
  • 向量检索:search_memory_documents,接入RAG,给Agent提供领域知识
  • 数据库查询:query_database,但要严格控制只能执行SELECT,防止注入
  • 用户交互确认:ask_user_confirmation,这在执行敏感操作前非常有用
  • 子Agent调度:spawn_sub_agent,复杂任务拆给专门的子Agent去跑

每一种工具的封装难度和风险不一样,从架构上把它们的权限分开管理是很有必要的。

8. 写在最后的踩坑血泪史

工具调用这块我前后迭代了四五个版本,踩过的坑写出来能凑一篇长文。这里挑几个最有价值的经验分享,希望能帮你少走弯路。

第一个教训是:别让工具返回“自然语言”。早期版本里,我有个工具返回“查询成功,结果如下:xx”,模型在组织回答时经常把这句提示语也带上,导致用户看到的回复里有莫名其妙的“查询成功”。改成纯JSON输出之后,这个现象彻底消失了。

第二个教训是:工具定义务必版本化。工具参数变了、字段废弃了,要留有余地。我有一次改了工具参数的字段名,结果已部署的Agent全乱套了,排查了半天才发现是新旧工具定义混用了。现在我在工具定义里加version字段,改动时做兼容映射,情况好多了。

第三个教训是:不要信任模型的参数,一定要校验。就算模型选对了工具,参数也可能不合法。比如execute_sql工具,接进来的SQL如果没做只读校验,Agent可能会执行写操作。我在工具执行前加了白名单校验和语句类型检查,防止Agent被注入带偏。

第四个教训:日志记录比调试器好用。加了工具调用之后,异步推进的调用栈非常复杂,单步跟踪不太现实。我在关键节点打了结构化日志,每条日志带上消息ID、工具名、参数摘要、执行耗时。排障时拉出日志一看,问题在哪一步一目了然。

工具调用这块内容就讲到这。如果你照着把上面这套循环跑通了,你的Agent就已经具备“指挥外部工具干活”的基本能力。下一步可以试试给Agent接入真实的业务API、加上记忆能力,或者搭一个前端页面让大家直接对话。这些都是很自然的延伸,遇到新的坑,欢迎再交流。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询