AI Agent 从能跑到能扛:循环、状态与错误处理的可靠性设计

发布时间:2026/10/31 8:50:03
AI Agent 从能跑到能扛:循环、状态与错误处理的可靠性设计 1. 从能跑到能扛AI Agent 的可靠性分水岭很多人第一次接触 AI Agent都是从一段几十行的脚本开始的给模型一个系统提示词挂上几个工具函数然后写一个while循环让模型自己决定下一步干什么。跑通的那一刻确实很爽——模型会自己调用搜索、自己读文件、自己算数看起来像个能独立思考的小助手。但只要把它放到稍微真实一点的场景里问题就来了循环停不下来、工具调用参数乱填、上下文越滚越长最后直接超限、同一个任务两次跑出来的结果完全不一样。这些问题的根源其实不在于模型本身有多笨而在于大多数人把 Agent 当成了一个提示词工程问题而它本质上是一个系统可靠性问题。一个能跑的 Agent 和一个能扛的 Agent中间隔着的不是更聪明的模型而是一整套围绕循环、状态、错误处理和边界控制的设计。这篇内容想聊的就是这条分水岭。我会从最小的 Agent 循环讲起把Agent Loop、Function Calling、Prompt设计、Workflow编排这几个核心概念拆开揉碎然后一步步补上让系统变可靠的那些关键机制。适合已经跑通过 Demo、但一上真实任务就翻车的开发者也适合正在评估要不要自建 Agent 中台的技术负责人。读完之后你应该能清楚地知道一个 Agent 从玩具到生产可用中间到底要补哪几块。2. 最小 Agent 循环到底长什么样2.1 一个循环、三个角色、四个步骤抛开所有框架和封装一个 AI Agent 的最小循环其实只有三个角色在参与模型Model、工具Tools、运行时Runtime。模型负责决策工具负责执行运行时负责把两者串起来并维护状态。所谓 Agent Loop就是这三者之间不断往复的一个过程。把它拆成步骤大概是这样的运行时把当前对话历史包括系统提示词、用户输入、之前的工具调用结果打包发给模型。模型返回一个响应这个响应要么是我要调用某个工具参数是这些要么是我的最终答案是这些。如果模型要调用工具运行时执行对应函数把结果作为一条新消息追加到对话历史里。回到第 1 步直到模型给出最终答案或者触发某个终止条件。就这么简单。用伪代码表示核心逻辑不超过二十行messages [system_prompt, user_input] while not done: response model.chat(messages) if response.has_tool_call: result execute_tool(response.tool_call) messages.append(response) messages.append(tool_result_message(result)) else: done True final_answer response.content这个结构就是所有 Agent 框架的内核。LangChain 的 AgentExecutor、LangGraph 的 StateGraph、各种自研中台剥到最里面都是这个循环。理解这一点很重要因为后面所有的可靠性问题本质上都是这个循环在真实环境下暴露出来的边界问题。2.2 为什么最小循环必然不可靠最小循环能跑通是因为它假设了几件在现实中几乎不成立的事模型每次都会返回合法格式、工具调用永远成功、任务总能在几步内结束、上下文永远不会超限。任何一条被打破循环就会出问题。我见过最典型的翻车场景是这样的模型调用了一个搜索工具返回结果特别长几千字直接塞进上下文模型基于这个结果又调了一次搜索又塞几千字循环到第五六轮的时候上下文已经逼近模型上限要么直接报错要么模型开始遗忘前面的指令行为变得莫名其妙。这时候你去看日志会发现循环本身没写错错的是它没有任何刹车和减负机制。所以从最小循环到可靠系统要补的不是一个功能而是一组机制。下面几节我会逐个拆开讲。3. Function CallingAgent 的手脚也是最容易出错的地方3.1 工具定义的质量直接决定调用质量Function Calling是 Agent 和外部世界交互的接口。模型根据你提供的工具描述决定调不调、调哪个、传什么参数。这里有个很多人忽略的事实模型对工具的调用准确率很大程度上取决于工具描述写得好不好而不是模型有多强。一个糟糕的工具定义长这样{ name: search, description: 搜索东西, parameters: { query: {type: string} } }问题在哪搜索东西这个描述太模糊模型不知道搜什么、什么时候该搜、搜出来的结果是什么形态。参数query也没说明格式要求。结果就是模型可能在该用别的工具时用了它或者传一个含糊的自然语言句子进去。一个合格的描述应该是这样的{ name: search_knowledge_base, description: 在企业内部知识库中检索文档。当用户询问产品政策、流程规范、历史工单等内部信息时使用。不适用于实时新闻或通用常识查询。, parameters: { query: { type: string, description: 检索关键词建议使用名词短语避免完整句子。例如 报销流程 而不是 我想知道报销流程是什么 }, top_k: { type: integer, description: 返回结果数量默认 5最大 20 } } }差别在于明确了使用场景什么时候用、排除场景什么时候不用、参数格式怎么填。这三样东西写清楚模型的调用准确率会有肉眼可见的提升。3.2 参数校验不能只靠模型自觉即使描述写得再好模型偶尔还是会传错参数——类型不对、必填项缺失、值超出范围。这时候如果运行时直接把参数丢给函数执行轻则报错中断重则产生副作用比如删错了数据。可靠的做法是在工具执行前加一层参数校验。这层校验不需要多复杂用 JSON Schema 校验库就能搞定from jsonschema import validate, ValidationError def execute_tool(tool_call): schema TOOL_SCHEMAS[tool_call.name] try: validate(instancetool_call.arguments, schemaschema) except ValidationError as e: return { error: f参数校验失败: {e.message}, hint: 请检查参数格式后重试 } return TOOLS[tool_call.name](**tool_call.arguments)关键在于校验失败时不要把异常直接抛出去中断循环而是把错误信息作为工具结果返回给模型。模型看到参数校验失败top_k 必须是整数下一轮大概率会自己修正。这就是 Agent 自我纠错能力的来源——不是模型天生会纠错而是你把错误信息喂回给它了。3.3 工具结果的瘦身处理工具返回的结果往往比模型需要的多得多。一个数据库查询可能返回几十条记录一个网页抓取可能返回整页 HTML。如果原样塞进上下文几轮下来就会把上下文撑爆。我的做法是在工具层做结果裁剪而不是等到上下文管理阶段再处理。具体来说对列表类结果只保留前 N 条并明确告诉模型还有 X 条未显示。对长文本做摘要或截断保留关键字段。对结构化数据只提取模型决策需要的字段丢掉冗余元数据。def trim_result(raw, max_items5, max_chars2000): if isinstance(raw, list): trimmed raw[:max_items] note f共 {len(raw)} 条显示前 {max_items} 条 return {items: trimmed, note: note} text str(raw) if len(text) max_chars: return text[:max_chars] f...已截断原文 {len(text)} 字 return text这个处理看起来不起眼但它直接决定了你的 Agent 能跑多少轮。上下文是 Agent 最稀缺的资源工具层是控制它的第一道闸门。4. Prompt 设计不是写作文是写接口文档4.1 系统提示词的本质是行为契约很多人写系统提示词习惯写成一段热情洋溢的角色扮演你是一个专业的、乐于助人的、经验丰富的助手……这种写法对聊天机器人可能有用但对 Agent 来说几乎没用因为 Agent 需要的是明确的行为规则不是人设。Agent 的系统提示词应该像一份接口文档回答几个问题你能用哪些工具、什么情况下用哪个、遇到错误怎么办、什么情况下必须停下来问人、输出格式是什么。举个例子你是一个任务执行 Agent可以使用以下工具 - search_kb: 检索内部知识库 - query_db: 查询业务数据库 - send_email: 发送邮件 行为规则 1. 涉及数据查询时优先使用 query_db不要凭记忆回答。 2. 每次工具调用后检查返回结果是否为空。为空时最多重试 2 次仍为空则告知用户。 3. 发送邮件前必须向用户确认收件人和内容不得自行决定。 4. 如果连续 3 次工具调用都没有推进任务停止并汇报当前状态。 5. 最终回答使用简洁的中文不要复述工具原始输出。这份提示词里没有一句废话每一条都是可执行、可验证的规则。模型拿到这样的指令行为会稳定得多。4.2 提示词里的防呆设计真实环境里模型会遇到各种边界情况。提示词里提前把这些情况写清楚能省掉大量调试时间。我通常会加这几类防呆条款空结果处理工具返回空怎么办。重试上限同一个操作最多试几次。循环检测连续无进展时如何退出。确认机制哪些操作必须先问人。格式约束最终输出必须符合什么结构。这些条款看起来是在限制模型实际上是在给它一个安全网。没有这些约束模型在遇到意外时会自由发挥而自由发挥在 Agent 场景里往往意味着不可预测。4.3 提示词版本管理别让改动变成玄学Agent 的提示词会随着调试不断修改。如果没有版本管理很容易出现昨天还好好的今天改了两个字就崩了的情况而且根本回不去。我的建议是把提示词当成代码来管存在文件里、进版本控制、每次改动写清楚改了什么、为什么改。更进一步可以在运行时记录每次请求用的是哪个版本的提示词出问题时能快速定位。PROMPT_VERSION v2.3.1 # 日志里带上版本号 logger.info(fagent_run prompt_version{PROMPT_VERSION} task_id{task_id})这个习惯在单人开发时可能觉得多余但一旦有多人协作或者需要长期维护它能救命。5. Workflow 编排什么时候该让模型自由发挥什么时候该收权5.1 Agent 和 Workflow 不是对立的社区里经常有Agent 派和Workflow 派的争论其实这俩不是二选一的关系。Workflow 是确定性的流程编排Agent 是模型驱动的动态决策。一个成熟的系统往往是两者的混合主干流程用 Workflow 固定下来需要灵活判断的节点交给 Agent。举个实际例子。一个客服工单处理系统主干流程是固定的接收工单 → 分类 → 检索知识库 → 生成回复 → 人工审核 → 发送。这个主干用 Workflow 编排最合适因为步骤明确、顺序固定。但分类和生成回复这两个节点内部逻辑复杂、情况多变适合用 Agent 来处理。这样设计的好处是该确定的地方确定该灵活的地方灵活。全用 Agent系统不可控全用 Workflow遇到意外情况就卡死。5.2 用状态机约束 Agent 的行为边界如果你用 LangGraph 这类工具本质上就是在用状态机来编排 Agent。每个节点是一个处理步骤边定义了流转条件。这种结构天然适合做可靠性控制因为你可以在节点之间插入校验节点检查上一步输出是否合法。定义条件边根据状态决定走哪条路。设置最大步数防止无限循环。加入人工介入节点在关键决策点暂停。from langgraph.graph import StateGraph, END def should_continue(state): if state[steps] 10: return abort if state[needs_human]: return human_review if state[task_done]: return END return agent_step graph StateGraph(AgentState) graph.add_node(agent_step, agent_node) graph.add_node(human_review, human_node) graph.add_node(abort, abort_node) graph.add_conditional_edges(agent_step, should_continue)这段代码的价值不在于用了什么框架而在于它把什么时候停这件事从模型手里拿回来交给了确定性的代码逻辑。模型可以决定下一步做什么但能不能继续做由状态机说了算。5.3 并发场景下的 Agent 编排热词里有人问AI Agent 怎么扛并发这确实是个绕不开的问题。单机跑一个 Agent 和同时跑一百个 Agent是完全不同的挑战。核心矛盾在于Agent 是有状态的、长时运行的而并发要求资源可复用、请求可隔离。我的经验是分三层处理第一层会话隔离。每个 Agent 实例的状态对话历史、中间变量必须独立存储不能共享内存。用 session_id 做 key状态存 Redis 或数据库。第二层资源池化。模型调用、数据库连接、工具执行这些重资源用连接池管理避免每个 Agent 都新建连接。第三层限流与排队。Agent 任务往往耗时长不能无限接收。用队列做缓冲超出容量时排队或拒绝而不是硬扛。async def handle_agent_task(task, semaphore): async with semaphore: # 限制并发数 state await load_state(task.session_id) result await run_agent_loop(state, task.input) await save_state(task.session_id, state) return result这套组合下来单机能稳定支撑的并发数会比裸跑高一个数量级。关键不是某个技巧而是把状态和资源这两件事分开管理。6. 让循环真正可靠的几个关键机制6.1 终止条件给循环装上刹车最小循环最大的隐患就是没有明确的终止条件。模型可能陷入调用工具 → 结果不满意 → 再调用 → 还不满意的死循环。可靠系统必须有多重终止条件终止条件触发逻辑处理方式任务完成模型返回最终答案正常结束步数超限循环次数 阈值中断并汇报无进展检测连续 N 步状态无变化中断并汇报错误累积连续错误 阈值中断并告警超时总耗时 阈值中断并汇报这几条里无进展检测最容易被忽略但也最重要。判断无进展可以很简单比较连续两步的工具调用和结果如果完全一样就认为卡住了。def is_stuck(history, window3): recent history[-window:] signatures [hash_tool_call(h) for h in recent] return len(set(signatures)) 16.2 上下文管理Agent 的内存要主动回收上下文是 Agent 最宝贵的资源也是最容易失控的地方。除了前面说的工具结果瘦身还需要在循环层面做上下文管理。常见策略有三种滑动窗口只保留最近 N 轮对话老的丢弃。简单但会丢失早期信息。摘要压缩把早期对话总结成一段摘要保留关键信息。效果好但需要额外模型调用。分层记忆把重要信息如任务目标、关键结论单独存起来每轮都带上过程性内容按需检索。我一般用分层记忆 滑动窗口的组合任务目标和已确认的关键结论始终保留中间的工具调用过程只留最近几轮。这样既控制了长度又不会丢掉核心信息。6.3 错误处理把异常变成模型的输入前面提过工具执行失败时不要把异常抛出去而是把错误信息返回给模型。这个原则要贯彻到整个循环里。模型调用失败、格式解析失败、超时——所有这些都应该被捕获转成结构化的错误信息喂回循环。def safe_step(state): try: response model.chat(state.messages) return parse_response(response) except ModelError as e: return {error: str(e), retryable: True} except ParseError as e: return {error: f响应格式错误: {e}, retryable: True}配合重试上限这套机制能让 Agent 在遇到偶发问题时自愈而不是直接崩溃。实测下来加了这层之后任务成功率能提升不少尤其是那些依赖外部服务的场景。6.4 可观测性出问题时你得知道发生了什么Agent 的行为是动态的出了问题很难复现。所以日志和追踪不是可选项是必需品。至少要记录每一轮的输入消息、模型输出、工具调用和结果。每一步的耗时。终止原因。提示词版本、模型版本。有了这些出问题时能完整回放整个执行过程定位到具体是哪一步、哪个决策出了问题。没有这些调试 Agent 基本靠猜。7. 从 Demo 到生产我踩过的几个坑7.1 别在提示词里塞太多工具刚开始做 Agent 时我恨不得把所有能用的工具都挂上去觉得工具越多能力越强。结果发现工具数量超过十几个之后模型的调用准确率明显下降——它开始混淆相似的工具或者干脆不用工具凭记忆回答。后来我的做法是按场景分组不同任务类型加载不同的工具子集。客服场景只挂知识库和工单工具数据分析场景只挂数据库和图表工具。工具少了模型的选择反而更准。7.2 模型返回的 JSON 不一定合法即使你要求模型只返回 JSON它也可能返回带 markdown 代码块包裹的、带解释文字的、或者干脆格式错误的 JSON。所以解析这一步必须做容错def parse_json_safe(text): text text.strip() if text.startswith(): text text.split()[1] if text.startswith(json): text text[4:] try: return json.loads(text) except json.JSONDecodeError: # 尝试提取第一个 JSON 对象 match re.search(r\{.*\}, text, re.DOTALL) if match: return json.loads(match.group()) raise这段代码看起来丑但能救回大量本来会失败的请求。7.3 测试用例要覆盖坏路径大多数人测试 Agent 时只测正常流程给个明确任务看它能不能完成。但真正出问题的是坏路径工具返回空、模型返回格式错误、任务本身无解、用户中途改需求。这些场景必须专门构造测试用例。我的做法是维护一个坏路径测试集每次改提示词或工具定义都跑一遍这个集合确保没有引入回归。这个习惯让我的 Agent 在真实环境里的稳定性提升非常明显。7.4 人工介入不是失败是设计有些任务就是不该让 Agent 全自动完成比如涉及资金、涉及对外发送、涉及不可逆操作。在这些节点设置人工确认不是 Agent 能力不足而是负责任的设计。把什么时候必须问人写进提示词和状态机比事后追责有用得多。8. 写在最后的一点个人体会做 Agent 这几年我最大的感受是Agent 的难点从来不在模型而在系统。模型能力每年都在涨但循环控制、状态管理、错误处理、边界约束这些东西是无论模型多强都必须自己搭的。一个提示词写得再漂亮如果没有可靠的循环和终止机制放到真实场景里照样翻车。如果你现在正卡在Demo 能跑但一上真实任务就崩的阶段我的建议是先把最小循环的终止条件和错误处理补上再考虑加工具、换模型、上框架。顺序反了会走很多弯路。工具和框架是放大器它们放大的是你系统的可靠性而不是弥补它。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询