☰
Agent Infra:智能体基础设施的工程实践与核心模块拆解
2026/10/20 14:22:52 网站建设 项目流程

1. 为什么“Agent Infra”成了大厂不愿明说的暗线

“Agent Infra”这个词,最近一年在技术圈里出现的频率越来越高,但真正把它讲清楚的人并不多。很多人第一次听到会以为是“智能体基础设施”的缩写,然后下意识觉得这又是一个被包装出来的概念。但如果你真正动手搭过一个能跑起来的智能体系统,就会发现事情远没有那么简单——它背后牵扯的是一整套工程体系的重新组织,而不是加一个API调用那么简单。

我先把结论放在前面:Agent Infra本质上是一套让智能体能够稳定、可观测、可扩展地运行在真实业务场景中的工程底座。它要解决的问题,不是“模型能不能回答”,而是“模型回答完之后,谁来记住上下文、谁来调度工具、谁来控制成本、谁来保证不出事”。这些问题在Demo阶段可以忽略,一旦进入生产环境,每一个都会变成拦路虎。

那为什么说它是“巨头的隐秘战场”?因为大模型本身的能力正在快速趋同,头部几家在基础模型上的差距越来越小,真正拉开产品体验差距的,是谁能把智能体跑得更稳、更便宜、更可控。而这个能力,恰恰不体现在模型参数上,而是体现在基础设施层。某头部平台内部做过一个统计,同一个模型在同样的任务上,经过不同Infra层调度后,任务完成率能差出30个百分点以上。这个差距,就是Infra的价值。

至于“每个工程师的入场券”,我的理解是:过去几年,工程师的核心竞争力在于会不会调模型、会不会写Prompt。但接下来,能不能设计一套让智能体可靠运转的系统,会成为区分普通开发者和高级工程师的分水岭。这不是危言耸听,而是我过去一年在多个项目中反复验证过的现实。你不需要成为算法专家,但你必须理解上下文管理、工具编排、状态持久化、失败重试这些工程问题。

这篇文章适合三类人看:第一类是想从传统后端转向智能体方向的工程师,第二类是在团队里负责搭建智能体平台的Tech Lead,第三类是产品经理或技术负责人,想搞清楚Agent Infra到底包含哪些模块、值不值得投入。我会从整体设计思路讲到具体实现细节,再到踩坑经验,尽量把每个环节都拆开讲透。

2. 拆解Agent Infra的核心模块与设计取舍

2.1 一个智能体系统到底由哪些层组成

很多人一上来就写Agent逻辑,结果写到一半发现上下文爆炸、工具调用混乱、错误无法追踪。根本原因是没有先把分层想清楚。我习惯把Agent Infra分成五层来看,从下到上依次是:

  • 模型接入层:负责统一封装不同模型的调用接口,处理鉴权、限流、重试、降级。这一层的核心目标是让上层不感知具体用的是哪个模型。
  • 上下文管理层:负责对话历史、记忆、知识片段的存储与检索。这是最容易被低估的一层,也是决定智能体“聪不聪明”的关键。
  • 工具编排层:负责注册工具、解析调用意图、执行工具、处理返回结果。这一层决定了智能体能做多少事。
  • 状态与流程层:负责多步任务的编排、状态机管理、中断恢复。这一层决定了智能体能不能处理复杂任务。
  • 可观测与治理层:负责日志、追踪、成本统计、安全过滤。这一层决定了系统能不能上生产。

这五层不是必须严格分开的,但你在设计时脑子里一定要有这张图。我见过太多项目把工具编排和状态管理揉在一起,结果后期想加一个“任务中断后恢复”的功能,发现代码根本改不动。

2.2 为什么不能直接用现成框架

市面上已经有不少智能体框架,功能看起来很全,但我在实际项目中很少直接拿来用。原因有三个:

第一,抽象层级不匹配。很多框架为了通用性,把接口设计得非常抽象,导致你想做一个稍微定制化的逻辑,就要绕很大一圈。比如你想在工具调用前加一个权限校验,在某些框架里需要继承三四个类才能实现。

第二,可观测性不足。Demo阶段你不需要知道每一步发生了什么,但生产环境里,你必须能回答“这次调用为什么失败”“这个结果是怎么来的”。很多框架的日志粒度不够细,出了问题只能靠猜。

第三,成本不可控。智能体系统最容易失控的就是Token消耗。一个没有做上下文裁剪和缓存策略的系统,跑一天下来成本可能是预期的五到十倍。现成框架往往不会帮你处理这些。

所以我的建议是:框架可以用来参考设计思路,但核心的上下文管理和工具编排层,最好自己实现。这两层的代码量其实不大,但定制化需求最多,自己掌控才踏实。

