AI Agent开发实战:从LangChain选型到并发调优的完整经验

发布时间:2026/10/2 22:42:53
AI Agent开发实战:从LangChain选型到并发调优的完整经验 最近几个月我陆陆续续把手里的几个 AI Agent 项目从想法推到了线上从最开始拿 LangChain 写 demo到后来基于 FastAPI 重构成带状态机的服务再到处理并发、优化上下文、接入企业内部工具整个过程踩了不少坑。今天这篇不聊概念就聊聊实际操作中攒下来的一些经验尤其是围绕从0到1搭建 AI Agent、怎么扛并发、怎么选技术栈、练手项目怎么选这些大家问得最多的问题。如果你正准备上手 Agent 开发或者已经在用框架但被各种诡异报错折磨这篇文章应该能帮你省掉不少试错时间。我会从架构选型讲到代码实现再讲讲线上运行时的那些破事最后整理一条我觉得比较靠谱的学习路线。1. AI Agent 是个什么玩意儿为什么值得折腾先花点时间把AI Agent这个词说清楚。很多人把它跟 Chatbot 混为一谈其实区别挺大的。Chatbot 是你问一句它答一句核心是对话Agent 的核心是任务。它得自己拆解目标、决定调用哪个工具、观察结果、调整下一步动作最终交付一个结果而不是一段文字。用大白话说Chatbot 是客服Agent 是办事员。这也是为什么 Agent 项目比普通 LLM 应用更折腾。你写的不是一条 prompt 链路而是一套决策循环模型输出意图程序执行工具结果再喂回模型模型再判断下一步。这个循环里的每一个环节都会出错模型可能选错工具工具可能返回脏数据状态可能混乱甚至 Agent 会在一个错误的动作上反复横跳。理解了这个本质后面很多问题就能对号入座了。那为什么还值得折腾因为一旦跑通收益是实实在在的。我做过一个数据整理类 Agent以前人工处理一份报表要 40 分钟现在 Agent 十分钟跑完还能顺手生成摘要另一个是内部工单分类和初步回复的 Agent接入后客服团队的工作量降了大概三成。这就是 Agent 的价值它不是帮你写得更快而是替你把重复劳动接过去。2. 技术选型与架构设计别一上来就堆框架2.1 核心组件与职责划分我见过不少人一上来就 LangChain LangGraph 向量库 消息队列全上最后项目死在复杂度上。Agent 再怎么花哨核心组件就四块模型层负责理解意图、生成回复、决定动作。可以是 OpenAI、Claude也可以是国产开源模型关键看场景对延迟和成本的要求。记忆/上下文层负责保存对话历史、任务状态、中间结果。简单场景直接用内存里的消息列表复杂场景才需要向量数据库做长期记忆。工具层Agent 能执行的操作比如查数据库、调 API、发邮件、操作文件。工具是 Agent 的手脚决定了它能干什么活。控制层负责编排整个执行流程决定什么时候调模型、什么时候调工具、怎么处理错误和循环。这是最核心也最容易被忽视的部分。2.2 为什么我选了 FastAPI LangChain LangGraph先说结论如果你是做面向用户的服务FastAPI 基本是首选。理由很简单它原生支持异步性能好生态成熟Swagger 文档直接生成前后端联调方便。而且 FastAPI 的依赖注入机制在做 Agent 服务时特别顺手比如把 LLM 客户端、向量库连接、Redis 连接池都挂在依赖里代码会干净很多。LangChain 的价值在于提供了统一的工具调用接口和大量现成的组件比如各种文档加载器、输出解析器、回调处理器。虽然网上对 LangChain 褒贬不一但说实话它在快速验证想法阶段是真的快。LangGraph 解决的是状态管理问题。Agent 的核心是循环和分支——模型决定调工具工具返回到模型模型可能再调工具也可能结束。如果你用纯代码写这个循环维护状态流转会很痛苦。LangGraph 的本质是一个基于图结构的状态机节点是动作调模型、调工具、发消息边是状态转移条件。它把循环画出来代码结构就清晰了。我现在的标准组合是FastAPI 做服务层LangGraph 做控制层LangChain 做工具和模型封装层。这套组合的好处是每一层职责单一出问题能快速定位。2.3 一个容易被忽略的问题工具注册与参数校验工具层看着简单实际上最容易出幺蛾子。我刚开始做一个 Agent工具是直接写在代码里的 if-else 判断后来工具多了就乱了。正确的做法是用统一的函数装饰器做工具注册同时用 Pydantic 做参数校验。我踩过一个很典型的坑一个查询天气的工具参数是城市名模型传了个北京市朝阳区进来工具直接把它当城市代码去查结果查出来一个莫名其妙的地方。后来我给所有工具加了严格参数校验同时用 few-shot 示例引导模型传正确参数这类问题少了很多。所以给模型用的工具参数边界必须收紧不能让模型自由发挥。3. 从0到1一个练手项目的完整复盘3.1 场景定位给干活找到一个真实入口很多新手问我练手项目怎么选我的建议是别做全能助手做一个具体场景的单任务执行者。因为 Agent 最难的部分是稳定不是功能多。全场景 Agent 涉及的状态分支太多新手大概率会迷失在调试里。我做的一个比较成功的练手项目是会议纪要与待办提取 Agent。输入是会议录音转写文本输出是结构化会议纪要 待办事项 负责人。这个场景看起来很窄但它覆盖了 Agent 的全部核心环节解析输入 → 拆解任务 → 调用 LLM 生成 → 结构化输出 → 写入文件/数据库。麻雀虽小五脏俱全做一遍下来基本就摸清了 Agent 的脾气。3.2 关键实现State、节点与工具注册LangGraph 里的核心概念是 State。它就是一个全局字典保存 Agent 运行过程中的所有信息比如当前对话历史、剩余工具调用次数、中间结果等。我把 State 定义成这样的数据结构from typing import TypedDict, List from langgraph.graph import StateGraph, END class AgentState(TypedDict): messages: List[dict] # 对话历史 tool_outputs: List[str] # 工具返回结果 remaining_steps: int # 最大循环次数防止死循环然后定义两个节点一个叫call_model负责向 LLM 发起请求并判断是否需要调用工具一个叫call_tool负责执行工具并把结果写回 State。def call_model(state: AgentState): response llm.invoke(state[messages]) # 判断是调用工具还是返回最终结果 if response.tool_calls: return {messages: [response], remaining_steps: state[remaining_steps] - 1} else: return {messages: [response]} def call_tool(state: AgentState): # tool_calls 是 LLM 返回的工具调用指令 for tool_call in state[messages][-1].tool_calls: result tools_registry.execute(tool_call) return {tool_outputs: result, messages: [result]}然后把这些节点连成图加上条件边让 LLM 决定是继续调用工具还是结束graph StateGraph(AgentState) graph.add_node(call_model, call_model) graph.add_node(call_tool, call_tool) graph.set_entry_point(call_model) graph.add_conditional_edges(call_model, should_continue) graph.add_edge(call_tool, call_model) app graph.compile()remaining_steps这个字段我特别强调一下它是我在实战中加上的也是我认为任何 Agent 项目都必须有的东西。没有这个限制你就会遇到模型在某个环节反复调用工具把上下文撑爆炸冤枉钱花一大堆。我一般设成 8~10 步超过就强制结束并返回部分结果。3.3 参数配置与细节调优Agent 的性能很多体现在参数上。我分享几个用得上的temperature做工具调用的 Agent我基本设置在 0.1~0.3 之间。它需要确定性高不需要创造力。max_tokens一定要限制单次输出长度。否则模型可能会在工具调用失败后开始长篇大论解释抱歉我无法完成白白浪费时间和费用。timeoutLLM 请求必须设置超时。我在生产环境中遇到过 LLM 服务卡住 60 秒不响应的情况如果没有超时整个 Agent 就挂在那。retry对于瞬时错误网络超时、429 限流要考虑退避重试。我一般用指数退避起始 1 秒最多重试 3 次。这些都是小地方但加在一起决定了 Agent 的稳定度。说一个我之前遇到的真实场景某次演示时模型突然返回乱码因为我把 max_tokens 设太小输出被截断了。后来我把输出分成思考片段和最终答案两段思考片段单独设限制才解决这个问题。4. 最头疼的并发问题Agent 怎么扛流量4.1 先搞清楚瓶颈在哪有人问AI Agent 怎么扛并发我的第一反应是先别急着想并发方案先弄清瓶颈在哪。Agent 的请求链路很长通常包含模型调用、工具调用、可能的数据库访问、向量检索等。不分析瓶颈就直接上并发优化大概率是白忙活。实测中发现瓶颈几乎总是出在 LLM 调用上。比如你的 Agent 一次任务要调用 3 次模型每次 5 秒那单个请求的总耗时就是 15 秒。并发 10 个请求如果没有连接池和合理的排队机制LLM API 端会直接给你报限流。4.2 三个层级的优化方案我在项目里按三层来解决问题第一层降低单请求的模型调用次数。这是性价比最高的优化。在 LangGraph 图里我加了一个提前终止策略——如果前一轮工具返回的数据已经足够生成答案就不再进入下一轮模型调用。有些任务从 5 次调用降到 2 次吞吐量直接翻倍。第二层合理使用缓存。对于同一用户、同一场景的相似请求把 Agent 的部分结果缓存起来。我用的缓存粒度不是整个结果而是工具调用的结果。因为工具结果往往是结构化数据重复率很高比如查询员工信息、查库存这些结果完全可以缓存。我用 Redis 做工具结果缓存key 是工具名 参数哈希TTL 设 10 分钟实测缓存命中率大约在 30%~40%。第三层异步化与连接池管理。FastAPI 本身就是异步框架但要注意不要在 async 函数里写同步阻塞调用。LLM 官方库比如 OpenAI Python SDK 有异步版本要记得用。同时用httpx.AsyncClient统一管理连接池避免每次请求新建连接。4.3 一个可落地的并发控制方案很多人会直接想上消息队列但我觉得如果单机并发不超过 50先别折腾队列。我的做法是用asyncio.Semaphore控制并发数比如限制同一时刻最多 20 个 Agent 任务在跑超出的请求直接返回 503前端提示当前任务繁忙请稍后重试内部用asyncio.Queue做简单的排队。这里的关键是你要清楚 LLM API 的限流阈值是多少。如果你的账号 QPM每分钟请求数是 60那么同时处理 60 个 Agent 任务大概率会触发限流。设置并发上限时要留 30% 的余量。我在生产环境里的配置是QPM 上限 60我设的并发上限是 20单 Agent 内部最多 3~4 次模型调用所以实际每分钟调用量在 60~80 之间刚好卡在安全线内。4.4 实测数据与取舍我说一组我项目的实测数据供参考一个跑在 2C4G 的容器上的 Agent 服务功能是工单分类 初步回复单次任务平均耗时 12 秒单机并发 20QPS 稳定在 1.5 左右。对比下来用缓存之前 QPS 只有 0.8优化后提升了接近一倍。如果单机撑不住再考虑横向扩展。这里有个小技巧重任务和轻任务分开部署。我的 Agent 服务里简单的文本摘要任务走快速通道复杂的多轮工具调用走重任务通道。两条通道分开部署互不干扰避免一个慢任务拖垮整批请求。5. 常见问题与排查技巧实录5.1 上下文爆炸与幻觉AI Agent 最常见的坑是上下文无限膨胀。每次工具调用结果都塞进 messages十几轮之后上下文已经上万 token既贵又容易让模型产生幻觉。我在 LangGraph 的 State 设计里做了一个取舍工具的输出只保留摘要不保留全文。模型需要的不是原始数据而是数据的结构和关键信息。比如查数据库返回 100 行记录我不会直接塞给模型而是先做一个summarize_tool_output的步骤把 100 行压缩成共 100 条其中符合条件的关键条目如下...。这样模型拿到的上下文干净幻觉率也会下降。5.2 Agent 卡在循环里出不来不知道你有没有遇到过Agent 反复调用同一个工具每次都报错但它就是不换思路。我调试时把每次的 prompt 和工具输出都打印出来发现模型在中文转拼音这个工具上不断重试因为输入里有一个生僻字工具一直报错。模型不会说我换个方式它就硬试。后来我在 prompt 里明确规定工具调用失败两此后必须更换思路或将问题上报给用户同时用条件边限制同一个工具的连续调用次数。5.3 工具输出的数据管线问题还有一个容易被忽视的坑Agent 的工具返回类型和 LLM 期望的类型不一致。比如工具返回的是 Python dict但模型看到的是一个字符串化的 dict它的判断就会出偏差。我在工具注册层统一做了序列化处理所有工具返回都转成 JSON 字符串同时加上类型标注信息模型就很少再犯迷糊了。5.4 并发场景下的限流排障刚才提到并发实际排障时我遇到过一种很隐蔽的情况服务类似正常但就是偶发超时。后来通过日志排查发现是某个 LLM API 客户端库设置了默认的最大连接数导致并发上来后请求排队。日志里可以看到耗时分布正常请求 2 秒被阻塞的需要 8 秒。排查方法是看耗时分布不是看平均耗时。平均 4 秒看着还行但 P95 已经超过 10 秒了这就是连接池不够的表现。这个排查思路建议做 Agent 的朋友都记住。6. 工具链盘点与学习路线6.1 国内产品和平台从扣子到中台最近也有不少朋友在问国内有哪些 Agent 产品和平台。除了自己攒一套技术栈用现成的平台确实能大幅降低入门成本。扣子Coze我体验过它做得比较成熟拖拽式编排内置大量插件比如查询资讯、图片识别、语音合成这些直接接好就能用。适合非技术背景的产品经理、运营人员快速做验证。由于它是可视化编排灵活性相对有限复杂的状态流转还是要到自定义代码里写。我建议的方式是先用扣子做 MVP验证跑通后再用代码重写核心链路。各种 Agent 中台比如阿里的魔搭、字节的扣子企业版、百度的千帆等自然也各有侧重。它们的共同价值是降低 Agent 开发门槛提供模型管理、Prompt 管理、知识库、编排工具一站式服务。但我个人觉得如果你是要做严肃的生产系统还是要理解底层原理不要在平台上绑太死。平台升级频繁、接口变动这些风险都是实际要面对的问题。6.2 从0到1的Agent开发学习路线我总结一下自己走过和观察到的靠谱路线分为四步第一步写好原生工具调用。不要一上来就上 LangChain先用 OpenAI/Claude 的官方 API手写函数调用的 JSON 解析和工具分发代码。写个 3~5 个工具的小 Agent完整走通循环逻辑。第二步引入框架。用 LangChain 重写一遍之前手写的功能体会框架带来的便利和它的限制。这步的核心是理解封装了什么、替你做主了什么。第三步用 LangGraph 搭建有状态控制流的 Agent。理解 State、节点、边、条件分支尝试实现一个多步骤任务比如分析 PDF → 提取数据 → 生成报告 → 发送邮件。第四步做工程化改造。加上日志、监控、缓存、并发控制、异常处理。这一步才是生产级 Agent 和 demo 的分水岭。对于有 Java 背景的现在 Spring AI 生态也发展起来了spring-ai-agent相关的内容值得了解一下。它的好处是能跟现有 Spring 项目无缝集成统一使用 Spring 的编程模型来写 Agent。Java 生态的 Agent 例子比 Python 少但如果你公司是 Java 技术栈选个更贴近团队现有能力的方案也算合理。很多人在第三步到第四步之间放弃说我研究不明白了。其实本质原因是动手少了。准备的练手项目最好是和手头工作相关比如自动生成周报、自动整理文档、自动做数据统计。有真实场景驱动学习效率完全不一样。6.3 说句关于期货交易Agent的实在话热搜里有人问个人使用AI Agent可以做期货交易吗。我的态度很明确做模拟盘的策略研究和信号提示没问题做全自动实盘交易个人用户务必慎重。原因有几个一是交易Agent需要极高的稳定性和低延迟一个卡顿或误操作可能导致很大损失二是模型幻觉在投资决策场景里是致命的LLM 可能会依据一段过时的新闻推理出一个看似合理的交易信号三是合规风险。真要研究建议把 Agent 定位为信息搜集和策略分析辅助让它输出研究报告而不是直接下单。任何涉及钱的领域都要先确保逻辑可靠性和风控手段再谈自动化。7. 个人使用AI Agent的几个小技巧最后分享一些我自己习惯的实践经验这些可能平时文档里不会写。小技巧一一定要给 Agent 的每一步打日志。不是简单的 log 输出而是结构化日志包含当前节点名称、输入的 state 快照、模型返回内容、工具返回内容、耗时。我用 JSON 格式写入排障时直接按 request_id 串联起整条链路。没有这套日志Agent 排障基本靠猜效率极低。小技巧二Prompt 里显式写入边界条件。比如如果你只能获得部分数据请明确告知用户不要自己脑补信息如果工具调用失败两次请直接询问用户下一步指令。这些边界条件能在 AI 卡壳的时候有效止损。小技巧三用评测集来兜底。我做 Agent 项目时会积累一个 30~50 条输入组成的评测集每次改动代码就批量跑一遍用脚本统计成功率、平均耗时、失败类型。这个习惯帮我避免了很多次改好了 A 却改坏了 B的问题。小技巧四不在代码里写死模型版本而是通过配置管理。模型一换Agent 的行为会有微妙变化建议用配置中心或环境变量控制模型版本方便快速回滚。小技巧五研究新产品时多关注底层接口的稳定性。不管是扣子还是中台接口的稳定性直接影响你的业务别只看展示层面顺不顺手。我在实际使用中最深的体会有两个。一个是 Agent 的调试难度和传统程序的调试完全不在一个量级因为每一轮的输出结果不完全可控所以日志和评测比什么都重要。另一个是刚开始做 Agent 别贪多一个功能闭环做扎实了比十个花哨功能跑不稳有用得多。希望这些经验能让你少走一些弯路。最后再分享一个我最近在用的思路把 Agent 拆成大脑和手脚两个部分大脑负责规划和决策手脚是确定性很强的代码模块。大脑允许出错手脚必须可靠。像这样把不稳定的模型和稳定的工程结构分层隔离是目前我觉得做生产级 Agent 最务实的路线。