ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从0手搓AI Agent:核心原理、实践踩坑与落地指南

从0手搓AI Agent:核心原理、实践踩坑与落地指南 最近技术群里讨论度最高的一份资料大概就是那份《从0手搓AI Agent》的PDF了。原文档的好处是没堆术语、没绕弯子直接带着你把Agent从无到有搭出来。但说实话文档里更多是“怎么做”的步骤很多“为什么这么做”的底层逻辑没有摊开讲。这篇我打算把那本PDF里的核心思路结合我自己做Agent项目时踩过的坑、填过的洞一次性说清楚到底什么叫AI Agent手搓一个Agent要解决哪些关键问题你拿DeepSeek这类模型到底能干什么、不能干什么以及从一个“能跑的Demo”到一个“能落地的应用”之间还差哪些东西。这篇文章适合谁看你如果是刚接触Agent、想搞清楚它和普通AI聊天的区别或者已经在折腾LangChain、Spring AI但总觉得框架太黑盒想自己动手控一遍底层逻辑那这篇内容应该能帮你省不少时间。1. Agent与LLM的本质区别先别急着写代码1.1 大模型是“大脑”Agent是“完整的人”网上关于Agent和LLM区别的讨论特别多但大部分都讲得太抽象了。我习惯用一个类比DeepSeek、GPT这类模型像是一个只会动嘴、从不干活的天才顾问你问他任何问题他都能给你一套漂亮方案但你让他把文件从A目录挪到B目录他做不到因为他的手没有和电脑连起来。AI Agent就是给这个天才顾问装上“手”和“脚”。在技术结构上Agent是一个完整的执行闭环感知需求、拆解计划、调用工具、观察结果、修正下一步动作。它不再只是“生成一段文字”而是“完成一个任务”。DeepSeek属于“AI模型/LLM”不是Agent。它是Agent的“大脑”组件你拿它来对话、写作、分析文本都没问题但要让Agent自动查天气、写文件、调接口你需要在外层做一堆编排工作。1.2 一次对话与一次Agent任务之间差了三个核心能力同样是“帮我在数据库里查一下上个月的销售额”直接问LLM和交给Agent是两种完全不一样的链路。普通对话模式用户提问 → LLM根据已有知识回答。LLM如果不知道数据就会一本正经地编一个数字给你。Agent执行模式用户提问 → LLM分析需求 → Agent把任务拆成“连接数据库、编写查询语句、执行查询、解读结果”四步 → 调用数据库接口工具 → 拿到真实数据 → 把结果组织成回答。如果第一条SQL报错Agent还可以读错误信息、修改SQL、再跑一次。所以说Agent需要具备的最核心三样东西工具调用能力、多步规划能力、记忆能力。没有这三样哪怕底层模型再聪明它也只是个聊天机器人不是Agent。1.3 当你在“搭建Agent”时到底在搭建什么根据我自己做项目的经验绝大多数所谓的Agent项目核心工作量并不在模型训练上——你不需要自己训练一个LLM甚至不需要微调。真正的工作量在三个方面提示词系统设计让模型明白“你现在是一个客服/数据分析师/代码审查员”并且学会“在什么情况下调用什么工具”。工具层封装把业务系统的能力查库存、发消息、跑脚本、读文件抽象成一个个模型能调用、能理解返回值的函数。状态与流程控制一个复杂任务可能需要十几步任何一步出错怎么回退、怎么重试、怎么终止这些都需要代码来控制而不是把一次性决定权全部交给模型。说实话刚入门的时候最容易犯的错就是以为Agent开发就是“写一段很复杂的提示词”。提示词重要吗重要但它只是整个Agent系统的一个环节。真正的体验差异都在工具和流程里。2. 手搓Agent的组成结构拆解2.1 一张图拆开看Agent的五层结构我把那本PDF里的Agent结构结合自己手搓项目的经验总结成五层层级角色核心组件典型实现记忆层记住“之前聊了什么”“这个项目背景是什么”短期上下文窗口 长时向量记忆内存数组 Chroma/FAISS规划层决定“下一步要做什么”Planner、ReAct循环、任务拆解器用大模型 结构化输出约束工具层连接外部系统执行真正动作工具注册表、参数校验器、执行器Python函数 JSON Schema行动层把模型决策转化成可执行动作代码解释器、API调用器、浏览器控制器Execute工具、HTTP客户端控制层调度各层、处理异常、控制循环状态机、退出条件、重试机制Python主循环2.2 规划层ReAct循环是最容易上手的主干ReAct是Reasoning Acting的缩写核心思想是让模型“边想边做”先观察当前信息推理出下一步该干什么然后调用一个动作观察返回结果再推理下一步……直到任务结束。用手写代码实现一个最简单的ReAct循环其实不需要什么高级框架伪代码逻辑长这样while True: # 1. 把历史步骤、工具结果、当前目标拼成上下文 prompt build_react_prompt(goal, observations, history) # 2. 让模型输出“思考 下一个动作” response llm.chat(prompt) action parse_action(response) # {tool: search_db, args: {sql: ...}} # 3. 执行动作拿到观察结果 if action[tool] FINISH: return action[answer] observation execute_tool(action[tool], action[args]) history.append(observation)这么一套循环跑起来你就已经拥有一个最原始的Agent了——能规划、能调用工具、能根据结果继续调整。实践下来ReAct这种模式在任务可拆成五六步以内的场景里非常好用但任务一旦超过十几步上下文会越拖越长、模型容易在中间迷失方向。所以复杂场景我更推荐用Plan-and-Execute先让模型一次性把全流程计划列出来再一步步执行、按需修正上下文占用更少、稳定性更高。2.3 工具层让模型“学会用你给的接口”很多从没做过Agent的人第一次看“工具调用”这个概念会懵。模型明明只是个文本输入输出的东西怎么调用函数原理其实不复杂。如果你用的是OpenAI或DeepSeek这类支持Function Calling的模型调用时除了把聊天消息传过去还会额外传一份“工具清单”每项工具包含名称、描述、参数结构。模型看到用户需求后不直接回复答案而是输出一个JSON表示“我打算调用哪个工具、参数是什么”。你的代码收到这个JSON真正执行本地函数再把返回值作为一条新消息发给模型让它继续回答。这里我踩过的坑是很多模型对中文工具描述理解不稳定。有一次我写了个工具叫get_region_risk描述写“获取区域风险等级”模型经常误调用另一个get_region_data。后来改成描述里带案例——“当用户询问某个城市是否危险时调用此工具返回值为high/medium/low”误调率立刻降了一大截。工具描述写得越像“你会在什么场景下使用它”模型的准确度越高。2.4 记忆层短期靠上下文长期靠向量库Agent的记忆是很多人忽略的一环但恰恰是它决定了Agent能不能在真实业务里长期可用。短期记忆就是当前对话的上下文窗口把之前几轮对话和操作历史塞进Prompt里。这里要自己写上下文管理逻辑比如时间长了就做摘要压缩防止Token爆炸。长期记忆则要解决“Agent今天记住的东西明天还能记得吗”的问题。我的做法是每次任务结束后把关键结论、用户偏好、数据口径存到向量数据库Chroma或FAISS下次任务开始时先根据当前问题做相似度检索把相关历史记录拉回到上下文中。一个很生活化的例子你问Agent“帮我按上次的口径统计一下渠道成本”如果Agent没有长期记忆它只能让你解释“上次的口径”是什么。一旦有了记忆层它就会自己去向量库里捞出上次的统计逻辑直接干活。这个能力对业务方来说体感差异极其明显。3. 从0搭建一个能跑起来的Agent实操全流程3.1 技术选型看场景不被框架绑架手搓Agent首先要决定的是用谁来做主控。我自己的建议是如果目标是快速验证想法用Python。原因是生态最全OpenAI SDK、DeepSeek SDK、LangChain都支持得最好自己用FastAPI包一层接口也快。如果目标是要塞进公司现有的Java技术栈尤其像Spring Cloud微服务环境那优先考虑Spring AI。Spring AI提供了一套相对统一的模型接入抽象支持OpenAI、DeepSeek等虽然它的Agent抽象不如Python那边成熟但胜在能和Spring生态无缝衔接适合企业级应用打成独立平台。关于“Jenkins AI Agent”这类场景我也补充一句Agent不一定只能做人机对话它完全可以作为自动化流水线里的一个节点。比如CI流水线跑完后让Agent读构建日志、自动定位报错模块、给出修复建议。这类Agent不用太多“花活”核心做扎实一个事“读日志 → 发现问题 → 调用修复脚本”实用性就很强。3.2 第一个最小Agent Demo让它学会“查天气和算数学”我做第一个Agent练手项目时没有用任何Agent框架直接用DeepSeek API Python写了三十行代码做了两个工具一个是天气查询模拟返回一个是计算器。目标是测试模型能不能根据用户意图自动选择合适的工具来调用。import json from openai import OpenAI client OpenAI(base_urlhttps://api.deepseek.com, api_key你的key) tools [ { type: function, function: { name: get_weather, description: 查询指定城市的当前天气温度, parameters: { type: object, properties: { city: {type: string, description: 城市名如北京} }, required: [city] } } }, { type: function, function: { name: calculator, description: 进行数学四则运算, parameters: { type: object, properties: { expression: {type: string, description: 数学表达式如 (35)*2} }, required: [expression] } } } ] def call_tool(name, args): if name get_weather: # 真实项目里这里请求天气服务 return {city: args[city], temperature: 26} if name calculator: return {result: eval(args[expression])} return {error: unknown tool} messages [{role: user, content: 北京现在多少度顺便算一下(35)*2等于多少}] resp client.chat.completions.create( modeldeepseek-chat, messagesmessages, toolstools, tool_choiceauto, ) for item in resp.choices[0].message.tool_calls: tool_result call_tool(item.function.name, json.loads(item.function.arguments)) messages.append({ role: tool, tool_call_id: item.id, content: json.dumps(tool_result) }) final client.chat.completions.create(modeldeepseek-chat, messagesmessages) print(final.choices[0].message.content)这一版代码跑通之后你就能亲眼看到模型在收到一个包含“查询”和“计算”两种意图的复合问题时自动拆成两个工具调用并基于两个工具的返回值生成最终答复。别小看这个Demo它已经把Agent最核心的“意图识别 → 动作编排 → 工具执行 → 结果综合”链路跑通了。你就以这个为基础不断往上塞新工具、加记忆、加流程控制。3.3 实操中的关键细节上下文消息格式不能做错手搓Agent最常见的翻车点是维护消息历史时没按模型的角色规范来。工具调用结果必须用role: tool的角色回填并且tool_call_id必须和模型返回的id一致否则多数模型会直接报错或者不认这个结果。而且要注意工具结果这一轮消息和最终生成回答的消息之间必须保持连续。你如果往消息列表里插入了一段其它内容模型的理解就会乱。我开始手搓的时候在这个问题上卡了快两个小时后来养成了习惯每次调用模型之前先打印一下当前messages的结构一眼检查就不会再出这种低级问题。3.4 加入记忆层别一上来就上向量库很多人一开始搭Agent就急着接向量数据库但我建议先分清需求如果你的Agent只是“单次任务型”那只用短期上下文就够了如果你的Agent要承载连续多轮的服务比如私人助理、企业知识库问答再上向量库。我第一次做长期记忆的时候用了Chromadb代码很简单import chromadb chroma chromadb.Client() collection chroma.get_or_create_collection(agent_memory) # 保存记忆 collection.add( ids[memory_001], documents[用户偏好用按商品维度的口径统计渠道成本], metadatas[{type: preference, time: 2025-06-01}] ) # 检索记忆 res collection.query(query_texts[渠道成本统计方式], n_results3)实践经验是不要把原始对话直接丢进向量库否则检索出来全是噪音。正确做法是先让模型整理总结把“用户要求”“关键结论”“数据口径”提炼成结构化文本再存。检索质量会提升一个档次。3.5 上线部署别让模型变成单点故障Debug完的Agent下一步就是部署。我的推荐方案是FastAPI Docker把Agent服务封装成HTTP接口对外提供/chat和/task两个端点。from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): session_id: str user_input: str app.post(/chat) def chat(req: ChatRequest): result run_agent(req.session_id, req.user_input) return {reply: result}部署时一定要做的事有三件设置并发上限防止所有人同时请求导致模型限流记录完整的调用日志和token用量方便复盘和审计给工具调用加白名单至少不要允许Agent随便读环境变量、删文件、发起任意网络请求。这一条在企业里尤其重要。4. 常见问题与排查技巧实录4.1 模型输出结构不稳定回答里夹带JSON以外的内容现象模型在需要输出工具调用的JSON时偶尔会多出来“好的我来为您查询”这种废话。方案是两招一起用设置response_format{type: json_object}强制JSON输出在解析端做好容错比如用正则抽出第一个{到最后一个}再解析。这两招加一起在我的项目里把解析失败率从8%降到了不到0.5%。4.2 Agent陷入无限循环现象Agent在某个步骤反复调用同一个工具输出永远一样。原因通常有两个上下文太长模型忘了最初目标顺着最近一步反复执行或者工具返回的错误信息不明确模型认为换个参数就能成功其实永远会失败。我的排查套路是日志里打印每轮的“目标复述当前步骤数”强制在系统提示词里让模型每五步回顾一次原始目标同时给主循环加一个max_iterations硬上限超了直接终止并让模型总结已发现的卡点。从工程上防止死循环比指望模型自律靠谱得多。4.3 上下文Token爆炸现象Agent跑复杂任务每轮都把全部历史塞给模型很快到达上下文上限。我用的是三层策略短期窗口只保留最近N轮关键内容超过N轮后让模型生成摘要并替换旧消息任务中产生的中间工具结果不全部保留原始返回而是提取“结论”部分。具体来说工具返回一堆数据时先让一个轻量模型提取要点只把要点放回上下文。思路很简单Agent的上下文不是保险柜什么都往里堆只会撑爆它。4.4 多工具场景下选错工具现象工具超过十个以后模型开始频繁“张冠李戴”。排查经验是先看是不是工具描述含糊给每个工具描述加一个“典型触发场景”再看是不是工具之间存在功能重叠能合并就合并。我有一次觉得两个工具功能有差异保留了一个很细的边界模型根本分不清最后合并成一个工具加一个开关参数准确率反而上去了。4.5 如何给Agent写测试用例Agent和传统函数最大的不同是同样的输入每次输出可能不一样。所以测试不能只断言“最终回答是否等于某个字符串”。我实践下来比较有效的方案是准备一份包含正常场景、边界场景、恶意输入的任务集跑完看三件事任务完成率是否返回正确结果、工具调用率是否合理调用工具而不是全靠嘴硬编、资源消耗token数、步数。每改动一次Agent就跑一遍这个回归集对比指标变化。5. 从Demo到场景Agent能用在哪些真实业务里5.1 单Agent回报率有限多Agent协作才能解决复杂问题多个Agent协同工作是当前特别火的方向也是面试里经常被问到的高频题。我理解的多Agent其实就两种模式管道模式——A完成一个环节把结果交给B继续处理适合有固定流程的业务编排者-执行者模式——一个“老板”Agent负责判断这个任务需要哪些专长动态招聘对应的“员工”Agent去干活适合开放性不强的复杂任务。做多Agent的初期我一个很大的体会是不要为了堆Agent而堆Agent。每个Agent的引入都应该对应一个问题它的领域边界在哪它和别Agent之间通过什么协议交换数据它失败了谁来兜底这三个问题答不清多Agent几百行代码下去大概率是个灾难。5.2 企业级Java平台Spring AI Spring Cloud落地Agent现在大量企业技术栈都是Java系所以Spring AI热度一直很高。Spring AI相比Python生态的好处是可以和Spring Cloud网关、Nacos注册中心、Sentinel熔断等企业基础设施直接整合。我见过比较成熟的落地方案是把Agent拆成独立服务注册到注册中心上层通过Gateway按用户会话路由到不同的AgentAgent内部使用Spring AI的ChatClient接口屏蔽具体模型差异公司想从OpenAI切换到DeepSeek或者其它国产模型只需要改配置不用改业务代码。ChatClient client ChatClient.builder(chatModel).build(); String reply client.prompt() .system(你是一个订单查询Agent负责查订单状态) .user(订单12345现在到哪了) .call() .content();企业级平台另一个躲不开的点是权限与审计。Tool层必须接入IAM权限校验用户能调用的工具范围由后端统一控制而不是由Agent自己决定。这一点如果不在设计初期就考虑后面补成本极高。5.3 PLC编程、自动化流水线里的Agent热词里有“AI Agent与PLC编程”这个组合听着跨界但在工业场景里很有价值。PLC编程的特点是大量重复性逻辑如电机启停、报警处理以前靠工程师查手册手工写梯形图现在可以用Agent自动生成初始程序框架工程师只需审查和微调。这里的关键不是让Agent替代PLC工程师而是让Agent把检索、模板匹配、初稿生成这些脏活累活干掉。我个人的判断是这类垂直领域的Agent比通用客服Agent更容易落地出业务价值。5.4 面试高频题速查Agent开发的底层理解不可绕开基于我和面试过不少候选人聊下来的经验Agent方向的题大多围绕这几个维度Agent与LLM的本质区别答案核心是“LLM只生成文本Agent是能规划、调用工具、记忆并闭环执行任务的系统”。DeepSeek属于LLM但可以通过Function Calling机制作为Agent的大脑使用。Agent的核心组成规划、记忆、工具、行动、控制。多Agent协作模式管道模式 vs 编排者-执行者模式。如何保证Agent输出稳定结构化输出、上下文管理、重试机制、测试回归集。说实话Agent方向的面试题没有太多速成套路那些只背概念不自己写过代码的候选人一问到“Agent循环怎么控制终止”就会露馅。想真的学好Agent最靠谱的方式还是花一个周末亲手跑通一个最小Demo。6. 最后说点自己的体会那份《从0手搓AI Agent》的PDF我看完最深的感触是它通篇没有把Agent讲得多玄乎而是强调你从写第一行循环开始理解Agent的脉搏。我自己的经历是从看框架文档一头雾水到跑通第一个会自动调用工具的Demo再到给企业搭出能独立承担业务流程的Agent服务大概花了三周。前两周的绝大部分时间都耗在了上下文管理、工具容错、异常重试这些看起来很笨的细节上。但这些细节恰恰决定了Agent是不是真的可靠。如果你现在准备动手我建议按这个顺序走先写一个纯人工规定的有限步骤Agent跑通主流程加上工具调用异常处理加记忆最后再考虑多Agent编排。每加一层能力就补一组回归测试。最后再分享一个小技巧调试Agent的时候不要只看它最终有没有答对。把每一轮的“思考过程、工具选择、中间返回值”全部打印出来才能真正看清它在哪儿拐错了弯。绝大多数看似“模型不聪明”的问题原因都在提示词边界、工具描述、上下文结构这些代码层上面。把这些基本功打磨扎实手搓Agent没有想象中那么难。
返回列表