ARTICLE DETAIL

资讯详情

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

AI Agent 开发工程师(三):实战——用代码造出第一台能用的工具型 Agent

AI Agent 开发工程师(三):实战——用代码造出第一台能用的工具型 Agent 上一篇我们留下了什么上一篇我们把 Agent 的四根柱子——规划、工具、记忆、执行——逐根打磨成了能写出来的代码最后用一个不依赖任何框架的MiniAgent骨架把它们串了起来。但那个骨架是干跑的它认识工具、有循环、会记上下文可它还没真受过一次活——大多数人在这一步把手都能动就是没让它真的去干一件有用的事。这一篇我们把MiniAgent接上真实的模型 API 和一个真实的网络工具让它去做一件你亲手交给它的真实任务。做完你会有一台真正能干活的工具型 Agent而且从选模型、写工具、到看它一步一步调工具拿结果全程你都看得见。这是我们这套教程的第一个里程碑从能解释到能干活。关于环境本篇沿用上篇约定Python 3.10需要pip install openai一次。模型部分我们用智谱glm-4-flash——它走 OpenAI 兼容接口、支持 function calling、可免费调用。把base_url和api_key换成任何其他 OpenAI 兼容服务DeepSeek、通义、Kimi 等都行代码基本不用改。1. 真实工具一个能查实时行情的函数上篇的骨架里我们用查天气当例子工具。天气不是数据刚需也查不到活的行情。本篇我们给 Agent 装一把真能联网的手一个查实时股票行情的函数。选它有两个好处数据确凿可验证——你问它贵州茅台今天多少它站着跟你说出真实数字错了你一眼看出不可能被糊弄。是工具型 Agent 最常见的形态——“把某个信息源包成一个函数让模型自由取用”。下面这个函数查的是国内免费公开的行情接口腾讯的qt.gtimg.cn不需要任何密钥importurllib.requestdefquery_stock(symbol:str)-str:查询一只 A股/港股/指数的实时行情。 symbol 格式sh600519(沪市贵州茅台)、sz000001(深市平安银行)、 hk00700(港股腾讯控股)、sh000001(上证指数)。urlfhttps://qt.gtimg.cn/q{symbol}requrllib.request.Request(url,headers{User-Agent:Mozilla/5.0})rawurllib.request.urlopen(req,timeout10).read().decode(gbk)bodyraw.split()[1].rstrip()fieldsbody.split(~)iflen(fields)37:returnf无法解析:{symbol}name,code,pricefields[1],fields[2],fields[3]prev_close,open_pricefields[4],fields[5]pct,high,low,volumefields[32],fields[33],fields[34],fields[36]return(f{name}({code}) 现价{price}涨跌幅{pct}% 昨收{prev_close}f今开{open_price}最高{high}最低{low}成交量({volume}手))这就是一个普通到不能再普通的 Python 函数给代码给行情。fields数组里哪一格是现价、哪一格是涨跌幅你要不查文档就把别人写好的这段直接贴来用——它是腾讯行情的固定字段顺序第 3 格现价、第 32 格涨跌幅。把symbol换成sz000001就能查平安银行。想换成查天气“查航班”“搜网页”思路完全一样写一个函数往里传查询词返回一段文本。甚至不用联网——你已有的内部数据库、本地文件、Excel都是一样的封装法。2. 给模型装上手JSON Schema 工具描述模型本身不执行函数。模型只负责说出它想调哪个工具、传什么参数真正的执行仍是你自己的query_stock。你通过一份 JSON Schema 告诉模型有这么个工具、能干嘛、要什么参数TOOLS[{type:function,function:{name:query_stock,description:查询 A股实时行情。代码格式sh600519(沪市贵州茅台)、sz000001(深市平安银行)、hk00700(港股腾讯控股)、sh000001(上证指数)。,parameters:{type:object,properties:{symbol:{type:string,description:股票代码如 sh600519}},required:[symbol]}}}]就这一段模型就知道有个query_stock要一个symbol字符串。你可以加第二个工具、第三个——description越清楚模型越能命中正确的工具、填对参数。这一步很下工夫后面我们让模型调两个工具时看得出它的价值。下面把 Agent 拿到带 JSON Schema 的工具后从收到问题到调工具拿到答案的完整决策循环画出来——模型负责决策你的代码负责执行这一环一环就是后面所有 Agent 的骨架生成 tool_call: nameargs回传 roletool 消息还需工具已有足够信息用户问题LLM 决策代码路由到函数并执行拿到真实结果LLM 观察后继续生成用户可读的回答返回给用户一台能联网的 MiniAgent现在把上篇的MiniAgent刷一遍同样的循环只是把tools从查天气换成查行情把模型端点从通用改成智谱 OpenAI 兼容地址。importjsonfromopenaiimportOpenAI clientOpenAI(api_key你的API密钥,# 例如智谱大模型平台申请的 keybase_urlhttps://open.bigmodel.cn/api/paas/v4/,)MODELglm-4-flash# 支持 function calling 的免费模型defquery_stock(symbol:str)-str:# ...上面那段查实时行情的函数原样贴进来...return行情文本NAME2FN{query_stock:query_stock}# 名称 - 真实函数 的映射表classMiniAgent:def__init__(self,client,tools,name2fn,max_rounds6):self.clientclient self.toolstools self.name2fnname2fn# 模型说出名字这里查函数self.messages[]# 短期记忆多轮消息self.max_roundsmax_roundsdefchat(self,user_msg:str)-str:self.messages.append({role:user,content:user_msg})for_inrange(self.max_rounds):respself.client.chat.completions.create(modelMODEL,messagesself.messages,toolsself.tools)msgresp.choices[0].messageifnotmsg.tool_calls:# 没有工具要调 收敛返回最终回答finalmsg.content self.messages.append({role:assistant,content:final})returnfinal# 有工具调用 逐个执行并把结果喂回上下文self.messages.append(msg)fortcinmsg.tool_calls:argsjson.loads(tc.function.arguments)resultself.name2fn[tc.function.name](**args)self.messages.append({role:tool,tool_call_id:tc.id,content:json.dumps(result,ensure_asciiFalse),})return达到最大轮数未收敛。这段代码跟你上篇看到的几乎一样——因为 Agent 的循环就长这样变的只是工具和端点。全程实录第一次能干活现在做一件真实任务。问你一个问题“帮我查贵州茅台和腾讯控股的最新股价”这个任务光靠模型自己答不了它训练时的数据不实时必须调工具。我们让它跑一次并打印出每一步agentMiniAgent(client,TOOLS,NAME2FN)print(agent.chat(帮我查贵州茅台和腾讯控股的最新股价分别给出现价和涨跌幅。))这一行代码背后发生的是这样一段思考→行动→观察的完整循环我把它拆开画给你看轮次角色发生了1模型(assistant)说我需要查行情发调用query_stock(symbolsh600519)1工具(tool)你的query_stock实际请求腾讯接口返回贵州茅台现价1模型(assistant)又说需要查腾讯发query_stock(symbolhk00700)1工具(tool)返回腾讯控股现价2模型(assistant)收敛不再调工具把两个现价拼成一句完整回答真实运行结果帮我查贵州茅台和腾讯控股的最新股价分别给出现价和涨跌幅。 Agent: 贵州茅台现价 1252.57 元、涨跌幅 -0.36%腾讯控股现价 430.000 港元、涨跌幅 2.63%。这就是你的 Agent 第一次从你自己嘴里说出一个它训练时根本不知道、但当下实实在在拿到的实时答案。它没被教过茅台今天 1252那是它当下亲手查出来的。里程碑成立。让它干点更有价值的一次调两个工具、还能总结你手里已经有现成的代码现在再加一个总评工具让 Agent 不只查、还会把查到的数据整理成白话deftoday_brief(stock:str,detail:str)-str:把一只股票行情整理成一句简评示例把两个数字点成一句话。returnf「{stock}」一句话现价{detail}TOOLS2TOOLS[{type:function,function:{name:today_brief,description:把一只股票的行情数据整理成一句简评。,parameters:{type:object,properties:{stock:{type:string,description:股票名称},detail:{type:string,description:行情细节}},required:[stock,detail]}}}]这次让它查三只并分别总结# TOOLS2 沿用上面的 TOOLS再补一个 today_brief 入口NAME2FN2dict(NAME2FN);NAME2FN2[today_brief]today_brief agentMiniAgent(client,TOOLS2,NAME2FN2)q查一下平安银行、招商银行、贵州茅台的最新价并用一句话总结每只今天的表现。print(agent.chat(q))你会看到 Agent 依次调today_brief三人次每查一只就总结一句最后给你一段带名字点评的总结。多工具、多次调用、多轮上下文靠的就是模型说调谁 → 你执行 → 结果塞回 messages这个机制——不用你写一根流程编排线。踩坑想让 Agent 干更多先把这三点想清楚工具描述要写人话给模型看。description里啰嗦不要紧关键要把这个工具能干什么、什么算数讲清楚别惜字。模型就是靠它判断该用哪个工具。参数校验在函数里做别依赖模型传对。模型传进来的symbol可能是sh600519也可能传个乱七八糟的。你的函数要能兜住错误、返回一句参数非法让模型自己改。糟糕的做法是把异常堆栈抛给模型它读不懂。同一轮可能连续调很多工具把NAME2FN表从名字到函数维护得干净点别在循环里写一堆if。从工具型到下一步本篇的核心收获里程碑达成了你现在有一个真的会调工具的 Agent。它读你规定的实时行情也真能查出来再把查到的结果组织成你能读懂的句子。我们做的几件事是工具型 Agent 的全部要点没有一件绕过你自己写函数、模型说它该调哪个这个本质。把真实世界的数据源行情接口包成一个普通函数 工具。用JSON Schema告诉模型有什么工具、怎么调。循环里模型说调谁 → 你执行 → 结果塞回上下文 工具型 Agent 运行的灵魂。想要更多手段多加工具、把函数写得更健壮、把max_rounds调大。下一篇《给 Agent 装上长期记忆从记住上下文到真正记得》会把镜头转到记忆那一挂——让 Agent 不只记住当前这一轮对话还能跨会话回想更早的上下文。手里工具已经能调记忆这块再补齐它就真的走向能长期陪伴的成熟 Agent 了。完整可运行的代码你可以照着上面的片段拼起来或在本篇发布页的配套仓库里找到整份脚本贴进终端就能跑通。
返回列表