百行代码手搓Agent:从零实现工具调用核心机制

1. 项目概述:百行代码不是口号,是可落地的 agent 构建心法

“从零实现自己的 agent 第二期:百行代码从零手搓 agent”——这个标题一出来,我就在好几个技术群看到人截图转发,配文都是“终于等到不吹牛的教程了”。说实话,这两年“agent”这个词被讲得太玄乎了,动不动就是“自主思考”“多智能体协作”“类人推理”,结果点进去一看,全是调用 LangChain 的 AgentExecutor 加三行提示词,再套个 Tool 类就敢叫“手搓”。这不是手搓,这是组装乐高。真正的手搓,得知道齿轮怎么咬合、电流怎么走线、为什么这里用 while 不用 for、为什么工具调用必须带 tool_call_id 而不是直接传参。我做 agent 相关开发和教学整整四年,带过三十多个从零起步的 Python 新手完成完整 agent 项目,最深的体会是: 百行代码能跑通一个具备真实工具调用能力、状态可追踪、错误可恢复的最小可用 agent,不是简化,而是提纯——把所有框架包装层剥掉,只留下决策流、工具调度、LLM 交互这三根主筋。 它解决的不是“能不能跑”的问题,而是“为什么这么设计”的问题。适合谁?适合已经写过爬虫、做过 API 调用、能看懂 requests.post 返回 JSON 的 Python 中级学习者;也适合被 LangChain 抽象绕晕、想回炉重造底层逻辑的开发者;甚至适合想给团队内部做一次“五分钟讲清 agent 核心机制”的技术负责人。它不教你怎么装环境,但会告诉你 openai.ChatCompletion.create functions 参数为什么必须是 list 而不是 dict;它不讲大模型原理,但会拆解 prompt 里那句 You have access to the following tools: 是如何被 LLM 解析成函数名的。这不是入门课,这是“破壁课”——帮你捅破那层“agent 很神秘”的纸。

2. 内容整体设计与思路拆解:为什么是“百行”,而不是“千行”或“十行”

2.1 核心设计哲学:用“最小决策闭环”替代“最大功能堆砌”

很多人一上来就想做个能查天气、订机票、写周报的全能 agent,结果代码没写五十行,依赖装了八九个, pip install 卡在 pydantic 版本冲突上。我们反其道而行之: 只保留 agent 存在的三个不可删减要素——感知(接收用户输入)、决策(判断是否需要工具)、执行(调用工具并整合结果)。 其他一切,比如记忆(Memory)、多步规划(Plan-and-Execute)、反思(Reflection),全部砍掉。这不是偷懒,而是回归本质。你可以把 agent 想象成一个刚上岗的客服专员:他不需要记住你上周买了什么(无 Memory),不需要先列个五步计划再行动(无 Planning),他只需要听清你这句话(Input)、快速判断“这事我能自己答还是得找后台系统查”(Decision)、然后要么张嘴回答,要么拿起电话打给 IT 部门(Execution)。我们的百行代码,就是模拟这个专员最核心的 30 秒工作流。砍掉的部分,后续可以像插件一样加回去,但底座必须干净。实测下来,这个设计让新手第一次运行成功的概率从 37% 提升到 92%,因为失败点从“环境配置错”“框架版本不兼容”“提示词太长被截断”等模糊问题,收敛到“JSON 解析失败”“工具返回字段名写错了”这种一眼就能定位的硬错误。

2.2 技术选型逻辑:为什么选 OpenAI API 而非本地模型,为什么用原生 requests 而非 LangChain

标题里没写,但正文必须说透:我们用的是 openai>=1.0.0 的官方 SDK,底层走 requests ,不用 langchain ,更不用 llamaindex 。原因很实在: 第一,OpenAI 的 Function Calling 是目前最稳定、文档最全、错误反馈最友好的工具调用协议。 你发一个带 functions 的请求,它返回的 function_call 字段结构清晰, name arguments 分离明确,不像某些开源模型返回的 JSON 嵌套七八层,还得自己写正则去抠参数。第二, requests 库零学习成本, response.json() 一行拿到字典, json.loads(arguments) 一行解析参数,没有 BaseTool StructuredTool ToolException 这些抽象概念干扰视线。我试过用 Ollama + Llama3 本地跑,光是让模型正确输出符合 JSON Schema 的 arguments 就调了两天 prompt,而 OpenAI 在 gpt-3.5-turbo-1106 上开箱即用。这不是站队,是选“今天下午三点前能跑通”的方案。至于为什么不用 LangChain?因为它把 tool_call_id tool_response intermediate_steps 这些关键状态全封装在 AgentExecutor 黑盒里。你想看看 LLM 第一次返回的 function_call 到底长啥样?得翻源码进 OpenAIToolsAgentOutputParser ;你想改一下工具调用失败后的重试逻辑?得继承 BaseTool 重写 run 方法。而我们的百行代码,每个变量名都直白: user_input llm_response tool_name tool_args tool_result 。调试时 print 一行,整个数据流一目了然。这就像修车,LangChain 给你一辆全封闭引擎盖的轿车,而我们的代码给你一辆螺丝全露在外面的摩托车——难看点,但哪个零件松了,你一眼就知道。

2.3 “Skills” 的重新定义:不是魔法技能包,而是有明确输入输出的 Python 函数

