1. 从零到一:AI Agent 开发与上线的全局拆解
这两年“AI Agent”这个词被炒得火热,但真正动手做过一个能上线、能扛住真实流量、还能持续迭代的 Agent 项目的人,其实并不多。我前后参与过三个不同规模的 Agent 项目,从最初用 Coze 这类低代码平台快速搭原型,到后来用 FastAPI + LangChain + LangGraph 自己撸一套完整后端,踩过的坑可以说能写一本小册子。这篇文章不打算跟你聊“Agent 是什么”这种概念性的东西,而是把我从开发到上线整个链路上真正遇到的问题、做过的取舍、以及那些文档里不会写的经验,一次性摊开来讲。
先明确一下这篇文章适合谁看。如果你是完全没接触过 Agent 开发的小白,我会在关键节点补充基础概念,保证你能跟上;如果你已经写过一些 Demo,但卡在“怎么让它稳定跑起来”“怎么部署到线上”“并发一上来就崩”这些环节,那这篇内容应该能帮你省下不少试错时间。核心关键词就三个:AI Agent、开发、上线。我会围绕这三个词,把架构选型、核心模块实现、并发处理、部署方案、以及上线后的监控与迭代,全部串一遍。
需要提前说明的是,Agent 开发这个领域变化极快,今天的最佳实践可能三个月后就被推翻。所以我不会给你一套“标准答案”,而是把决策逻辑和判断依据讲清楚,让你能根据自己项目的实际情况做选择。这比直接抄一套配置要有价值得多。
2. 架构选型:别一上来就追求“全自研”
2.1 低代码平台 vs 自研框架,到底怎么选
我见过太多团队在项目启动阶段就陷入“技术选型焦虑”,花两周时间对比各种框架,结果一行业务代码没写。我的建议很直接:先用最低成本验证核心逻辑,再决定要不要自研。
如果你要做的是一个内部工具、或者验证一个想法,Coze、Dify 这类平台完全够用。它们的优势在于:可视化编排、内置工具调用、自带知识库管理,基本上拖拖拽拽就能跑通一个 Agent 的完整链路。我最早做的一个客服问答 Agent,用 Coze 从零到能用只花了半天时间。但问题也很明显:定制化能力有限、性能天花板低、数据不在自己手里、复杂业务逻辑很难表达。
当你遇到以下情况时,就该考虑自研了:需要接入内部私有系统且平台不支持自定义工具、需要处理高并发场景、需要对 Prompt 和上下文做精细控制、需要把 Agent 能力嵌入到已有产品中。这时候,FastAPI + LangChain/LangGraph是目前比较主流的一套组合。FastAPI 负责 API 层和并发处理,LangChain 提供 LLM 调用和工具链的抽象,LangGraph 则用来编排多步骤、有状态的工作流。
选 LangGraph 而不是自己写状态机的原因很简单:Agent 的核心难点不在于调用 LLM,而在于多轮对话中的状态管理、工具调用的错误恢复、以及条件分支的流转。LangGraph 把这些东西抽象成了图结构,节点是操作,边是流转条件,状态在节点间传递。自己实现一套不是不行,但容易在边界情况上翻车。
2.2 主流 Agent 架构模式与适用场景
目前市面上常见的 Agent 架构大概有这么几种,我整理了一个对比表,方便你根据场景选择:
| 架构模式 | 核心思路 | 适用场景 | 复杂度 |
|---|---|---|---|
| ReAct | 推理+行动交替,边想边做 | 工具调用类任务,如搜索、计算 | 低 |
| Plan-and-Execute | 先规划完整步骤再逐步执行 | 多步骤复杂任务,如报告生成 | 中 |
| Multi-Agent | 多个 Agent 分工协作 | 需要不同角色配合的场景 | 高 |
| Reflexion | 执行后自我反思并修正 | 对结果质量要求高的任务 | 中 |
我个人的经验是,80% 的场景用 ReAct 就够了。很多人一上来就想搞 Multi-Agent,觉得多个 Agent 协作听起来更高级,但实际上调试难度呈指数级上升,而且效果未必比单 Agent 好。除非你的任务确实需要不同领域的专业知识分工,否则别碰 Multi-Agent。
2.3 技术栈选型的几个关键决策点
除了框架,还有几个选型决策会影响后续开发和上线:
LLM 供应商的选择。不要绑定单一供应商,至少在代码层面做好抽象。我吃过这个亏——早期项目直接调某家的 SDK,后来因为成本和稳定性问题想换,发现代码里到处都是耦合。后来统一用 OpenAI 兼容的接口格式,切换成本就低多了。
向量数据库的选择。如果 Agent 需要 RAG(检索增强生成),向量库是绕不开的。小规模用 Chroma 或 FAISS 本地跑就行,上了规模再考虑 Milvus 或 Qdrant。别一上来就上分布式向量库,运维成本很高。
状态存储的选择。Agent 的对话状态、工具调用记录需要持久化。Redis 做短期缓存,PostgreSQL 做长期存储,这个组合基本能覆盖大部分场景。
3. 核心模块开发:把每个环节做扎实
3.1 Prompt 工程:Agent 的大脑
Prompt 是 Agent 的灵魂,但很多人对它的重视程度远远不够。我见过把 Prompt 随便写几句就上线的,结果 Agent 行为完全不可控。一个好的 Agent Prompt 通常包含这几个部分:
- 角色定义:明确 Agent 的身份和能力边界
- 任务描述:清晰说明要完成什么
- 工具说明:每个工具的功能、参数、使用时机
- 输出格式:规定返回结果的结构
- 约束条件:什么能做、什么不能做
- 示例:Few-shot 示例,帮助模型理解预期行为
这里有个实操技巧:把 Prompt 当成代码来管理。用版本控制工具管理 Prompt 的每次修改,记录每次修改的原因和效果。我在项目里专门建了一个 prompts 目录,每个 Prompt 一个文件,配合 Git 做版本管理。这样当效果回退时,能快速定位是哪次修改导致的。
另一个坑是 Prompt 过长导致的性能问题。当工具数量多、示例多的时候,Prompt 可能膨胀到几千甚至上万 token,不仅增加成本,还会稀释关键信息的权重。解决办法是动态组装 Prompt——根据当前对话上下文,只注入相关的工具说明和示例,而不是把所有东西都塞进去。
3.2 工具调用:Agent 的手脚
工具调用是 Agent 区别于普通 Chatbot 的核心能力。实现工具调用时,有几个关键点需要注意:
工具描述的准确性。LLM 是根据工具的名称和描述来决定是否调用的。如果描述模糊,模型就会误用或漏用。我一般会写清楚:这个工具做什么、什么时候用、参数是什么格式、返回什么。比如一个查询订单的工具,描述里要明确“当用户询问订单状态、物流信息时使用此工具”。
参数校验与错误处理。LLM 生成的参数不一定合法,必须在执行前做校验。我通常用 Pydantic 定义参数模型,自动做类型检查和格式验证。校验失败时,把错误信息返回给 LLM,让它重新生成参数,而不是直接报错给用户。
超时与重试。外部工具调用可能超时或失败,必须设置合理的超时时间和重试策略。我的经验是:单个工具调用超时设 10 秒,最多重试 2 次,重试间隔用指数退避。超过重试次数后,把失败信息返回给 Agent,让它决定是换工具还是告知用户。
幂等性设计。有些工具调用是有副作用的,比如创建订单、发送消息。这类工具必须做幂等处理,防止 LLM 重复调用导致重复操作。通常的做法是让 LLM 生成一个唯一请求 ID,服务端根据这个 ID 去重。
3.3 记忆管理:让 Agent 记住上下文
Agent 的记忆分为短期记忆和长期记忆。短期记忆就是当前对话的历史,长期记忆则是跨对话的知识积累。
短期记忆的管理核心是上下文窗口的合理利用。LLM 的上下文窗口有限,不可能把所有历史都塞进去。常见的策略有:滑动窗口(只保留最近 N 轮)、摘要压缩(把早期对话总结成一段话)、关键信息提取(只保留实体和意图)。我一般用组合策略:最近 5 轮保留原文,更早的做摘要,同时把对话中出现的关键实体单独存一份。
长期记忆的实现通常依赖向量数据库。把重要的对话内容、用户偏好、历史决策向量化存储,需要时通过相似度检索召回。这里有个容易忽略的点:不是所有信息都值得存。我见过把每轮对话都存进向量库的,结果检索出来的全是噪音。应该只存那些有长期价值的信息,比如用户的明确偏好、重要的业务决策、反复出现的问题。
3.4 多轮对话与状态机设计
复杂的 Agent 往往需要多轮交互才能完成任务,这就涉及到状态管理。LangGraph 的 StateGraph 是比较好用的工具,它把整个流程定义成一张图,每个节点是一个操作,边定义了流转条件。
设计状态机时,我遵循几个原则:状态尽量扁平,避免嵌套过深导致难以追踪;每个状态转换要有明确的触发条件,不能模糊;异常状态要有兜底处理,不能让 Agent 卡死。比如用户中途改变意图、工具连续失败、超时等情况,都要有对应的处理分支。
4. 并发与性能:上线前的必修课
4.1 AI Agent 的并发瓶颈到底在哪
很多人以为 Agent 的并发瓶颈在 LLM 调用,其实不完全是。我实测下来,瓶颈通常出现在这几个地方:
LLM API 的速率限制。这是最直接的瓶颈。大部分 LLM 供应商都有 RPM(每分钟请求数)和 TPM(每分钟 token 数)限制。当并发请求超过限制时,要么被限流,要么排队等待。解决办法一是申请更高的配额,二是做请求队列和限流控制。
工具调用的响应时间。如果 Agent 依赖外部 API 或数据库查询,这些调用的延迟会直接叠加到整体响应时间上。一个涉及 3 次工具调用的 Agent,如果每次调用 2 秒,光工具调用就 6 秒了。
上下文长度带来的计算开销。上下文越长,LLM 的推理时间越长,成本也越高。长上下文场景下,单次请求可能就要十几秒。
状态存储的读写延迟。每次对话都要读写状态,如果存储层性能不够,也会成为瓶颈。
4.2 异步处理与请求队列的实操方案
FastAPI 天生支持异步,这是它的优势。但要注意,异步不等于并发。如果代码里有同步阻塞的操作,整个事件循环都会被卡住。我踩过的坑是在异步函数里直接调用了同步的数据库驱动,结果并发一上来全部超时。后来全部换成了异步驱动(asyncpg、aioredis),问题才解决。
请求队列方面,我一般用 Redis 做简单的队列,配合 Celery 或 ARQ 做任务分发。对于 Agent 这种长耗时任务,同步等待返回结果体验很差,更好的做法是:提交任务后立即返回一个 task_id,客户端轮询或通过 WebSocket 获取结果。这样既能控制并发,又能提升用户体验。
限流策略上,我用的是令牌桶算法。根据 LLM 供应商的配额,设置合适的令牌生成速率,请求前先获取令牌,获取不到就排队或拒绝。这样能保证不会因为突发流量触发供应商的限流。
4.3 缓存策略:哪些能缓存,哪些不能
缓存是提升性能的利器,但 Agent 场景下的缓存要特别小心。LLM 的调用结果不能简单缓存,因为同样的输入在不同上下文下可能有不同的输出。但以下几类内容是可以缓存的:
- 工具调用结果:对于查询类工具,如果参数相同且数据时效性要求不高,可以缓存几分钟
- 向量检索结果:相同的查询向量,检索结果可以缓存
- Prompt 模板:组装好的 Prompt 可以缓存,避免重复拼接
- 用户会话状态:用 Redis 缓存,减少数据库读写
缓存的失效策略也很关键。我一般用 TTL + 主动失效的组合:设置一个合理的过期时间,同时在数据更新时主动清除相关缓存。
4.4 压测与容量规划
上线前必须做压测,这是铁律。我一般用 Locust 或 k6 做压测,重点关注这几个指标:P50/P95/P99 响应时间、吞吐量、错误率、资源利用率。
容量规划的思路是:先确定单实例能承载的 QPS,然后根据预期流量计算需要的实例数,再留 30% 的余量应对突发。但 Agent 场景有个特殊之处:不同请求的耗时差异很大。简单的问答可能 1 秒返回,复杂的多步任务可能要 30 秒。所以不能简单按 QPS 来算,要按“并发处理中的请求数”来算。
我的经验值是:单个 FastAPI 实例(2 核 4G),如果 Agent 平均响应时间 5 秒,大概能支撑 20-30 个并发请求。超过这个数,响应时间会明显上升。当然这跟具体实现关系很大,一定要自己压测。
5. 部署上线:从开发环境到生产环境
5.1 环境配置与依赖管理
开发环境和生产环境的配置差异是很多问题的根源。我坚持的原则是:环境配置全部通过环境变量注入,代码里不硬编码任何环境相关的值。用 pydantic-settings 管理配置,不同环境用不同的 .env 文件。
依赖管理用 Poetry 或 uv,锁定版本号。我见过因为依赖版本不一致导致线上行为跟开发环境不同的案例,排查了大半天。requirements.txt 或 lock 文件必须提交到版本控制,部署时严格按照 lock 文件安装。
Docker 化是标配。Dockerfile 要遵循多阶段构建,减小镜像体积。基础镜像选择上,python:3.11-slim 是个不错的起点。注意把依赖安装和代码复制分开,利用 Docker 的层缓存加速构建。
5.2 容器化与编排方案
单机部署用 Docker Compose 就够了,把 API 服务、Redis、PostgreSQL、向量库都编排在一起。上了规模再考虑 Kubernetes。
K8s 部署时,有几个 Agent 场景特有的注意点:
健康检查要区分 liveness 和 readiness。liveness 检查进程是否存活,readiness 检查是否准备好接收流量。Agent 服务启动时可能需要加载模型或建立连接,这段时间 readiness 应该返回未就绪。
资源限制要合理。Agent 服务通常是 IO 密集型而非 CPU 密集型,CPU 限制可以低一些,但内存要给够,因为上下文和缓存都占内存。
优雅关闭。收到终止信号后,要等待正在处理的请求完成再退出,避免请求中断。FastAPI 配合 uvicorn 的 graceful shutdown 可以做到这点。
5.3 域名、SSL 与反向代理配置
生产环境必须用 HTTPS,这是底线。Nginx 做反向代理是标配,配置时注意这几点:
upstream agent_backend { server 127.0.0.1:8000; keepalive 32; } server { listen 443 ssl http2; server_name agent.example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://agent_backend; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # Agent 响应可能较慢,超时时间要设长 proxy_read_timeout 120s; proxy_send_timeout 120s; } }注意:Agent 的响应时间通常比普通 API 长,Nginx 的 proxy_read_timeout 默认 60 秒,很容易超时。根据你的 Agent 最长响应时间设置,建议至少 120 秒。
如果是流式输出(SSE),还需要额外配置:关闭 proxy_buffering,设置 chunked_transfer_encoding。
5.4 灰度发布与回滚机制
Agent 的上线不能一刀切,因为 Prompt 或模型的微小改动都可能导致行为变化。我一般用灰度发布:先放 5% 的流量到新版本,观察关键指标(响应时间、错误率、用户反馈),没问题再逐步扩大比例。
回滚机制必须提前准备好。除了代码回滚,还要考虑:Prompt 回滚、模型版本回滚、配置回滚。我习惯把每次上线的 Prompt、配置、代码版本打成一个 release 包,回滚时整体切换。
6. 上线后的监控、迭代与避坑
6.1 关键监控指标与告警设置
上线只是开始,监控才是保证稳定运行的关键。我重点监控这几类指标:
业务指标:请求量、成功率、平均响应时间、Token 消耗量、工具调用成功率。这些指标反映 Agent 的整体健康度。
技术指标:CPU、内存、网络 IO、数据库连接数、Redis 命中率。这些反映基础设施的状态。
质量指标:用户满意度(点赞/点踩)、对话轮次、任务完成率、异常终止率。这些反映 Agent 的实际效果。
告警设置上,我遵循“少而精”的原则。告警太多会导致麻木,真正的问题反而被忽略。我一般只对这几项设告警:错误率超过 5%、P95 响应时间超过阈值、Token 消耗异常增长、工具调用连续失败。
6.2 日志、追踪与问题定位
Agent 的问题定位比普通服务难,因为涉及 LLM 这个黑盒。我的做法是全链路追踪:每次请求生成一个 trace_id,把 Prompt、LLM 响应、工具调用、状态变化全部记录下来,关联到同一个 trace_id 下。
日志要结构化,用 JSON 格式,方便检索和分析。关键字段包括:trace_id、用户 ID、对话轮次、当前节点、输入输出、耗时、Token 数。
有个实用技巧:把每次 LLM 调用的完整 Prompt 和响应存下来。当用户反馈问题时,能直接看到当时 LLM 看到了什么、返回了什么,定位效率高很多。当然要注意脱敏和存储成本。
6.3 常见问题速查与避坑清单
下面这张表是我在实际项目中遇到的高频问题及解决方案,直接可以当速查表用:
| 问题现象 | 可能原因 | 排查方向 | 解决方案 |
|---|---|---|---|
| Agent 不调用工具 | 工具描述不清、Prompt 未强调 | 检查工具描述和 Prompt | 优化描述,增加调用示例 |
| 工具调用参数错误 | 参数定义不清晰 | 查看 LLM 生成的参数 | 用 Pydantic 严格校验,返回错误让 LLM 重试 |
| 响应时间过长 | 上下文过长、工具调用慢 | 分析各环节耗时 | 压缩上下文、工具调用加缓存 |
| 并发上不去 | 同步阻塞、连接池不足 | 检查代码是否有阻塞操作 | 改异步、扩大连接池 |
| 对话状态丢失 | 状态存储失败 | 检查 Redis/DB 连接 | 加持久化、做状态恢复 |
| 输出格式不稳定 | Prompt 约束不够 | 检查输出解析逻辑 | 用结构化输出、加格式校验 |
| Token 消耗过高 | Prompt 冗余、上下文未压缩 | 统计各环节 Token 消耗 | 精简 Prompt、做上下文摘要 |
| 重复调用工具 | 缺少幂等设计 | 检查工具调用记录 | 加请求 ID 去重 |
6.4 持续迭代:Prompt 优化与效果评估
Agent 上线后,迭代的重点通常从“能不能用”转向“好不好用”。Prompt 优化是主要手段,但不能凭感觉改。我一般用 A/B 测试:把流量分成两组,一组用旧 Prompt,一组用新 Prompt,对比关键指标。
评估指标的设计很关键。对于问答类 Agent,可以用准确率、召回率;对于任务类 Agent,用任务完成率、平均轮次;对于对话类 Agent,用用户满意度、对话时长。有条件的话,建一个标注数据集,定期做离线评估,这样迭代有据可依。
还有个容易被忽略的点:收集 bad case 并分类。把用户反馈的问题、异常终止的对话、效果差的案例收集起来,归类分析。往往改一个 Prompt 能解决一类问题,比盲目优化效率高得多。
6.5 成本控制:Token 消耗的优化技巧
Token 成本是 Agent 项目的主要运营成本之一,尤其是上了规模之后。我总结了几条实用的优化技巧:
Prompt 精简。去掉冗余的描述和示例,用更简洁的表达。我做过一次 Prompt 优化,把系统 Prompt 从 2000 token 压到 800 token,效果基本不变,成本降了 60%。
上下文压缩。早期对话做摘要,只保留关键信息。摘要本身也消耗 Token,但比保留原文便宜得多。
模型分级。简单任务用小模型,复杂任务用大模型。比如意图识别用便宜的小模型,复杂推理用大模型。这样能在保证效果的前提下降低成本。
缓存复用。前面提到的缓存策略,对成本控制也有帮助。相同的查询直接返回缓存结果,省去 LLM 调用。
流式输出。虽然不直接降低成本,但能提升用户体验,让用户感觉响应更快,间接减少重复请求。
7. 一些掏心窝子的经验
做 Agent 项目这两年多,最大的感受是:技术只是其中一部分,更多时候是在跟不确定性打交道。LLM 的行为不像传统代码那样确定,同样的输入可能得到不同的输出。这就要求我们在设计时留足容错空间,在监控上做足功夫,在迭代时保持耐心。
另一个体会是:不要追求一步到位。我见过太多项目想一开始就做一个“全能 Agent”,结果复杂度失控,最后不了了之。更好的路径是:先做一个能解决单一问题的简单 Agent,跑通上线流程,积累经验,再逐步扩展能力。
还有一点:用户反馈比任何指标都重要。数据能告诉你哪里出了问题,但只有用户能告诉你他们真正想要什么。我习惯定期看用户的原始反馈,很多优化方向都是从里面来的。
最后分享一个实用的小习惯:维护一个“坑本”。每次遇到问题、解决问题后,把现象、原因、解决方案记下来。时间长了,这就是你最宝贵的知识库。我现在的坑本已经记了上百条,新项目启动时翻一翻,能避开大部分常见的坑。