Agent 形态一天一个样,Infra 到底该为谁而建?
如果你最近在写 Agent 相关项目,大概率会有一种感觉:今天刚把单 Agent 的问答流程跑通,明天社区就在讲多 Agent 协作编排;你还在纠结要不要引入某个 Agent 框架,新出的 Skill、MCP 标准又开始争夺注意力;再往后看,什么 Agent 记忆、Agent 安全、Agent 可观测性,每一个方向都能延伸出一整套工具链。
更让人头疼的是,这些形态变化并不只是概念层面的热闹。它是真的会落到工程决策里:架构要不要改、技术选型要不要换、基础设施要不要重新投入。很多团队就卡在这个位置上——Agent 应用做了一些,但底层设施还没跟上;想认真建设 Infra,又怕现在投入的底子,过两个月就成了别人口中“过时的形态”。
所以要回答的核心问题不是“Agent 今天长什么样”,而是:Agent 形态一直在变,Infra 到底该为谁而建?
这篇文章不会推荐你去跟某一个框架、某一种形态绑死。我会先拆清楚 Agent 形态变化背后的本质,再分析 AI Infra 真正要服务的能力边界,最后给出一套不依赖具体形态的基础设施设计思路,并附上可以直接落地的代码示例和排查路径。
1. 这篇文章真正要解决的问题
先给一个明确判断:Agent 形态会继续变,但 Agent 执行的基础需求不会大变。Infra 应该为执行稳定性而建,而不是为形态变化而建。
为什么这么说?你可以回忆一下最近遇到的 Agent 开发问题。很多时候出问题的并不是“用了哪个框架”或者“Agent 有没有记忆”,而是更朴素的工程问题:调用 LLM 超时了怎么办,工具执行到一半失败了怎么恢复,多个 Agent 协作时日志怎么串起来排查,某个技能的权限边界怎么控制。这些问题不会因为 Agent 从单 Agent 变成多 Agent、从 ReAct 变成 Plan-and-Execute 就自动消失。
真正值得建设的 Infra,是那些跨形态稳定的能力:
- Agent 从启动到结束的完整生命周期管理。
- 模型调用、工具调用的可靠执行与重试恢复。
- 分布式上下文与记忆的存取。
- 完整链路可观测性。
- 权限、数据隔离与安全边界。
- Agent 效果评估与版本回滚机制。
判断一个 Infra 该不该为某类 Agent 形态建设,标准也很简单:如果明天这种形态被替换,这套基础设施还能不能继续为别的 Agent 服务?如果答案是不能,那你建设的可能是业务代码,而不是基础设施。
这篇文章适合三类读者:第一类是正在做 Agent 应用,但被快速变化的框架和形态弄得焦虑的开发者;第二类是负责团队基础设施,需要为 Agent 场景规划技术方案的架构师;第三类是刚入门 Agent 开发,想跳过花哨概念、直接理解工程本质的新手。
2. 先看清 Agent 形态变化的本质
围绕“是什么”的问题,在 Agent 开发语境下可以从两个层面看:第一层是 Agent 的对外形态,也就是用户看到的交互方式和能力边界;第二层是 Agent 的内部工作模式,也就是它如何处理任务、调用工具、维护状态。
当前讨论比较多的形态包括:对话式单 Agent,由一个 LLM 实例承担推理和工具调用;Plan-and-Execute 模式,先规划任务再分批执行;多 Agent 协作,把复杂目标拆给多个角色分工完成;以及基于 Skill、MCP 等标准扩展能力的 Agent。形态变化频繁,本质上说明 Agent 应用还处于早期探索阶段,这也是正常的。
我的判断是:虽然这些形态在编排层差异很大,但落到 Infra 层面,它们有强共性。任何 Agent 形态都逃不开几个核心环节:接收任务、构造上下文、调用模型、执行工具、维护状态、产出结果、评估质量。你可以把形态看成“上层应用模式”,把下面这组环节看成“执行基座”。
用一个类比帮助理解:汽车的外形每年都在变,发动机技术、电池技术也在演进,但底盘、制动、转向这些基础系统需要稳定的工程标准。Agent 的形态就是外形,而 Infra 要解决的是底盘、制动和转向。
以下表格对比了常见 Agent 形态,重点看它们在 Infra 层面的共同需求:
| 形态 | 编排方式 | 典型场景 | Infra 共性需求 |
|---|---|---|---|
| 单 Agent | 一个 LLM 循环处理 | 问答、任务执行 | 生命周期、工具调用、上下文管理 |
| Plan-and-Execute | 规划器 + 执行器 | 多步骤复杂任务 | 任务状态持久化、执行恢复 |
| 多 Agent 协作 | 多个角色互相通信 | 复杂工作流 | 消息路由、全局追踪、权限隔离 |
| Skill 扩展型 | 插件化能力扩展 | 垂直领域应用 | 技能注册、依赖管理、安全校验 |
无论采用哪种形态,底层仍然需要解决相同的问题:LLM 调用不是百分百稳定的,工具执行可能失败,状态需要跨步骤保存,多步骤的执行链路需要能被追踪和回溯。这些才是 Infra 应该关注的稳定部分。
3. AI Infra 的服务对象:执行生命周期,而不是形态名称
关于“Infra 到底该为谁而建”的问题,我的观点是:为 Agent 的执行生命周期而建,而不是为某种 Agent 形态的名称而建。
原因有三层。
第一层,形态是业务层的选择,而执行生命周期是每个 Agent 都逃不掉的。你可以今天选择 ReAct,明天换成多 Agent 协作,但每一次执行都会经历“接收任务 → 构建上下文 → 循环推理 → 工具调用 → 结果返回”的流程。这个流程本身就是最稳定的 Infra 抓手。
第二层,Agent 出错的位置高度集中在执行生命周期中。从社区经常出现的错误信息就能看到,很多问题都是执行层面的:执行提供方没有及时响应,执行被异常终止,框架报错要求重新提示模型。这些不是形态问题,而是生命周期可靠性问题。
第三层,面向执行生命周期建设 Infra,才能避免与快速变化的上层框架耦合。如果 Infra 是按“多 Agent 协作”“Skill 挂载”这类具体形态设计的,那么下一个形态出现时,之前的投入就会部分失效。如果 Infra 是按“生命周期 + 可插拔能力”设计的,上层形态可以像换皮肤一样简单替换。
所以,在建任何基础设施之前,建议先定义清楚:你服务的是 Agent 的一次执行,而不是服务某一个 Agent 的外壳。执行生命周期里的每个阶段,才是你真正要做的产品。
一个典型 Agent 执行生命周期包含以下阶段:
- 接入与解析:接收用户请求,解析意图,提取任务参数。
- 上下文组装:从记忆服务、业务系统、向量库中取出必要信息。
- 模型调用:按策略调用 LLM,处理超时、限流、异常。
- 工具执行:模型决定调用工具,执行并校验返回结果。
- 状态维护:保存任务进度、中间结果、对话记忆。
- 结果评估:判断是否达成目标,是否触发重试或人工介入。
- 终态处理:输出最终结果,记录追踪日志,释放资源。
把基础设施对准这七个阶段,你得到的是一套能够兼容未来形态的稳定底座。
4. 面向 Agent 的 Infra,应该沉淀哪些能力
既然目标是为执行生命周期服务,那么能力拆分就要围绕生命周期展开。下面这六个方向,是目前 Agent Infra 建设中公认比较关键的模块。
4.1 执行引擎与生命周期管理
这是最核心的模块。执行引擎负责拉起一次 Agent 执行、调度内部步骤、处理异常、保证最终进入终态。设计时需要关注几个点:
- 执行状态机:定义 Agent 的合法状态流转,如 pending、running、waiting_tool、failed、succeeded、cancelled。
- 超时与重试:LLM 调用或工具调用超过阈值时,要有明确的降级策略。
- 隔离与并发:多任务执行时不能相互干扰,资源和上下文必须隔离。
- 断点恢复:长任务执行到一半崩溃时,能否从最后成功步骤恢复。
执行引擎不是要重复造一个框架,而是要在框架之上提供稳定约束。框架负责推理循环,Infra 负责执行治理。
4.2 记忆和状态服务
所谓 Agent 记忆,本质上就是一组对状态和上下文的读写接口,同时需要区分不同层次。短时记忆对应当前任务的上下文窗口,工作记忆对应同一会话内的多轮信息,长期记忆则跨越会话,需要持久化存储。
工程落地时,记忆服务可以考虑分层设计:
- 工作区:运行时的临时状态,任务结束可清理。
- 会话存储:按 session_id 保存交互记录。
- 长期记忆库:向量数据库加元数据索引,供后续请求检索。
重点是接口要统一。上层 Agent 不关心底层是 Redis、MySQL 还是向量库,它只需要 get、save、search 三个能力。
4.3 工具调用与标准化
工具调用是 Agent 与业务系统交互的桥梁,也是最容易失控的地方。建设 Infra 时,需要制定工具注册、参数校验、执行鉴权、结果规范化的统一标准。
具体包括:
- 工具注册中心:集中登记工具名称、描述、参数 Schema。
- 权限绑定:每个工具声明访问范围,执行前校验授权。
- 结果统一包装:把工具返回结果包装成 Agent 可消费的结构化格式。
- 熔断与限流:第三方工具不稳定时,不能拖垮整个 Agent 执行。
工具标准化还有额外好处:当新的 MCP、Skill 标准出现时,你可以在注册层做适配,而不是改动所有业务代码。
4.4 可观测性与完整链路追踪
Agent 的排查难度比传统应用高很多,原因是多步骤、多模型、多工具调用交织在一起。只有把每个环节都记录清楚,才能定位问题。
可观测性至少要覆盖四个维度:
- 日志:每次模型调用、工具调用的输入输出摘要。
- 指标:执行成功率、平均延迟、Token 消耗、工具失败率。
- 链路追踪:用 trace_id 串联一次 Agent 执行的所有步骤。
- 调用链回放:留存模型推理内容、工具执行参数,便于事后分析。
没有可观测性的 Agent Infra,就像没有仪表盘的飞机。飞得起来,但不知道什么时候会出问题。
4.5 安全与权限边界
Agent 相比传统接口多了一个不确定性来源:LLM 可能会生成意料之外的工具调用参数。基础设施必须在执行层设置安全护栏。
安全设计重点包括:
- 最小权限:Agent 默认没有权限,按任务动态授权。
- 参数校验:工具调用的参数不能直接信任模型输出,必须经过 Schema 校验。
- 敏感操作保护:删除、修改、传输类操作需要二次确认或人工审批。
- 数据隔离:不同租户、不同业务的上下文必须隔离。
- 审计日志:记录谁通过什么工具访问了什么数据。
4.6 评估与回归能力
这个模块经常被忽略,但它是 Agent 工程化最关键的环节之一。Agent 的行为有概率性,改动一个 Prompt 或换一个模型,都可能影响结果质量。没有评估体系,就无法安全迭代。
最小可行评估方案可以包括:建立评测集、定义评分标准、每次变更后跑回归、对比新旧版本效果、决定是否发布。
把评估纳入 Infra,意味着你已经意识到 Agent 不只是“调模型”,而是需要持续治理的软件系统。
5. 一个最小的 Agent Infra 设计示例
为了把抽象能力落成可运行的方案,我用一个最小示例演示:如何围绕执行生命周期来设计一套不绑定具体形态的 Agent 执行引擎。
先定义状态流转,不做复杂实现,而是给出核心抽象:
# 文件路径:agent_infra/state.py from enum import Enum class AgentState(str, Enum): PENDING = "pending" RUNNING = "running" WAITING_TOOL = "waiting_tool" FAILED = "failed" SUCCEEDED = "succeeded" CANCELLED = "cancelled"接着定义一个标准的工具调用接口。这个接口的价值在于,上层 Agent 框架可以五花八门,但最终执行工具时都走同一个入口,统一鉴权、统一校验、统一追踪:
# 文件路径:agent_infra/tool.py from dataclasses import dataclass from typing import Any, Callable, Dict from enum import Enum class ToolResultStatus(str, Enum): OK = "ok" ERROR = "error" @dataclass class ToolCall: name: str arguments: Dict[str, Any] trace_id: str session_id: str @dataclass class ToolResult: status: ToolResultStatus data: Any = None error: str = "" class ToolRegistry: """ 工具注册中心:统一管理工具元信息与执行入口。 """ def __init__(self): self._registry: Dict[str, Callable[[Dict[str, Any]], Any]] = {} def register(self, name: str, func: Callable[[Dict[str, Any]], Any]): if name in self._registry: raise ValueError(f"tool already registered: {name}") self._registry[name] = func def execute(self, call: ToolCall) -> ToolResult: func = self._registry.get(call.name) if func is None: return ToolResult(status=ToolResultStatus.ERROR, error=f"tool not found: {call.name}") try: result = func(call.arguments) return ToolResult(status=ToolResultStatus.OK, data=result) except Exception as exc: return ToolResult(status=ToolResultStatus.ERROR, error=str(exc))然后是记忆服务接口。记忆服务的核心是屏蔽底层存储差异,给 Agent 提供统一读写能力:
# 文件路径:agent_infra/memory.py from abc import ABC, abstractmethod from typing import Any, Dict, List class MemoryService(ABC): @abstractmethod def save(self, session_id: str, key: str, value: Any) -> None: ... @abstractmethod def get(self, session_id: str, key: str) -> Any: ... @abstractmethod def search(self, query: str, top_k: int = 10) -> List[Dict[str, Any]]: ... class InMemoryMemoryService(MemoryService): """ 仅用于本地演示的简单实现。 生产环境请使用 Redis、PostgreSQL、向量数据库等替换。 """ def __init__(self): self._store: Dict[str, Dict[str, Any]] = {} self._vectors: List[Dict[str, Any]] = [] def save(self, session_id: str, key: str, value: Any) -> None: self._store.setdefault(session_id, {})[key] = value if isinstance(value, str): self._vectors.append({"session_id": session_id, "key": key, "content": value}) def get(self, session_id: str, key: str) -> Any: return self._store.get(session_id, {}).get(key) def search(self, query: str, top_k: int = 10) -> List[Dict[str, Any]]: # 演示逻辑:按字符包含匹配,生产环境应替换为向量检索 results = [] for item in self._vectors: if query in item["content"]: results.append(item) if len(results) >= top_k: break return results最后,一个极简执行器。它不依赖具体 Agent 框架,而是把“模型调用 + 工具执行”循环的公共逻辑收敛起来:
# 文件路径:agent_infra/executor.py import time import uuid from typing import Callable, Dict, Any class AgentExecutor: """ 极简执行器:面向执行生命周期提供统一入口。 这里用回调模拟 LLM 调用,真实使用时替换为具体模型 API。 """ def __init__( self, llm_call: Callable[[str], str], tool_registry: Any, memory: Any, max_steps: int = 5, ): self.llm_call = llm_call self.tool_registry = tool_registry self.memory = memory self.max_steps = max_steps def run(self, user_input: str, session_id: str) -> str: trace_id = uuid.uuid4().hex self.memory.save(session_id, "user_input", user_input) context = user_input for step in range(self.max_steps): prompt = f"Task: {context}\nStep: {step}" response = self.llm_call(prompt) if response.startswith("TOOL_CALL:"): tool_name, arg_text = response.split(":", 1)[1].split("|", 1) call_result = self.tool_registry.execute( ToolCall( name=tool_name, arguments={"raw": arg_text}, trace_id=trace_id, session_id=session_id, ) ) context = f"tool result: {call_result}" continue if response.startswith("FINAL:"): return response.split(":", 1)[1] context = response return "reached max steps without final answer" def cancel(self, session_id: str) -> None: """取消会话可在这里实现,通过状态标记或消息通知执行循环停止。""" self.memory.save(session_id, "status", "cancelled")这段代码的价值不在功能完整,而是演示一种设计取向:执行器只关心生命周期和流程控制,把模型实现、工具实现、存储实现全部通过接口解耦。换个 Agent 形态时,执行器不需要重写,你只需要替换上层的规划策略或协作策略。
6. 从 Agent 执行错误反推 Infra 应该补什么
最近很多人讨论 Agent 开发,提到比较多的痛点是执行阶段不可控。比如执行提供方没有及时响应,或者执行被异常终止,又或者框架提示“可以通过重新提示模型再试一次”。
这些信息其实在给 Infra 建设提需求。
“执行提供方没有及时响应”,核心问题是超时控制缺失。Infra 必须在模型调用层设置多级超时,区分连接超时、读取超时、整体超时,并配置降级策略。
“执行异常终止”,核心问题是异常恢复缺失。长任务执行中断后要从哪里恢复,中间状态保存在哪,是否具备重放能力,这些是 Infra 要回答的。
“可以重新提示模型再试一次”,核心问题是重试策略缺失。无差别重试可能放大故障,合理的方案是设置最大重试次数、退避策略、上下文裁剪策略。
也就是说,这些报错不是孤立的玄学问题,而是可以归纳到执行生命周期治理的经典工程问题。当你面对一个新的 Agent 异常,可以先检查它落在生命周期的哪个阶段,再检查对应阶段的 Infra 能力是否覆盖。
推荐一个排查顺序:
- 上下文是否完整:trace_id、session_id 是否正确传递。
- 模型调用是否超时:检查超时参数、限流状态、模型服务健康度。
- 工具执行是否失败:检查工具注册、参数校验、依赖服务状态。
- 状态恢复是否有效:检查重试逻辑、上下文是否被正确保存。
- 评估是否触发:是否达到终态,结果质量是否可接受。
这五步可以覆盖大部分常见故障。如果五步查完还没定位,就需要回顾可观测性链路是否漏掉了关键环节。
7. 常见问题与排查方法
下面用表格把 Agent Infra 建设中常见问题、可能原因、排查方式和解决方案列出来:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| Agent 执行超时 | LLM 调用未设置合理超时 | 检查模型调用日志,确认阻塞位置 | 配置连接超时、读取超时、整体超时,并增加重试降级 |
| 执行异常终止 | 任务状态未持久化,进程重启后无法恢复 | 查看执行状态机日志,确认断点位置 | 引入持久化存储,设计断点恢复机制 |
| 工具调用总是失败 | 工具注册缺失或参数校验不通过 | 检查工具注册中心,打印参数 Schema | 统一工具注册和参数校验,增加非法参数提示 |
| 多步骤排查困难 | 日志没有关联 trace_id | 查看日志是否包含 trace_id,能否串联全链路 | 在入口生成 trace_id,所有调用透传 |
| 上下文越传越乱 | 记忆服务接口不统一 | 检查上下文保存和读取逻辑 | 抽象统一记忆接口,区分会话级和任务级状态 |
| 模型输出不稳定 | 未建立评估与回归机制 | 对比多次输出,检查 Prompt 或模型版本 | 建设评测集,每次变更跑回归 |
| Agent 权限过宽 | 工具调用缺少鉴权 | 检查工具执行日志中的权限记录 | 默认最小权限,按任务动态授权 |
| 安全问题 | 模型直接构造敏感操作参数 | 检查工具入参是否可被模型任意控制 | 增加参数白名单、敏感操作二次确认 |
8. 最佳实践与工程建议
8.1 基础设施设计面向稳定能力,不要绑定形态
建设 Infra 时,所有组件都要能回答“换个形态还能不能用”这个问题。工具注册、记忆服务、可观测性链路、权限中心都是稳定能力。而具体任务规划、Prompt 编排、Agent 角色定义则是易变部分,不要把它们写进基础设施层。
实际项目中,更推荐用接口隔离易变与稳定。比如先定义一个通用的 Agent 执行器接口,再分别实现 ReAct、Plan-and-Execute 或 Multi-Agent 变体。执行器内部可以依赖 Infra,但 Infra 不能反向依赖具体执行器。
8.2 先用最小闭环,再逐步扩展
很多团队犯的错误是一上来就搭建庞大的 Agent 编排平台。更稳妥的路径是:先跑通一个最小闭环,让一个 Agent 能稳定完成一项任务,再逐步增加记忆、评估、多 Agent 协作等能力。
最小闭环至少应该包含:
- 一个 LLM 调用封装。
- 一个工具执行入口。
- 一份执行日志。
- 一个基于评测集的效果基线。
跑通闭环之后,每加一个模块都要有明确收益。如果加了记忆功能,但场景根本不需要跨会话信息,那就是过度设计。
8.3 可观测性第一优先
Agent 场景尤其需要可观测性,因为一次执行涉及的调用数量可能是普通接口的几倍甚至几十倍。建议从第一天就把 trace_id 透传、日志结构化、调用链串联做起来。
一个实用的落地做法是:定义一份 Agent 执行日志规范,要求每次模型调用和工具调用都输出包含 trace_id、session_id、耗时、Token 消耗、输入摘要、输出摘要的 JSON 日志。之后排查问题时,直接按 trace_id 聚合查询即可。
8.4 安全边界必须内建,不能后补
由于 LLM 工具调用存在不确定性,安全边界必须在执行周期内强制校验,而不是依赖上游接口自觉。最小权限、参数白名单、敏感操作审批、审计日志,这四件事应当在第一版就设计进去。
如果等到上线后再补安全,会非常被动。因为你无法控制模型在某个时刻生成什么样的工具参数,必须在执行入口做统一拦截。
8.5 建立评测体系后再谈迭代
没有评测体系,Agent 的每一次 Prompt 调整或模型更换都是一次赌博。建议用最原始的评估方式起步:维护一份包含典型任务和预期结果的测试集,每次变更后跑一遍,记录成功率、失败样例。
评估体系不需要一开始就自动化。哪怕靠人工逐条查看测试样例,也比凭感觉迭代好得多。等测试集稳定后,再逐步引入自动化评分、回归对比、在线灰度评估。
8.6 关于 Agent 框架,保持“可替换”心态
现在 Agent 开发中会遇到类似“该不该用框架”的讨论,而实际更稳妥的策略是:框架可以选,但不要被框架锁死。把框架当作编排层的一部分,通过执行器接口与 Infra 解耦。这样即使团队更换框架,也不影响记忆服务、工具注册中心、可观测性和评估模块。
工程上没有银弹,Agent 框架也是。选择框架的关键标准是:它是否简化了你的问题,而你能否在它失效时替换掉它。如果你发现自己无法回答后者,说明框架耦合过深,这是架构风险,不是框架的缺点。
9. 总结与后续学习方向
Agent 形态还会继续更新,新的概念、新的协议、新的框架也会不断出现。如果只盯着形态变化做技术选型,基础设施很容易变成一次次推倒重来。更好的路径是把注意力放在稳定层:执行生命周期、工具调用标准、记忆服务、可观测性、安全边界、评估回滚。这六个方向,才是 Agent 应用走向工程化的真正底座。
这篇文章重点讲清了三个问题:Agent 形态变化的本质是应用层创新,Infra 更应该围绕执行生命周期来建设,以及如何用最小设计思路搭建不绑定具体形态的基础设施。读者可以先用文中的最小执行器示例跑通闭环,再逐步补充记忆和评估模块,同时把可观测性和安全边界作为第一优先级。
如果你想继续深入,可以在下面几个方向做下一步实践:
- 选择一个开源 Agent 框架,尝试把它的执行循环接入你自己的执行器接口。
- 整理一份你自己的 Agent 评测集,哪怕只有 20 条真实任务,后续也会很有价值。
- 为当前项目补齐 trace_id 链路透传和结构化日志,先解决“能不能查”的问题。
- 认真过一遍工具调用的权限模型,确保每个工具都有明确的访问边界和审计记录。
如果这篇文章能帮你少走一点弯路,建议收藏备用。后续遇到 Agent 执行不稳定、排查困难、形态频繁变化的问题时,可以回到这套思路重新评估:你的 Infra,到底是在为形态服务,还是为执行稳定性服务。