2.3 设计时最容易犯的三个错误

第一个错误是把上下文当成简单的消息列表。很多人就是把历史消息拼成一个数组,每次全量传给模型。这在对话轮次少的时候没问题,一旦超过十轮,Token消耗会急剧上升,而且模型对早期信息的注意力会下降。正确的做法是分层管理:近期消息保留原文,中期消息做摘要,远期信息存入向量库按需检索。

第二个错误是工具注册没有版本管理。你今天注册了一个“查询订单”的工具,明天业务改了接口返回格式,如果没有版本管理,所有依赖这个工具的智能体都会出问题。我的做法是每个工具都带一个版本号,智能体配置里指定用哪个版本,升级时灰度切换。

第三个错误是忽略幂等性设计。智能体调用工具时,可能因为超时重试导致同一个操作执行两次。如果这个操作是“扣款”或者“发消息”,后果就很严重。所以每个工具都要考虑幂等键的设计,这一点后面会详细讲。

3. 上下文管理:决定智能体智商的关键工程

3.1 上下文窗口的分配策略

上下文管理是Agent Infra里最核心也最容易被做砸的部分。我先给一个我常用的分配比例,你可以根据自己的场景调整:

上下文区域建议占比说明
系统指令10%角色定义、行为约束、输出格式要求
近期对话30%最近3-5轮完整对话
中期摘要20%更早对话的压缩摘要
检索知识25%从向量库或知识库检索的相关片段
工具定义10%当前可用工具的描述
预留空间5%防止突发长输入导致溢出

这个比例不是固定的,但核心思路是:不要让任何一类信息独占上下文。我见过一个项目把工具定义写了两千多Token,结果留给对话的空间只剩一点点,智能体表现得像个失忆患者。

具体实现上,我会写一个ContextBuilder类,它接收当前对话状态,按照优先级依次填充各个区域,最后检查总Token数是否超限。如果超限,就从优先级最低的区域开始裁剪。裁剪的顺序是:先压缩中期摘要,再减少检索片段数量,最后才动近期对话。

3.2 摘要压缩的实操方法

摘要压缩听起来简单,做起来有很多细节。我的做法是分两步:

第一步是触发时机。不是每轮对话都做摘要,而是当对话轮次超过阈值(比如8轮)或者Token数超过阈值(比如上下文窗口的40%)时才触发。触发后,把最早的三轮对话拿出来做摘要。

第二步是摘要生成。这里有个坑:如果你直接用模型来生成摘要,摘要本身也会消耗Token,而且可能丢失关键信息。我的做法是用一个轻量模型来做摘要,并且给摘要模型一个明确的模板:

SUMMARY_PROMPT = """ 请将以下对话压缩成不超过100字的摘要,必须保留: 1. 用户明确提出的需求或问题 2. 已经确认的关键信息(如订单号、时间、金额) 3. 尚未解决的问题 不要包含寒暄和重复内容。 对话内容: {conversation} """

实测下来,用轻量模型做摘要,成本只有主模型的十分之一左右,而且因为任务简单,效果反而更稳定。摘要生成后,我会把它存到一个summary字段里,和原始对话分开存储,这样既保留了可追溯性,又不会占用上下文。

3.3 记忆检索的触发条件设计

记忆检索是另一个容易做过头的地方。有些系统每轮对话都去向量库检索,结果检索回来的片段和当前问题根本不相关,反而干扰了模型判断。我的经验是:只在需要的时候检索。

判断“需要”的条件有三个:一是用户问题里出现了明确的实体(如订单号、产品名),二是当前对话中缺少回答所需的信息,三是用户明确要求“回忆”或“之前说过”。这三个条件满足任意一个,才触发检索。

检索时也有技巧。不要只用一个查询向量去搜,而是把用户问题拆成多个子查询,分别检索后再合并去重。比如用户问“我上周买的那个东西发货了吗”,可以拆成“上周的订单”和“发货状态”两个查询。这样召回率会明显提升。

注意:检索回来的片段一定要做相关性过滤。我通常会用一个小模型给每个片段打一个0到1的相关性分数,低于0.6的直接丢弃。这一步能过滤掉大量噪音。

4. 工具编排与状态管理:让智能体真正能干活

4.1 工具注册的标准化设计

工具编排的第一步是工具注册。我见过很多项目把工具写成一堆散落的函数,调用时靠if-else判断,后期维护极其痛苦。正确的做法是定义一个标准的工具描述结构:

