先泼一盆冷水:别被“5分钟”“小白”“秒变大神”这些词冲昏头。智能体不等于“一个ChatGPT套壳”,也不是“调个API就完事”。但我也能负责任地告诉你,只要你掌握了正确的最小实现思路,5分钟跑通一个能调用工具、能自主决策的Agent演示,完全做得到。这篇文章不会教你去背那些玄乎的论文,也不会扔给你一个三万行的框架源码,而是用最直白的方式,从零手写一个真正能干活的智能体,讲清楚它背后的几个关键机理,再把我踩过的坑一并交代清楚。
先说结论:一个最简的AI智能体,本质就是“大模型 + 一组工具 + 一个循环”。大模型负责读任务、出计划、决定调哪个工具,工具负责执行具体的动作,循环则负责判断“任务是不是已经终结”。所有号称“Agent化”的产品,真正核心的骨架就是这个循环,剩下的记忆、规划、多角色协作,全是围绕这个骨架长出来的增强功能。你只要把这个骨架钉死在自己脑子里,再看任何Agent框架,都是一层窗户纸。
1. 先想清楚:AI智能体到底是什么,为什么大家都在做
1.1 从大模型到智能体的本质跃迁
如果你只用过大模型做“给它一段文本,它回一段文本”,那你知道的还只是“对话机器人”。对话机器人的特点是:所有信息都存在于你的提问里,所有回答只是语言生成。但智能体不一样,它更像一个“实习生”:你给一个模糊目标,它需要自己拆解目标、查阅资料、操作工具、核对结果、调整计划,然后交付一个结果。
拿一个真实场景类比:你和一个人说“帮我查一下最近两周线上订单的退货率,并按城市排名”。对话机器人会怎么做?它会直接编一个数据给你,因为它的训练数据里根本不可能有你业务系统里的订单数据。智能体会怎么做?它会先意识到“我没法直接访问你的业务库”,于是决定调用一个“查询数据库”的工具,填入查询SQL,拿到结果后再调用“生成表格”的工具,最后把排名结果整理成文字。这就是本质区别:模型不再单靠记忆回答,而是通过工具和环境交互,用“动手”弥补“记忆”的不足。
所以我一直认为,判断一个系统是不是智能体,就看三条:
- 它有没有调用外部工具的能力;
- 它有没有“任务未完成则继续执行”的循环;
- 它的路径是不是由模型动态决策的,而不是写死的if-else。
如果三条都满足,哪怕代码只有100行,它也是个智能体。如果三条都不满足,哪怕界面再炫,也只是个接口包装。
1.2 谁适合读这篇,以及你该怎么用它
这篇文章最合适的读者有三类。
第一类是后端或全栈开发者,你已经会调用大模型API,但现在想做点更有“自动化”感觉的东西,想把Agent接进业务流程里。第二类是数据分析师或运维工程师,你每天要处理大量重复性的查询、核对、报告工作,想让一个Agent替你跑腿。第三种是纯小白,你可能只会写一点Python,甚至不是程序员,但对大模型很感兴趣,想知道“Agent到底怎么搭起来的”。
对小白我说句实在话:你不需要先啃完机器学习课程,只需要会一点最基础的Python语法,知道什么是函数、什么是循环、什么是字典,就能跟上这里的代码。整篇的核心代码不到150行,没有任何脏活累活。
对开发者我更建议你别跳过前面的原理部分。很多人一上来就套LangChain或各类商业化框架,结果报错都看不懂,就是因为不理解Agent循环那几步在做什么。先把手写版弄明白,再看框架,你会觉得框架全是在替你写你本来就会的那几步。
2. 5分钟跑通一个最小智能体
2.1 环境准备与选型
既然是“从零构建”,我建议最少依赖。你只需要两样东西:
- 一个能调用的LLM API接口,带消息对话能力和函数调用能力;
- Python 3.9以上的环境,再装一个
requests库,用来发HTTP请求。
这里我不特意指名某家的API,因为各家接口大同小异,你用哪家的都行。你只需要把你的API密钥存到环境变量里,别硬编码在代码里,这是第一条安全底线。如果公司内部的模型服务,那更好,内网调用通常还免去网络问题。
为什么要坚持“不依赖框架”?因为我见过太多人学习Agent的方式是“先装个LangChain,再看文档学着调”。那叫学会用别人的封装,不叫理解Agent。我这边建议你至少手写一次,把“模型输出字符串 -> 解析工具名和参数 -> 执行工具 -> 拼接结果返回给模型”这条链路亲自打通,之后再上框架,你会发现自己能读框架源码了。
提示:如果你完全不会Python的dict、list、for循环,先去补一下基础语法,大约两小时。不补也能抄,但调试时会痛苦。
2.2 一个能跑的主循环
我直接给出核心的Agent循环代码。这个循环是整篇文章的“心脏”。
import requests import json import os # 你的模型API配置 API_URL = os.getenv("LLM_API_URL", "https://your-llm-service.example.com/v1/chat/completions") API_KEY = os.getenv("LLM_API_KEY", "") HEADERS = { "Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}" } # 工具注册表:所有可供Agent调用的工具都放这里 TOOL_REGISTRY = {} def register_tool(): """装饰器:把函数注册为Agent可调用的工具""" def decorator(func): TOOL_REGISTRY[func.__name__] = func return func return decorator def call_llm(messages, tools=None): """调用大模型API,返回完整响应""" payload = { "model": "your-model-name", "messages": messages, "tools": tools or [], "tool_choice": "auto" } resp = requests.post(API_URL, headers=HEADERS, json=payload, timeout=60) resp.raise_for_status() return resp.json()["choices"][0]["message"] def run_agent(user_input, max_steps=10): """Agent主循环:思考 -> 调用工具 -> 观察结果 -> 继续或结束""" messages = [ {"role": "system", "content": "你是一个智能体,你可以通过调用工具来解决用户的问题。" "如果需要工具,请按指定格式返回工具调用;否则直接给出最终答案。"}, {"role": "user", "content": user_input} ] tools = [ { "type": "function", "function": { "name": "calculator", "description": "计算数学表达式,比如加减乘除、幂运算等", "parameters": { "type": "object", "properties": { "expression": {"type": "string", "description": "数学表达式,如 (1+2)*3"} }, "required": ["expression"] } } } ] for step in range(max_steps): print(f"\n[Step {step + 1}] 模型思考中...") message = call_llm(messages, tools=tools) # 情况1:模型决定直接回答,结束循环 if message.get("content") and not message.get("tool_calls"): return message["content"] # 情况2:模型要求调用工具 if message.get("tool_calls"): # 把模型这条回复追加到对话历史里 messages.append(message) for tool_call in message["tool_calls"]: tool_name = tool_call["function"]["name"] args = json.loads(tool_call["function"]["arguments"]) print(f"[Agent] 调用工具 {tool_name},参数: {args}") # 执行工具 if tool_name in TOOL_REGISTRY: tool_result = TOOL_REGISTRY[tool_name](**args) else: tool_result = f"错误:未找到工具 {tool_name}" # 把工具结果返回给模型 messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": json.dumps(tool_result, ensure_ascii=False) }) # 情况3:什么都没返回,视为异常 if not message.get("content") and not message.get("tool_calls"): return "模型返回为空,请检查API配置或输入内容" return "达到最大步数,任务未完成。" @register_tool def calculator(expression: str): """一个安全的最小计算器,只允许数字和四则运算""" import re # 只保留数字、运算符、括号、小数点,挡住所有注入 if not re.match(r'^[0-9+\-*/().\s]+$', expression): return {"error": "表达式包含非法字符"} try: result = eval(expression, {"__builtins__": {}}, {}) return {"result": result} except Exception as e: return {"error": str(e)}这个代码的运行流程你仔细看一遍:模型每次回复,要么输出最终文字,要么输出一个“tool_calls”结构,里面带着工具名和从文本里解析出来的JSON参数。你的程序负责执行工具,把执行结果作为一条新消息塞回去,模型看到结果后再决定下一步。
这就是Agent最底层的动作逻辑:模型出意图,代码出执行力。模型永远不直接操作文件、数据库、网络,它只是“提出操作请求”,由你的代码执行。这个设计能最大程度避免模型乱改数据,也方便你对每个动作做审计。
2.3 让Agent拥有“触手”:注册更多工具
上面的代码里,我用一个装饰器@register_tool来注册工具。你可能觉得奇怪:为什么不直接用if-else?用过你就知道,装饰器这种方式的好处是扩展工具时你不需要改动主循环,加一个函数就是加一个能力。如果以后系统里面有100个工具,主循环代码依然只有这几十行。
我们立刻加两个最常用的工具:查询当前时间和查用户信息。
from datetime import datetime @register_tool def get_current_time(format: str = "%Y-%m-%d %H:%M:%S"): """返回当前时间,可指定格式""" return {"time": datetime.now().strftime(format)} @register_tool def get_user_info(user_id: str): """模拟查询用户信息,比如用户的积分、等级""" fake_db = { "U1001": {"name": "张三", "level": "VIP3", "points": 12345}, "U1002": {"name": "李四", "level": "普通用户", "points": 80}, } return fake_db.get(user_id, {"error": "用户不存在"})同时,你需要把工具描述补充到tools变量里。这里要特别提醒:工具描述写得好不好,直接决定模型会不用这个工具。大模型没有读你代码的能力,它只能看你的描述来决定何时调用。描述要尽量写清楚“这个工具在什么场景下用”“参数含义是什么”“返回什么格式”。我看到很多人的Agent“明明注册了工具但是永远不调用”,八成就是工具描述太敷衍。
另外,工具的入参最好都用基础类型:字符串、数字、布尔值。不要传嵌套对象。模型输出JSON参数时,越简单越不容易出错。
2.4 测试一下效果
跑一下这个完整代码,输入“帮我计算一下(12 + 15) * 8 等于多少,顺便告诉我今天的日期”。你会看到类似这样的输出:
[Step 1] 模型思考中... [Agent] 调用工具 calculator,参数: {'expression': '(12+15)*8'} [Agent] 调用工具 get_current_time,参数: {'format': '%Y-%m-%d'} [Step 2] 模型思考中... 结果:(12 + 15) * 8 = 216,今天的日期是2025-06-12。到这里,你已经拥有一个“会用计算器、会看日期”的智能体了。整个过程如果不是网速慢,确实可以在5分钟以内跑通。这个演示虽小,但已经把“模型规划”和“工具执行”解耦得清清楚楚。
3. 把“会说话”变成“会做事”:工具调用的底层逻辑
3.1 为什么必须让模型“调用工具”,而不是把数据全塞进上下文
可能有人会问:既然模型能读文本,我为什么不能把所有知识都写进提示词?答案是三个字:不现实。
先算一笔账。假设你的业务数据库有一万条订单数据,每条平均200个token,全塞进上下文就是200万token。不说成本问题,主流模型根本处理不了这么大的上下文。就算你的模型支持超长上下文,让模型在几万条数据里自己“找”答案,它很可能在计算中犯低级错误。模型本质是做统计推断的,不是做精确检索的。
工具调用的意义在于:数据留在它该在的地方,模型只负责发出查询指令。数据库查询、文件搜索、系统命令,这些精确的活交给专业工具做,模型只接收精准处理后的结果。这样既减少token,又提升可靠性。比如上面计算器例子,如果你不用工具,而是让模型直接心算“(12+15)*8”,它可能算错;一旦用计算器工具,保证100%正确。
我还想提醒一个安全视角:工具调用让模型的行为变得“可审计”。模型的每一个动作都会留下一条明确记录:“在哪一步、调了哪个工具、参数是什么、结果是什么”。这在需要合规审计的场景里极其重要。如果让模型直接Python解释器裸跑代码,一旦提示词注入,破坏力不可控;而你用白名单工具,每条工具都写清楚能干什么,模型就算被恶意提示词诱导,也只能调用预定义的能力。
3.2 工具调用的三种主流形式,你怎么选
目前主流的大模型工具调用有三种实现形态,你需要知道差别。
第一种是原生函数调用,就是上面代码里展示的。模型输出结构化的tool_calls,你的代码分发给注册函数。优点:稳定、省token、逻辑清晰。缺点:模型需要支持函数调用能力,老一点的模型不支持。第二种是代码解释器模式,模型输出一段Python代码,你的沙箱环境执行代码并把输出回传。优点:表达力强,能做复杂的数据分析;缺点:安全风险大,必须有沙箱,token消耗高。第三种是纯文本协议式,模型输出一个固定格式的文本,比如“工具名(参数)”或JSON,你的代码用正则或解析器去解析。优点:任何模型都能用;缺点:解析容易出错,尤其遇到模型手滑多写个逗号的时候。
我的建议是:如果模型支持原生函数调用,优先用原生;如果不支持,退而求其次用文本协议。代码解释器模式不推荐新手玩,等你有完善沙箱了再说。
3.3 写一个常用工具,你会理解“工具边界”的价值
再看一个更贴近业务的例子:查订单金额。假设你有个本地CSV文件,里面是订单数据。我们不希望模型直接读文件,而是给它一个“查询订单总额”的工具。
import csv @register_tool def get_order_total(order_id: str): """根据订单ID查询订单总金额,返回金额数字""" with open("orders.csv", "r", encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: if row["order_id"] == order_id: return {"order_id": order_id, "total_amount": float(row["amount"])} return {"error": "订单不存在"}你会发现,这个工具把“文件内部结构”藏了起来,模型只需要提供order_id,就能拿到一个干净的结果。你完全不用担心模型去读CSV里的其他几百个字段导致上下文爆炸。这就是工具边界设计的意义:把脏活、杂活、有权限差异的活,封装成一个个“小接口”,模型面对的是整齐的能力列表,而不是原始数据海洋。
现实里的Agent,通常会有十几个到几十个这样的工具,分别对接订单系统、库存系统、消息推送服务等。每一个工具背后都是一个微服务API,Agent的价值就是根据用户目标自主编排这些API的调用顺序。
4. 记忆与状态管理:让Agent“记得”说过的话
4.1 短期记忆:必须自己维护对话历史
如果你只跑通了上面的代码,你会发现一个问题:Agent在一次任务的多个步骤之间是有“短期记忆”的,因为我们一直在往messages里追加消息。但是一旦任务结束,换个新问题,如果还是用一个新的messages列表,之前的对话就全忘了。
这是很多新手最容易忽略的。很多人的Demo“好像没问题”,一放到业务场景里,用户说“刚才你算的那个数,再帮我乘2”,Agent直接懵了,因为它根本没记住“刚才那个数”是多少。
解决办法很简单:在会话级把messages存起来。比如用Redis、数据库或内存里的dict,以session_id为key保存整个消息列表。每次新用户消息进来,从库里取出列表,追加一条user消息,再传给模型。
sessions = {} def get_session(session_id): return sessions.setdefault(session_id, [ {"role": "system", "content": "你是一个智能体..."} ]) def chat(session_id, user_input): messages = get_session(session_id) messages.append({"role": "user", "content": user_input}) ...这里的关键坑在token:如果session无限长,早晚会超出模型上下文窗口。所以一定要做消息裁剪。我常用的策略是:只保留最初的system提示、最近N轮对话,以及所有还没结束的工具调用。更激进的策略是把历史对话做摘要,塞一条摘要消息进去。摘要方式适合长对话,但会损失细节。
注意:裁剪消息时,不要把“model的某条包含tool_calls的消息”单独删掉,也不要把对应的tool结果删掉。两者必须成对保留,否则模型看到tool结果却不知道是响应哪个调用,或者看到一个调用却没有结果,都会导致逻辑混乱。
4.2 长期记忆:用向量库做“可检索的记忆抽屉”
除了短期对话历史,Agent经常需要跨会话记住用户偏好、历史事实、业务知识。真正生产级的做法是向量数据库:把已确认的事实或长期信息切片,做向量化存储,使用时按语义相似度召回。
我不建议你在第一版就上向量库,除非你已经有明显的知识检索需求。原因很简单:向量库意味着你要处理文本切分、Embedding接口、相似度阈值设置,复杂度一下子上去。前期完全可以先用一个简单的JSON文件或数据库表,按用户维度存“关键事实”。
比如用户告诉你“我是上海的用户,经常买咖啡”,你可以让Agent在对话中提取这类偏好,写入用户档案表。下次开启新会话时,把用户档案里的关键条目作为system消息的一部分传给模型。这个方案成本极低,但在“个性化”层面的收益立竿见影。
当然,如果你要做一个“基于私有知识库问答”的Agent,比如“帮工程师查询内部技术文档”,那还是得用向量检索。上传文档 -> 切片 -> Embedding -> 存向量库;用户提问 -> 向量化 -> TopK召回相关片段 -> 拼进上下文 -> 模型总结。这一步的详细实现能写一整篇,这里就不展开了。
4.3 记忆管理的最佳实践:别把整个历史都丢给模型
我总结几个自己用的比较稳的经验。
第一,结构化记忆优于原文记忆。与其把用户上一轮说的十句话原样保存,不如顺手抽取出“用户偏好:咖啡、上海、预算单价30元以下”这种结构化档案。模型读结构化信息比读长对话更高效。第二,定期做遗忘。记忆是会被噪声污染的,用户随口提过一句“我下周可能去北京”,两周后这个信息就没价值了。给记忆加一个时效字段,过期就清掉。第三,关键信息必须稽核。不要直接让模型把用户的话写进长期记忆库,最好加一个“确认”环节:如果模型想写入长期记忆,先向用户复述确认,避免幻觉数据污染记忆库。
5. 踩坑实录:从能跑到跑稳
5.1 最常见的几个Bug,百分之八十的新手都会遇到
我把半年里见过的高频问题整理成一个速查表,你如果遇到类似报错,直接对照着排查。
| 现象 | 根因 | 解决办法 |
|---|---|---|
| 模型死活不调用工具,直接编结果 | 工具描述不清晰;模型未启用函数调用开关;工具参数名与描述不一致 | 补全工具描述;检查API参数;打印tools内容人工核对 |
报错tool_call_id不匹配 | 工具结果返回时没有带上原始的tool_call_id;或消息顺序被打乱 | 确保每条角色为tool的消息的tool_call_id与对应tool_call.id一致 |
| 工具参数解析失败:JSONDecodeError | 模型输出的arguments不是合法JSON | 加一层容错:先尝试json.loads,失败则用正则抽取,再失败就请模型重新输出 |
| Agent在同一个工具上反复调用,形成死循环 | 模型没有看到“最终目标已经达到”的判定条件 | 在system提示里明确写“如果你已经获得足够信息,请直接给出最终答案,不要再调用工具” |
| 上下文越变越长,很快触发token上限 | 每轮都追加完整历史;工具结果太长也被全量回传 | 消息裁剪;工具结果截断,只保留前几百字符;摘要历史 |
| API超时/限流 | 并发过高或工具执行时间过长 | 增加重试与退避;工具执行加超时限制;控制并发数 |
第一条我特别想强调。很多新手一遇到“模型不调工具”,第一反应是改代码,其实九成是描述写得不够好。模型对工具的理解完全来自描述文本,描述越具体,调用越准确。比如get_current_time的描述,光说“获取当前时间”可能不够,最好带上“当用户问今天几号、现在几点、星期几时,调用此工具”,这样模型在意图识别时命中率会高很多。
5.2 如何防止Agent“一本正经地胡说八道”
模型幻觉是Agent落地最大的拦路虎。尤其在多步骤任务里,如果某一步工具返回了个错误,模型很可能脑补一个正确结果给你,然后继续往下走。我的处理办法是在system提示里加上一条硬规则:“工具的返回结果必须被如实引用,不得修改或编造工具未提供的数据。如果工具返回错误,直接向用户说明错误原因,禁止猜测。”
更稳的做法是给Agent加一个输出校验层:重要事实必须来自工具结果,模型生成的文字里如果包含了数字、日期、金额等关键字段,程序可以反向检查这些字段是否在工具返回值中出现过。出现了才放行,没出现就要求模型重新生成。这个方案虽然不能完美拦截所有幻觉,但对业务场景已经足够有效。
还有一个容易被忽略的点:工具执行一定要加超时。如果工具去调第三方服务,对方响应慢,Agent的整个循环会被卡住。建议每个工具内部都设置一个独立超时时间,一旦超时就返回“执行超时,请稍后重试或换一种方式”。这样Agent至少不会无限等待下去。
5.3 我实际调试中踩过的几个坑
第一,我在早期调试时,为了让模型“多用工具”,在system提示里写了“你应该尽可能调用工具”,结果模型把“查时间”这种简单事也非要调一次get_current_time,搞出一堆无用调用。后来改成“只有必要时才调用工具,能直接回答就直接回答”,调用次数立刻降下来了。工具调用是手段,不是目的,设计提示词时要把这个优先级讲清楚。
第二,关于工具参数的默认值。模型给出的JSON参数有时候会漏掉非必填字段,比如get_current_time(format="...")。如果你用Python的**kwargs直接传给函数,缺字段可能报“missing required argument”。我的方案是:定义工具函数时全部给默认值,或者在函数内部做一次参数补齐。宁可让工具“宽松一点”,也不要因为参数缺失让整个循环崩掉。
第三,日志是Agent开发的第一生产力。我在开发时,每一步都打印完整的messages内容、模型返回的raw JSON、工具执行耗时。因为Agent的失败往往不是一次能定位的,可能是某个步骤模型理解错了、某个参数变形了。这时候没有完整日志,你只能靠猜。所以我把话放在这里:不打印完整中间过程的Agent开发,等于闭着眼睛开车。
6. 进阶扩展:从Demo到可用产品
6.1 让Agent学会拆任务:Plan-and-Execute模式
最小演示只能算“单步工具调用”。真实任务往往需要多步规划。比如用户说“统计本月销售额,和上月对比,生成一份简要分析报告”,这就不是一个工具能搞定的。
一种很实用的升级方式是Plan-and-Execute:Agent在收到目标后,先不急着执行,而是列出三个环节的计划,比如:“第一步,查询本月销售额;第二步,查询上月销售额;第三步,比较并生成报告”。然后按计划逐步执行。实现上只需把循环改造成“先调一次模型,专门输出计划;再逐计划调用工具;每完成一步,更新状态”。这种模式在复杂任务里比“模型自由发挥”稳定得多,缺点是更耗token、更慢。
但你要警惕“过度规划”:有些Agent会列出五六步计划,其实两步就能完成。解决方案是给计划加一个“必要性校验”,或者让模型“最少步骤优先”。我在实践中倾向于让模型先判断“解决这个问题最少需要几个工具”,再根据最少步骤制定计划,效果比单纯让它“尽量详细”好很多。
6.2 构建评估集与回归测试,别让自己被Demo欺骗
很多人做Agent Demo时觉得“跑通了”,上线跑了一周才发现:昨天还能完成的流程,今天模型更新后突然不会调工具了,或者某个Prompt改动后,原来能通过的用例全崩了。这就是缺少回归测试的代价。
我在项目里会维护一张“黄金测试集”,大概20到50条用例,覆盖不同意图和复杂度。每次改动代码或提示词,都会跑一遍测试,看看成功率有没有下降。
test_cases = [ {"input": "计算 123*456", "expect_tool": True}, {"input": "今天是几号", "expect_tool": True}, {"input": "介绍一下你自己", "expect_tool": False}, {"input": "查询用户U1001的积分", "expect_tool": True, "expect_keyword": "12345"}, ]跑测试时,不仅要看最终结果对不对,还要看工具调用次数是否合理。比如一条简单问题调了五次工具,哪怕结果对,也说明提示词有冗余。把这些指标纳入“质量门禁”,你才能保证Agent在持续演进中不坏掉。
6.3 降本增效的三个实用策略
Agent比普通对话调用API的次数更多,因为每个任务要多次和大模型交互。所以成本问题必须提前考虑。我的经验是三部曲。
第一,模型分级。简单工具调用和分类任务用便宜的小模型,复杂规划、报告生成用强大的大模型。Agent内部可以加一个“意图复杂度评估”环节,先让模型用一句话判断任务难度,再决定走哪条模型通道。第二,缓存高频结果。对于“查今天天气”“查某订单状态”这种重复查询,如果工具结果是确定性的,可以加一个短时缓存,不用每次都让Agent走完整循环。第三,减少无效工具调用。每次工具调用都消耗一轮模型交互,所以工具描述里要能帮模型过滤掉大量“不可能需要的工具”。工具数量超过二三十个时,可以考虑先让模型从工具列表里检索“候选工具Top5”,再基于少量高相关工具做规划,能省不少token。
写给你的一点真心话
从跑通第一个手写Agent到现在,我最深的体会是:Agent的难点从来不在代码,而在“如何恰当地约束和引导模型”。模型的能力很强,但它没有常识、没有边界感,它会老老实实按你的描述去调用工具,也会在你描述不清时自由发挥给你编一个结果。所以真正的Agent开发,功夫全在工具设计、提示词约束、日志观测和回归测试这些“看起来不酷但决定生死”的事情上。
如果你刚跑通自己的第一个Agent,我建议你做的下一个实验是:给它接上三个真正的业务工具,设计一个跨工具的复合任务,让它独立完成。你会亲眼看到模型“计划->执行->纠错”的全过程。那一刻,你才算真正从一个会写代码的人,变成一个会驾驭智能体的人。剩下的事情,就是反复暴露问题、反复修边界,直到它成为一个让你敢放心交活的搭档。