1. 从热榜前五看 AI agent 的底层基建潮9 月 22 日这天的 GitHub Trending 榜单挺有意思前五名里三个项目都在做同一件事——给 AI agent 造地基。不是那种套壳聊天的玩具项目而是真正在解决 agent 落地时绕不开的硬骨头状态管理、工具调用编排、多轮任务拆解、执行过程可观测。这个信号其实比单个项目本身更值得聊因为它说明整个社区的重心正在从“让模型能说话”转向“让模型能干活”。我自己从去年开始陆续搭过几个 agent 项目从最简单的 ReAct 循环到后来用 LangGraph 做带状态的工作流踩过的坑基本都集中在“地基”这一层。模型能力再强如果状态丢了、工具调错了、任务拆到一半断了整个 agent 就是不可用的。所以看到热榜这个分布我一点都不意外反而觉得这才是正常的技术演进节奏。这篇文章我会围绕“AI agent 地基”这个核心把热榜现象背后的技术逻辑拆开讲清楚。包括 agent 到底需要哪些基础设施、为什么状态管理和编排层是当前瓶颈、从 0 到 1 搭建一个能扛并发的 agent 需要关注什么、以及国内开发者在实际落地时常见的几个坑。适合已经写过 demo 但想往生产环境推的开发者也适合刚接触 agent 想搞清楚“除了调 API 还要学什么”的新手。2. AI agent 地基到底在造什么2.1 从“能聊天”到“能干活”缺的那几块砖很多人第一次接触 AI agent 的概念会觉得不就是让模型调用几个函数吗。写个 demo 确实简单定义一个工具列表把用户输入丢给模型模型返回要调用的工具名和参数执行完把结果塞回去再问一轮。二十行代码就能跑通。但这个东西一旦要处理真实任务问题就全冒出来了。第一个问题是状态。多轮对话里模型需要记住之前做了什么、当前进行到哪一步、哪些中间结果还有效。纯靠 messages 数组堆上下文token 消耗爆炸不说模型还会被无关历史干扰。第二个问题是编排。一个复杂任务往往需要拆成子任务子任务之间有依赖关系有的能并行有的必须串行还要处理失败重试和超时。第三个问题是可观测。agent 执行到一半卡住了你得知道它卡在哪一步、当时的状态是什么、调用了哪个工具返回了什么。没有这层调试基本靠猜。热榜上那几个项目本质上都在补这几块砖。有的专注做状态持久化和恢复有的做可视化编排有的做执行追踪。它们不是模型本身但决定了模型能不能被可靠地用在生产环境里。2.2 为什么现在集中爆发时间点很关键。过去一年模型能力提升很快尤其是工具调用和结构化输出这块基本可用了。但基础设施没跟上导致大量 agent 项目停留在 demo 阶段。社区积累了一批真实需求知道痛点在哪所以集中开始造轮子。另一个原因是 agent 的形态在分化。早期大家做的都是通用助手现在越来越多垂直场景的 agent 出现比如代码 agent、数据分析 agent、客服 agent。不同场景对地基的要求不一样通用方案覆盖不了就催生了更细分的项目。热榜前五里三个做地基说明这个分化已经到了需要专门基础设施的阶段。提示如果你现在还在用纯 messages 数组管理 agent 状态建议尽早了解一下基于图或状态机的编排方案。这不是过度设计而是任务复杂度上去之后必然要面对的问题。3. 状态管理agent 记忆的工程化实现3.1 为什么 messages 数组不够用刚开始写 agent 的时候我也觉得 messages 数组挺好用。用户说一句模型回一句工具调用结果也塞进去简单直接。但任务一复杂就崩了。比如一个需要查数据库、调 API、再汇总生成报告的任务中间会产生大量中间结果。全塞进 messages 里上下文很快超限而且模型会开始混淆哪些是有效信息哪些是废弃的中间态。更麻烦的是恢复。agent 执行到第三步失败了你想从第三步重试但 messages 数组里没有明确的状态标记你不知道前两步的副作用是否已经产生、能不能安全重跑。这在生产环境里是致命的。状态管理的核心思路是把 agent 的执行过程显式建模。不是把所有东西都塞进对话历史而是维护一个结构化的状态对象记录当前步骤、已完成步骤、中间结果、待办事项。每一步执行前先读状态执行后更新状态。这样恢复的时候只需要从状态对象里读不用去解析对话历史。3.2 状态持久化的几种方案对比实际落地时状态存哪里选择挺多的各有取舍。方案优点缺点适用场景内存字典零依赖快进程重启就丢无法水平扩展本地开发、单机 demoRedis读写快支持过期数据结构需要自己设计持久化需额外配置中小规模生产环境关系数据库事务保证查询灵活读写延迟相对高schema 变更麻烦需要审计和复杂查询的场景对象存储容量大成本低延迟高不适合频繁读写归档和冷数据我自己的经验是开发阶段用内存字典快速迭代上线前换成 Redis 加定期落库。Redis 存热状态关系库存历史快照。这样既保证了执行时的低延迟又能在出问题时回溯。状态对象的设计也有讲究。不要存原始 messages存结构化字段。比如current_step、completed_steps、artifacts、pending_tools。每个字段的更新逻辑要明确避免多处写入导致状态不一致。3.3 状态恢复的实操要点状态恢复不是简单地把状态读出来继续跑。有几个细节容易忽略。第一是幂等性。恢复后重跑的那一步如果之前已经产生了副作用比如发了邮件、写了数据库重跑会导致重复。所以每个步骤要么设计成幂等的要么在状态里记录副作用是否已产生。第二是超时处理。agent 卡住的时候状态可能停留在“执行中”。恢复逻辑要能识别这种中间态决定是重试还是标记失败。我一般会加一个last_heartbeat时间戳超过阈值就认为该步骤已死走重试或失败流程。第三是版本兼容。状态结构可能会随代码迭代变化恢复旧状态时要能处理字段缺失或类型变化。简单做法是给状态加版本号恢复时按版本做迁移。# 状态对象示例 state { version: 2, task_id: xxx, current_step: fetch_data, completed_steps: [parse_input, validate], artifacts: {user_query: ...}, pending_tools: [], last_heartbeat: 1695380000, side_effects: {email_sent: False} }注意状态里不要存大对象比如完整的 API 响应。存引用或摘要需要时再取。否则状态会膨胀得很快读写都变慢。4. 编排层让 agent 按计划干活4.1 从 ReAct 到图编排的演进逻辑最早的 agent 编排基本就是 ReAct 模式思考、行动、观察循环直到任务完成。这个模式简单有效但只适合线性任务。一旦任务需要分支、并行、循环嵌套ReAct 就力不从心了。图编排的思路是把 agent 的执行流程建模成有向图。节点是执行单元边是流转条件。这样分支、并行、循环都能自然表达。LangGraph 就是典型代表用状态图来定义 agent 的行为。热榜上做编排的项目基本都在往这个方向走。为什么图编排更适合生产环境因为它是显式的。ReAct 的流程藏在模型的思考里你没法精确控制它下一步走哪。图编排把流程写在代码里模型只负责在节点内做决策整体走向由开发者控制。这在需要审计和合规的场景里是刚需。4.2 节点设计与条件路由的实操设计图的时候节点粒度很关键。太粗一个节点里干太多事失败不好定位。太细节点数量爆炸维护成本高。我的经验是按“可独立失败和重试”来划分节点。一个节点应该是一个原子操作要么全成功要么全失败失败了能单独重跑。条件路由是图编排的核心。每个节点执行完后根据状态决定走哪条边。路由条件要尽量简单明确避免复杂的嵌套判断。我一般用状态里的明确字段做路由比如state[next_action]而不是让模型直接输出路由决策。模型可以在节点内建议下一步但最终路由由代码根据状态决定。# 条件路由示例 def route_after_analysis(state): if state[analysis_result][needs_more_data]: return fetch_more_data elif state[analysis_result][is_complete]: return generate_report else: return human_review并行节点的处理也要注意。多个节点同时执行时状态更新会有竞争。要么用锁要么让并行节点只读不写结果汇总到主状态时再统一更新。我倾向于后者简单且不容易出死锁。4.3 错误处理与重试策略agent 执行过程中出错是常态不是异常。工具调用超时、API 限流、模型输出格式不对这些都会发生。编排层必须内建错误处理。重试策略要分类型。瞬时错误网络抖动、限流适合指数退避重试。逻辑错误参数不对、状态不一致重试没用应该走修复流程或转人工。我一般给每个节点配一个重试配置指定最大重试次数、退避策略、以及重试耗尽后的降级动作。降级动作可以是跳过该节点、走备用路径、或者标记任务失败并通知。关键是不要让整个 agent 因为一个节点失败就完全卡死。要有兜底路径。还有一个容易忽略的点是超时。每个节点都要设超时不能无限等。超时后按失败处理走重试或降级。全局也要有超时防止 agent 整体跑太久。5. 并发与性能agent 扛并发的关键设计5.1 agent 并发和普通 Web 并发有什么不同普通 Web 服务的并发瓶颈通常在数据库和网络 IO。agent 的并发瓶颈更复杂。首先模型调用本身是慢的一次几秒到几十秒而且通常有速率限制。其次agent 是有状态的并发执行时状态隔离和一致性要额外处理。第三agent 的执行路径长一个请求可能触发几十次模型调用和工具调用资源占用时间长。这意味着 agent 的并发设计不能照搬 Web 服务的思路。无状态水平扩展在这里不直接适用因为状态需要共享或路由。模型调用的速率限制也要求做队列和削峰。5.2 任务队列与削峰填谷我的做法是把 agent 执行异步化。请求进来先入队返回一个 task_id。后台 worker 从队列取任务执行执行过程中状态写 Redis。客户端通过 task_id 轮询或订阅结果。队列的选择看规模。小规模用 Redis List 或 Stream 就够。大规模上专业消息队列。关键是队列要支持优先级和延迟因为有些任务需要优先处理有些需要延迟重试。削峰的关键是控制 worker 的并发数。不是越多越好因为模型调用有速率限制。worker 数量要跟模型配额匹配超了只会大量失败重试反而更慢。我一般会做一个令牌桶限流worker 取任务前先拿令牌拿不到就等。5.3 状态隔离与共享的平衡并发执行时每个任务的状态必须隔离。用 task_id 作为 key 前缀天然隔离。但有些状态需要共享比如全局配置、工具注册表、缓存。这些用只读的方式共享启动时加载运行时不修改。还有一种情况是多个任务需要协作比如一个主任务拆成多个子任务并行执行。这时候子任务的状态要能汇总到主任务。我的做法是主任务维护一个子任务列表每个子任务完成后把结果写到主任务的状态里。写入时加锁或用原子操作避免竞争。提示并发数不是拍脑袋定的。先测单次 agent 执行的平均模型调用次数和耗时再结合模型配额算。比如单次执行平均 10 次模型调用每次 3 秒模型配额是每分钟 60 次那单 worker 大概能支撑每分钟 2 个任务要支撑每分钟 20 个任务就需要 10 个 worker。6. 常见问题与排查技巧实录6.1 agent 执行到一半卡住怎么办这是最常见的问题。表现是任务状态一直停在“执行中”没有进展。排查思路分几步。先看心跳。如果last_heartbeat很久没更新说明 worker 可能挂了或卡在某个调用上。去看 worker 日志定位卡在哪个节点。如果是模型调用卡住检查是否有超时设置。如果是工具调用卡住检查工具本身的超时和重试。如果心跳正常但状态不变说明 agent 在某个循环里打转。常见原因是路由条件写错了导致一直在两个节点之间来回跳。检查路由逻辑加一个最大循环次数限制。还有一种情况是模型输出了无法解析的格式导致节点执行失败但错误没被捕获。检查节点的异常处理确保所有异常都有记录。6.2 模型输出格式不稳定的处理让模型输出结构化数据比如 JSON时格式错误很常见。尤其是复杂嵌套结构模型容易漏字段或加多余内容。我的处理方式是三层防护。第一层在 prompt 里给明确的格式示例越具体越好。第二层用模型的结构化输出功能如果支持比如 JSON mode 或 function calling。第三层解析失败时走修复流程把错误信息塞回去让模型重新生成最多重试两次。如果重试还失败就降级。比如把结构化输出降级为文本输出后续用规则解析。或者标记该步骤失败走人工介入。6.3 工具调用参数错误的排查工具调用参数错误通常有两类。一类是模型理解错了传了错误的参数值。另一类是参数格式不对比如该传数字传了字符串。排查时先把模型输出的原始参数打出来看。如果是理解错误检查工具描述是否清晰参数说明是否明确。模型对模糊的描述会自由发挥。如果是格式错误在工具定义里加类型约束并在执行前做校验。我一般会在工具执行前加一层参数校验不符合的直接返回错误信息给模型让它重新生成。这样比执行到一半失败再回滚要好。问题现象可能原因排查动作解决方向状态停在执行中worker 挂了或调用卡住查心跳和 worker 日志加超时重启 worker状态不变但心跳正常路由循环查路由条件和循环计数修路由加最大循环限制模型输出解析失败格式不稳定看原始输出加格式示例用结构化输出加重试工具参数错误描述不清或格式不对看原始参数改工具描述加参数校验并发上不去模型限流或 worker 不足看限流日志和队列长度调 worker 数加令牌桶6.4 几个踩过的坑第一个坑是状态更新没加锁。并行节点同时写状态后写的覆盖先写的导致数据丢失。后来改成并行节点只读结果汇总到主状态时统一写问题解决。第二个坑是重试没考虑幂等。一个发邮件的步骤失败了重试结果用户收到两封。后来给每个有副作用的步骤加了幂等键重试前先检查是否已执行。第三个坑是超时设太长。一个工具调用卡住worker 等了十分钟才超时期间占着资源不干活。后来把超时调到合理范围一般工具调用不超过 30 秒模型调用不超过 60 秒。第四个坑是状态结构变更没做兼容。上线新版本后旧状态恢复时报错。后来加了版本号和迁移逻辑旧状态自动升级到新结构。7. 从 0 到 1 搭建 agent 的实操路径7.1 技术选型的几个决策点搭 agent 之前先想清楚几个问题。第一任务复杂度如何。如果只是简单的问答加一两个工具调用不需要上重型编排框架直接写循环就行。如果任务有多步骤、分支、并行那就需要图编排。第二状态需要持久化吗。如果 agent 执行时间短、失败可接受重头再来内存状态就够。如果需要断点续跑、审计追踪那就需要持久化。第三并发要求多高。低并发单进程就够高并发需要队列加多 worker。第四团队熟悉什么技术栈。Python 生态在 agent 这块最成熟LangChain、LangGraph、AutoGen 都是 Python 的。如果团队是 Java 背景Spring AI 也在快速跟进。选熟悉的别为了新而新。7.2 最小可用 agent 的搭建步骤我一般从最小可用版本开始跑通了再逐步加东西。第一步定义状态结构。明确 agent 需要记住哪些信息设计成结构化对象。第二步定义工具。每个工具要有清晰的描述、参数定义、执行逻辑、错误处理。第三步写主循环。最简单的就是 ReAct 循环调模型、解析输出、执行工具、更新状态、判断是否结束。第四步加持久化。把状态存 Redis每步更新。第五步加编排。如果任务复杂把主循环改成图定义节点和边。第六步加并发。请求入队worker 消费状态隔离。第七步加可观测。每步打日志记录输入输出和耗时方便排查。# 最小 agent 主循环示意 def run_agent(task_id, user_input): state load_state(task_id) or init_state(user_input) while not state[done]: response call_model(state) action parse_action(response) if action[type] tool: result execute_tool(action[name], action[args]) state[artifacts][action[name]] result elif action[type] finish: state[done] True state[result] action[output] save_state(task_id, state) return state[result]7.3 上线前的检查清单上线前我会过一遍这些点。状态持久化是否可靠进程重启后能否恢复。错误处理是否覆盖所有节点有没有兜底路径。超时是否都设了有没有无限等待的地方。并发控制是否到位模型调用有没有限流。日志是否完整出问题能否定位。幂等性是否保证重试会不会产生重复副作用。状态版本兼容是否处理旧状态能否恢复。这些点看起来琐碎但每一个出问题都可能导致线上事故。我见过因为没设超时导致 worker 全被占满的也见过因为没做幂等导致用户收到重复通知的。提前检查比事后救火划算得多。8. 国内开发者落地 agent 的现实考量8.1 模型选择与成本控制国内做 agent模型选择是个现实问题。不同模型在工具调用、结构化输出、长上下文上的能力差异很大。我的建议是先用能力强的模型把流程跑通再考虑用更便宜的模型替换部分节点。成本控制的关键是减少不必要的模型调用。很多步骤其实可以用规则处理不需要模型。比如参数校验、格式转换、简单路由这些用代码做又快又便宜。模型只用在真正需要理解或生成的地方。另一个技巧是缓存。相同的输入和上下文模型输出通常稳定。可以缓存模型响应命中缓存直接返回。对于重复性高的任务缓存能省不少成本。8.2 网络与依赖的稳定性国内访问一些海外服务可能不稳定这会影响 agent 的执行。我的做法是把外部依赖做抽象加一层适配器。这样切换服务时只改适配器不影响业务逻辑。同时给所有外部调用加重试和超时避免因为网络问题导致 agent 卡死。依赖版本也要锁定。agent 项目依赖多版本冲突很常见。用锁文件固定版本避免不同环境行为不一致。8.3 从 demo 到生产的鸿沟demo 和生产之间隔着很多工程问题。demo 只关心功能跑通生产要关心稳定性、可观测、可维护、成本。我见过很多 demo 很惊艳但上不了线的 agent 项目问题基本都在工程化上。跨越这个鸿沟的关键是尽早引入工程实践。状态持久化、错误处理、日志、监控、限流这些不是上线前才加的而是从第一天就该考虑的。前期多花点时间搭好地基后期会省很多事。热榜上那些做地基的项目本质上就是在帮大家跨越这个鸿沟。它们把通用的工程问题抽象出来让开发者不用每个项目都重新造轮子。这也是为什么它们能上热榜——需求真实且普遍。我个人在实际操作中的体会是agent 项目最难的不是模型调用而是让整个系统可靠地运转。模型能力会越来越强但工程问题不会自动消失。把地基打牢模型能力的提升才能转化为实际可用的产品。