ARTICLE DETAIL

资讯详情

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

Agent SDK 实战:从工具定义到 LangGraph 编排的 Python 落地指南

Agent SDK 实战:从工具定义到 LangGraph 编排的 Python 落地指南 1. 从能聊到能干活Agent SDK 到底解决了什么很多人第一次接触大模型都是从一个对话框开始的问一句答一句聊得挺热闹。但真到了业务里问题立刻暴露——模型只会说不会做。你让它帮你查一下今天的订单异常它给你编一段听起来很合理的分析你让它把结果写进表格它告诉你我无法直接操作文件。这就是典型的能聊不能干。Agent SDK 这类工具出现的核心动机就是给模型装上手脚。它把大模型的推理能力和外部工具函数、API、数据库、文件系统连接起来让模型不只是输出文本而是能决定我现在该调用哪个工具、传什么参数、拿到结果之后下一步做什么。这个循环就是所谓的Agent Loop智能体循环思考 → 行动 → 观察 → 再思考直到任务完成。我自己的理解是Agent SDK 本质上是三样东西的组合一个推理引擎通常就是大模型本身负责决策。一套工具注册机制把你写的 Python 函数暴露给模型让它知道有哪些能力可用。一个循环控制器管理多轮调用、状态传递、终止条件。为什么现在这件事变得可行了因为模型的原生能力尤其是函数调用 / tool calling成熟了。以前你要靠 prompt 里写一堆请输出 JSON 格式然后自己解析稍微复杂点就崩。现在模型原生支持结构化输出SDK 帮你把模型想调用哪个函数这件事标准化了稳定性完全不是一个量级。那这套东西适合谁我的判断是三类人最该上手一是手里有重复性业务流程、想自动化的后端或数据工程师二是做 AI 应用、需要把模型能力产品化的开发者三是想理解 Agent 底层原理、不想只会调 API 的学习者。如果你只是想让模型帮你写写文案那其实用不上 Agent SDK直接对话就够了。但只要你的场景里出现需要查数据、需要多步操作、需要根据中间结果决定下一步这些特征Agent SDK 就是绕不开的工具。这篇内容我会围绕一条完整的落地链路来讲从环境搭建、工具定义、循环控制到用 LangGraph 做更复杂的编排再到实际业务里踩过的坑。全程用 Python代码可以直接抄。2. 环境搭建别在第一步就埋雷2.1 Python 版本与虚拟环境的选择逻辑Agent SDK 这类库对 Python 版本有硬性要求通常需要 3.9 以上LangGraph 现在更推荐 3.10 或 3.11。我见过太多人卡在版本问题上所以这一步必须说清楚。先说安装。Windows 用户去 python 官网下载安装包时务必勾选 Add Python to PATH这个勾选项决定了你后面能不能在命令行直接敲python。macOS 用户如果系统自带的是 2.x 或者老版本 3.x建议用pyenv管理多版本别去动系统自带的那个容易把系统工具搞坏。Linux 上同理apt install python3装的版本可能偏旧需要自己编译或用第三方源。装完之后验证python --version pip --version如果pip报错或者版本对不上用python -m pip install --upgrade pip来修不要直接敲pip因为多版本环境下pip可能指向另一个 Python。接下来是虚拟环境。这一步很多人嫌麻烦跳过结果就是依赖冲突。我的习惯是每个项目一个独立环境python -m venv .venv # Windows .venv\Scripts\activate # macOS / Linux source .venv/bin/activate激活之后命令行前面会出现(.venv)前缀这时候装的包才只属于这个项目。VS Code 里记得把解释器切到这个虚拟环境否则你终端里装好了编辑器里还是找不到包这个坑我踩过不止一次。2.2 依赖安装与常见报错处理核心依赖其实不多pip install openai-agents pip install langgraph langchain-core pip install python-dotenvpython-dotenv是用来管理密钥的别把 API Key 硬编码在代码里这是基本素养。建一个.env文件OPENAI_API_KEY你的密钥然后在代码里from dotenv import load_dotenv; load_dotenv()加载。安装过程中最常见的几个报错我列个表方便对照报错信息根本原因处理方式No module named agents装错了包名确认是openai-agents而非agentsCould not find a version that satisfiesPython 版本过低升级到 3.10SSL certificate verify failed网络证书问题更新certifi或检查系统时间导入langgraph报循环依赖依赖版本冲突先卸载再按官方推荐版本重装提示如果你在公司内网pip 可能走的是私有源装不上某些包时先问一下运维有没有镜像源别自己瞎折腾半天。还有一个细节langchain-core和langgraph的版本要匹配。LangGraph 迭代很快有时候你照着半年前的教程装API 已经变了。我的建议是装完之后pip freeze requirements.txt锁一下版本团队协作时大家环境一致能省掉大量在我这能跑的扯皮。3. 把业务函数变成 Agent 的工具3.1 工具定义的本质给模型一份能力清单Agent 能不能干活取决于你给它注册了哪些工具。所谓工具说白了就是一个普通的 Python 函数加上一段描述告诉模型这个函数是干嘛的、什么时候该用、参数是什么。先看一个最朴素的例子。假设业务场景是查询订单状态from agents import function_tool function_tool def query_order_status(order_id: str) - str: 根据订单号查询订单当前状态。 Args: order_id: 订单编号通常是 12 位数字字符串。 # 这里替换成真实的数据库查询或 API 调用 mock_db { 202401010001: 已发货, 202401010002: 待付款, } return mock_db.get(order_id, 未找到该订单)关键点在于那个docstring。模型就是靠这段文字来判断用户问订单状态时我该调用这个函数。所以描述要写得像给新同事交接工作一样清楚这个函数做什么、参数含义、返回什么。我见过有人 docstring 写个查询结果模型根本不知道该不该调这就是描述不到位。function_tool装饰器会自动把函数的类型注解和 docstring 转成模型能理解的 schema。类型注解一定要写order_id: str不能省否则模型不知道参数类型容易传错。3.2 参数设计与返回值处理的经验工具的参数设计有几个原则都是踩坑换来的第一参数尽量用基础类型。字符串、数字、布尔值最稳。如果你传一个复杂的嵌套字典模型很容易构造错。如果业务确实需要复杂结构拆成多个简单参数或者让模型先传 JSON 字符串你在函数内部解析。第二返回值要结构化且简洁。模型拿到工具返回结果后要理解它如果你返回一大坨原始数据既浪费 token 又容易让模型抓不住重点。我的做法是返回精简后的关键字段或者返回一个明确的成功/失败标识加数据。第三函数内部一定要做异常处理。工具执行失败时别让异常直接抛出去把整个 Agent 循环打断而是返回一个描述错误的字符串让模型知道这次没成功可以换个方式再试function_tool def query_order_status(order_id: str) - str: 根据订单号查询订单当前状态。 try: # 真实查询逻辑 result do_query(order_id) return f订单 {order_id} 状态{result} except Exception as e: return f查询失败{str(e)}请确认订单号是否正确这样模型收到查询失败之后可能会追问用户订单号而不是直接崩溃。3.3 多工具协作时的命名与边界当你有十几个工具时命名和职责边界就变得极其重要。我建议遵循两个规则命名体现动作和对象query_order_status、create_refund_request、send_notification一看就知道干嘛的。别用handle_data这种含糊的名字。一个工具只做一件事不要写一个万能工具根据参数分支做不同的事。模型面对这种工具时判断成本很高容易选错。拆成多个小工具让模型自己组合。工具数量也不是越多越好。实测下来单个 Agent 挂 5 到 15 个工具比较舒服超过 20 个之后模型选择准确率会下降。如果业务确实复杂考虑用多个专职 Agent 分工而不是堆工具。4. Agent 循环一次完整任务的执行链路4.1 从用户输入到最终答复的完整过程理解了工具我们来看一次完整执行。用 OpenAI Agents SDK 跑一个最小可用的 Agentfrom agents import Agent, Runner from dotenv import load_dotenv load_dotenv() agent Agent( name订单助手, instructions你是一个订单处理助手帮用户查询订单状态、处理退款申请。, tools[query_order_status, create_refund_request], ) result Runner.run_sync(agent, 帮我查一下订单 202401010001 的状态) print(result.final_output)这短短几行背后发生了什么我拆开讲组装上下文SDK 把instructions系统提示、用户输入、工具 schema 一起打包发给模型。模型决策模型判断用户要查订单我应该调用query_order_status参数是202401010001。执行工具SDK 解析出这个调用意图实际执行你的 Python 函数拿到返回值。回传观察结果把函数返回值作为工具执行结果再发给模型。模型生成最终答复模型看到已发货组织成自然语言回复用户。如果任务需要多步比如查订单如果已发货就发个通知那第 2 到第 4 步会循环多次直到模型认为任务完成输出最终文本。这个循环就是 Agent 的心脏。4.2 循环控制什么时候该停循环控制是最容易被忽视、也最容易出问题的地方。默认情况下SDK 会设置一个最大轮次比如 10 轮防止模型陷入死循环。但光靠这个不够我遇到过模型反复调用同一个工具、每次都拿到相同结果、然后继续调的情况。几个实用的控制手段设置最大轮次Runner.run_sync(agent, input, max_turns8)超过就强制停止。在工具返回值里给明确信号如果某个操作已经完成返回操作已完成无需重复调用模型看到这个提示通常会停。instructions 里写清楚终止条件比如当用户的问题已经得到回答时直接输出答案不要继续调用工具。注意不要指望模型永远理性。生产环境里循环次数、超时时间、单次任务成本都要有硬性上限否则一个失控的循环可能烧掉你不少额度。4.3 上下文与状态管理多轮任务里状态管理是个绕不开的话题。简单场景下SDK 会自动维护对话历史你不用管。但一旦涉及用户上一步说了什么、工具返回了什么、现在处于流程哪一步就需要显式管理。我的经验是能靠对话历史解决的就别自己造状态机。只有当流程有明确的分支和阶段比如先验证身份 → 再查询 → 再确认 → 再执行才引入显式状态。这时候 LangGraph 就派上用场了下一节细讲。5. 用 LangGraph 编排复杂工作流5.1 为什么简单 Agent 不够用单个 Agent 加一堆工具能解决大部分问答 单步操作的场景。但真实业务往往更复杂需要按固定顺序走多个阶段、某些阶段要人工确认、失败要能重试、不同条件走不同分支。这时候如果全塞给模型自由决策结果就是不可控——你不知道它下一步会干嘛。LangGraph 的思路是把工作流显式地画成一张图节点是处理步骤边是流转关系。模型只在需要它决策的节点里发挥作用其余流程由代码控制。这样既有灵活性又有确定性。5.2 状态、节点与边的设计LangGraph 的核心概念就三个State状态一个贯穿整个流程的数据结构通常是个字典或 TypedDict所有节点共享。Node节点一个函数接收当前状态返回状态更新。Edge边定义节点之间的流转可以是固定的也可以是条件判断。一个订单处理流程的例子from typing import TypedDict from langgraph.graph import StateGraph, END class OrderState(TypedDict): order_id: str status: str need_refund: bool result: str def check_order(state: OrderState) - OrderState: status do_query(state[order_id]) return {status: status} def decide_refund(state: OrderState) - str: if state[status] 已发货: return refund return notify def process_refund(state: OrderState) - OrderState: # 调用退款逻辑 return {result: 退款已提交} def notify_user(state: OrderState) - OrderState: return {result: 订单状态已通知用户} graph StateGraph(OrderState) graph.add_node(check, check_order) graph.add_node(refund, process_refund) graph.add_node(notify, notify_user) graph.set_entry_point(check) graph.add_conditional_edges(check, decide_refund, { refund: refund, notify: notify, }) graph.add_edge(refund, END) graph.add_edge(notify, END) app graph.compile()这段代码把查订单 → 根据状态决定走退款还是通知这个逻辑固化下来了。模型可以在check_order里做智能判断但整体流程是确定的。5.3 把 Agent 作为图中的一个节点LangGraph 和 Agent SDK 不是二选一而是可以组合。最常见的模式是把 Agent 封装成一个节点让它在图里承担需要智能决策的那一步。def agent_node(state: OrderState) - OrderState: result Runner.run_sync(agent, f处理订单 {state[order_id]}) return {result: result.final_output}这样整个系统就是确定性流程 局部智能的混合体。我个人的偏好是能用代码写死的逻辑就别交给模型模型只负责它真正擅长的部分——理解自然语言、处理模糊输入、生成人类可读的输出。这样系统既稳又省。5.4 条件分支与人工介入节点LangGraph 还有个很实用的能力中断与恢复。比如退款金额超过某个阈值时需要人工审批。你可以用interrupt在流程中间暂停等人工确认后再继续from langgraph.checkpoint.memory import MemorySaver app graph.compile(checkpointerMemorySaver(), interrupt_before[refund])配合 checkpointer流程状态会被保存人工审批完再恢复执行。这个机制在真实业务里价值很大因为很多操作不能全自动必须留个人工闸门。6. 落地时真正会咬人的几个坑6.1 工具描述写不好模型就选错工具这是最高频的问题。两个工具功能相近时模型经常选错。解决办法是在 docstring 里明确写出什么时候用这个、什么时候不要用function_tool def query_order_status(order_id: str) - str: 查询订单的物流和支付状态。 适用于用户想知道订单当前进展。 不适用于用户想修改订单或申请退款那些请用其他工具。 把边界写清楚模型的选择准确率会明显提升。这个技巧是我试了很多次才总结出来的比单纯调 prompt 有效。6.2 参数类型不匹配导致的静默失败模型有时候会把数字传成字符串或者把列表传成单个值。如果你的函数没做类型校验可能不报错但结果不对。建议在工具函数开头做一次显式校验def query_order_status(order_id: str) - str: if not isinstance(order_id, str) or not order_id.isdigit(): return 订单号格式不正确请提供纯数字订单号 ...返回明确的错误提示模型收到后会自动纠正重试。6.3 上下文膨胀与成本失控多轮循环里每一轮都会把历史消息重新发给模型token 消耗是累积的。一个跑了十几轮的任务成本可能是单次对话的几十倍。控制手段工具返回值尽量精简别把整个数据库记录塞回去。设置最大轮次别让模型无限循环。长流程用 LangGraph 拆分每个节点只带必要状态而不是把全部历史都传下去。6.4 异常处理与重试策略外部 API 会超时、数据库会抖动这些都要在工具层处理。我的做法是工具内部做有限次重试比如 2 次仍然失败就返回错误描述让模型决定下一步。不要在工具里无限重试那会把整个 Agent 卡死。import time def with_retry(func, retries2, delay1): for i in range(retries): try: return func() except Exception: if i retries - 1: raise time.sleep(delay)6.5 日志与可观测性Agent 跑起来之后你最大的困惑往往是它为什么这么做。所以从第一天就要打日志记录每次模型决策、每次工具调用、每次返回结果。LangGraph 有内置的 tracingAgent SDK 也能接回调。别等到线上出问题才想起来加日志那时候你连复现都难。7. 从 Demo 到生产还差什么跑通一个 Demo 和上线一个稳定系统中间隔着不少东西。我按优先级列几个必须补的第一密钥和配置管理。别把 Key 写死在代码里用环境变量或配置中心。不同环境开发、测试、生产用不同的 Key 和参数。第二限流与超时。每个工具调用都要有超时整个 Agent 任务也要有总超时。没有超时保护的自动化流程迟早会挂。第三幂等性。涉及写操作下单、退款、发通知的工具一定要考虑重复调用的问题。模型可能因为重试而多次触发同一个操作用唯一请求 ID 做幂等控制。第四灰度与回滚。新流程先小流量跑观察日志和成功率没问题再放量。Agent 的行为有不确定性一次性全量上线风险太大。第五人工兜底。再智能的 Agent 也会遇到处理不了的情况要有一个转人工的出口而不是让用户对着一个卡住的机器人干等。这套东西搭起来之后你会发现 Agent 真正的价值不在于它多聪明而在于它能把原本需要人工重复操作的流程自动化并且处理那些输入不规范、需要理解自然语言的环节。Python 生态在这块的优势很明显——工具函数就是普通函数编排框架成熟调试手段齐全。我自己在实际项目里的体会是先把流程用代码画清楚再决定哪些节点交给模型。很多人一上来就想让模型包办一切结果做出来的东西又贵又不稳。反过来把模型当成流程里的一个智能零件系统的可控性和性价比都会好很多。这个思路转变比学任何具体框架都重要。
返回列表