class ToolDefinition: name: str # 工具唯一标识 version: str # 版本号 description: str # 给模型看的描述 parameters: dict # JSON Schema格式的参数定义 handler: Callable # 实际执行函数 idempotent: bool # 是否幂等 timeout: int # 超时时间(秒) retry_policy: dict # 重试策略

这个结构里,description和parameters是给模型看的,决定了模型能不能正确调用;handler和retry_policy是给执行引擎用的,决定了调用能不能稳定完成。两者要分开设计,不要混在一起。

工具描述怎么写很关键。我的经验是:描述里要包含“什么时候用”和“什么时候不用”。比如一个“查询天气”的工具,描述里要写“当用户询问某地天气时使用;当用户只是闲聊提到天气时不要使用”。这样能显著减少误调用。

4.2 多步任务的编排模式

智能体处理复杂任务时,往往需要多步操作。这里有两种主流模式:一种是ReAct模式,即推理-行动循环,模型每一步都决定下一步做什么;另一种是Plan-and-Execute模式,先让模型制定完整计划,再逐步执行。

两种模式各有优劣。ReAct灵活,能根据中间结果调整策略,但容易陷入循环;Plan-and-Execute可控,但计划一旦制定就不好调整。我的做法是混合使用:先用Plan-and-Execute生成一个粗粒度计划,然后每一步执行时用ReAct做细粒度调整。

具体实现上,我会维护一个任务状态机:

class TaskState: plan: list # 整体计划 current_step: int # 当前步骤 step_results: dict # 每步结果 status: str # running/paused/completed/failed context: dict # 任务级上下文

每一步执行前,检查status;执行后,更新step_results并判断是否需要调整计划。如果某一步失败,根据失败类型决定是重试、跳过还是终止整个任务。

4.3 中断恢复的工程实现

中断恢复是生产环境必须考虑的问题。用户可能中途关闭页面,或者网络断开,或者任务执行时间太长需要异步处理。如果没有恢复机制,用户回来发现任务丢了,体验会很差。

我的实现方案是:每一步执行完后,把TaskState序列化存到数据库。存储的粒度要细,不能只存最终结果。恢复时,从数据库读取最新的TaskState,检查current_step和step_results,从断点继续执行。

这里有个细节:如果中断发生在工具调用过程中,恢复时要判断这个调用是否已经产生了副作用。这就是为什么前面强调工具要幂等。对于非幂等的工具,我会在调用前先写一条“调用中”的记录,调用成功后更新为“已完成”,恢复时根据这个记录决定是否重新调用。

提示:序列化TaskState时,不要把大对象(如完整的对话历史)塞进去,只存必要的引用和摘要。否则数据库会膨胀得很快。

5. 可观测性与成本控制:上生产前的必修课

5.1 追踪体系的搭建

智能体系统的调试难度远高于传统系统,因为它的行为是不确定的。同一个输入,两次运行可能走不同的路径。所以追踪体系必须从第一天就搭好。

我的做法是给每次请求分配一个trace_id,然后在每个关键节点打点:模型调用、工具调用、上下文构建、摘要生成。每个打点记录输入、输出、耗时、Token消耗。这些数据汇总到一个追踪表里,出问题时可以按trace_id把整条链路串起来看。

追踪数据不要只存日志文件,要存到可查询的存储里。我通常用一张宽表,字段包括trace_id、step、input_summary、output_summary、latency_ms、token_in、token_out、timestamp。这样既能做实时监控,也能做离线分析。

5.2 Token成本的控制手段

Token成本是智能体系统最容易失控的地方。我总结了几个有效的控制手段:

第一,缓存。相同的系统指令和工具定义,不要每次都重新计算Token,可以缓存起来。对于频繁出现的用户问题,也可以缓存模型回答。

第二,模型分级。不是所有任务都需要用最强的模型。意图识别、摘要生成、相关性打分这些任务,用轻量模型就够了。只有核心推理环节才用大模型。

第三,输出长度限制。给模型设置max_tokens,并且在系统指令里明确要求“简洁回答”。我见过一个项目因为没限制输出长度,模型每次回答都写一大段,成本直接翻倍。

第四,批量处理。如果有多个独立的子任务,可以合并成一次模型调用,让模型一次性输出所有结果。这样能减少调用次数和系统指令的重复消耗。

5.3 安全过滤的落地方式

安全过滤不是加一个关键词黑名单就完事了。智能体系统面临的风险包括:提示注入、工具滥用、数据泄露。我的做法是分三层过滤:

第一层是输入过滤,在用户输入进入系统前,检测明显的注入模式,比如“忽略之前的指令”这类话术。第二层是工具调用过滤,在工具执行前,检查参数是否合法、是否有权限。第三层是输出过滤,在返回给用户前,检查是否包含敏感信息。

