最近总有人在群里问Claude Agent SDK到底是干嘛的,怎么别人都在聊“编程控制AI助手”,我却还在傻傻地一次一次复制粘贴对话。我拿它做了几个自动化小项目之后,最大的感受是:这个SDK不是把聊天界面搬到代码里,而是把AI从一个“你问一句它答一句”的聊天框,变成了一个能自己规划、自己动手、自己检查结果的工作流引擎。
这篇文章我想从一个实际开发者的角度,把它们最核心的“Agent执行循环”怎么运作、代码怎么写、坑往哪儿踩,整理成一份可以直接照着做的笔记。适合已经用过普通模型API、想进一步做自动化任务的开发者,也适合想搞明白“Agent到底和普通API调用有什么区别”的初学者。
1. 先搞清楚:Agent SDK到底解决什么问题
过去我们写AI应用,最常见的模式是“构造请求,发出去,拿结果”。一次prompt配一个response,模型没有记忆、没有工具、没有自主决策,所有的流程分支逻辑都得你自己在代码里用if-else写好。比如你想让AI“查一下本周销售数据,总结异常,然后发一封提醒邮件”,你得拆成好几轮调用,每轮的结果自己解析、自己传参、自己判断下一步——麻烦,而且AI的中间推理能力完全没用上。
Agent SDK改变的是这件事:你给模型一个目标,告诉它能用哪些工具,然后它自己决定调用哪个工具、传什么参数、看到结果后下一步干什么。模型不再是一个“单次问答器”,而是一个有规划能力的执行体。
一个比较好懂的类比:普通API像一个遥控器,你按一次,电视换一个台;Agent模式像一个实习生,你布置一个“帮我把市场部上周的数据整理成PPT”的目标,它会自己打开表格文件、翻数据、找模板、生成图片,遇到缺数据还会回头问你。你盯住目标和结果就好,过程交给它。
1.1 从API调用到Agent范式的转变
这个转变不光是接口形态变了,而是思维模式变了。传统API调用,状态完全由你维护:用户说了什么、AI回了什么、下一步要调用哪一个逻辑,全在业务代码里。而Agent SDK把这套“维护状态”的活儿拿进了模型循环本身,你的代码从“流程控制者”退居为“资源提供者”。
具体来说,SDK内部运行着一个被叫做“Agent循环”的机制,它大致长这样:
- 把系统提示、对话历史、当前可用工具发给模型。
- 模型返回一个回复,这个回复可能是给用户的最终文本,也可能是一组“工具调用请求”。
- 如果是工具调用请求,你的代码去执行对应函数,拿到执行结果。
- 把执行结果放回对话上下文,继续发给模型。
- 模型根据新的信息再决定是继续调用工具,还是给出最终答复。
这个循环会一直重复,直到模型认为任务完成。你不再需要预先编排“第一步做什么、第二步做什么”,因为模型在每轮都会根据实时结果重新决策。这种范式在一些复杂、无法预先枚举分支的任务里有天然优势。
1.2 它和你熟悉的Chat Completions API有什么不同
很多看过OpenAI或Anthropic基础接口的开发者都会问:我直接调/v1/messages接口,传入历史消息,也能模拟多轮对话,为什么还需要专门的Agent SDK?
我用一个表格把我实际使用中的感受对比出来:
| 对比维度 | 普通消息接口 | Agent SDK |
|---|---|---|
| 基本单位 | Single request-response,一次性 | 多轮Agent循环,自动迭代 |
| 状态管理 | 完全由外部代码保存 | 上下文在循环中自动维护 |
| 工具调用 | 需要手动处理tool_use块,再回传tool_result | SDK帮你在循环中完成大部分编排 |
| 决策能力 | 只回答当前请求 | 能规划步骤、动态调整策略 |
| 适用场景 | 简单问答、一次性文案生成 | 需要调用系统、处理数据、多步骤任务 |
| 开发成本 | 流程都自己写,代码多但可控 | 上手简单,但要理解循环逻辑 |
| 调试方式 | 看一次请求的入参出参 | 要追踪多轮工具调用的中间状态 |
在真实项目里,二者其实可以混用。很多复杂的业务系统,外层用Agent做决策规划,内层某些单点任务仍然用普通接口甚至专用模型做。Agent SDK的意义在于把“多轮规划+工具执行”这部分脏累活标准化了,而不是彻底取代传统API。理解了这一点,下面再上手就不容易发懵。
2. 动手前的准备工作
我假设你已经注册好了模型API的访问权限,并且拿到了API key。这个SDK目前主要支持Python和TypeScript,下面我用Python举例——因为我日常的自动化脚本大多跑在Python 3.10+的环境里。
2.1 环境与依赖安装
先创建虚拟环境,避免依赖冲突影响其他项目:
python -m venv venv source venv/bin/activate # Windows下是 venv\Scripts\activate pip install claude-agent-sdk anthropic python-dotenv安装完确认版本号没问题:
pip show claude-agent-sdk我遇到过不少人直接pip install claude-agent-sdk装的是别的同名旧包,装完导入报错。装之前建议先看一下包的信息确认是对的版本,我遇到过直接把别人的依赖包或者同名旧包装了进来。另外,如果是在国内网络环境,pip源可能需要配置,关于网络环境的设置这里就不展开了,按你自己的网络习惯处理即可。
2.2 密钥管理与基础配置
API key不要写死在代码里。我一般用项目根目录的.env文件来放所有环境变量,然后用python-dotenv加载:
from dotenv import load_dotenv import os load_dotenv() ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") MODEL_NAME = os.getenv("CLAUDE_MODEL", "claude-tool-use-test-model")模型名选型上,我建议优先选支持工具调用的模型版本。不同模型的工具调用能力有差异,某些老模型只能回复文本。SDK通常也会在能力检测时候判断当前模型是否支持工具调用,不支持会直接抛错。
提示:
.env文件一定要加进.gitignore,这个属于基本素养,但每次发Git仓库的时候总会有人不小心把key带出去。不要问我怎么知道的。
3. 核心实操:第一个Agent程序的完整流程
正式开始之前,先理解一下SDK里最核心的几块拼图:客户端、模型、工具、执行循环。SDK面对的“工具”不只是内置能力,你可以给它注册任意Python函数,模型会根据你的描述和参数schema自行决定是否调用。
3.1 最小可运行的Agent循环
下面这段代码,是我习惯的“最小可用”结构。它做的事是:创建一个能访问两个工具(一个是查询订单状态、一个是模拟发送通知)的Agent,然后让模型自己决定要不要调用它们:
import os from dotenv import load_dotenv from claude_agent_sdk import Agent, tools load_dotenv() # 工具1:查询订单状态 def get_order_status(order_id: str) -> str: """根据订单号查询当前订单的处理进度""" status_map = { "A001": "已支付,待发货", "A002": "已发货,运输中", "A003": "已签收", } return status_map.get(order_id, "订单不存在") # 工具2:发送催付通知 def send_payment_reminder(order_id: str, message: str) -> str: """向指定订单的下单用户发送提醒通知,返回发送结果""" return f"已向订单 {order_id} 发送提醒: {message}" # 用tools装饰器把函数注册给模型 @tools.register def get_order_status(order_id: str) -> str: ... @tools.register def send_payment_reminder(order_id: str, message: str) -> str: ... # 创建Agent客户端 agent = Agent( model=os.getenv("CLAUDE_MODEL", "claude-tool-use-test-model"), system_prompt=( "你是一个电商客服助手。当用户咨询订单状态时,先调用get_order_status查询。" "如果订单未付款或状态异常,调用send_payment_reminder发送提醒。" ), tools=[get_order_status, send_payment_reminder], ) result = agent.run("用户问订单A001现在什么状态?") print(result.text)跑一下看看输出,模型大概率会先调用get_order_status("A001"),拿到“已支付,待发货”的结果后,再根据你的system prompt决定是否继续发提醒。这个“决定-执行-观察-再决定”的过程,就是Agent和普通API最大的区别所在。
3.2 工具定义:让Agent有手有脚
上面示例里的@tools.register是一种简化写法,它背后做了几件事:
- 把函数的签名、参数类型、docstring解析成模型能看懂的JSON Schema描述。
- 把函数名作为工具名暴露给模型。
- 执行时用参数校验保护你的函数。
你需要特别留意:模型的工具调用是一个“意图声明”,而不是实际执行。它返回的是一段类似{"name": "get_order_status", "arguments": {"order_id": "A001"}}的结构,你的代码要真正去执行函数,并把结果写回上下文。我在用普通API手写工具调用时经常在“执行结果怎么格式化回传”上栽坑,Agent SDK把这一截封装好了,但如果你想深度控制,也可以拿到原始的工具调用请求自行实现。
关于工具定义,有三个容易忽略但很重要的点:
- 函数的docstring会被模型当作工具说明。写清楚功能、参数含义、返回值含义,模型调用的准确率会明显提升。写成“查询订单状态”比“查询数据”好用得多。
- 参数要尽量原生类型。SDK底层会用JSON Schema描述你的函数签名,类型越明确,模型越不容易传出奇怪的值。能用
str、int、bool就用这些基础类型,少用自定义对象。 - 工具数量不是越多越好。模型同时可选的工具太多时,偶尔会选错,尤其是名字相似。我实际测试中,一次会话里给5到8个工具时决策最稳。超过10个,误调用率就开始抬头了。
3.3 规划-执行-观察循环的完整走查
为了让调试更清楚,我建议新手先手动实现一遍Agent循环,再去用SDK的高级封装。伪代码思路大概是:
messages = [initial_user_message] while True: response = client.create_message( model=model, messages=messages, tools=tool_definitions, tool_choice="auto", ) messages.append(response.assistant_message) if response.stop_reason == "tool_use": for tool_call in response.tool_calls: result = execute_tool(tool_call) messages.append({ "role": "tool_result", "tool_call_id": tool_call.id, "content": result, }) continue if response.stop_reason == "end_turn": print(response.text) break这个循环就是Agent SDK内部的核心逻辑。你在SDK上做的任何封装,本质上都是对这个循环的扩展:加日志、加中断控制、加人工审批、加预算上限。
我建议你至少手动实现一次这个循环,不是为了生产环境用它,而是为了理解token怎么消耗、中间状态怎么流转、模型哪些环节容易出问题。掌握了循环原理,后面用高层API时遇到诡异问题也不会一头雾水。第二次改造我往往会把循环包成一个“可暂停的生成器”,每一轮都暴露中间状态,这样既能继续用SDK,又能做人工介入。
4. 进阶玩法与关键细节
基础能跑通之后,真正的工程化改造才刚刚开始。下面这几个方向是我在项目里反复打磨过的地方,也是从“demo能跑”到“能稳定上线”之间最大的差距所在。
4.1 让输出像流水一样:流式响应的处理
普通调用时,你要等模型把整段回答生成完才能收到结果。在Agent场景里,一次任务可能要经过好几次工具调用,每一轮模型都可能生成几百到几千个token,全等齐再返回会非常慢。
SDK支持流式响应(SSE事件流),体验上模型是“边想边说”的:
stream = agent.run("帮我整理一下这周的项目进度周报", stream=True) for event in stream: if event.type == "text_chunk": print(event.text, end="", flush=True) elif event.type == "tool_use_start": print(f"\n[调用工具] {event.tool_name},参数: {event.arguments}") elif event.type == "tool_use_result": print(f"\n[工具返回] {event.result}")流式模式下你能实时看到模型调用了哪个工具、参数是什么、返回了什么、下一步要干嘛。这不仅是体验上的提升,更是调试和用户交互的关键。比如你在做一个自动化周报系统,用户在界面上能看到Agent“正在查数据、正在生成图表、正在撰写结论”的节奏,而不是干等十秒后突然蹦出一篇长文。
注意:流式事件在处理长输出时,注意要把文本片段拼接完整再落库或展示,片段边界不一定在标点符号上,直接截断容易切出残句。
4.2 上下文要省着用,否则很快“失忆”
Agent每执行一次工具调用,工具返回的结果都会拼接到上下文中。如果工具返回一个几千行的数据表格,下一轮再让模型分析,这轮“历史”就会占掉大量上下文窗口。
我常用的策略有几个:
- 给工具加“摘要模式”。比如数据库查询工具,默认只返回前20行记录加一个总数统计,模型需要更多细节时单独再查一次。这比一下把500行裸数据全塞进去稳得多。
- 定期压缩历史。当对话轮数多了,把已经结束的工具调用结果用“总结性文本”替换掉,比如“已查询用户A001的订单状态,结果为已签收”。这样下一轮模型仍然知道发生过什么,但不用携带原始大段日志。
- 控制
max_tokens。输出token上限不只是控制长度,更是控制成本。工具调用中模型的中间推理如果写得过长,既拖慢速度也增加费用,适当调低max_tokens能逼它简洁一些。
我之前做自动化数据分析Agent时,一条SQL查询工具返回了6000多行数据,直接爆了上下文窗口。后来改成先执行一个“预览查询”工具只返回行数和前10行样例,再提供“分页查询”工具按需取数据,彻底解决了这个问题。这种“大数据量不进上下文”的思路在Agent工程里几乎是必须的。
4.3 权限与安全边界:别让Agent冲出去撞人
如果把Agent接入了真实业务系统,安全边界就比代码正确性更重要。模型没有主观恶意,但它可能因为prompt理解偏差或工具使用错误,做一些超出你预期的事。
我总结的安全原则:
- 工具权限最小化:发给模型的工具,权限范围只到“完成当前任务需要的最低限度”。需要只读查询就绝对不要开放写接口。能只查自己负责的客户数据,就不要授权全库查询。
- 高危操作人工审批:删除、发消息、转账、改配置这类操作,不要直接让Agent自动执行。SDK或你自己的封装里可以加“人工确认”节点,模型生成工具调用请求后,先把请求展示给管理员,确认通过才真正执行。
- 验证工具输入:即使模型传了参数,你的函数也要做边界检查。例如
send_email工具里校验收件人格式、发送内容长度,这些原生校验永远不能少。 - 外部不可信输入要标记:如果Agent工具的返回内容来自网页或未经审核的用户输入,在回填给模型前可以加警告前缀如“[来源:外部网页,可能包含错误信息]”,让模型谨慎对待。
安全上的教训,我不太想展开讲我自己的惨痛经历,但有一条忠告:永远假设Agent会做一件你没想到的、危险的、看起来合理的事,然后提前用人工审批和最小权限把它拦住。
4.4 长任务的“监工”与自愈机制
Agent跑一个几十步的长任务,中途可能出错、可能迷茫、可能在一个分支里反复打转。我加的“监工”逻辑是:
- 设置最大步数上限,比如50步,超过就自动停止并归档当前进度。
- 每个工具调用前记录一次“正在干什么”,工具返回后记录“得到了什么”。
- 如果连续3次工具调用返回结果完全一样(重复相同动作),就主动发一条提示让模型换策略。
- 任务失败时,把当前上下文和中间日志打包,作为下一次尝试的“事故报告”输入给新Agent实例。
这个机制在跑长爬虫或批量数据处理任务时尤其好用。AI Agent不是永不犯错的神,它也会陷入局部最优,只是表现方式变成“反复调用同一个工具”。一个外部监督循环,能避免大量的token浪费。
5. 我踩过的坑和排查思路
这个部分我觉得价值最高,因为你在官方文档里很难找到这些“边角料”。下面每一个坑,都是我实际跑项目时撞过的。
5.1 工具执行返回格式不规范
Agent调用工具后,你的函数返回值会被放进对话上下文,模型需要从这段文本里理解执行结果。如果返回的是一段乱七八糟的日志、异常堆栈,甚至是一个Python对象的内存地址字符串,模型下一轮大概率会懵。
我排查过最奇葩的一次:工具函数忘了return,Agent拿到的结果是None,它思考了半天后回复“查询结果为空,任务无法继续。”看起来完全合理,实际上是因为工具没返回。
建议:工具返回尽量用简洁、有结构的文本或JSON。异常情况也要返回可读结果,比如“查询失败:订单不存在”而不是让异常冒泡到SDK层导致整个Agent崩掉。
5.2 Agent陷入死循环,token哗哗烧
我第一版自动报告Agent,有一次跑飞了,持续调用了27次数据库查询工具,原因是一个查询条件的参数拼接出了逻辑错误,模型每次查到的都是空结果,它不信邪,反复调整参数重试。那次光工具调用的token费用就够我在食堂吃一周。
根治办法:给循环加步数上限和异常重试上限。并行执行工具时特别要注意这个,因为每并行一次就有好几路同时烧token。我的经验是:除特殊查询类工具外,不要允许同一个工具连续被调用超过3次。
5.3 上下文越长,模型越“飘”
上下文接近窗口上限时,模型的回复质量会下降,而且不明显报错,只是变得啰嗦、重复、甚至开始“忘记”早期的重要结论。我一度以为模型坏了,后来发现是上下文塞太满。
我的策略:
- 每个Agent任务开始前,先明确“会话需要保留的信息清单”,比如除了必要的历史,其他过程性信息都做压缩。
- 每次工具返回后,立刻判断该结果是否还需要在后续步骤中被引用。不需要,则在下一轮发送前移除或压缩。
- 用
usage接口监控每次请求的输入token数,设定告警阈值,超过就触发上下文整理。
5.4 并发超限与限流
Agent会自动并行调用多个工具,这个“自动并发”是好事也是坑。我在同时跑20个Agent实例处理批量任务时,遇到了比较频繁的API限流,错误信息是类似429的状态码。
排查思路:
- 降低并发:把每批Agent实例数下调到合理范围,不要默认全量并发。
- 加入重试器:SDK底层或你自己的请求层加指数退避重试,遇到限流等待几秒再试。
- 工具调用也占用API额度:尤其注意,Agent每调用一次工具,都是一次独立的模型请求。工具调用越频繁,API消耗越大,限流风险也越高。
5.5 工具传参“幻觉参数”
模型偶尔会给工具传一个你没定义过的参数,或者把字符串传给int类型参数。这是大模型的老毛病。除了依赖SDK的参数校验,你的工具函数最好也做防御性解析,传错了返回友好错误信息,而不是直接异常退出。毕竟Agent循环的容错能力很强,只要错误结果能回传,它下一轮可能会自己纠正过来。
6. 实战案例:两天搭一个自动化周报Agent
理论说完了,我拿一个真实做过的案例复盘一下整个落地思路。一个做电商运营的朋友托我做一个自动化周报系统,需求是:每周一自动读取销售数据库,分析上周各品类销售趋势,标注异常,生成HTML周报邮件发给团队。
这个过程如果用传统API硬写,你需要:写SQL查询→解析数据→写prompt分析→再写prompt生成HTML→再调发送邮件的接口,每一步的数据传递、格式转换、错误处理都要自己写。用Agent SDK的思路就清晰很多:
第一步:注册工具
@tools.register def query_sales_data(sql: str) -> str: """执行销售数据查询SQL,返回JSON格式查询结果""" @tools.register def get_customer_feedback(category: str) -> str: """获取某个品类的本周用户评价摘要""" @tools.register def send_email(to_addr: str, subject: str, html_body: str) -> str: """发送HTML格式邮件"""第二步:写系统提示
system_prompt = """ 你是一个电商数据分析助理。目标是生成一份上周销售周报。 流程: 1. 先用query_sales_data查询上周各品类销售额、订单量。 2. 对比前一周数据,标注异常涨跌。 3. 对异常品类,用get_customer_feedback查评价,推测原因。 4. 生成HTML格式周报,结构包含:核心数据KPI、品类趋势、异常分析、下周建议。 5. 用send_email发送给指定邮箱。 生成报告前,先向用户简单列出你的计划。发送邮件前,必须请求确认。 """第三步:运行Agent
agent = Agent( model=MODEL_NAME, system_prompt=system_prompt, tools=[query_sales_data, get_customer_feedback, send_email], max_steps=30, ) result = agent.run("请生成上周周报并发送给运营团队,收件人是ops@company.com")让它跑一遍,Agent会自己按规划执行:先查数据,发现某个销售出现下滑,于是去调评价数据找原因,然后写HTML,最后在发送前停下来等你确认。这个行为链路,你在prompt里没有写死任何一条SQL或分支,完全由模型根据工具返回值动态决策。
实际测试中会遇到的问题:模型第一次生成的SQL可能语法不对,工具返回错误后它会自己改写重试。查询结果超过上下文上限,就需要给它加一个“先汇总再报”的工具逻辑。发送邮件前,因为我在system prompt里要求“必须请求确认”,Agent会在最后一步停下来问你是否发送——这正是我们想要的安全边界控制。
如果想把周报做成每日自动运行,在外面包一层cron或调度框架就好了。Agent本身只负责“从目标到结果”的思考,调度和外围运维还是交给传统代码。
最后说点实操体会
用Claude Agent SDK一段时间之后,我的感觉是它对“流程可变性高、需要判断力、涉及外部操作”的任务增益特别大。但前提是你把上下文管理、工具边界、预算保护这些基础设施想清楚了,不然Agent确实会帮你把问题上限和费用上限一起提高。
我个人在实际操作中最大的心得是:不要把Agent当成一个更智能的API,而是当成一个能力边界明确的员工。你给它清晰的岗位说明书(system prompt)、工具库(注册好的函数)和工作红线(权限与确认机制),它就能帮你干不少重复性工作;你不给它边界和约束,表面上看它好像“什么都会”,实际上你会花更多时间给它收拾烂摊子。
最后再分享一个小技巧:如果你第一次跑Agent结果不理想,先别急着改prompt,把中间每一轮工具调用、参数、返回结果完整打印出来看一遍。多半是工具返回结果跟模型预期不一致,或者工具说明写得让模型误会了。先把工具的状态理清楚,再优化文字,效果会立竿见影。