你有没有跑过一个 Agent,半夜被账单叫醒——它在一个「用户想退订」的任务上,循环调用了 200 多次工具还没停,OpenAI 的用量提醒比你的报警系统还勤快?
如果你用 OpenAI Agents SDK 自己搭过 agentic loop,这种事迟早撞上。循环停不下来、状态存不住、长任务中途掉线、出了问题完全没法复盘——这四个坑,几乎每个自托管 Agent 的团队都踩过一遍。
我是秋天,一个还在写 Java 的讲师,也在用 WorkBuddy 跑一人公司的活儿。9 月 10 号 OpenAI 把 Agents API 推到公测,等于把「长任务 Agent 的脏活」接管了过去。这篇不吹概念,直接拆它到底托管了什么、代码怎么写、你的 Java 后端怎么接、什么时候它其实不划算,以及我见过的五个真实踩坑。这不是学院派问题——Agent 从 demo 走向上线,第一道坎就是「怎么让它稳稳跑完一个长任务而不破产」。
自托管 Agent 的三座大山
先说清楚痛点,才知道 Agents API 省了哪块。
第一座山是循环控制。SDK 里 agentic loop 是「模型自己决定调不调工具、调几次」。你不设上限,它就真能调到你破产。我们 9 月 15 号那篇讲过 Spring AI 2.0.1 要给 ToolCallingAdvisor 上调用次数上限(ToolCallLimitExceededException),道理一模一样:agentic loop 必须熔断。
第二座山是状态与断点。一个跨小时的任务,进程一重启,上下文全没了。要做持久化,你得自己接 Redis、自己做 checkpoint、自己处理恢复——LangGraph 那套 checkpointing 就是为这个生的,但代价是学习曲线陡、样板代码多。
第三座山是长上下文的钱。任务跑两小时,上下文越堆越大,token 越烧越狠,到后面一半 token 花在「重复读前面已经读过的东西」上。这跟 Google ADK 2.0 把路由踢出 LLM、token 从 5152 砍到 2265 是一个思路:长任务的成本,大头不在模型聪明,在于你让模型反复搬同一堆上下文。
Agents API 的卖点,就是这三座山它替你扛:托管 session 编排、上下文压缩(compaction)、subagents 协调、自带沙箱,而且官方说「无额外 API 费,按 token 和工具调用计」。
Agents API 到底托管了什么
一句话:你只给「任务 + 模型 + 工具 + 环境」,剩下的编排 OpenAI 管。
- Session 编排:长任务的状态由云端托管,进程重启不影响,天然断点续跑。
- 上下文压缩:上下文过长时自动 compaction,不用你手写摘要逻辑。
- Subagents:一个大任务可以拆给多个子 agent 并行或串行干,编排由托管层负责。
- 自带沙箱:Blaxel / Cloudflare / Daytona / E2B / Modal / Runloop / Vercel 七家沙箱可选,代码在隔离环境跑,凭证不泄漏。
- 工具按需揭示:类似 Skills 渐进披露,上下文只装当前需要的工具,省 token。
冷启动快、token 省,是它相比「自己用 SDK 死循环」最大的两个体感差异。但代价也很明确:强绑 OpenAI,而且当前仅美国数据驻留、不支持 ZDR。金融、医疗这类数据出不了境的业务,先别急着上。
上下文压缩到底省在哪
这是最容易被忽略、也最值钱的一点。一个长任务,模型每轮都要把「完整历史」重新读一遍再决定下一步。任务越长,单轮成本越高,呈现超线性增长。
compaction 的做法是:当上下文超过阈值,把已经「用过且稳定」的中间结果压缩成摘要,只保留近期窗口 + 摘要。轮次 1 的详细推理,到轮次 20 时可能只剩一句话摘要。这样单轮成本从「随长度线性涨」变成「被压住」。
举个账:假设每轮上下文 4k token、任务 20 轮,朴素做法累计要读 80k token,其中约 60k 是前面已经读过的重复内容——你为「搬运历史」重复付了钱。compaction 后,早期轮次被压成摘要,累计可能降到 30k 出头,省一半多。省下的不是模型聪不聪明,是你不再为重复上下文买单。所以长任务上托管,省的不是模型调用费,是上下文搬运费,这笔账很多人算反了。
代价是:压缩可能丢细节。所以生产上要把「不可压缩的硬约束」(比如「绝不能给张三发邮件」「退款上限 500」)单独存成一个不进压缩通道的指令区,或者做成工具侧的硬校验,而不是指望模型记住。这一点后面踩坑五会再讲。
可跑代码:用 SDK 定义 agent 和子 agent
Agents API 托管的是「SDK 那套能力」,所以你本地能跑的 SDK 代码,基本就是上云前的样子。下面这段是标准的 OpenAI Agents SDK 写法,装 `openai-agents` 就能跑(需要 OPENAI_API_KEY):
@function_tool
def query_order(customer_id: str) -> str:
# 真实场景里这里查你的订单库
return f"customer={customer_id} open_orders=2 total=¥1280"
agent = Agent(
name="售后助手",
instructions="你是电商售后助手,需要查订单时调用 query_order 工具。",
tools=[query_order],
)
result = Runner.run_sync(agent, "查一下客户 C10086 的未结订单")
print(result.final_output)
工具用 `@function_tool` 装饰,签名里的类型注解既是入参约束,也自动变成给模型的 tool schema。模型决定要不要调、调几次——所以「循环控制」在这里依然要自己设上限,SDK 不会替你熔断,这是上云前你本地就要养成的习惯。
长任务真正的价值在 subagents。一个「做一份小红书种草文案」的任务,拆成调研 + 撰写两步:
research = Agent(
name="调研员",
instructions="只做资料调研,产出 5 条要点列表,不要写文案。",
)
writer = Agent(
name="撰写员",
instructions="根据调研要点写小红书风格文案,语气口语、带 emoji。",
)
orchestrator = Agent(
name="主编",
instructions="先让调研员出要点,再把要点交给撰写员成稿。",
handoffs=[handoff(research), handoff(writer)],
)
result = Runner.run_sync(
orchestrator,
"做一份『AI 记账工具』的小红书种草文案",
)
print(result.final_output)
`handoff` 把子 agent 注册成「主编」可以转交的对象。模型在运行时自己决定先调研再撰写。这段本地能跑;上 Agents API 后,subagents 的调度、失败重试、上下文压缩由托管层兜,你不用再手写编排状态机。
带护栏跑:熔断 + 输入护栏一个都不能少
光有 agent 不够,生产上必须加两道闸。下面这个例子加了输入护栏(拦截敏感信息)和用量上限(熔断):
Agent, Runner, RunConfig, UsageLimits,
input_guardrail, GuardrailFunctionOutput, RunContextWrapper,
)
@input_guardrail
async def no_pii(ctx: RunContextWrapper, agent: Agent,
user_input: str) -> GuardrailFunctionOutput:
# 真实场景接合规服务检测身份证/手机号
hit = ("身份证" in user_input) or ("手机号" in user_input)
return GuardrailFunctionOutput(
output_info=None,
tripwire_triggered=hit,
message="输入含敏感个人信息,已拦截",
)
agent = Agent(
name="客服助手",
instructions="处理售后咨询,禁止索要用户敏感信息。",
input_guardrails=[no_pii],
)
result = Runner.run_sync(
agent,
"我要退订,这是我的手机号 138xxxx",
run_config=RunConfig(
usage_limits=UsageLimits(max_tokens=8000, max_turns=12)
),
)
print(result.final_output)
`max_turns=12` 就是 agentic loop 的熔断线,`max_tokens=8000` 是成本天花板。这两个参数不设在长任务上,就是在赌运气。另外 SDK 默认开启 tracing(OpenAI traces),出问题能回放每一步工具调用,这是自托管最容易缺的可观测性。
护栏不止输入侧。输出护栏(OutputGuardrail)能在 Agent 给出最终答案前再拦一道,比如检测是否泄露了内部价目表、是否给出了违规承诺。tracing 则是另一道保险:它把每次工具调用、每次模型决策记成一条可回放的 trace,出问题你不是对着日志瞎猜,而是能一步步看到「它在第 7 轮为什么调了发短信工具」。长任务一旦跑飞,没有 trace 你连「它到底干了啥」都还原不出来,这比多花几个 token 贵得多。所以评估要不要用托管,可观测性兜底也算进账里的一项。
你的 Java 后端怎么接:让它当「工具」
重点来了。OpenAI 的 Agent 再聪明,也得调你的业务系统才能真正干活——查订单、下单、发短信,这些逻辑在你的 Java 微服务里。接法有两种,我更推荐第一种,因为最稳、最可控。
第一种:暴露成 HTTP 工具。 你的 Spring Boot 把业务能力包成一个 REST 端点,Agent 侧用通用的 HTTP 工具调用它。你的后端就是第一道防线:
@RequestMapping("/agent-tools")
public class AgentToolController {
private final OrderService orderService;
public AgentToolController(OrderService orderService) {
this.orderService = orderService;
}
// 云端 Agent 通过 HTTP 工具调用这个端点
@PostMapping("/query-order")
public Map<String, Object> queryOrder(
@RequestBody Map<String, String> req) {
String customerId = req.get("customer_id");
OrderSummary summary = orderService.summary(customerId);
Map<String, Object> out = new HashMap<>();
out.put("open_orders", summary.getOpenCount());
out.put("total", summary.getTotal());
return out;
}
}
然后在 Agent 侧描述这个工具(名字、入参、返回),模型就会在需要查订单时打这个 URL。好处是:你的 Java 服务一行不用改架构,只是多开一组给 Agent 用的内部端点;鉴权、幂等、限流全在你自己手里。
第二种:暴露成 MCP 工具。 如果你已经在用 Spring AI 的 MCP(我们 9 月 17 号那篇讲过 `@McpTool` 注解把老系统变成 Agent 工具),直接把方法标成 MCP tool,Agent 通过 MCP 协议调,省去自己写 HTTP 描述和鉴权胶水:
public Map<String, Object> queryOrder(
@McpToolParam(description = "客户ID") String customerId) {
OrderSummary s = orderService.summary(customerId);
Map<String, Object> out = new HashMap<>();
out.put("open_orders", s.getOpenCount());
out.put("total", s.getTotal());
return out;
}
两种都行,但取舍不同:HTTP 简单、零额外依赖,适合工具少、调用方单一的早期;MCP 赢在「工具定义标准化」——模型能自动发现你的工具、工具多了不用每个都手写描述,多 agent 协作或跨团队复用工具定义时优势明显。小团队先用 HTTP 试水,工具多了(超过 5 个、或要被多个 agent 调)再统一收口到 MCP。无论哪种,一条铁律:Agent 调你的工具时,工具侧必须有鉴权 + 幂等 + 限流。Agent 是模型在驱动,它可能会重复调用、可能把错误参数传进来。后端不能是裸奔的——这是很多团队第一次接 Agent 就出生产事故的原因。
把一个场景串一遍:退订流程怎么 Agent 化
光讲零件容易飘,我用「用户要退订」这个真实场景把上面所有东西串一遍,你就知道它们怎么咬合了。
用户说「我要退订」。Agent(主编)先调 query_order 工具查这个客户有没有未结订单——这一步走的是你 Java 后端的 REST 端点,鉴权在工具侧。如果有未结订单,Agent 不会自己擅作主张,而是走 HITL:把情况抛回人工确认「客户 C10086 有 2 笔未结共 ¥1280,确认退订并处理尾款?」。人工点确认后,Agent 才调「处理退订」工具。这个工具在 Java 侧是幂等的:同一个退订请求号重复进来,只生效一次,避免模型重试导致退两次。最后调「发通知」工具告知用户。整个链路里,Agent 负责「判断下一步调谁」,你的 Java 工具负责「每一步的安全与正确」,分工干净。
注意两道闸都在工具侧:退订这种有副作用的操作必须人工确认 + 幂等;发通知这种「看起来无害」的操作,也要限流,否则模型一激动连发 20 条。这就是为什么我反复说——Agent 再聪明,安全边界得长在你自己的后端上。
真实踩坑五连
我见过(和听过)的五个最贵的坑,按发生频率排:
Loop 失控烧钱:没设 `max_turns`,一个「帮我盯着竞品价格」的任务变成了每 5 秒查一次 API,一天烧掉几百刀。对策:所有长任务强制 `usage_limits`,并把「监控类」需求改成定时任务而不是常驻 Agent。 工具副作用没拦:Agent 调了「发退款」工具 3 次,因为它以为前两次失败就重试了,用户收到三笔退款。对策:有副作用的工具必须幂等 + 二次确认(HITL),退款、发钱、删数据这类操作别让模型自己连发。 沙箱里读到不该读的凭证:把生产数据库密码挂进沙箱环境变量,Agent 顺手把连接串回传给了用户。对策:沙箱只给最小权限,敏感凭证走独立的密钥服务,Agent 拿不到明文。 压缩把硬约束压没了:compaction 把「不要给张三发邮件」压成了一句模糊摘要,结果还是发了。对策:不可压缩的硬约束走工具侧硬校验,别依赖模型记忆。 数据出境踩雷:以为 Agents API 通用,结果客户合同进了美国节点,合规一出事全公司背锅。对策:涉及出境限制的数据,留在自建环境,Agent 只调摘要接口。 工具描述写错导致模型乱调:tool schema 的 description 写「查订单」,模型理解成「能改订单」,于是一句话把订单状态改了。对策:工具描述要写清「只读 / 只查」还是「会修改」,修改类工具名字里就带动词(update_ / create_),让模型和使用者都看清边界。
避坑:什么时候它其实不划算
别被「托管」冲昏头。Agents API 不是银弹,下面几种情况你自己用 SDK 更省:
- 短平快的单次问答:用户问一句答一句,毫秒级返回,自己跑 SDK 延迟最低、最便宜,没必要上托管长任务。
- 数据不能出境:当前仅美国数据驻留、不支持 ZDR。合规卡死的业务直接 pass。
- 要完全自己掌控 loop:强绑 OpenAI,如果你的模型是 Claude / Gemini / 本地模型,这套托管用不了,老老实实用 LangGraph 或 Spring AI 自己搭。
该用托管的信号很明确:任务会跨小时甚至跨天、需要状态持久、需要 subagents 编排、你不想养一套编排 + checkpoint + 压缩的基础设施。这种「长任务」才是 Agents API 的主场。
举个判断例子帮你对着看:你做个「每天早 9 点给老板发一份竞品动态简报」的任务——这是定时、短、可预测,自己用 SDK 写个 cron + 一次调用最省,别上托管。但如果你做的是「监控一个开源项目一周,发现相关 issue 就调研、写方案、提交 PR」,这种跨天、要反复调多个工具、中途可能要人确认的任务,托管才显出价值。区别在于「要不要养一套长跑基础设施」,而不是「模型聪不聪明」。
下面这张表帮你快速对照:
| 方案 | 适合场景 | 持久化 | 成本可控 | 绑定 |
|------|----------|--------|----------|------|
| 自托管 SDK | 短任务、想完全控 loop | 自己接 | 自己管 | OpenAI |
| Agents API 托管 | 长任务、跨天、要编排 | 云端托管 | 设上限 | OpenAI |
| LangGraph | 复杂分支、要审计 | checkpoint 成熟 | 自己管 | 模型无关 |
| Spring AI 2.0 | Java 体系内 RAG/工具 | advisor 链 | 自己管 | 模型无关 |
渐进迁移路线:别推倒重来
如果你现在已经用 SDK 自托管跑着 agent,别推倒重来。迁移到 Agents API 是渐进的,三步就够:
第一步,先给现有 agent 补上 `run_config` 的 `usage_limits`,把循环熔断——这一步不碰架构,纯加护栏,今天就能做。第二步,把工具侧补齐鉴权、幂等、限流,让 Agent 敢调你的真实系统,而不是只在 demo 里调假数据。第三步,再把「跨小时、要编排」的那类任务切到托管层,短任务继续留本地。代码基本不用改,只是编排责任和基础设施从你这边挪到云端。很多团队卡在「要不要整体迁移」,其实根本不用——先让最痛的长任务上云,其余原地不动,收益就已经回来了。
进阶:和 Spring AI 怎么共存
很多 Java 团队已经在用 Spring AI 2.0 做 RAG、做工具调用。我的建议是分工:Spring AI 管你「自己系统内的 AI 能力」(查库、生成、RAG),OpenAI Agents API 管「跨系统长任务编排」(需要调多个外部服务、跑很久的那种)。两者通过 MCP 或 HTTP 打通——Spring AI 那侧的方法暴露成工具,Agents API 这侧把任务编排和长跑托管掉。
这样你既不用把核心业务迁到 OpenAI,又能用上托管长任务的能力。Java 后端还是你的地盘,Agent 只是个会调你工具的「外包项目经理」。
今天带走的三句话
长任务 Agent 的脏活(编排、压缩、持久化、沙箱)现在能托管了,9 月 10 号公测,值得试。 你的 Java 后端别改架构,开一组内部工具端点(HTTP 或 MCP)让 Agent 调,鉴权幂等限流自己守。 短任务自己跑 SDK 更省,只有「跨小时 + 要持久 + 要编排」才上托管;上之前先设 token 和轮次上限。顺带一句:不管用哪家,工具侧的安全(鉴权、幂等、限流)永远是你自己的责任,别甩给模型。




