DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Agents SDK 报 MaxTurnsExceeded,工具成功以后还有哪一步没结束

MaxTurnsExceeded 限制模型交互轮数。检查重复工具调用、返回信息和 handoff 的退出条件,分别设置轮数与时间预算;异常不会自动回滚已执行的外部操作。

Agents SDK 报 MaxTurnsExceeded,工具成功以后还有哪一步没结束

日志里工具已经返回成功,Runner 最后仍抛出:

Max turns (10) exceeded

先别把 max_turns 改成一百。工具调用完成之后,模型通常还要读到工具结果,再决定继续调用、转交其他 agent,还是给出最终答案。工具成功只证明其中一步完成;整个 agent run 可能仍在循环。

本文以 OpenAI Agents SDK 源码快照 125efa02 的 Runner 为依据,示例接口在 openai-agents 0.23.1 中核对。max_turns 限制的是模型交互轮数,不是单个工具执行了多少秒,也不是工具数量。一轮里可以出现多个工具调用。

从最后几轮找重复动作

先看三个事实:哪一个 agent 在运行、模型要求调用哪个工具、工具实际返回什么。只记录工具函数“执行成功”很容易漏掉返回给模型的内容。例如数据库查到了订单,但包装层只返回“OK”,模型拿不到订单状态,下一轮就又查一次。

接着看重复是否有进展。如果每次查询参数都相同,结果也相同,应检查提示词是否让模型必须获得一个实际上不存在的字段。多 agent 场景还要看 handoff:A 认为任务属于 B,B 又转回 A,也会消耗轮数。

修复应落在具体退出条件上。例如:查到状态后直接回答;订单不存在时明确告知,结束本次任务;工具返回暂不可用时报告失败,不再对同一条件无限重试。这些约束也应体现在工具说明和返回结果中。

给边界留在代码里

下面是一个只读工具示例。需要 openai-agents、已配置的 OPENAI_API_KEY,以及你有权限使用的 OPENAI_MODEL。运行会调用模型并可能产生费用;固定订单数据只是演示工具协议。

import asyncio
import os
from agents import Agent, Runner, MaxTurnsExceeded, RunConfig, function_tool

@function_tool
def lookup_order(order_id: str) -> dict[str, str]:
    """查询一次订单。found 时回答状态;not_found 时告知不存在,不重复查询同一编号。"""
    if order_id == "demo-17":
        return {"status": "found", "delivery": "delivered"}
    return {"status": "not_found"}

async def main():
    agent = Agent(
        name="order-reader",
        model=os.environ["OPENAI_MODEL"],
        instructions="只查询用户指定的订单一次。根据工具结果回答后结束;不要猜测其他订单号。",
        tools=[lookup_order],
    )
    try:
        async with asyncio.timeout(30):
            result = await Runner.run(
                agent, "查询 demo-17", max_turns=3,
                run_config=RunConfig(tracing_disabled=True),
            )
    except MaxTurnsExceeded:
        print("轮数达到上限,请检查本次运行记录;不自动重新执行任务")
    except TimeoutError:
        print("执行超时,需要核查工具是否已经完成")
    else:
        print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

这个示例的 30 秒只是应用侧时间预算,不能保证已经发出的外部操作被撤销。Python 的 asyncio.timeout 需要 Python 3.11 或更高版本。

什么时候才该增加 max_turns

把正常任务的步骤列出来。如果任务确实要多次取数、检查、汇总,并且每轮都推进了状态,增加上限有依据。验收时同时保留异常用例:让工具一直返回无法推进的结果,确认流程仍会在有限轮数内失败,而不是无限运行。

如果工具会发邮件、扣库存或创建订单,达到上限后尤其不能整段重跑。先根据业务 ID 查操作结果,再决定从哪里继续。MaxTurnsExceeded 不保证前面的工具没有执行,也不会自动回滚它们。

官方依据:Runner 源码、运行循环与异常说明。