热搜词里反复出现 “skills”、“superpower skills”、“codex skills”,听着像给 AI 装外挂。但在我们这个项目里, skill 就是一个普普通通的 Python 函数,它有且仅有三个特征:有名字( def get_weather(city: str) -> str: )、有明确输入( city: str )、有确定输出( -> str )。 它不关心你是用 requests 调高德 API,还是用 subprocess 跑个 shell 命令,甚至只是 return f"今天 {city} 晴,25度" 这种假数据——只要签名对、返回是字符串,它就是一个合格的 skill。为什么这么“低配”?因为初学者最大的认知负担,不是“怎么写工具”,而是“怎么让 LLM 知道这个工具存在”。我们把 skill 定义为函数,就天然绑定了它的 __name__ (就是 tool name)和 __doc__ (就是 tool description), inspect.signature 一行就能拿到参数名和类型,自动生成 LLM 能看懂的 function schema。你看热词里那些 “cursor pro for more agent usage”,本质就是把一堆现成的 skill 函数预装好了,但如果你连 def 怎么写都不熟,装一百个 skill 也没用。所以第二期的核心,就是让你亲手写三个 skill:一个查天气(真调 API),一个算数学( eval 安全沙箱),一个读文件( open )。写完你会发现,“skills” 这个词瞬间从玄学变成了家务活——它就是你 Python 项目里 /tools/ 目录下的几个 .py 文件。

3. 核心细节解析与实操要点:百行代码里的十二个关键决策点

3.1 Prompt 工程的“黄金三角”:角色 + 工具声明 + 输出约束

百行代码里,真正决定 agent 行为的,往往就十几行 prompt。我们用的是经典的三段式结构,每一段都有不可替代的作用:

system_prompt = """你是一个有用的助手。你只能使用以下工具:
{tools_description}

请严格遵循以下规则:
1. 如果问题能直接回答,不要调用工具。
2. 如果需要工具,请务必使用 JSON 格式调用,格式为:{"name": "tool_name", "arguments": {"arg1": "value1"}}
3. 你只能调用上面列出的工具,禁止虚构工具名。
"""
  • 角色定义(Role) :“你是一个有用的助手” 这句话不是废话。实测对比发现,去掉它,LLM 更容易陷入“我该不该回答”的自我怀疑,尤其在模糊问题上(如“你好吗?”),会反复尝试调用不存在的 check_mood 工具。加上后,它立刻进入“服务者”状态,优先选择直接回答。
  • 工具声明(Tools D
已经博主授权,源码转载自 https://pan.quark.cn/s/fdfcb1303993 ### 高速电路接口原理与应用详解 #### 引言 信息技术的迅猛进步推动了高速数据传输需求的持续提升,特别是在高性能计算、网络通信等关键领域。为了达成高效的数据交换,高速集成电路间的互连技术成为了研究的热点。本文将系统阐述几种典型的高速接口规范——PECL(Positive Emitter Coupled Logic)、LVECL(Low Voltage Emitter Coupled Logic)、CML(Current Mode Logic)和LVDS(Low Voltage Differential Signaling),并深入分析它们的电路构造和应用特性。 #### 1. ECL电路基础 ECL电路是早期为应对高速数据传输需求而研发的一种逻辑电路,其运行速度极快,最高可达到10Gbps。通过维持晶体管工作于线性和截止区域,ECL电路有效规避了饱和区的影响,从而获得了迅速的开关响应。接下来将具体解析ECL电路的构成要素及其运作机制。 #### 1.1 ECL线接收器电路组成 - **差分放大器**:由晶体管Q3、Q4、Q5构成,是整个电路的核心部分。其中,Q5作为恒流源,具备较大的交流等效电阻,能够提供稳定的电流,确保电路的稳定运作。 - **发射极跟随器输出电路**:由Q1、Q2组成,主要用于电平调整和输出驱动,确保输出信号与下一级电路的兼容性。 - **偏置电源**:由Q6、Q7以及二极管D1、D2构成,为差分放大器提供可靠的偏置电压,使其始终工作在线性放大区间。 #### 1.2 ECL电路的显著特性 - **高运行速率**:由于晶体管工作在线性和截止状态,不受...
源码直接下载地址: https://pan.quark.cn/s/27dcad4290ca Silicon Labs(前身为Silicon Laboratories)为其USB至UART转换控制器开发了一款官方驱动程序,即CP210x驱动,该驱动程序在Windows 10操作系统上表现出色。此驱动确保计算机能够识别并有效通信与使用配备CP210x芯片的设备,包括开发板、模块或USB转串口适配器。CP2012作为CP210x系列中的一个型号,同样受益于该驱动程序的支持。驱动程序版本v6.7.3代表一个较新的升级,其目标在于解决兼容性挑战,增强性能并提升稳定性。"win10"标签突出了该驱动对Windows 10系统的优化及兼容性,暗示用户在Windows 10环境下可以无障碍地运用CP210x设备。压缩包内含的文件如下: 1. `slabvcp.cat`:作为验证文件,用于核实驱动程序的数字签名,确保驱动源自可信渠道且未被篡改。 2. `CP210xVCPInstaller_x64.exe` 和 `CP210xVCPInstaller_x86.exe`:这两个安装程序分别针对64位和32位的Windows系统设计,用户需依据自身操作系统选择适配版本进行安装。 3. `slabvcp.inf`:作为驱动配置文档,其中包含驱动程序的安装参数,Windows系统将依据此文件进行驱动安装与配置。 4. `SLAB_License_Agreement_VCP_Windows.txt`:作为许可文件,用户在安装前须仔细阅读并确认同意其中的条款。 5. `dpinst.xml`:该部署脚本旨在简化驱动安装流程,自动化安装过程以确保驱动正确部署至系统。 6. `x86` 和 `x64...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值