ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从LLM到智能体:Function Calling实战指南与Agent构建入门

从LLM到智能体:Function Calling实战指南与Agent构建入门 1. 项目概述为什么我们需要 Agent最近和不少刚接触大模型应用开发的朋友聊天发现一个挺普遍的现象大家一上来就直奔“Agent”这个听起来很酷炫的概念但聊深了才发现很多人其实没太搞清楚 LLM大语言模型和 Agent 到底有什么区别。最常见的误解是以为只要调用了 OpenAI 的 API写了个能对话的程序就算是做了一个 Agent。这其实有点像刚学会开车就觉得自己能去跑 F1 方程式了。我自己在构建和部署智能工作流的过程中也踩过不少坑。最深刻的一个体会是LLM 是一个强大的“大脑”但它自己不会“动手”而 Agent 是那个给大脑配上“手”和“脚”并告诉它“何时、何地、如何”去行动的完整系统。这个“配手脚”的过程核心就是Function Calling函数调用。今天我就想从一个一线实践者的角度掰开揉碎地聊聊从理解 LLM 与 Agent 的本质区别开始到如何通过 Function Calling 这个关键技术真正迈出构建实用 Agent 的第一步。无论你是想自动化处理周报还是想做一个能联网查资料、订机票的智能助手搞明白这个基础逻辑都能让你少走很多弯路。2. 核心概念拆解LLM vs. Agent在动手写代码之前我们必须先把概念地基打牢。很多人混淆这两者是因为它们都处理自然语言但它们的定位和能力边界有本质不同。2.1 LLM一个博学但“被动”的文本预测器你可以把 LLM比如 GPT-4、Claude、文心一言等想象成一个在图书馆里泡了十几年、读过互联网上几乎所有公开文本的“超级学霸”。它的核心能力是根据你给它的上文Prompt预测出最可能的下文是什么。它擅长什么回答问题、总结内容、翻译、写诗、编故事、进行逻辑推理。只要你用文字描述清楚任务它就能生成高质量的文字回复。它的局限是什么它活在“文本的世界”里。它不知道今天的天气不能帮你发一封邮件不能查询数据库也不能操作你的电脑。它所有的“知识”都截止于其训练数据的时间点且无法主动与外部世界交互。它更像一个无所不知的“顾问”但你得把问题用文字准确地递进去它再把答案用文字递出来。注意很多人误以为 LLM “理解”了问题。更准确的描述是它基于海量数据中的统计规律生成了最符合你问题语境和人类交流习惯的文本序列。这种“理解”是模式匹配的结果而非真正的认知。2.2 Agent一个拥有“大脑”和“工具”的自主行动者Agent智能体则是一个更完整的“角色”。它通常由几个核心部分组成大脑LLM负责理解用户指令、进行规划、决策和生成自然语言。记忆用于存储对话历史、执行上下文或长期知识。工具集Tools这是关键这是一系列 Agent 可以调用的外部函数比如“搜索网络”、“执行计算”、“调用 API 发送邮件”、“查询数据库”。执行引擎协调以上组件决定何时思考、何时调用工具、如何处理工具返回的结果。所以一个 Agent 的工作流程是这样的用户说“帮我查一下北京明天天气然后告诉我是否需要带伞”。Agent 的“大脑”LLM会分析这个指令并规划步骤“第一步需要调用‘天气查询工具’参数是‘北京’和‘明天’。第二步拿到天气结果比如‘小雨’后我需要推理得出‘需要带伞’的结论。第三步用自然语言组织答案告诉用户。” 然后它的执行引擎就会按这个规划先去调用外部天气 API拿到数据后再把数据喂回给 LLM 生成最终回复。两者的核心区别可以总结为下表特性LLM (大语言模型)Agent (智能体)核心能力文本生成与补全规划、决策、使用工具、执行任务交互范围封闭的文本输入/输出开放的可与外部系统、API、环境交互状态性通常是无状态的每次对话独立通常是有状态的拥有记忆和上下文主动性被动响应你问它答主动规划可分解复杂任务并逐步执行类比一个博学的顾问一个配备了电脑、手机和各种专业软件的助理搞清楚这个区别你就明白了单纯调用 LLM API 完成一次对话你只是请顾问回答了一个问题。而构建一个 Agent你是招聘并装备了一个能独立完成一系列任务的助理。Function Calling就是给你这位“助理”配备标准“工具”并教会它使用说明书的核心机制。3. Function Calling 深度解析Agent 的“工具手”如果说 LLM 是 Agent 的大脑那么 Function Calling 就是连接大脑和外部世界的“神经末梢”和“手”。它是目前主流大模型平台如 OpenAI、 Anthropic实现 Agent 能力的基础协议。3.1 什么是 Function Calling简单说Function Calling 允许开发者向 LLM描述一组它可以调用的函数工具。然后当用户输入一个请求时LLM 并不直接生成最终答案而是会判断“要完成这个请求我需要调用哪个工具调用时需要传入什么参数” 接着它会返回一个结构化的 JSON 数据指明要调用的函数名和参数。开发者拿到这个 JSON 后在自己的代码里真正执行这个函数比如调用一个真实的天气 API并将执行结果再传回给 LLM。最后LLM 结合工具返回的结果生成面向用户的自然语言回答。这个过程的关键在于“声明”与“执行”的分离。LLM 只负责“思考”需要做什么以及怎么做声明而具体的“动手”操作执行由开发者可靠、安全的代码来完成。这既发挥了 LLM 强大的理解和规划能力又避免了让它直接操作外部系统可能带来的不可控风险。3.2 一个完整的 Function Calling 工作流拆解让我们通过一个“查询天气并建议着装”的经典例子把整个流程走一遍。假设我们有一个get_weather工具。步骤 1定义工具Tool Definition在调用 LLM 的 API 时除了常规的对话消息messages我们还需要传入一个tools参数。这个参数是一个列表里面描述了所有可用的工具。对于get_weather描述如下{ type: function, function: { name: get_weather, description: 获取指定城市当前或未来的天气情况, parameters: { type: object, properties: { location: { type: string, description: 城市名称例如北京、上海 }, date: { type: string, description: 日期格式为 YYYY-MM-DD。默认为今天。 } }, required: [location] } } }实操心得description字段至关重要LLM 完全依赖这个描述来判断何时调用该函数。描述要清晰、准确说明工具的用途、适用场景。parameters的描述也要详细这直接关系到 LLM 能否正确提取用户指令中的参数。比如这里强调了location是“城市名称”LLM 就会从“北京明天天气”中提取出“北京”而不会提取成“北京市朝阳区”。步骤 2LLM 分析请求并返回工具调用请求Tool Call我们将用户消息“北京明天天气怎么样”和工具定义一起发送给 LLM例如gpt-4。LLM 不会直接回复“北京明天晴25度”而是会返回类似这样的内容{ role: assistant, content: null, tool_calls: [ { id: call_abc123, type: function, function: { name: get_weather, arguments: {\location\: \北京\, \date\: \2023-10-28\} } } ] }注意这里的content是null因为模型认为需要先调用工具。tool_calls数组里包含了它想调用的函数详情。步骤 3开发者执行函数Execute Function我们的程序接收到这个 JSON 后解析出name是get_weatherarguments是{location: 北京, date: 2023-10-28}。然后我们在后台写好的get_weather函数被调用它可能去请求了和风天气或 OpenWeatherMap 的 API并拿到了真实的天气数据# 伪代码示例 def get_weather(location, date): # 调用真实天气 API api_result call_weather_api(location, date) return { location: location, date: date, condition: api_result[condition], // 例如 晴 temp_max: api_result[temp_max], temp_min: api_result[temp_min], humidity: api_result[humidity] } # 执行 weather_result get_weather(北京, 2023-10-28)步骤 4将结果返回给 LLM 并生成最终回复Send Result Back我们把工具执行的结果以特定的消息格式追加到对话历史中再次发送给 LLM。{ role: tool, content: {\location\: \北京\, \date\: \2023-10-28\, \condition\: \晴\, \temp_max\: 22, \temp_min\: 12, \humidity\: 45}, tool_call_id: call_abc123 // 必须对应步骤2中的调用ID }步骤 5LLM 合成最终答案Final AnswerLLM 看到了工具返回的真实数据现在它就可以基于这些数据生成一个友好、自然的回答了“北京明天10月28日天气晴朗最高气温22度最低气温12度湿度45%是个出门的好天气。”至此一个完整的、基于 Function Calling 的 Agent 交互闭环就完成了。用户得到了一个结合了实时外部数据的准确回答而这一切都源于 LLM 的规划能力和对工具的理解。4. 从零构建你的第一个 Agent实战指南理解了原理我们动手实现一个简单的 Agent。这个 Agent 的目标是成为一个“智能计算助手”它不仅能聊天还能在需要时进行数学计算。我们将使用 OpenAI API 和 Python。4.1 环境准备与依赖安装首先确保你有一个 Python 环境3.8和一个有效的 OpenAI API 密钥。# 创建项目目录并安装必要库 mkdir my_first_agent cd my_first_agent python -m venv venv # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate pip install openai python-dotenv使用python-dotenv来管理你的 API 密钥避免硬编码在代码中。创建一个.env文件OPENAI_API_KEY你的-api-key-here4.2 定义核心工具集我们将定义两个工具calculate执行数学计算。get_current_time获取当前时间模拟一个需要外部信息的工具。在tools.py中定义import math import datetime # 工具1计算器 def calculate(expression: str) - str: 执行一个数学表达式计算。 支持加减乘除(,-,*,/)、乘方(**)、括号和常见数学函数如sqrt, sin, cos等。 参数: expression (str): 数学表达式例如 \(3 5) * 2 / 4\, \sqrt(16)\ 返回: str: 计算结果或错误信息。 # 安全警告在实际生产中直接eval是危险的这里仅用于演示。 # 应使用更安全的表达式解析库如 ast.literal_eval 配合自定义解析器。 try: # 为表达式中的数学函数添加math.前缀使其在安全命名空间内可用 safe_dict {**math.__dict__} # 移除不安全的 builtins safe_dict[__builtins__] {} result eval(expression, {__builtins__: None}, safe_dict) return str(result) except Exception as e: return f计算错误: {e} # 工具2获取当前时间 def get_current_time(timezone: str Asia/Shanghai) - str: 获取指定时区的当前日期和时间。 参数: timezone (str): 时区字符串默认为\Asia/Shanghai\。其他如\America/New_York\。 返回: str: 格式化的日期时间字符串。 try: import pytz tz pytz.timezone(timezone) except ImportError: # 如果未安装pytz使用本地时间并提示 current_time datetime.datetime.now() return f当前本地时间: {current_time.strftime(%Y-%m-%d %H:%M:%S)} (未安装pytz使用本地时间) except Exception as e: return f时区错误: {e} current_time datetime.datetime.now(tz) return current_time.strftime(f%Y-%m-%d %H:%M:%S ({timezone})) # 工具映射字典方便根据名称调用函数 TOOLS { calculate: calculate, get_current_time: get_current_time, } # 工具定义列表用于发送给OpenAI API TOOL_DEFINITIONS [ { type: function, function: { name: calculate, description: 执行数学计算。支持加减乘除、乘方、括号和常见数学函数如sqrt, sin, cos, log等。, parameters: { type: object, properties: { expression: { type: string, description: 需要计算的数学表达式例如 (35)*2, sqrt(16)log(100, 10) } }, required: [expression] } } }, { type: function, function: { name: get_current_time, description: 获取指定时区的当前日期和时间。, parameters: { type: object, properties: { timezone: { type: string, description: 时区名称例如 Asia/Shanghai, America/New_York, UTC。默认为 Asia/Shanghai。 } }, required: [] } } } ]重要警告上面的calculate函数为了演示简单使用了eval()这在生产环境是极其危险的因为它允许执行任意代码。在实际项目中你必须使用安全的数学表达式解析库如ast.literal_eval但只能处理简单字面量或专门的库如numexpr、sympy或者自己编写解析逻辑。这里仅为演示 Function Calling 流程。4.3 实现 Agent 执行引擎这是最核心的部分负责与 LLM 对话、处理工具调用、维护对话状态。创建agent_engine.pyimport os import json from openai import OpenAI from dotenv import load_dotenv from tools import TOOLS, TOOL_DEFINITIONS # 加载环境变量 load_dotenv() class SimpleAgent: def __init__(self, modelgpt-4o-mini, system_promptNone): 初始化一个简单的Agent。 参数: model: 使用的OpenAI模型如 gpt-4o, gpt-4o-mini, gpt-3.5-turbo。 system_prompt: 系统提示词用于设定Agent的角色和行为。 self.client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) self.model model self.messages [] # 设置系统提示词 if system_prompt: self.messages.append({role: system, content: system_prompt}) else: self.messages.append({ role: system, content: 你是一个乐于助人的智能助手可以回答问题和使用工具。当用户的问题需要计算或查询时间时请调用相应的工具。使用工具时请确保参数准确。 }) def chat(self, user_input): 处理用户输入的一轮对话。 参数: user_input (str): 用户输入的消息。 返回: str: Agent的最终回复。 # 1. 添加用户消息到历史 self.messages.append({role: user, content: user_input}) # 2. 调用LLM并告知它可用的工具 try: response self.client.chat.completions.create( modelself.model, messagesself.messages, toolsTOOL_DEFINITIONS, tool_choiceauto, # 让模型自动决定是否调用工具 ) except Exception as e: return f调用API时出错: {e} # 3. 获取LLM的响应消息 response_message response.choices[0].message self.messages.append(response_message) # 将助手的响应可能是工具调用请求加入历史 # 4. 检查响应中是否包含工具调用请求 tool_calls response_message.tool_calls if tool_calls: # 5. 并行执行所有被请求的工具 for tool_call in tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) print(f[Agent 正在调用工具] {function_name}参数: {function_args}) # 调试信息 # 找到对应的工具函数并执行 if function_name in TOOLS: function_to_call TOOLS[function_name] try: # 根据函数签名动态传参 function_response function_to_call(**function_args) except Exception as e: function_response f工具执行出错: {e} else: function_response f错误未知的工具 {function_name} # 6. 将工具执行结果作为一条新消息追加到历史中 self.messages.append({ role: tool, tool_call_id: tool_call.id, content: str(function_response), # 确保内容是字符串 }) # 7. 将包含工具结果的历史再次发送给LLM让它生成最终回复 second_response self.client.chat.completions.create( modelself.model, messagesself.messages, ) final_message second_response.choices[0].message self.messages.append(final_message) # 将最终回复加入历史 return final_message.content else: # 如果没有工具调用直接返回LLM的回复 return response_message.content def get_conversation_history(self): 获取当前的对话历史用于调试或展示。 return self.messages # 主程序入口 if __name__ __main__: agent SimpleAgent(modelgpt-4o-mini) print(智能计算助手已启动输入 quit 或 exit 退出。) print(- * 40) while True: try: user_input input(\n你: ) if user_input.lower() in [quit, exit, 退出]: print(再见) break if not user_input.strip(): continue response agent.chat(user_input) print(f助手: {response}) except KeyboardInterrupt: print(\n程序被中断。) break except Exception as e: print(f发生错误: {e})4.4 运行与测试现在运行你的 Agentpython agent_engine.py你可以尝试以下对话你“3的平方加上4的平方等于多少”助手内部过程LLM 识别出需要计算调用calculate工具参数为expression: “3**2 4**2”。工具返回“25”。LLM 收到结果后回复“3的平方是94的平方是16它们的和是25。”你“现在纽约是几点”助手LLM 调用get_current_time参数为timezone: “America/New_York”。工具返回当前纽约时间。LLM 合成回复。你“你好请介绍一下你自己。”助手LLM 判断无需调用工具直接生成自我介绍。通过这个简单的例子你已经亲手构建了一个具备基础工具使用能力的 Agent。它虽然简单但完整包含了定义工具、LLM 决策、执行函数、反馈结果的完整 Agent 核心循环。5. 进阶技巧与避坑指南当你开始构建更复杂的 Agent 时以下几个方面的经验能帮你节省大量时间。5.1 工具设计的艺术如何写出“好”的工具描述工具描述 (description和参数description) 的质量直接决定了 LLM 调用工具的准确率。以下是一些原则明确意图和边界清晰说明这个工具是干什么用的以及什么情况下应该被调用。例如“查询天气”比“获取信息”好“当用户询问未来三天内的天气时调用此工具”比“查询天气”更精确。使用自然语言同义词在描述中涵盖用户可能使用的多种问法。例如在计算器描述里可以写“当用户请求进行数学计算、算术运算、解方程或需要数值结果时调用此工具。”参数描述要具体对于location参数描述为“城市或地区的完整名称例如‘北京市’、‘New York City’避免使用缩写或简称”可以显著提升参数提取的准确性。处理模糊性如果用户输入“查一下天气”缺少地点参数。你可以在工具定义中设置location为必填这样 LLM 可能会在决定调用工具后再生成一个追问用户地点的回复而不是强行调用一个参数不全的工具。5.2 处理复杂任务多步骤规划与执行单个工具调用很简单但现实任务往往是多步骤的。例如“帮我查一下北京和上海明天的天气对比一下哪里温度更高。”LLM 的规划能力强大的 LLM如 GPT-4能够自动将这种任务分解为步骤1调用天气工具查询北京明天天气 - 步骤2调用天气工具查询上海明天天气 - 步骤3对比两个结果中的最高温度 - 步骤4生成结论。你不需要手动编程分解只需提供好工具LLM 会自动进行规划。ReAct 模式这是 Agent 领域一个经典框架代表“推理Reasoning 行动Acting”。LLM 的输出会交替出现“思考链”一段内部推理文字和“工具调用”。虽然 OpenAI 的 Function Calling 默认隐藏了“思考链”但其底层逻辑是类似的。对于复杂任务你可以在系统提示词中鼓励模型进行逐步推理例如“请逐步思考复杂问题必要时使用工具。在最终答案前可以简要说明你的推理步骤。”5.3 错误处理与稳定性一个健壮的 Agent 必须能处理各种意外。工具执行失败网络超时、API 返回错误、参数无效等。你的工具函数应该返回清晰的错误信息如“天气服务暂时不可用”而不是抛出异常导致整个 Agent 崩溃。LLM 收到错误信息后通常能生成对用户友好的解释如“抱歉暂时无法获取天气信息请稍后再试。”LLM 的“幻觉”调用有时 LLM 可能会误解用户意图调用一个完全不相关的工具或者生成完全不符合工具参数格式的arguments。你的代码需要做防御性检查检查工具名称是否存在。验证参数是否符合 JSON 格式并尝试进行类型转换和默认值填充。对于关键工具可以设置“确认机制”在真正执行前让 LLM 复述一遍它理解的任务和参数用户确认后再执行适用于高风险操作如发送邮件、下单。上下文长度管理长时间的对话会导致messages历史越来越长最终可能超出模型的上下文窗口。你需要设计一个“记忆管理”策略例如只保留最近 N 轮对话。定期对历史对话进行总结压缩将摘要作为新的系统消息。将重要的信息如用户偏好、任务目标存入一个独立的“长期记忆”向量数据库在需要时检索。5.4 系统提示词System Prompt的打磨系统提示词是 Agent 的“人格设定”和“行为准则”。一个好的提示词能极大提升 Agent 的可靠性和用户体验。advanced_system_prompt 你是一个专业、高效且谨慎的智能助理。你的核心能力是使用工具来帮助用户解决实际问题。 请遵循以下原则 1. **准确性优先**对于涉及事实、数据、计算的问题务必使用工具核实不要依赖自身知识可能过时或不准。 2. **分步思考**遇到复杂任务先在内心规划步骤不需要输出然后一步一步执行。 3. **安全边界**你只能使用我提供给你的工具。如果用户请求超出你的能力范围如需要联网但无搜索工具请礼貌说明。 4. **结果验证**对于计算、查询类工具返回的结果在回复用户前可以简单进行合理性检查例如气温是否在合理范围。 5. **交互清晰**如果用户指令模糊缺少必要参数请主动、友好地追问。 你的目标是成为用户最信赖的数字伙伴。 将这个提示词用在初始化SimpleAgent时你会发现 Agent 的行为会更加稳健和可控。6. 常见问题与排查实录在实际开发中你肯定会遇到各种问题。这里记录了几个我踩过的坑和解决方法。问题 1LLM 总是不调用工具而是尝试自己回答。可能原因 1工具描述太模糊或与问题不匹配。检查你的description是否清晰涵盖了用户可能的问题场景。用更具体、场景化的语言重写描述。可能原因 2系统提示词鼓励了“自力更生”。如果你的系统提示词是“你是一个知识渊博的助手”模型可能倾向于展示其内部知识。改为强调“你擅长使用工具来获取准确信息”。可能原因 3模型能力不足。gpt-3.5-turbo在复杂工具调用上不如gpt-4系列可靠。对于关键应用建议使用gpt-4o或gpt-4o-mini。解决方案在 API 调用中可以强制指定tool_choice参数。如果想强制使用某个工具可以设为{type: function, function: {name: calculate}}。但通常更好的方法是优化提示词和工具描述。问题 2工具被调用了但参数总是提取错误。案例用户说“帮我算一下三分之一加四分之一”LLM 调用calculate时参数可能是expression: “三分之一加四分之一”这显然无法计算。原因LLM 有时会原样提取用户口语化的表述而不是转换成标准的数学表达式。解决方案在工具描述中明确参数格式“参数必须是一个标准的数学表达式使用阿拉伯数字和运算符例如 ‘1/3 1/4’。”在工具函数内部做预处理写一个函数将“三分之一”转换为“1/3”。这需要一些简单的自然语言处理规则。使用更强大的模型GPT-4 在理解指令和格式转换上通常比 GPT-3.5 好得多。问题 3多轮对话中Agent “忘记”了之前用工具获取的结果。原因这是上下文管理问题。虽然工具调用和结果都保存在messages历史里但在超长对话中模型可能会“忽略”较早的信息。解决方案在每一轮交互后可以主动将关键信息“提炼”出来以用户或系统消息的形式再次强调。例如在查询天气后除了让模型回复你还可以自动追加一条消息“[系统提示用户已查询北京明天天气结果为晴22度。]” 这能强化模型的短期记忆。问题 4处理需要多个工具顺序执行的复杂指令时Agent 卡住了。场景用户说“查北京天气然后计算比今天高5度是多少。”理想流程调用天气工具 - 得到温度T - 调用计算工具参数为“T 5”。卡住情况LLM 可能在第一步后就直接尝试用文本推理来回答而不发起第二次工具调用。解决方案优化系统提示词明确告诉模型“在一个任务中你可以根据需要多次调用工具”。也可以在设计上让第一个工具天气查询返回结构化的数据如JSON这个数据更容易被第二个工具计算器作为参数引用。不过更复杂的流程通常需要更高级的 Agent 框架如 LangChain、AutoGen来管理状态和控制流。构建一个真正鲁棒、实用的 Agent 是一个迭代的过程。从理解 LLM 与 Agent 的根本区别开始掌握 Function Calling 这把钥匙然后从简单的单工具 Agent 做起逐步处理更复杂的场景、优化提示词、完善错误处理。这条路没有捷径但每解决一个实际问题你对智能体如何“思考”和“行动”的理解就会加深一层。
返回列表