这三层过滤都要记录日志,但不要因为过滤失败就完全拒绝服务,而是降级处理。比如输入过滤命中后,可以给模型加一个更强的约束指令,而不是直接报错。

6. 常见问题与排查技巧实录

6.1 智能体陷入循环怎么办

这是最常见的问题。智能体反复调用同一个工具,或者反复说同一句话。排查思路是:先看追踪日志,确认循环发生在哪一步;然后检查工具返回结果是否为空或异常,导致模型认为需要重试;最后检查系统指令里是否有“必须完成”这类强制要求,导致模型不敢停止。

解决方法有三个:一是设置最大步数限制,超过就强制终止;二是给工具返回结果加上明确的“成功/失败”标识,让模型知道什么时候该停;三是在系统指令里加入“如果连续两次得到相同结果,请停止并告知用户”。

6.2 工具调用参数错误怎么排查

参数错误通常是因为工具描述不够清晰,或者参数Schema定义有歧义。排查时,先把模型实际生成的参数打印出来,和Schema对比。如果发现模型经常填错某个字段,就在描述里加一个示例。

我遇到过一个案例:一个日期参数,模型总是填成“今天”而不是具体日期。后来在描述里加了“请填写YYYY-MM-DD格式的具体日期,不要使用相对时间”,问题就解决了。

6.3 上下文溢出如何提前发现

上下文溢出不会报错,但会导致模型表现下降。我的做法是在ContextBuilder里加一个检查:如果总Token数超过窗口的85%,就打一条警告日志,并记录当前的分配情况。这样可以在问题恶化前发现。

另外,我会定期分析追踪数据,看哪些请求的上下文占用最高,然后针对性优化。比如某个工具的描述特别长,就考虑精简;某类检索片段总是被裁剪,就考虑调整检索策略。

6.4 常见问题速查表

问题现象可能原因排查方法解决手段
智能体反复调用同一工具工具返回结果不明确查看工具返回内容增加成功/失败标识,设置最大步数
回答质量突然下降上下文溢出或摘要丢失关键信息检查Token占用和摘要内容调整分配比例,优化摘要模板
工具调用参数错误描述不清或Schema有歧义对比实际参数和Schema补充示例,明确格式要求
成本异常升高缓存失效或模型分级失效分析Token消耗分布检查缓存策略,确认模型路由
任务中断后无法恢复状态未持久化或幂等性缺失检查TaskState存储记录细化存储粒度,补充幂等键

7. 从零搭建一个最小可用Agent Infra的实操路径

7.1 第一周:搭骨架

第一周的目标是让一个最简单的智能体跑起来。不要追求功能完整,先把五层结构搭出来。模型接入层先接一个模型,上下文管理层先用最简单的消息列表,工具编排层先注册一个工具,状态层先不做,可观测层先打日志。

这一周的关键是跑通链路。你会遇到各种环境问题、鉴权问题、格式问题,这些都是正常的。我的经验是,第一周不要写太多代码,多花时间在调试和验证上。

7.2 第二周:加上下文管理

第二周开始做上下文管理。先实现分层分配,再实现摘要压缩,最后实现记忆检索。每加一个功能,都要用追踪数据验证效果。比如加了摘要压缩后,看Token消耗是否下降,回答质量是否保持。

这一周最容易犯的错误是过度设计。不要一上来就做复杂的向量检索,先用简单的关键词匹配,验证流程通了再升级。

7.3 第三周:完善工具编排

第三周重点做工具编排。把工具注册标准化,实现多步任务编排,加上中断恢复。这一周的工作量最大,因为要处理各种边界情况。

我的建议是先做单工具多步,再做多工具协作。单工具多步相对简单,能帮你把状态机跑通。多工具协作涉及工具选择、参数传递、结果合并,复杂度高很多。

7.4 第四周:上可观测和治理

最后一周做可观测和治理。把追踪体系搭起来,加上成本统计和安全过滤。这一周的工作看起来不直接产生功能,但决定了系统能不能上生产。

做完这四周,你就有了一套最小可用的Agent Infra。接下来就是根据业务需求不断迭代。记住一个原则:先让系统跑起来,再让它跑得稳,最后让它跑得便宜。顺序不要颠倒。

我在实际项目中的体会是,Agent Infra的搭建过程很像盖房子。地基没打好,上面装修得再漂亮也住不久。而地基的工作,恰恰是最不显眼、最容易被跳过的。如果你正在做智能体相关的项目,建议先把这篇文章里的几个核心模块对照检查一遍,看看哪些地方还有缺口。补上这些缺口,比加新功能重要得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询