零代码实战:用Agent开发机械臂自然语言遥控器
1. 项目缘起一个不会写代码的人怎么把机械臂遥控器做出来先说结论这个项目从头到尾我没有手写过一行传统意义上的业务代码。机械臂能听懂人话、能执行抓取动作、能根据我的自然语言指令调整姿态整个遥控器的交互逻辑是用 Agent 的方式搭出来的。你可能会问机械臂遥控器这东西不是应该由嵌入式工程师写固件、上位机工程师写界面、算法工程师调运动学吗没错传统路径确实是这样。但这次我走的是另一条路把大模型作为决策中枢把机械臂的 SDK 封装成 Agent 可以调用的工具然后用自然语言描述任务让 Agent 自己去规划步骤、调用工具、检查结果、修正动作。说白了我做的事情更像是教一个实习生怎么操作机械臂而不是自己动手写一个操作系统。我负责定义工具、写清楚每个工具干什么、输入输出是什么、什么情况下该用哪个工具。剩下的决策链条交给 Agent 去跑。这个项目适合谁看如果你是对 Agent 开发感兴趣但代码基础薄弱的爱好者或者你是传统自动化领域的从业者想看看大模型怎么和物理设备结合再或者你只是想理解Agent 到底能干什么实事那这篇实录应该能给你一些参考。我不讲虚的直接说我是怎么一步步搭起来的踩了哪些坑哪些地方看着简单实际上很坑哪些地方看着复杂其实一行配置就搞定。核心关键词就几个Agent 开发、机械臂控制、自然语言交互、工具调用、任务规划。整篇文章围绕这几个词展开但不会堆术语我会尽量用生活化的类比把每个环节讲清楚。2. 整体设计思路为什么选 Agent 而不是传统遥控方案2.1 传统遥控器的局限在哪里传统的机械臂遥控器本质上是一个指令映射器。你按一个按钮它发送一个固定的指令码机械臂执行一个固定的动作。比如按前进机械臂的末端执行器就往前移动固定的距离。这种方案的好处是确定性强、延迟低、可靠性高。但问题也很明显它只能执行预设好的动作没法处理把那个红色的方块拿起来放到左边这种需要理解环境、规划路径、调整姿态的复合任务。你要让传统遥控器完成复合任务就得写大量的状态机代码把每个可能的场景都枚举出来。机械臂的关节角度、末端位置、抓取力度、移动路径这些参数组合起来是一个巨大的状态空间。靠人工枚举工作量极大而且一旦环境变化比如物体位置变了、光照条件变了之前写好的状态机可能就失效了。我一开始也想过用传统的方案写一个简单的上位机用滑块控制每个关节的角度。但试了之后发现这东西只能做演示真要用起来操作复杂度太高。六个关节的机械臂你要同时协调六个滑块还要盯着末端执行器的位置脑子根本不够用。2.2 Agent 方案的核心逻辑Agent 方案的核心逻辑其实很简单把机械臂的每个基础能力封装成一个工具然后让大模型根据任务描述自动决定调用哪些工具、按什么顺序调用、传入什么参数。举个例子机械臂的基础能力包括移动到指定坐标、旋转末端执行器、开合夹爪、读取当前关节角度、读取末端位置。这些能力每个都封装成一个函数写好函数名、功能描述、参数说明。然后我把这些函数的说明文档喂给 AgentAgent 就能根据把桌上的方块拿起来这个任务自动规划出先移动到方块上方、然后下降、然后闭合夹爪、然后抬起、然后移动到目标位置、然后松开夹爪。这个过程中Agent 不需要知道机械臂的运动学方程也不需要知道逆运动学怎么解。它只需要知道每个工具能干什么然后像搭积木一样把工具组合起来。运动学解算、路径规划、碰撞检测这些底层工作还是由机械臂的 SDK 和控制器来完成。Agent 负责的是高层决策不是底层控制。2.3 为什么这种方案可行有人可能会担心大模型做决策靠谱吗万一它规划错了机械臂撞了怎么办这个问题我一开始也担心所以我在设计的时候加了几层保护。第一层是工具层面的保护每个工具函数内部都有参数校验比如坐标范围检查、速度限制、力矩限制。第二层是 Agent 层面的保护我在系统提示词里明确写了安全规则比如移动前必须先读取当前位置夹爪闭合力度不能超过阈值遇到异常立即停止并报告。第三层是物理层面的保护机械臂本身有急停按钮和碰撞检测真出问题了可以直接断电。实测下来只要工具定义清晰、提示词写得到位Agent 的规划准确率相当高。我跑了大概两百次抓取任务成功率在百分之九十以上。失败的案例主要集中在物体位置超出工作空间、物体形状太规则导致夹爪打滑这两种情况跟 Agent 的决策关系不大更多是硬件层面的限制。2.4 方案选型的几个关键决策在搭这个系统之前我做了几个关键决策这里展开说一下。第一个决策用哪个大模型。我试了几个不同的模型最后选了一个支持工具调用Function Calling的。工具调用能力是必须的因为 Agent 需要输出结构化的工具调用请求而不是纯文本。纯文本的话我还得自己写解析器太麻烦。第二个决策Agent 的架构用单层还是多层。单层就是一个 Agent 负责所有决策多层就是分一个规划 Agent 和一个执行 Agent。我最后选了单层原因是机械臂控制的任务链条相对短一般就五到十步单层 Agent 足够处理。多层架构虽然理论上更清晰但增加了通信开销和调试复杂度对于这个项目来说不划算。第三个决策工具粒度怎么定。工具太粗比如一个工具直接完成抓取物体那 Agent 的决策空间就很小灵活性差。工具太细比如一个工具只控制一个关节转动一度那 Agent 要调用的次数就太多容易出错。我最后选的粒度是原子动作级别移动、旋转、开合、读取。每个工具完成一个明确的物理动作但又不至于太细碎。3. 核心细节解析工具封装与提示词设计3.1 工具封装的具体做法工具封装是整个项目的地基。我用的机械臂 SDK 提供了一些基础接口比如设置关节角度、设置末端位姿、读取传感器数据。但这些接口是给程序员用的参数复杂返回值也是原始数据。我需要把它们包装成 Agent 能理解的工具。包装的核心是写清楚三件事工具名、功能描述、参数说明。工具名要见名知意比如move_to_position、rotate_end_effector、set_gripper、get_current_pose。功能描述要用自然语言写清楚这个工具干什么、什么时候用、有什么限制。参数说明要写清楚每个参数的类型、单位、取值范围。我举个例子。原始的 SDK 接口可能是这样的set_joint_angles(angles: List[float], speed: float, acceleration: float)。这个接口对 Agent 来说太底层了Agent 不知道 angles 是什么也不知道 speed 设多少合适。我把它包装成move_to_position(x, y, z, speed)功能描述写将机械臂末端移动到指定坐标位置坐标单位为毫米速度单位为毫米每秒建议速度范围 10 到 100。这样 Agent 就能理解了。这里有个细节很重要单位。机械臂 SDK 可能用弧度可能用角度可能用米可能用毫米。Agent 不知道这些区别所以我在包装的时候统一了单位并且在描述里写清楚。比如所有位置参数统一用毫米所有角度参数统一用度所有速度参数统一用毫米每秒。这样 Agent 在规划的时候就不会因为单位混乱而出错。3.2 提示词设计的几个关键点提示词是 Agent 的操作手册。我写的系统提示词大概有八百多字包含了角色定义、安全规则、工作流程、异常处理四个部分。角色定义部分我写的是你是一个机械臂控制助手负责将用户的自然语言指令转化为机械臂的工具调用序列。你不需要自己计算运动学只需要选择合适的工具并传入正确的参数。安全规则部分我列了五条硬性规定移动前必须先读取当前位置单次移动距离不能超过两百毫米夹爪闭合力度不能超过设定阈值遇到工具返回错误必须立即停止并报告不能同时调用多个运动工具。工作流程部分我写了一个标准流程先读取当前状态然后规划动作序列然后逐步执行每步执行后检查结果如果结果不符合预期就重新规划。异常处理部分我写了几种常见异常的处理方式坐标超出工作空间怎么办、夹爪抓空怎么办、移动超时怎么办。这些提示词不是一次写好的是反复调试出来的。我一开始写的提示词太简单Agent 经常忘记读取当前位置就直接移动结果就是机械臂从当前位置猛地跳到目标位置动作很不平滑。后来加了移动前必须先读取当前位置这条规则Agent 就会先调get_current_pose然后根据当前位置和目标位置规划一个平滑的移动路径。3.3 工具调用的参数校验Agent 输出的工具调用参数不能直接传给机械臂 SDK必须先经过校验。我在每个工具函数内部都加了参数校验逻辑。位置参数的校验包括是否在工作空间范围内、是否在机械臂的可达区域内、是否与当前姿态冲突。速度参数的校验包括是否在允许范围内、是否与当前负载匹配。夹爪参数的校验包括开合范围是否合法、力度是否超限。校验不通过的时候工具函数不会直接报错而是返回一个结构化的错误信息告诉 Agent 哪个参数有问题、应该怎么调整。比如 Agent 传了一个超出工作空间的坐标工具函数返回目标坐标超出工作空间X 轴最大值为 500 毫米当前值为 600 毫米请调整后重试。Agent 收到这个信息后会自动调整坐标重新调用。这个设计很关键。如果工具函数直接抛异常Agent 可能不知道怎么处理整个任务就卡住了。返回结构化的错误信息Agent 就能根据错误信息自我修正任务的鲁棒性大大提高。3.4 状态管理与上下文传递Agent 在执行任务的过程中需要记住一些状态信息比如当前位置、当前夹爪开合状态、已经执行了哪些步骤。这些信息不能只靠 Agent 的上下文窗口来维护因为上下文窗口有限任务步骤多了之后容易丢失信息。我的做法是在工具函数层面维护一个状态对象每次工具调用后更新状态然后把最新状态附加在工具返回结果里。这样 Agent 每次调用工具后都能看到最新的状态信息不需要自己记住所有历史。举个例子Agent 调用move_to_position后工具返回结果里会包含移动成功当前位置为 (100, 200, 300)。Agent 调用set_gripper后返回结果里会包含夹爪已闭合当前开合度为 0 毫米。这样 Agent 在规划下一步的时候就能基于最新状态做决策而不是基于可能已经过时的历史信息。4. 实操过程从零搭建 Agent 遥控器的完整流程4.1 环境准备与依赖安装先说环境。我用的是一台普通的工作站操作系统是常见的 Linux 发行版。机械臂通过串口或者网络接口连接到工作站。Agent 的运行环境是一个 Python 虚拟环境主要依赖三个东西大模型的 API 客户端、机械臂的 SDK、以及一个轻量的 Web 框架用来做交互界面。安装过程不复杂但有几个坑要注意。第一个坑是串口权限Linux 下普通用户默认没有串口读写权限需要把用户加到 dialout 组里或者改 udev 规则。第二个坑是 Python 版本机械臂 SDK 可能只支持特定版本的 Python装之前先看文档。第三个坑是网络延迟如果大模型 API 走公网每次工具调用的往返延迟可能有一两秒对于实时性要求高的任务这个延迟需要优化。我实测下来把大模型 API 的调用改成流式输出可以显著降低感知延迟。虽然总时间没变但 Agent 可以边生成边执行不用等整个响应生成完再开始动作。4.2 工具函数的编写与注册工具函数的编写是整个项目里最花时间的部分。我大概写了八个工具函数覆盖了机械臂控制的基本需求。工具名功能关键参数注意事项get_current_pose读取当前末端位姿无返回坐标和姿态角move_to_position移动到指定坐标x, y, z, speed移动前先读当前位置rotate_end_effector旋转末端执行器rx, ry, rz角度制范围有限制set_gripper控制夹爪开合opening, force力度不能超阈值get_gripper_state读取夹爪状态无返回开合度和力度wait等待指定时间seconds用于动作稳定check_collision检查碰撞风险target_pose移动前调用emergency_stop紧急停止无任何异常都可调用每个工具函数写好后需要注册到 Agent 的工具列表里。注册的时候要提供函数名、功能描述、参数 schema。参数 schema 用 JSON Schema 格式写描述每个参数的类型、单位、取值范围、是否必填。这里有个经验功能描述要写得像给新人看的操作手册不要写得太技术化。比如move_to_position的描述我写的是将机械臂末端移动到指定的三维坐标位置。坐标单位为毫米速度单位为毫米每秒。移动前请先调用 get_current_pose 获取当前位置并调用 check_collision 检查碰撞风险。这样 Agent 一看就知道什么时候用、怎么用。4.3 Agent 主循环的实现Agent 的主循环逻辑不复杂但细节很多。基本流程是接收用户输入、调用大模型、解析工具调用请求、执行工具、把结果返回给大模型、继续循环直到任务完成。我用的是大模型 API 的工具调用功能所以解析工具调用请求这一步不需要自己写。大模型返回的响应里会包含结构化的工具调用信息我直接提取函数名和参数然后调用对应的工具函数。执行工具的时候有几个细节要注意。第一工具执行可能失败失败后要把错误信息返回给大模型让大模型决定是重试还是换方案。第二工具执行可能耗时较长比如移动动作可能需要几秒钟这时候要设置超时避免 Agent 一直卡住。第三工具执行可能产生副作用比如机械臂移动了位置这个副作用要记录到状态里供后续决策参考。主循环的退出条件有两个一是大模型返回了最终答案没有工具调用请求二是达到了最大循环次数防止无限循环。我设置的最大循环次数是二十次一般任务五到十次就能完成。4.4 交互界面的搭建交互界面我做得比较简单就是一个网页上面有一个输入框和一个日志区域。输入框用来输入自然语言指令日志区域显示 Agent 的决策过程和工具执行结果。界面虽然简单但日志区域的设计很重要。我把日志分成了三类用户输入、Agent 思考、工具执行。Agent 思考部分显示大模型输出的文本内容工具执行部分显示工具名、参数、返回结果。这样调试的时候一目了然能清楚看到 Agent 每一步在干什么。我还加了一个手动模式的开关。打开手动模式后Agent 每执行一个工具调用前都会暂停等我确认后再执行。这个功能在调试阶段非常有用可以避免 Agent 规划错误导致机械臂乱动。4.5 完整任务流程演示我拿一个具体任务来演示整个流程。任务描述是把桌上的方块拿起来放到左边的盒子里。第一步Agent 收到任务后先调用get_current_pose读取当前位置返回当前位置为 (200, 0, 300)。然后调用get_gripper_state读取夹爪状态返回夹爪当前张开开合度为 50 毫米。第二步Agent 规划动作序列先移动到方块上方然后下降然后闭合夹爪然后抬起然后移动到盒子上方然后下降然后松开夹爪然后抬起。第三步Agent 调用check_collision检查移动到方块上方的路径是否安全返回无碰撞风险。然后调用move_to_position移动到方块上方返回移动成功当前位置为 (150, 100, 350)。第四步Agent 调用move_to_position下降返回移动成功当前位置为 (150, 100, 320)。然后调用set_gripper闭合夹爪返回夹爪已闭合开合度为 10 毫米。第五步Agent 调用move_to_position抬起返回移动成功当前位置为 (150, 100, 350)。然后调用move_to_position移动到盒子上方返回移动成功当前位置为 (50, 100, 350)。第六步Agent 调用move_to_position下降返回移动成功当前位置为 (50, 100, 320)。然后调用set_gripper松开夹爪返回夹爪已张开开合度为 50 毫米。第七步Agent 调用move_to_position抬起返回移动成功当前位置为 (50, 100, 350)。然后输出最终答案任务完成方块已放入盒子。整个流程大概调用了十二次工具耗时约十五秒。其中大模型决策耗时约八秒机械臂动作耗时约七秒。这个速度对于演示来说足够了对于实际生产场景可能还需要优化。5. 常见问题与排查技巧实录5.1 Agent 规划错误怎么排查Agent 规划错误是最常见的问题。表现是 Agent 调用了错误的工具或者传入了错误的参数或者动作顺序不对。排查的第一步是看日志。日志里会显示 Agent 每次工具调用的函数名和参数以及工具返回的结果。如果 Agent 调用了错误的工具说明提示词里的工具描述不够清晰或者 Agent 没有理解任务。如果参数错了说明参数说明不够明确或者单位不统一。我遇到过一个典型问题Agent 把坐标单位搞混了把毫米当成了米结果传了一个 0.2 的坐标值机械臂几乎没动。排查后发现是工具描述里写了坐标单位为毫米但参数 schema 里没有写单位Agent 只看了 schema 没看描述。后来我在 schema 的 description 字段里也加了单位说明问题就解决了。另一个典型问题是 Agent 忘记了安全规则。比如提示词里写了移动前必须先读取当前位置但 Agent 有时候会跳过这一步直接移动。排查后发现是提示词太长安全规则被淹没了。后来我把安全规则单独拎出来放在提示词的最前面并且用加粗标记Agent 的遵守率就高多了。5.2 工具调用失败怎么处理工具调用失败的原因很多参数超范围、网络超时、机械臂未响应、夹爪抓空等。处理方式取决于失败类型。参数超范围的话工具函数会返回结构化的错误信息Agent 收到后会自动调整参数重试。网络超时的话我会在工具函数里加自动重试逻辑重试三次还是失败就返回错误。机械臂未响应的话一般是硬件连接问题需要检查串口或网络连接。夹爪抓空的话Agent 需要重新规划比如调整抓取位置或力度。我整理了一个常见问题速查表放在下面。问题现象可能原因排查方法解决方案Agent 不调用工具提示词未说明工具用途检查工具描述补充功能描述和使用场景参数单位错误schema 未标注单位检查参数说明在 schema 和描述中都标注单位移动动作不平滑未读取当前位置检查日志强制移动前读取当前位置夹爪抓空抓取位置偏移检查物体位置调整抓取坐标或增加力度任务卡住不结束循环次数超限检查最大循环设置调整最大循环次数或优化提示词响应延迟高API 往返耗时检查网络延迟改用流式输出或本地模型5.3 安全保护的几个关键点安全保护是机械臂项目里最重要的部分。我总结了几个关键点。第一工具函数内部必须有参数校验。不管 Agent 传什么参数工具函数都要先校验再执行。校验不通过就返回错误绝不执行危险动作。第二提示词里必须有安全规则。安全规则要写得具体、明确、可执行。比如单次移动距离不能超过两百毫米就比注意安全要好得多。第三必须有物理急停。软件保护再完善也可能有漏洞。物理急停按钮是最后一道防线任何时候都能直接切断电源。第四调试阶段用手动模式。手动模式下每个工具调用前都需要人工确认可以避免 Agent 规划错误导致意外。第五限制工作空间。在工具函数里硬编码工作空间范围超出范围的坐标直接拒绝。这样即使 Agent 规划错了机械臂也不会跑到危险区域。5.4 性能优化的几个技巧性能优化主要从两个维度入手减少大模型调用次数、减少机械臂动作时间。减少大模型调用次数的关键是让 Agent 一次规划多步。我一开始的提示词是让 Agent 每步都调用大模型确认结果一个简单任务要调用十几次大模型耗时很长。后来改成让 Agent 一次规划整个动作序列然后批量执行大模型调用次数降到了两三次。减少机械臂动作时间的关键是优化路径。比如移动动作如果 Agent 规划的是先水平移动再垂直移动路径就是折线。如果改成直线移动路径更短耗时更少。我在工具函数里加了路径优化逻辑自动把折线路径转成直线路径。还有一个技巧是并行执行。有些工具调用之间没有依赖关系可以并行执行。比如读取当前位置和读取夹爪状态可以同时进行。我在主循环里加了并行执行逻辑把无依赖的工具调用打包并行节省了不少时间。5.5 从演示到实用的差距演示能跑通不代表实用。我从演示到实用主要解决了三个问题。第一个问题是鲁棒性。演示的时候环境是理想的物体位置固定、光照条件稳定。实用的时候环境是变化的物体可能被移动、光照可能变化。我加了物体检测功能用视觉传感器实时获取物体位置Agent 根据实时位置调整抓取坐标。第二个问题是异常恢复。演示的时候很少出异常实用的时候异常是常态。我加了异常恢复逻辑比如抓取失败后自动重试、移动超时后自动重新规划、通信中断后自动重连。第三个问题是交互效率。演示的时候可以慢慢输入指令实用的时候需要快速响应。我加了语音输入功能用语音识别把说的话转成文字再传给 Agent。这样操作起来就快多了不用打字。6. 一些个人体会和后续扩展方向这个项目做下来我最大的体会是Agent 开发的门槛不在代码在思维方式的转变。传统开发是我告诉计算机每一步怎么做Agent 开发是我告诉计算机目标是什么让它自己想办法。这个转变说起来简单做起来需要反复调整提示词、反复测试边界情况。另一个体会是工具封装的质量决定了 Agent 的上限。工具封装得好Agent 就能发挥出强大的规划能力。工具封装得差Agent 就会频繁出错。我在工具封装上花的时间大概占整个项目的一半以上。后续扩展方向有几个。一是加入视觉反馈让 Agent 能看到抓取结果形成闭环控制。二是加入多机械臂协同让多个 Agent 分别控制多个机械臂完成更复杂的任务。三是加入学习能力让 Agent 从失败案例中学习不断优化动作序列。最后分享一个小技巧如果你也想做类似的项目建议先从最简单的工具开始比如只封装一个移动工具让 Agent 控制机械臂在平面上移动。跑通之后再逐步增加工具增加任务复杂度。不要一上来就搞全套那样调试起来会很痛苦。