1. 从“模型崇拜”到“基建觉醒”:为什么Agent Infra成了新战场
过去两年,大家的目光几乎都聚焦在模型本身——参数规模、榜单排名、推理能力。但如果你最近半年跟一线做AI应用的团队聊过,会发现一个微妙的变化:大家不再只聊“用哪个模型”,而是越来越多地聊“怎么把模型用起来”。这个“用起来”的过程,就是Agent Infra要解决的核心问题。
所谓Agent Infra,我把它理解为“智能体基础设施”。它不是某个具体的模型,也不是某个单一工具,而是一整套让AI Agent能够稳定、高效、可观测地完成复杂任务的底层支撑体系。你可以把它想象成一家餐厅的后厨:模型是那位手艺精湛的大厨,但如果没有备菜区、传菜通道、洗碗间、库存管理系统,大厨再厉害也只能一次做一道菜,而且做完就乱。Agent Infra就是那个让大厨能同时处理几十道菜、还能保证不出错的后厨体系。
为什么说这是“巨头的隐秘战场”?因为模型层的竞争已经进入白热化,参数和榜单的差距在缩小,而真正决定一个AI产品能不能落地、能不能规模化、能不能赚钱的,恰恰是中间这层基础设施。谁把Agent Infra做扎实了,谁就能让开发者在自己的生态里“住下来”,形成迁移成本。这就像当年云计算的竞争,表面上是卖算力,实际上是卖一整套开发、部署、运维的便利性。
那为什么又说这是“每个工程师的入场券”?因为Agent Infra涉及的技术栈非常宽——任务编排、工具调用、记忆管理、上下文压缩、错误恢复、可观测性、成本控制——这些都不是靠调一个API就能解决的。它需要工程师真正理解分布式系统、状态机、缓存策略、异步任务这些“老手艺”,同时又要对LLM的行为特性有直觉。换句话说,这是传统后端工程能力与AI应用理解的一次交汇,而目前同时具备这两方面经验的人并不多。
这篇文章,我想从一线实操的角度,把Agent Infra拆开来讲清楚:它到底包含哪些模块,每个模块的核心难点是什么,有哪些常见的坑,以及一个普通工程师可以从哪里切入。我不会只讲概念,而是会给出具体的方案对比、参数选择逻辑和排查技巧。如果你正在做AI应用,或者想从传统后端转向AI基础设施,这篇内容应该能帮你省下不少试错时间。
2. Agent Infra的核心模块拆解与选型逻辑
2.1 任务编排引擎:Agent的“中枢神经”
任务编排是Agent Infra最核心的部分。一个Agent接到用户请求后,需要决定:先做什么、后做什么、什么时候调用工具、什么时候向用户确认、失败了怎么重试。这个过程如果用代码硬编码,很快就会变成一团乱麻。所以我们需要一个编排引擎。
目前主流的编排思路有三种。第一种是基于状态机的显式编排,比如用LangGraph这类框架,把每个步骤定义成节点,用边来表示跳转条件。这种方式的优点是可控性强,每一步都看得见,适合流程相对固定的场景,比如客服工单处理、数据ETL流水线。缺点是灵活性差,遇到没预料到的分支就容易卡住。
第二种是基于ReAct循环的隐式编排,也就是让模型自己决定下一步做什么,通过“思考-行动-观察”的循环来推进。这种方式灵活,适合探索性任务,比如研究型Agent、开放式问答。但缺点是容易陷入死循环,或者调用一些不该调用的工具,而且调试起来很痛苦,因为每次运行的路径可能都不一样。
第三种是混合模式,也是我在实际项目中用得最多的:用状态机定义主干流程,在关键节点嵌入ReAct循环。比如一个合同审核Agent,主干流程是“读取合同→提取条款→比对规则库→生成报告”,这是固定的;但在“提取条款”这一步,可以用ReAct循环让模型自己决定要不要调用OCR工具、要不要查历史案例。这样既保证了整体可控,又保留了局部灵活性。
选型的时候,我一般会问三个问题:这个任务的步骤是否可枚举?失败后的恢复策略是否明确?是否需要人工介入?如果三个答案都是“是”,那就用状态机;如果都是“否”,那就用ReAct;如果介于两者之间,就上混合模式。
注意:不要一上来就追求“全自动”。很多团队在早期为了演示效果,让Agent完全自主决策,结果上线后各种意外。我的经验是,先做“人机协同”的编排,把人工确认节点显式地画在状态机里,等跑顺了再逐步减少人工介入。
2.2 工具调用层:让Agent真正“动手”
Agent和聊天机器人最大的区别,就是它能调用外部工具。但工具调用这件事,坑非常多。首先是工具描述的质量。很多人写工具描述就写一句“查询天气”,然后抱怨模型总是调错。实际上,工具描述应该包含:功能说明、参数格式、返回值示例、什么情况下用、什么情况下不用。我通常会写成一个结构化的JSON Schema,并且在description里用自然语言补充边界条件。
其次是工具的数量控制。我见过一个Agent挂了四十多个工具,结果模型的选择准确率急剧下降。后来我们做了分组,把工具按场景分成“订单相关”“用户相关”“物流相关”,每次只给模型暴露当前场景下的工具子集,准确率立刻回升。一般来说,单次暴露给模型的工具不要超过15个,超过就要考虑分层路由。
第三是工具调用的幂等性。Agent可能会因为重试而重复调用同一个工具,如果这个工具是“创建订单”或者“发送短信”,就会出问题。所以每个工具都要设计成幂等的,或者引入一个去重机制。我们当时的做法是给每个工具调用生成一个唯一的request_id,在服务端做去重缓存,缓存时间设为任务的最大超时时间。
还有一个容易被忽略的点:工具调用的超时和熔断。Agent调用外部API时,如果对方挂了,不能让整个Agent卡死。我们给每个工具设置了独立的超时时间(一般3-10秒),并且用熔断器模式,连续失败5次就暂时禁用该工具,过30秒再半开重试。这些参数需要根据实际SLA来调,没有万能值。
2.3 记忆与上下文管理:Agent的“工作记忆”
Agent的记忆系统通常分三层:短期记忆(当前对话的上下文)、长期记忆(跨会话的知识)、工作记忆(当前任务的中间状态)。这三层的管理策略完全不同。
短期记忆最直接的问题就是上下文窗口有限。一个长对话很容易超过模型的token限制。常见的做法是滑动窗口加摘要:保留最近N轮对话,更早的对话用模型生成摘要。但这里有个细节——摘要的粒度。如果摘要太粗,会丢失关键信息;如果太细,又起不到压缩作用。我的经验是,按“任务阶段”来摘要,而不是按固定轮数。比如一个订票任务,可以分成“确认需求”“查询航班”“选择航班”“填写信息”“支付”几个阶段,每个阶段结束后生成一个阶段摘要,这样既压缩了上下文,又保留了任务脉络。
长期记忆一般用向量数据库来做语义检索。但这里有个常见误区:把所有历史对话都塞进向量库。结果检索出来的内容噪音很大,反而干扰模型。正确的做法是只存“值得记住”的内容,比如用户的偏好、重要的事实、成功的解决方案。我们当时用了一个简单的规则:如果用户明确说“记住这个”或者某个信息在后续对话中被重复引用,就存入长期记忆。另外,长期记忆需要定期清理和去重,否则会越来越臃肿。
工作记忆是任务执行过程中的临时状态,比如“当前已经收集到的参数”“已经调用过的工具”“待确认的问题”。这部分我建议用结构化的方式存储,比如一个JSON对象,而不是纯文本。这样在恢复任务或者调试的时候,能一眼看清当前状态。我们当时用Redis存工作记忆,设置合理的TTL,任务完成后自动过期。
实操心得:上下文压缩不要等到快满了才做。我一般会在上下文用到70%的时候就开始触发摘要,留出缓冲空间。另外,摘要生成最好用便宜的小模型,不要用最贵的那个,因为摘要本身不需要太强的推理能力。
2.4 可观测性与评估:Agent的“仪表盘”
Agent系统最让人头疼的就是“黑盒感”。用户说“它刚才为什么那么做”,你很难回答。所以可观测性是Agent Infra里不可或缺的一环。我通常会把可观测性分成三个层次:日志、追踪、评估。
日志是最基础的,但Agent的日志不能只记“输入什么输出什么”,还要记录:每一步的决策理由、调用了哪些工具、每个工具的耗时和结果、token消耗、成本。我们当时用OpenTelemetry做埋点,把每个Agent任务当成一个trace,每个步骤当成一个span,这样在Jaeger里就能看到完整的调用链。
追踪之上是评估。评估分两种:在线评估和离线评估。在线评估是在真实流量中抽样,用规则或者小模型判断这次任务是否成功。离线评估是构建一个测试集,定期跑回归。这里的关键是定义清楚“成功”的标准。比如一个客服Agent,成功可以定义为“用户问题解决且没有转人工”;一个数据分析Agent,成功可以定义为“生成的SQL能正确执行且结果符合预期”。标准不同,优化方向就不同。
我还想强调一点:成本可观测性。Agent的token消耗可能比普通对话高一个数量级,因为有多轮思考和工具调用。如果不监控成本,很容易出现“一个任务烧掉几块钱”的情况。我们当时给每个任务设置了token预算,超过预算就触发告警或者降级到更便宜的模型。这个预算需要根据业务价值来定,比如一个高价值的销售线索挖掘任务,可以给到5万token;一个简单的问答,给2000token就够了。
3. 从零搭建一个Agent Infra的实操路径
3.1 环境准备与技术栈选型
假设你现在要从零开始搭一个Agent Infra,我会建议这样的技术栈组合:编排层用LangGraph或者自己写一个轻量状态机(如果团队对Python生态不熟,用TypeScript写也可以);工具调用层用OpenAI的Function Calling格式或者Anthropic的Tool Use格式,这两个是目前事实上的标准;记忆层用Redis做短期和工作记忆,用Pgvector或者Qdrant做长期记忆;可观测性用OpenTelemetry加Grafana或者Langfuse。
这里我想特别说一下为什么推荐Pgvector而不是专门的向量数据库。对于大多数中小规模应用(百万级向量以内),Pgvector的性能完全够用,而且它和业务数据在同一个数据库里,做联合查询非常方便。比如你要查“某个用户的历史偏好”,可以直接用SQL join,不用跨系统同步。只有到了亿级向量、需要极致检索性能的时候,才需要考虑专门的向量数据库。
环境准备的具体步骤:先装Python 3.11以上(3.12对异步支持更好),然后用poetry或者uv管理依赖。我最近在用uv,安装速度比pip快很多,而且锁文件更可靠。核心依赖包括:langgraph、langchain-core、openai、redis、psycopg2、pgvector、opentelemetry-sdk。如果是生产环境,还要加上fastapi做API层,celery或者arq做异步任务队列。
注意:不要一上来就上Kubernetes。我见过不少团队,Agent还没跑通就先搭了一套K8s,结果大部分时间都在调网络和存储。先用Docker Compose把服务跑起来,等QPS上来了再考虑编排系统。
3.2 核心模块的代码实现与参数配置
先看编排引擎的最小实现。我用LangGraph定义一个简单的状态机,包含“理解意图”“调用工具”“生成回复”三个节点。关键代码如下:
from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated import operator class AgentState(TypedDict): messages: Annotated[list, operator.add] next_step: str tool_calls: list def understand_intent(state: AgentState): # 调用LLM判断用户意图 response = llm.invoke(state["messages"]) if response.tool_calls: return {"next_step": "call_tool", "tool_calls": response.tool_calls} return {"next_step": "generate_reply"} def call_tool(state: AgentState): results = [] for tool_call in state["tool_calls"]: result = execute_tool(tool_call) results.append(result) return {"messages": [{"role": "tool", "content": str(results)}], "next_step": "generate_reply"} def generate_reply(state: AgentState): response = llm.invoke(state["messages"]) return {"messages": [response], "next_step": END} graph = StateGraph(AgentState) graph.add_node("understand_intent", understand_intent) graph.add_node("call_tool", call_tool) graph.add_node("generate_reply", generate_reply) graph.set_entry_point("understand_intent") graph.add_conditional_edges("understand_intent", lambda x: x["next_step"]) graph.add_edge("call_tool", "generate_reply") graph.add_edge("generate_reply", END) app = graph.compile()这段代码看起来简单,但有几个参数需要仔细调。第一个是LLM的temperature。对于意图理解节点,我建议用0或者0.1,保证决策稳定;对于生成回复节点,可以用0.7左右,让回复更自然。第二个是max_tokens。意图理解节点不需要太长输出,设256就够了;生成回复节点根据业务需求设,一般1024-2048。第三个是超时时间。每个LLM调用都要设超时,我一般设30秒,超过就重试或者降级。
工具调用的实现要注意错误处理。下面是一个带重试和熔断的工具执行函数:
import time from functools import wraps def with_retry(max_retries=3, backoff=1.5): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): last_exception = None for attempt in range(max_retries): try: return func(*args, **kwargs) except Exception as e: last_exception = e wait = backoff ** attempt time.sleep(wait) raise last_exception return wrapper return decorator @with_retry(max_retries=3) def execute_tool(tool_call): tool_name = tool_call["name"] tool_args = tool_call["args"] # 根据tool_name路由到具体实现 if tool_name == "query_weather": return query_weather(**tool_args) elif tool_name == "search_database": return search_database(**tool_args) else: raise ValueError(f"Unknown tool: {tool_name}")记忆管理的实现,短期记忆用Redis的List结构,每个会话一个key,设置TTL为24小时。长期记忆用Pgvector,建表语句如下:
CREATE TABLE long_term_memory ( id SERIAL PRIMARY KEY, user_id VARCHAR(64) NOT NULL, content TEXT NOT NULL, embedding vector(1536), created_at TIMESTAMP DEFAULT NOW(), last_accessed TIMESTAMP DEFAULT NOW() ); CREATE INDEX ON long_term_memory USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);这里的lists参数需要根据数据量来调。一般来说,lists设为sqrt(行数)比较合适。比如10万行数据,lists设316左右。太小会导致检索慢,太大会导致召回率下降。
3.3 部署与性能调优的关键参数
部署Agent服务时,有几个参数直接决定性能和成本。第一个是并发数。Agent任务通常是IO密集型的(等LLM响应、等工具返回),所以可以用异步IO来提高并发。我用FastAPI加uvicorn,workers设为CPU核数的2倍,然后用asyncio.Semaphore限制同时进行的LLM调用数,一般设为10-20,具体取决于你的API配额。
第二个是缓存策略。LLM调用很贵,能缓存就缓存。我通常做两级缓存:第一级是精确缓存,用Redis存“prompt hash → response”,TTL设1小时;第二级是语义缓存,用向量检索找相似的历史prompt,如果相似度超过0.95就直接返回历史结果。语义缓存能把重复问题的成本降低80%以上,但要注意设置合理的相似度阈值,太低会导致答非所问。
第三个是降级策略。当API限流或者成本超预算时,要有降级方案。我的做法是准备一个“小模型池”,比如用7B级别的本地模型处理简单任务,复杂任务才走大模型。判断标准可以用规则(比如token数小于500且不涉及工具调用)或者用小模型自己判断。
实操心得:部署时一定要加请求队列。我见过太多服务因为突发流量直接把API配额打满,然后所有请求都失败。用Redis或者RabbitMQ做一个缓冲队列,队列满了就返回“系统繁忙,请稍后重试”,比全部超时要好得多。
4. 常见问题与排查技巧实录
4.1 Agent陷入死循环怎么办
这是最常见的问题。Agent反复调用同一个工具,或者在不同节点之间来回跳转。排查思路分三步:先看日志,确认是哪个节点在循环;再看状态,检查工作记忆里是不是有某个条件一直没满足;最后看提示词,是不是工具描述或者系统提示里有歧义。
我遇到过一个典型案例:Agent在“查询订单”和“确认订单状态”之间反复跳。原因是工具返回的订单状态是“处理中”,而系统提示里写的是“如果订单状态不是‘已完成’,就继续查询”。模型就一直在查。解决办法是在提示词里加一句“如果连续两次查询结果相同,则停止查询并告知用户当前状态”。另外,在编排层加一个最大循环次数限制,比如同一个节点最多执行5次,超过就强制跳出。
还有一个隐蔽的死循环原因:工具返回的内容被截断了。比如工具返回了一个很长的JSON,但你的代码只取了前1000个字符,导致模型看不到关键字段,以为工具没返回,就反复调用。所以工具返回值的处理一定要完整,如果太长就做摘要,但不要静默截断。
4.2 工具调用准确率低怎么优化
工具调用准确率低通常有三个原因:工具描述不清、工具太多、模型能力不够。优化顺序应该是:先改描述,再减数量,最后才考虑换模型。
改描述的具体做法:把每个工具的description写成“功能+参数+返回值+使用场景+反例”。比如“查询天气”可以写成:“根据城市名查询当前天气。参数:city(字符串,城市中文名)。返回:温度、湿度、天气状况。使用场景:用户询问某地天气时。不要用于查询历史天气或未来预报。”这样模型就不容易搞混。
减数量的做法:按场景分组,每次只暴露相关工具。我们当时做了一个“工具路由器”,先用一个小模型判断用户意图属于哪个场景,然后只加载该场景的工具。准确率从62%提升到了89%。
如果以上都做了还是不行,再考虑换模型。一般来说,GPT-4级别的模型在工具调用上明显优于小模型,但成本也高。可以用“大模型决策+小模型执行”的混合模式。
4.3 上下文超限的应急处理
上下文超限是长对话的必然问题。应急处理方案有三种:截断、摘要、外置。截断最简单,但会丢失信息;摘要需要额外调用LLM,有成本;外置是把部分上下文存到外部存储,需要时再检索回来。
我的建议是组合使用:最近5轮对话保留原文,5-20轮做摘要,20轮以上的只保留关键事实(用规则提取,比如订单号、日期、金额)。另外,工具返回的大段内容不要直接塞进上下文,而是先存到工作记忆里,只在上下文里放一个引用ID,需要时再取。
还有一个技巧:用系统提示来“提醒”模型。在系统提示里加一句“如果用户提到之前的内容,请先调用recall_memory工具获取历史信息”。这样模型就不会因为看不到历史而胡编。
4.4 成本失控的排查与止损
成本失控通常表现为:某个任务消耗了异常多的token,或者某个工具被频繁调用。排查方法是看可观测性面板,按任务类型、用户、时间段分组统计token消耗。
止损措施分短期和长期。短期:给每个任务设token上限,超过就强制结束并返回“任务过于复杂,请简化后重试”。长期:优化提示词,减少不必要的上下文;用缓存减少重复调用;用更便宜的模型处理简单任务。
我自己的经验是,80%的成本来自20%的任务。所以重点优化那20%的高消耗任务,而不是平均用力。比如我们发现“数据分析”类任务平均消耗1.5万token,而“问答”类只有800token。于是我们专门为数据分析任务做了优化:把常用的数据表结构缓存起来,避免每次重新描述;把SQL生成和SQL执行分开,生成用大模型,执行用规则引擎。
4.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Agent反复调用同一工具 | 提示词歧义或状态未更新 | 查看工作记忆和工具返回值 | 加循环限制,明确停止条件 |
| 工具调用准确率低 | 描述不清或工具过多 | 检查工具描述和暴露数量 | 优化描述,按场景分组 |
| 上下文超限 | 对话过长或工具返回太大 | 统计token消耗分布 | 摘要+外置+截断组合 |
| 成本异常高 | 重复调用或模型选择不当 | 按任务类型统计token | 缓存+降级+预算控制 |
| 响应延迟高 | 串行调用或超时设置过长 | 查看trace耗时分布 | 并行化+缩短超时+异步 |
| 任务成功率下降 | 模型更新或工具变更 | 对比历史评估结果 | 回归测试+版本锁定 |
最后分享一个我踩过的坑:不要在生产环境直接用
latest版本的模型。我们有一次因为模型静默更新,导致工具调用格式变了,整个Agent服务挂了半天。后来我们锁定了具体版本号,并且每次升级前都跑一遍回归测试。这个习惯帮我省了很多半夜救火的时间。
5. 工程师如何拿到这张“入场券”
如果你是一个传统后端工程师,想切入Agent Infra,我的建议是从“工具调用层”入手。因为这层最接近你已有的技能——写API、处理错误、做缓存、控超时。你可以先写一个简单的工具注册中心,支持动态加载工具、自动生成Schema、统一错误处理。这个过程中你会自然接触到LLM的Function Calling格式、token计算、提示词工程。
如果你是一个AI应用工程师,想深入Infra,那建议从“可观测性”入手。因为这是目前最缺人的方向。大多数团队能把Agent跑起来,但跑起来之后两眼一抹黑。你可以学OpenTelemetry,学怎么埋点、怎么建Dashboard、怎么定义评估指标。这个技能在任何一个做Agent的公司都是刚需。
如果你是一个学生或者刚入行的开发者,我建议你先用LangGraph或者类似的框架做一个完整的项目,比如“自动调研助手”或者“智能客服工单系统”。不要只跑Demo,要把它部署到云上,让它真实地跑一周,处理真实的问题。你会遇到各种在Demo里永远不会出现的问题:网络抖动、API限流、用户输入奇怪字符、并发冲突。这些问题才是真正的经验。
Agent Infra这个领域,目前还没有形成标准答案。每个人都在摸索,每个团队都有自己的最佳实践。这意味着两件事:第一,你学到的知识可能很快就过时,所以要保持学习;第二,你有机会定义标准,而不是只跟随标准。对于工程师来说,这既是挑战,也是机会。
我个人的体会是,做Agent Infra最需要的不是某个具体技术,而是一种“系统思维”——你要同时考虑模型的概率性、系统的可靠性、成本的约束性、用户的体验性。这四个维度经常互相冲突,而你的工作就是找到平衡点。这种平衡能力,才是真正的“入场券”。