ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

AI Agent工程化实战:从Demo到生产环境的踩坑与成本控制

AI Agent工程化实战:从Demo到生产环境的踩坑与成本控制 去年年初我决定认真搞AI Agent相关的东西当时市面上关于Agent的讨论已经很多但大多数文章要么是概念科普要么是官方文档翻译真正讲清楚“怎么落地、怎么烧钱、怎么踩坑”的少之又少。我自己前后折腾了一年API费用、服务器费用、各种工具订阅加起来烧了几千块才算是把Agent从“能跑通demo”推进到“能在业务里扛住真实流量”。这篇就纯粹是我的实战总结没有教科书味适合那种已经会用Python、调过LLM接口、想更进一步把Agent做成真正产品的朋友。先说结论AI Agent不是“聊天机器人加个记忆”那么简单。它本质上是一套让大模型具备感知、决策、行动、反思能力的系统。一年下来我最大的体会是Agent的难点不在模型本身而在工程化——工具怎么设计、状态怎么管理、并发怎么扛、成本怎么控。接下来我把这些拆开讲。1. AI Agent到底是什么从“聊天”到“干活”的跨越1.1 Agent和普通API调用的本质区别很多人一开始会混淆。你直接调GPT的API输入一段文字生成一段回复那是Prompt Completion不是Agent。Agent的核心区别在于它能在一次任务中自己决定“下一步该干什么”也就是具备任务规划与工具调用的循环能力。举一个最直白的例子。你问普通大模型“帮我把这篇文章翻译成英文然后发到我的博客后台。”普通API只能给你翻译结果发博客这件事它干不了。但Agent可以它会先调用翻译工具处理文本再调用博客系统的API完成发布中间每一步如果出错了它还能根据错误信息自我修正。这就是所谓“让AI真的下地干活”。所以Agent的组成可以拆成四块大模型底座负责推理和决策不直接产出最终答案而是产出“行动计划”工具集包括代码执行器、HTTP API、数据库查询、文件读写等是Agent的“手脚”编排层决定调用哪些工具、按什么顺序调用这是LangGraph、Coze这类框架帮你处理的记忆与状态短期记忆负责当前任务的上下文长期记忆负责跨任务的偏好和知识这四块缺一块Agent都会变得很“笨”。比如没有工具集它只能空谈没有编排层多步任务会乱没有状态管理它就记不住自己做过什么。1.2 主流的Agent架构ReAct、Plan-Execute与图状态我自己实际用下来主流的Agent架构有三种选择哪一种直接影响后面的工程复杂度和效果。第一种是ReAct架构也就是Reason Act让模型交替进行推理和行动。模型观察当前状态决定调用某个工具看到工具返回结果后再次推理直到任务完成。这种架构实现简单、灵活适合“任务路径不确定”的场景比如让Agent帮你调研市场竞品它会自己决定先搜A公司还是先查B行业。缺点是步骤一多就容易“绕圈”成本也比较高。第二种是Plan-Execute架构先让Agent生成一个完整计划再按计划逐步执行执行过程中可以局部调整。这种架构适合步骤明确的任务比如数据处理流程读取-清洗-分析-出报告。优点是任务可控性强不会跑偏缺点是场景一旦变化计划可能失效。我实际用下来的感觉是plan的质量高低极大依赖模型的推理能力弱模型做planning基本是灾难。第三种是图状态架构也是我现在主力在用的。它把Agent的整个生命周期定义成一个状态机每个节点是一个处理步骤状态在节点之间流转。LangGraph就是这种思路。这种架构最大的优势是你可以在“某个节点挂了之后”精确地做重试、降级、人工介入而不是像ReAct那样从头再来。对于生产级别的Agent我强烈建议走这条路因为它的可观测性和容错性真的不是前两种能比的。我自己的项目从ReAct切到LangGraph之后成功率提升最明显尤其是处理带数据库读写和外部API同步的场景之前那种“跑N轮终于跑对结果”的撞运气情况基本没有了。1.3 单Agent和多Agent别为了“高级”强行上多Agent多Agent协作是这两年的热门话题好像不搞几个Agent互相聊天就不叫Agent产品。我的建议是中小项目老老实实用单Agent多Agent带来的心智负担和token消耗是很多人承受不起的。为什么这么说多Agent的本质是让多个角色各自带一套Prompt和工具通过消息传递协作。听起来很优雅但实际工程里的问题非常多。首先是成本一次任务如果有三个Agent互相传递结果token消耗可能是单Agent的三到五倍。其次是调试难度你很难定位“到底是哪个Agent理解错了”。最后是并发复杂度每个Agent如果都要并发调用资金和资源的压力会指数级上升。当然有些场景确实适合多Agent。比如客服系统里一个Agent负责意图识别一个Agent负责查订单一个Agent负责生成回复这种职责边界清晰的分工就很容易落地。所以我的经验是多Agent不是银弹它更适合“职责天然独立”的业务如果只是让多个Agent围着一个任务转那大概率是给自己找麻烦。2. 一年烧了几千块钱到底花在哪了2.1 API费用、算力费用、工具订阅费先算一笔账。我这一年的几千块大头主要是三类。第一类是大模型API费用占了大概60%。我用过GPT、Claude也试过国产模型平均下来每个月API账单在200-400之间。正常聊天其实烧不了这么多钱真正烧钱的是Agent的自主调用和Debug过程中的重复消耗。第二类是服务器费用。为了给Agent提供一个稳定可访问的环境我买过云服务器也用过容器服务这部分大概占20%。如果只是本地测试这钱其实可以不花但一旦你要接微信机器人、接企业微信、或者给外部用户提供访问入口服务器就省不掉。第三类是工具和平台订阅费。比如我买了一年的LangSmith用于追踪Agent运行过程还有数据集管理工具、向量数据库服务等加起来也占了20%左右。很多初学者会忽略这部分但实际调试Agent时没有一个可观测性工具你会被折磨到怀疑人生。2.2 Token消耗的“隐形黑洞”很多新手容易低估Agent的Token消耗。我自己第一次跑通一个带三四个工具调用的小Agent时看账单差点没坐稳——一次对话烧掉的Token是普通问答的几十倍。这里面有几个隐形黑洞值得单独说。第一个是系统Prompt。为了约束Agent的行为你会给一套很长的System Prompt可能几千字。一旦对话拉长每一轮交互都要把这套Prompt重新发给模型算一次这部分是固定开销。第二个是工具返回结果。Agent每调用一次工具工具返回的数据都会进入上下文这个数据量往往是很大的。比如你让它查一次数据库把全表100行记录返回回来一次就是几千Token。我踩过的一个坑是让Agent去调用一个分页查询接口结果因为Prompt里没约束返回条数Agent直接把一万条数据塞进了上下文那一次的token费用直接超了预算。第三个是重试。Agent跑任务难免失败失败后让它“重新想想再试一次”每一次重试都是全量重新计算的成本翻倍。我后来养成了一个习惯给Agent的工具加超时和上限让它最多重试两次宁可失败也不要无限重蹈覆辙。2.3 成本控制实操模型分级、缓存与降级控制成本不是不调Agent而是讲究策略。我的做法有三个。第一是模型分级。把复杂推理任务和简单任务拆开简单任务用便宜的小模型复杂任务用旗舰模型。比如我的项目中意图识别和实体提取用轻量模型计划和反思阶段用旗舰模型。这样整体成本能省30%到40%。第二是结果缓存。同一类请求如果前置条件相同直接把上一次的结果缓存下来。比如Agent去拉取股票行情、天气信息这些短期内不会变的数据设置一个5分钟级别的缓存省下来的token非常可观。第三是降级策略。高峰期或预算告急时把旗舰模型动态降级为中等模型质量虽然下降一些但至少服务还在跑。这个降级逻辑我用LangGraph的节点路由来实现在调用模型前根据当前配额做一次判断达到阈值就切到廉价模型。朋友们Agent能不能赚钱另说至少别让它把你搞破产。3. 技术栈选型Python、Rust还是Spring AI3.1 为什么我选了Python FastAPI LangGraphAgent的技术栈选择很多新手第一步就卡住了。市面上有Python系的LangChain、LangGraph有Java系的Spring AI也有新兴的Rust系框架还有扣子这类低代码平台。我的选择是Python系的FastAPI LangGraph这套组合目前在我心里的性价比最高。说几个具体理由。首先是生态Python的AI库和数据处理库最全LangGraph对状态管理的支持非常成熟。其次是改造成本如果你后续要加OCR、加向量检索、加数据预处理Python的第三方库几乎应有尽有不用自己重新造轮子。再次是FastAPI的异步特性在Agent的并发处理上能顶得住压力我在生产环境里用FastAPI网关接多个Agent实例稳定性明显比之前用同步框架好了很多。有些朋友可能会问为什么不选Spring AI。我承认Java体系在大型团队协作和企业集成上有优势但Spring AI目前的生态成熟度还是略逊于Python系。如果你所在团队本来就是Java技术栈没有Python基础那选Spring AI是合理的但如果你是个人开发者、或者团队技术栈不锁死我还是推荐Python毕竟试错成本低很多。3.2 Rust系Agent框架值得追吗关于“基于Rust语言AI Agent”这个话题我也关注过。Rust的优势是性能高、内存安全在需要极致并发和低延迟的场景下确实非常强。我也见过有人用Rust写Agent运行时再通过Python做业务层的编排两个语言配合使用。但我的意见很明确如果你不是对性能有特别极端的要求或者本身没有Rust基础千万别上来就用Rust写Agent。原因是Agent的瓶颈通常不在语言执行速度而在大模型的推理延迟和Token消耗。你的模型一调用就是两三秒语言层面的微秒级优化基本感觉不到。为了那点性能提升去承受Rust开发效率的下降得不偿失。我见过一个团队全员Rust写Agent写了三个月最终的Agent成功率还不如我用Python一周搓出来的原型高。3.3 低代码平台扣子(Coze)什么时候用、什么时候不用低代码平台这两年很火扣子就是典型代表。它让不会写代码的人也能搭出Agent还内置了大量插件和知识库功能。我试用过说实话它对非技术人员或者想快速验证想法的人来说真的是福音。但如果你是做严肃的、要上生产的业务我不建议完全依赖低代码平台。原因有几个一是调试和观测能力弱出了问题只能看不太详细的日志二是并发控制能力差真到了高并发场景容易崩溃三是平台级限制你没法自定义复杂的工具调用逻辑和状态流转。我的经验是可以用扣子做原型验证用几个小时把业务逻辑跑通然后觉得可行就迁到代码方案上。千万别囤积几百个“扣子Agent”而不去真正解决工程问题。4. 实操用FastAPI LangGraph搭一个能扛并发的Agent4.1 项目结构与核心配置接下来是我现在的标准项目模板。目标很清晰写一个能处理用户请求、自主调用工具、并能承受多用户并发访问的Agent服务。先说项目结构agent-service/ ├── app/ │ ├── main.py # FastAPI入口 │ ├── agent/ │ │ ├── graph.py # LangGraph状态图定义 │ │ ├── nodes.py # 各节点执行逻辑 │ │ ├── tools.py # 工具注册与实现 │ │ └── state.py # 状态定义 │ ├── core/ │ │ ├── config.py # 配置管理 │ │ ├── llm.py # 大模型客户端封装 │ │ └── cache.py # 缓存与降级逻辑 │ └── api/ │ └── routes.py # API路由 ├── tests/ ├── pyproject.toml └── deploy/ └── docker-compose.yml这个结构最核心的设计是“状态定义”和“节点分离”。状态是LangGraph的基石它管理整个Agent的上下文数据节点则是具体的执行逻辑每个节点只干一件事。4.2 状态图与节点逻辑拆解一个完整流程我们把Agent设计成五个节点接收输入、规划、执行工具、反思、生成回答。这五个节点之间的流转关系用LangGraph来表达非常直观它本质上是一个状态图每个节点之间的边可以设置条件路由。先看状态定义from typing import TypedDict, Annotated, List from langgraph.graph import StateGraph, END class AgentState(TypedDict): task: str # 用户的任务描述 plan: List[str] # Agent生成的计划步骤 step_index: int # 当前执行到第几步 tool_results: dict # 各工具返回结果 messages: List[dict] # 对话历史 attempts: int # 重试计数 final_answer: str # 最终回复这个状态的核心是tool_results和step_index。step_index让Agent知道“自己进行到哪了”tool_results让Agent在做决策时能够引用之前工具的结果。很多人搭Agent不注意状态设计用一个巨大的JSON字符串存所有东西后面改起来直接崩溃。再来看节点定义async def plan_node(state: AgentState): prompt build_plan_prompt(state[task], state[tool_results]) response await llm_client.chat(prompt, modelbig-model) steps parse_steps(response) return {plan: steps, step_index: 0} async def execute_node(state: AgentState): current_step state[plan][state[step_index]] tool_name, tool_args parse_action(current_step) result await tool_registry.call(tool_name, tool_args) return { tool_results: {tool_name: result}, step_index: state[step_index] 1 } async def reflect_node(state: AgentState): if state[step_index] len(state[plan]): return {should_continue: False} # 检查是否需要重试 if state[attempts] MAX_ATTEMPTS: return {should_continue: False} return {should_continue: True}这里有一个关键设计每个节点都返回一个包含状态更新的字典LangGraph负责将这些更新合并到全局状态中。这种设计使得每个节点都是纯函数式的易于测试和调试。图构建from langgraph.graph import StateGraph, END def build_graph(): g StateGraph(AgentState) g.add_node(plan, plan_node) g.add_node(execute, execute_node) g.add_node(reflect, reflect_node) g.add_node(answer, answer_node) g.add_edge(plan, execute) g.add_conditional_edge( execute, reflect_node, # 条件判断函数 {continue: plan, finish: answer, retry: execute} ) g.add_edge(answer, END) return g.compile()这里的条件路由是LangGraph最强大的地方。reflect_node返回一个路由决定如果计划没执行完就回到execute继续下一步如果执行完了就去生成答案如果出错但没有超过重试上限就重新执行当前步骤。这个机制比ReAct那种让模型自己决定下一步要可靠得多。4.3 并发控制怎么扛住高并发请求很多人的Agent项目一上生产就挂本质原因是并发处理没做好。我直接说重点Agent请求是长耗时任务一个请求可能耗时20-60秒如果你用同步方式处理两个并发就把进程卡死了。我的方案是FastAPI只负责接收请求、校验参数、创建任务然后把任务扔到消息队列我用的是Redis里的简易队列里由独立的Worker进程异步处理Agent逻辑处理完成后再通过WebSocket或轮询接口把结果返回给前端。from fastapi import FastAPI, BackgroundTasks from redis import Redis import uuid app FastAPI() redis_client Redis.from_url(redis://localhost:6379/0) app.post(/agent/run) async def run_agent(task: str, background_tasks: BackgroundTasks): task_id str(uuid.uuid4()) # 将任务推送到Redis队列 redis_client.lpush(agent_tasks, { task_id: task_id, task: task }) return {task_id: task_id} # Worker进程负责从队列中取任务并执行 def worker_loop(): while True: _, item redis_client.brpop(agent_tasks) task_id item[task_id] result run_agent_once(item[task]) redis_client.hset(agent_results, task_id, result)这个方案的优点很明显FastAPI只做I/O不阻塞背压能力强Worker可以横向扩展比如启动5个Worker就相当于有5个Agent在并行处理任务。我实测过单个Worker大概能扛住每秒几十个请求的接收瓶颈根本不在API层而在大模型API的并发限制。同时要注意限流。给Agent服务的每个用户加上配额比如每分钟最多触发10次Agent任务超出直接返回错误否则某些用户会把你的API预算在几分钟内烧穿。我在电商场景里被一个恶意脚本持续触发Agent一晚上烧掉了300多块钱后来加了用户级限流才算解决。4.4 工具注册与安全边界工具是Agent的“手脚”但也是安全风险的高发地。我的原则是所有工具调用必须走统一的注册中心所有工具的参数必须经过JSON Schema校验不允许Agent任意调用任意函数。class ToolRegistry: def __init__(self): self._tools {} def register(self, name, func, schema): self._tools[name] {func: func, schema: schema} async def call(self, name, args): tool self._tools.get(name) if not tool: raise ToolNotFound(name) validated_args validate_schema(args, tool[schema]) return await tool[func](**validated_args)这里最重要的是“白名单机制”。Agent可能会从模型那里收到各种参数组合一旦不加校验就可能出现灾难——比如让Agent直接执行os.system(rm -rf)。我踩过一个很痛的坑早期我给了Agent一个“执行任意Python代码”的代码执行器工具结果它在一次任务中真的生成了删除文件的代码并执行了。从那以后我再也不敢给Agent任何可以不加限制执行系统命令的工具。工具越通用风险越大工具越专用Agent的表现反而越好。5. 常见问题与避坑记录那些我烧钱买来的教训5.1 Prompt设计的坑Agent不是说越详细越好我一开始做Agent觉得System Prompt写得越详细Agent越聪明。结果发现完全不是。太长的Prompt会让模型注意力分散频繁触发无关的限制条件甚至让Agent在关键决策时“纠结”起来。我的建议是System Prompt只写三件事——角色边界、可用工具清单、输出格式要求。不要写太多业务细节业务细节放到具体工具描述里。比如你让Agent做订单查询在工具描述里写清楚“该工具根据订单号返回状态”比在System Prompt里写“你是订单管理专家请根据订单号查询订单状态订单状态有这些……”要有效得多。另外一个经验是工具描述本身也要克制。我见过有人给工具写两三百字的描述结果模型把描述里的每个词都当成限制条件导致工具调用成功率直线下降。工具的description尽量缩短到30字以内只写“做什么”和“什么时候用”。5.2 工具调用失败的循环卡死和无限重试Agent最让人抓狂的问题就是在一个工具调用失败后陷入死循环。最开始我用ReAct架构模型在收到错误提示后会不断生成新的参数重新调用每次还都生成一大段“分析”就像一个人卡在门口每次都换个理由再撞一次门让人血压飙升。我的解决方案有两个。第一是给每个工具调用设置独立的超时时间比如HTTP工具5秒超时数据库查询3秒超时超时统一抛出“timeout”错误第二是在反思节点里明确要求Agent“如果同一个工具连续失败两次放弃这个工具改用其他方案”甚至直接进入人工兜底流程。这种“快速失败”的设计让我的Agent从单任务平均8分钟缩短到1分半效果非常明显。这个教训告诉我们Agent的容错不是让它无限反思而是让它“有选择地放弃”。聪明地失败远好过固执地重试。5.3 上下文窗口溢出和记忆衰减上下文窗口溢出是另一个频率很高的故障。Agent执行过程中不断累积对话历史、工具返回结果很快就把上下文窗口塞满了。很多看似“跑通了”的Agent其实只是刚好没触发这个故障。我的实践方法是三层记忆策略第一层是明确的任务核心状态比如订单号、用户ID、查询条件这些必须完整保留第二层是工具返回的原始数据这些是可以在需要时重新获取的所以高峰时期我会把大块原始数据压缩成摘要第三层是历史对话中的闲聊信息这些通常直接丢弃。我记得有一次让Agent帮我做一份跨30天的数据统计报告它在第10天的时候开始出现幻觉把之前的数据统计错了。排查了很久发现是因为上下文太挤前面的数据被截断。后来我把“每天的统计结果”以结构化摘要的形式存入长期记忆每天只往模型上下文里传一份摘要问题彻底解决了。这个经验对处理长周期任务尤其重要。5.4 可观测性没有日志的Agent就是一个黑盒这句话我必须放在最后重点说。开发Agent的过程有一半时间在查“为什么它不走我预期的路”如果没有每一步的可观测数据排查问题基本靠猜。我强烈建议每个Agent工程都接入一套类似LangSmith或者自建轨迹日志的系统。至少要在以下节点打日志模型收到什么输入、模型返回什么决策、调用了哪个工具、工具返回什么结果、当前状态是什么。不要嫌日志多Agent跑一个任务可能就几千行日志但恰恰是这些日志能帮你快速定位问题在规划、执行还是反思环节。我之前有一个项目上线后用户投诉“Agent回答有时对有时错”我费了好大劲才发现是工具返回结果里偶发包含了一些重复数据而这些数据在传给模型时恰好被System Prompt里的格式化要求覆盖了。如果没有详细的轨迹日志这个问题我可能一辈子都找不到。5.5 个人使用Agent做期货交易这件事最后说说很多人关心的“个人用AI Agent做期货交易”。我的态度很明确技术上可以做但非常不建议。原因有三点一是大模型本身的推理延迟和不确定性根本无法胜任毫秒级的交易决策二是Agent调用交易接口一旦出错资金损失是真的会发生的三是交易策略的回测和验证需要大量数据支撑个人很难做好风控。我见过有人拿Agent做自动化交易结果在市场异常波动时因为止损逻辑没写对亏损很大。这件事在技术上值得研究但请一定只做模拟盘别拿真金白银去赌。6. 最后再分享两个实用心得一年下来我对Agent的认知有了很大的变化。最开始我觉得Agent就是“一个聪明的大模型”只要模型够强什么任务都能完成。后来我发现Agent更像一个“拿着工具的实习生”——它需要清晰的指令、合适的工具、及时的反馈更需要在关键节点上有人兜底。工程化的核心不是让Agent变得全能而是让它在有限的场景里稳定地发挥。再分享一个最后学到的技巧定期复盘Agent的失败案例。我每个月会把Agent运行中所有失败的任务导出分类整理看看失败集中在哪个环节然后针对性地优化。第一个月失败率还有20%第二个月降到12%半年后稳定在4%左右。这种“用数据调教Agent”的思路远比盲目调Prompt有效。如果你也准备入坑Agent我的建议是先从一个小场景开始比如一个能查天气、查快递、记待办的小Agent跑通“规划-执行-反思”的闭环然后再逐步增加工具和业务逻辑。别一开始就想着做一个通用的超级Agent那是少数大公司才有资源和精力做的事。对我们个人开发者来说把一个小而精的Agent做到稳定可用已经是很了不起的成绩了。
返回列表