从零构建生产级Agent:模型、框架与工具调用选型避坑指南
1. 先别急着选框架把“Agent”这个词拆清楚很多朋友第一次接触“从零构建Agent”这个系列时第一反应都是问我是不是选个LangChain或者找个Agent框架把大模型API一接就算完事了我过去也是这个思路直到在生产环境里踩了几次坑才明白技术选型的核心问题根本不在“框架A好还是框架B好”而是你到底要构建一个什么样的Agent。这个问题的答案没想清楚后面的框架、模型、记忆、编排全都会选偏。所以这一篇我先把概念边界和需求边界讲透。1.1 Agent、LLM、AI模型到底是什么关系我看到很多搜索记录里都在问“Agent和LLM和AI模型有什么区别”还有人会拿DeepSeek来问“它到底属于哪一类”。这个必须开篇就讲清楚因为90%的错误选型都源于概念混淆。LLM是大语言模型负责的是“根据输入生成下一个token”这件事。它本质上是一个概率推理引擎输入一串文字输出一段合理的延续。DeepSeek、GPT、Claude、Qwen这些都是LLM它们属于“AI模型”这个大范畴。AI模型是个更大的伞LLM、多模态模型、语音识别模型、扩散模型都算。而Agent是什么它是把模型包在一个目标导向的执行系统里。这个系统通常包含任务理解、规划、工具调用、结果观察、记忆读写、自我反思这几个能力。模型负责“思考”Agent负责“做事”。模型就像人的大脑Agent是大脑加上手、脚、记事本和一套工作流程。Skill和Agent的区别也在这里。Skill是能力包是经过封装的一段指令、脚本或数据Agent在需要时把它拿出来用。Agent是整个执行系统负责决定什么时候用哪个Skill、怎么组合多个Skill、怎么根据结果调整下一步。先有技能库再谈Agent这个顺序不要搞反。1.2 先定需求边界再谈选型在动手之前建议你先花半天时间回答下面四个问题用户是谁是内部工具的使用者还是外部产品的终端用户任务类型是什么是问答、内容生成、数据分析还是需要操作外部系统的自动化任务允许Agent访问哪些工具查数据库、发请求、读写文件、控制浏览器每加一个工具复杂度和风险都上一个量级。数据能不能出内网这直接决定了你是用API还是本地模型。这四个问题看起来基础但它们决定了技术选型的自由度。比如数据不能出内网那就基本告别了纯云端闭源模型你的选型范围会收敛到本地部署模型加自研工具链路。反过来如果只是做产品原型那就没必要一开始就搭一套复杂的多Agent编排单Agent加三五个工具就够跑起来了。“从零构建”的意义就在于此你不是去填一个框架的模板而是把Agent的每个环节都拆开看一遍再选出一套最小可用组合。这一篇的技术选型也是在为这个目标服务。2. 技术选型的核心框架从一条闭环链路出发我见过很多团队选型败在同一个点上先选了一套很热闹的技术栈然后反过来硬套业务需求。实际情况恰恰应该反过来先理清Agent运行的最小闭环再顺着闭环里的每个环节去选型。2.1 Agent运行的最小闭环是选型的坐标系任何一个Agent无论有没有框架都在跑下面这条链路接收任务把任务和系统提示词组织成模型输入。模型推理决定下一步是直接回答还是调用某个工具。如果调用工具把工具名字和参数从模型输出里解析出来执行工具。把工具结果回填给模型让模型继续推理。重复第2到第4步直到模型认为任务完成。把最终答案返回给用户并把关键信息写入记忆。这条闭环就是Agent的坐标系。选型的时候你要做的不是问“哪个框架最火”而是问“闭环里的每一步我拿什么去实现”。模型负责第2步工具层负责第3和第4步记忆模块负责第6步而整个循环的控制就是后面会讲到的Harness。有了这个坐标系你会发现很多框架之争其实没那么重要。框架只是把这条闭环封装好了真正的差距在于它对工具调用的容错率处理得好不好对上下文的管理透不透明以及你的业务和它封装的假设是否一致。2.2 选型维度清单一份可以照着打分的表我整理了一份自己常用的选型打分维度。你不需要每项都满分但要清楚哪些是硬指标哪些是可以妥协的。维度为什么重要主要考察点模型推理能力决定任务完成质量的上限逻辑推理、代码生成、指令遵循Function Calling稳定性决定Agent能不能可靠操作工具能否稳定输出合法工具参数、多工具并行能力上下文窗口决定单次任务能装多少信息支持的最大token数、超窗后的表现成本决定方案能不能长期跑每千token价格、工具调用产生的额外消耗延迟决定用户体验首token时间、工具来回次数生态与兼容性决定开发效率是否有OpenAI兼容接口、社区资源、SDK质量数据安全与部署方式决定能不能落地到目标环境云端API、私有化部署、数据合规调试便利性决定你排查问题要多久日志是否透明、能否看到完整请求体团队技术栈匹配度决定维护成本后端语言、前端壳子、运维能力另外我会格外加一项评估体系。技术选型的阶段就该顺手搭一个Evals集。准备二十个有代表性的任务每个技术方案跑一遍记录成功率、失败原因、平均耗时。没有Evals选型只能靠感觉后面Agent迭代也容易失控。3. 核心组件选型模型、框架、工具、记忆逐个过顺着闭环链路接下来我们把核心组件一个个过一遍。每个组件我都会给出我实际踩坑后的结论和理由而不是简单罗列选项。3.1 LLM选型第一优先级不是榜单而是Function Calling在没有Agent这个概念时大家选模型看的是推理能力和文本质量。但在Agent场景里我强烈建议把Function Calling的稳定性放在第一位。原因很简单Agent任务里模型输出的关键中间结果是一段结构化的工具调用指令。如果这个环节三天两头解析失败、参数缺字段、调用名字凭空编造后面的工具执行全会乱套。选模型的时候我一般会准备一个固定的工具集比如三个查询类工具、两个写入类工具让模型连续跑五十轮工具调用看它在这三个指标上的表现参数是否严格符合JSON Schema。是否能正确处理并行工具调用。在上下文比较长时是否还能保持工具调用格式不崩。闭源模型在这块通常更成熟比如各家的旗舰模型都对工具调用做了专门优化。开源模型里Qwen系列和DeepSeek系列在工具调用上做得比较认真部署用vLLM或者Ollama都行。需要说明的是DeepSeek这个常被问到的名字它本身是LLM不是Agent你可以用DeepSeek的API或开源权重来做Agent的“大脑”但Agent的那套循环逻辑还是得你自己搭。开源模型能不能用请以你真实工具集的测试结果为准别只看榜单分数。还有一个容易被忽略的点模型对tools参数的实现细节不同。有的模型要求工具名称必须满足特定字符集有的不支持parallel_tool_calls有的对strict模式支持得很差。这些差异在技术选型阶段就要摸清否则写代码的时候会很痛苦。3.2 框架选型先自研一个小循环再决定要不要上框架这个问题我犹豫了很久因为市面上框架的舆论声音太大。我的最终结论是如果你是要做生产级Agent不要一上来就套重型编排框架先自研一个最小循环跑通业务再根据痛点决定要不要引入框架。这么说的理由来自我真实的排障经历。框架把模型调用、提示词组装、工具分发、记忆管理都封装起来了这确实省事但代价是抽象层太厚。出问题的时候你根本看不到模型收到的实际请求是什么样只能一层一层翻框架源码。有一次我们排查一个工具调用循环出错的问题最后发现是框架版本升级后某个默认提示词偷偷改了格式。这种问题自研循环五分钟就能定位用框架可能要折腾一天。不同的框架适合不同的场景我给了个大概的参考直接调模型SDK适合核心链路简单、你需要完全掌控上下文和工具逻辑的场景。这是我最推荐的生产起点。LangChain生态全、组件多适合快速做复杂文档处理或搜索链路的原型。但生产化时要非常小心抽象泄漏。LlamaIndex如果你的Agent强依赖私有知识库、要做复杂文档索引这个方向值得考虑。AutoGen、CrewAI适合做多Agent研究实验可以快速搭出角色协作场景。生产落地前要做好收敛和降级准备。OpenAI Agents SDK、Claude Agent SDK这类官方轻量工具如果你已经确定用某一家的模型可以直接用官方SDK它们把Harness做得比较克制也能看到完整执行过程。选型的时候还要把“Agent安全”提前想好。Agent有没有可能被提示词注入工具有没有最小权限模型能不能操作高危接口这些在框架层没有银弹只能靠自己在工具层和权限控制上做约束。我的习惯是模型接SDK循环和Harness自己写记忆用现成的向量库封装多Agent先用单文件模拟跑通后再考虑上编排框架。3.3 工具调用一个被低估的Token消耗大户工具调用是Agent最有价值的部分也是最容易让成本失控的部分。很多人以为Token消耗主要来自用户输入和模型输出其实不是。你的工具Schema会在每一轮请求里被完整发送给模型工具越多、描述越长每轮烧的Token就越多。我有个很直观的对比一个只有两个工具的原型Agent每轮请求大概多消耗三百个Token在工具定义上如果把工具加到十个每个工具描述再写得详细一点这部分消耗会轻松破千。而Agent完成一个任务通常需要三到五轮工具调用成本就成倍上去了。控制办法是这么几个工具不是越多越好每个工具都必须有明确的使用场景。工具描述写得精简克制把详细使用文档拆出去需要时让Agent通过检索工具获取。给工具返回做裁剪尤其是数据库查询类的超过一定行数就截断或做摘要。工具执行必须设置超时和重试避免外部接口卡死整个Agent循环。工具名一定要用明确的英文标识不要用中文别看这是小事不同模型对中文工具名的兼容差别很大。工具调用的健壮性最终要靠一个统一的工具执行层来保证。我的做法是把所有工具包在一个注册表里输入统一走JSON Schema校验输出统一做序列化这样模型就算传了很奇怪的参数也不会直接炸掉整个进程。3.4 记忆设计短期、长期、永久记忆要分开做记忆是Agent从“能用”到“好用”的分水岭也是技术选型里最容易糊弄过去的部分。很多人一听到“记忆”就想上向量数据库其实记忆没那么玄乎核心是按生命周期拆开。短期记忆就是单次会话里的对话历史和工具调用记录它直接放在模型上下文窗口里。你要解决的是一件事窗口满了怎么办。常规做法是用最近N轮消息或者对旧消息做摘要压缩。这个最简单的短期记忆千万不要外包给复杂模块用它来控制成本非常有效。长期记忆是跨会话的经验和信息沉淀比如用户偏好、历史任务、知识点。这部分我会用向量库加业务表配合实现向量库做语义检索业务表存结构化事实。选型的时候别只看向量库本身的性能更关键的是“写入策略”和“召回策略”。你不可能把每个工具结果都塞进长期记忆那样很快就会被噪声淹没。实际做的时候要先有提取模块把重要信息从历史对话里抽出来再决定写不写长期记忆。永久记忆则更像是一份用户档案存储那些不该丢的底层事实比如用户姓名、身份、核心偏好。这部分建议直接存在关系型数据库或者Key-Value存储里不需要多牛的组件稳定可靠最重要。我见过不少项目把记忆设计成一锅粥所有历史一股脑塞进向量库结果检索出来全是无关片段模型被噪声带偏。记住一句话记忆不是存储问题而是取舍问题。4. 编排与协作单Agent、Harness还是多Agent技术选型到了编排层很多人的脑洞就开始放飞了。一上来就要多Agent协作、规划器加执行器、角色扮演。我想先泼一盆冷水先把Harness和Agent的区别搞明白再考虑要不要上多Agent。4.1 Harness是控制循环Agent是执行单元在Agent开发里Harness和Agent是两个经常被混在一起的概念。Agent本身是带指令、工具和模型配置的执行单元它决定“这个任务怎么做”。Harness则是控制循环本身决定“循环什么时候启动、什么时候终止、工具结果怎么回传、异常怎么处理”。你可以这样理解Agent是士兵Harness是作战指挥系统。士兵很能干但如果没有指挥系统他可能会在一个任务上反复执行到超时或者在工具报错之后不知所措。我见过的大量线上问题比如“Agent execution terminated due to error”根源都在Harness层不在Agent层。要么是循环没有设置最大步数要么是工具异常后没有恢复策略要么是上下文溢出后还在继续追加消息。这些问题跟模型聪明不聪明没关系纯粹是控制循环设计不健壮。所以我在自研循环的时候会非常认真地处理下面几件事最大步数限制比如十步内必须产出结果。工具异常的分级处理能自动重试的重试不能的返回给模型自己判断。上下文超限时的降级策略裁旧消息、写摘要、强制结束。每一步的执行日志完整保留请求和响应。这些控制逻辑比选哪个编排框架重要十倍。框架能帮你写好一部分但没写到的地方才是事故高发区。4.2 多Agent协作的现实代价算清楚再上多Agent协作确实是Agent技术里最有想象力的方向但它也是一个代价极高的方向。每个Agent都要带一份系统提示词、一份工具列表、一份记忆上下文总成本会随Agent数量线性甚至超线性增长。而且Agent之间的通信用的是自然语言自然语言天生不稳定A理解偏一点B再理解偏一点任务就完全跑偏了。我的判断标准是这样的如果任务天然需要隔离权限比如一个Agent只能读、一个Agent只能写那多Agent值得考虑。如果任务可以并行拆解比如同时去查三个独立数据源可以用并行子任务来做。如果你只是想把任务做得“更聪明”那就先别上多Agent把单Agent的工具和Harness打磨好再说。实际项目里单Agent加一组好工具能解决八成以上的问题。多Agent更像是对特定架构需求的回应而不是一个默认选项。真要上多Agent也要先定好协调模式是管理者分配任务还是自由讨论投票。前者稳定性高后者研究味道重自己权衡。5. 从0到一个能跑的Agent最小实现与配置理论讲完我更想分享一个落地路径。从零构建Agent第一步不是写几百行框架代码而是用一个最精简的循环把整条链路跑通。5.1 最小Agent循环的核心代码逻辑我用Python直接调模型SDK写一个最小循环核心代码大概长这样import json from openai import OpenAI client OpenAI( api_keyyour_api_key, base_urlyour_api_base_url, ) SYSTEM_PROMPT ( 你是一个能调用工具的Agent。 用户给你任务你决定是直接回答还是调用工具。 工具调用结果会在下一轮作为新的上下文提供给你。 ) def call_tool(name: str, args: dict) - str: # 这里放你的工具分发逻辑 if name get_weather: return 晴25摄氏度 if name search_web: return 这是搜索结果示例 return 未知工具 def run_agent(task: str, tools: list, max_steps: int 10) - str: messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: task}, ] for step in range(max_steps): response client.chat.completions.create( modelyour_model_name, messagesmessages, toolstools, ) msg response.choices[0].message messages.append(msg) if msg.tool_calls: for tc in msg.tool_calls: result call_tool( tc.function.name, json.loads(tc.function.arguments), ) messages.append({ role: tool, tool_call_id: tc.id, content: result, }) else: return msg.content raise RuntimeError(max steps exceeded)这段代码里每一个地方都值得留意系统提示词要收敛不要写一大堆世界观几百字内明确“什么时候直接回答、什么时候调用工具”就够。模型名和接入地址用变量控制不要写死在代码里方便切换服务商。工具分发逻辑用一个函数入口统一处理参数解析和返回序列化。每次循环都把模型消息和工具结果追加进messages让模型能看到完整的工具调用历史。max_steps是保命的它保证Agent不会因为某些原因无限自我对话。你可以拿这个循环去测不同模型的Function Calling能力第一次跑通后再逐步加上短期记忆、长期记忆、预设管理这些能力。5.2 预设配置和预设管理别把Agent写死在代码里当你从一个Agent扩展到一个Agent系统时第一件事就是做预设管理。Agent预设可以理解为一份配置它描述了这个Agent的系统提示词、使用的模型、允许调用的工具、温度、最大步数、要不要开启某个记忆模块。我强烈建议把预设做成JSON或YAML文件甚至可以做成后台接口下发。启动时客户端去拉取像接口/agentpresets/list这种返回的预设列表前端拿到后渲染成操作界面。这么做的直接好处是修改一个Agent的行为不需要发版运营人员在后台上调一下模型参数或工具权限就行。预设的字段至少该包含这些agent_id: doc_analyzer name: 文档分析助手 model: your_model_name temperature: 0.2 max_steps: 8 system_prompt: 你是文档分析助手专注于从用户上传的文档中提取结构化信息。 tools: - read_document - summarize_text - search_knowledge_base memory: short_term: true long_term: true permanent: true预设管理看起来是个小事但它是Agent工程化和原型Demo的分水岭。不做预设管理你的Agent系统永远停在“我今天又改了一版代码”的状态。6. 常见问题与排查心得不要以为技术选型阶段没有坑很多问题在选型结束、开始联调的时候才集中爆发。我挑几个最典型的问题连同排查思路一起写下来。6.1 一条报错还原现场agentpresets/list failed to fetch有一个报错非常经典“无法加载Agent预设。client api: agentpresets/list failed to fetch”。这种“failed to fetch”问题在Electron加Agent架构的桌面端应用里尤其常见。光看这行报错只能知道客户端调后端接口失败了但具体原因得按下面顺序排查后端服务到底有没有起来先curl一下接口地址看看通不通。前端配置的baseURL是不是对的本地开发环境最容易犯的错是接口地址写成了线上地址或者端口没对齐。有没有跨域问题浏览器环境直接调不同域名的接口CORS配置不对就会failed to fetch。返回的数据结构是不是符合前端预期如果后端返回了200但缺少presets字段前端同样会渲染失败。环境变量是不是丢了很多桌面应用把接口地址放在环境变量里打包后环境变量没生效就会复现这个报错。我处理这类问题有个习惯让前端在fetch之前先把请求地址打到日志里一步到位排查。别猜看实际请求。6.2 Agent执行到一半终止五个常见原因“Agent execution terminated due to error”这句话会让很多新手头疼。结合Harness的视角它的本质是控制循环在某个环节没接住异常只好强制终止。常见原因我总结了五个达到了最大步数模型一直在规划但始终没给出最终回答。解法是调高步数或优化提示词让它别纠结。工具返回格式模型解析不了工具结果是一大坨非结构化文本模型看不懂。解法是给工具结果包一层摘要或结构化包装。上下文溢出历史消息太多超出模型的上下文窗口。解法是做短期记忆裁剪别把全部历史堆进去。参数解析失败模型输出的工具参数不是合法JSON或者缺字段。解法是在工具执行层做兜底解析比如先尝试JSON修复。安全护栏触发内容审核或工具权限校验不通过循环被强制终止。这种情况要分清楚是误伤还是真有风险。排查的时候核心是看每一轮请求和响应的完整日志。所以Harness里加日志这件事在技术选型阶段就要列入需求否则后面出了事你会很煎熬。6.3 桌面端壳子怎么选Electron加Agent这套路线如果你做的Agent最后要交付成桌面应用技术选型还多一道选择题桌面端用谁来做。我看到很多项目选了Electron加Agent的组合这个选择本身合理因为Agent的很多SDK都是Node生态的Electron可以直接复用UI层也有成熟组件。像桌面Agent类应用用这套架构很顺手。Electron的代价是包体积大、内存占用高。你可以在开发早期先用Electron把产品验证跑通把Agent核心逻辑放在主进程渲染进程只负责界面展示。API Key这类敏感信息必须留在主进程不能出现在渲染进程的JS代码里否则用户看一眼DevTools就能把你的凭证扒出来。如果团队有前端背景动手能力也强后期可以评估Tauri这种更轻量的方案但前提是你能接受Rust侧的维护成本。技术选型的规律在这里也一样先选最顺手的方案跑通业务再用真实数据决定要不要换壳。最后分享一个小技巧技术选型阶段我建议你花一个下午写那个“裸Agent”循环然后拿它去测试所有你感兴趣的模型和工具。这个原型不要写得花哨但一定要能完整跑通一次真实业务任务。你要的答案不在任何一份对比表格里而在这一次真实的跑通过程里。跑通了选型就踏实了跑不通换工具的理由也就充分了。