这一页讲什么
你已经可以搭一个能回答聊天消息的 AI 代理。把它接到一个乐高机器人上 —— 让这个代理能看、能决策、能动 —— 只是多加了一个维度,但几乎每一个设计决策都会因此改变。真实世界是非确定的:电机会卡住,夹爪会抓偏,摄像头会看丢轨迹。第一次遇到意外时,朴素的 while True: 循环就崩了。
LangGraph 是一个 Python 框架,它把代理写成状态机而不是直来直去的循环。Pybricks 是开源固件,把乐高 hub 变成一台可以用 Python 控制、运行极快的微控制器。把两者拼起来,你得到一个"PC 当大脑,hub 当身体"的拆分,天然就支持故障恢复、视觉闭环、人在回路暂停 —— 不是靠堆 try/except 来撑。
本页把 NotebookLM 从 26 份资料里提炼出的内容浓缩成一份指南:为什么这种架构有效,怎么搭起来,它擅长哪类任务,哪些地方会踩坑,以及一个能在一个 SPIKE Prime hub 上跑通整套思路的 2 小时入门项目。
一分钟搞懂 LangGraph
LangGraph 是一个面向有状态、多代理应用的底层编排运行时。朴素的代理循环是顺序执行的,一旦进程崩了,所有上下文都没了;LangGraph 把工作流建模成一张有向图,节点之间用边连接,节点之间流动着一个共享的状态对象。仅这一个改动就解锁了循环、重试、分支、持久化、人审检查点 —— 这些都是手写循环永远做不彻底的事。
END。Act 回到 Plan,让 LLM 看到自己刚才那一步的实际结果。四个核心组成
- State(状态)。一个
TypedDict,在图里流动。每个节点都读它,返回一份"局部更新",LangGraph 把这些更新合并成完整状态。可以指定合并规则,比如add_messages这种 reducer 会声明 messages 字段应当被追加而不是被覆盖。 - Nodes(节点)。普通的 Python 函数。职责简单:读状态、做事(调 LLM、调工具、读传感器),返回状态更新。仅此而已。
- Edges(边),尤其是条件边。静态边声明"这个节点之后跑那个"。条件边是路由函数:读当前状态,返回下一个节点的名字 —— 分支、重试、终止都靠这一个机制。
- Checkpointers(检查点持久化)。LangGraph 在每个节点完成后把整份状态持久化(比如写到 SQLite),并用一个
thread_id串起来。如果进程崩了(BLE 掉了、你 Ctrl-C、笔记本休眠),用同一个thread_id再次唤起图,就从最后完成的那个节点继续。这才是物理世界里的代理能撑下去的根本原因。
人在回路(human-in-the-loop)中断
因为状态是被持久化的,所以你可以让图在任意指定节点暂停。编译时加上 interrupt_before=["act"],图就在任何物理动作之前停下,把控制权交还给你(或操作员)等待审核。你检查计划、批准或修改,然后用一句 resume 让它继续 —— 中间状态对象不动。对一个可能开下桌的机器人来说,这不是奢侈品,是安全带。
乐高这一端 —— 用 Python 控制乐高积木的四种方式
在 LangGraph 能发出命令之前,乐高那一端必须有人接收。今天能用的真实方案有四种,每一种在"简单 vs 强大"这条轴上落在不同位置。
| 方案 | 支持硬件 | 连接方式 | 优点 | 权衡 |
|---|---|---|---|---|
| Pybricks | SPIKE Prime、MINDSTORMS Robot Inventor、Technic Hub、BOOST、City Hub、EV3 | 在 hub 上刷自定义 MicroPython 固件;PC 通过 BLE(Nordic UART)经由 pybricksdev 或网页 IDE 与之通信 |
极快(电机循环远小于 1 ms)、低层访问、内置 PID、带陀螺仪的驱动基座、异步多任务、支持包括 HuskyLens AI 摄像头在内的第三方传感器 | 需要替换官方乐高固件(可逆),代码运行在微控制器 RAM 里,预算很紧 |
| 乐高 SPIKE App Python | SPIKE Prime、SPIKE Essential | 官方 SPIKE app 通过 BLE/USB 上传到 hub | 零配置,紧挨着学生熟悉的图形化编程界面,安全沙盒 | 封闭生态;v3.0 移除了 UART、原始 PWM、第三方硬件钩子 |
| ev3dev | 只支持 MINDSTORMS EV3 | 从 microSD 卡引导一个完整的 Debian Linux 系统到积木上;通过 Wi-Fi/Bluetooth SSH 进去 | 可以 pip 装任何包,跑完整的 CPython 生态,适合逻辑较重的研究项目 | EV3 硬件已官方停产;启动慢;积木在现代 hub 旁边显得耗电 |
| Build HAT on Raspberry Pi | 任意树莓派 + Build HAT 扩展板 + SPIKE / Robot Inventor 电机和传感器 | 树莓派上跑标准 Python,通过 HAT 的串口接口驱动电机 | 完整树莓派算力 —— OpenCV、本地 LLM、ROS —— 直接跑在机器人本体上 | 机器人变成 Pi + HAT + 8 V 电源包;失去紧凑、无线的乐高 hub 形态 |
给 LLM 代理用的默认选择是 Pybricks。它正好踩到合适的点:足够快,物理控制循环可以完全跑在 hub 上(应该跑在那里 —— 见 坑点);足够开放,PC 可以塞任意命令进来;文档在 docs.pybricks.com 上写得详细。
架构 —— 大脑在 PC,身体在 hub
最重要的一个设计决定是端-云分离:把高层推理留在 PC 上(LLM 思考要 2-8 秒),让反射级电机控制留在乐高 hub 上(Pybricks 每 ~2 毫秒跑一次 PID 循环)。两边通过低功耗蓝牙 (BLE) 上的 Nordic UART 协议通信。
为什么这种拆分重要:如果让 LLM 以人类反应速度直接驱动电机,它会落后真实世界几秒钟、错过决策窗口、直接撞东西。如果 LLM 只说"前进 5 厘米,然后告诉我你看到什么颜色",那么 hub 去做它擅长的部分,LLM 只在真正需要决策的时刻被叫起来。Token 消耗下降,延迟下降,安全反射(防撞停、倾斜守护)留在能毫秒级响应的地方。
代码讲解 —— 观察 → 规划 → 行动 → 再观察 的循环
PC 端代码,约 55 行。三个节点、一条条件边、一个回环。工具函数那里你可以把真正的 pybricksdev BLE 写入塞进去,其它部分是骨架而不是粘合剂。
💡 这个回环就是关键
就这一行 g.add_edge("act", "perception") 让整套东西从"开火即忘"变成了有状态的。每一次物理动作之后都跟着一次新鲜的观察,所以 LLM 下一次决策能看到刚才到底发生了什么,包括失败。电机卡住了?下一次观察会读到这一点;下一次规划会就此推理。不用写 try/except。
这种架构在什么时候赢 —— 四个示例任务
对那些 30 行直白 Pybricks 就能搞定的任务,LangGraph 多出来的复杂度不值得。它真正发挥作用的地方,是那些物理世界会可靠地把计划弄坏、而正确的反应是重新思考而不是原样重试的任务。
1. 抗干扰的循迹
带 HuskyLens AI 摄像头的 SPIKE Prime 跟着一条线走,但线上有突然的断裂、急转弯、光照变化。手写循环轮询摄像头并直接驱动电机;摄像头看丢线的瞬间,要么循环崩了,要么机器人开下桌。LangGraph 版本把感知和动作分开:线没了,条件边路由到一个推理节点,LLM 看一眼最近几步的坐标,自己讲一句猜测("线大概向左拐了"),下令做一个小范围的螺旋搜索把线重新找到。
2. 自主积木堆叠
一只机械臂去抓乐高积木堆金字塔。抓取失败,积木错位。直线脚本给每次抓取套一层硬邦邦的 try/except。LangGraph 版本用一张循环图:act 节点尝试抓取,下一次感知周期读夹爪传感器或摄像头,如果失败,图就回到一个反思节点,让 LLM 算一个新的接近角度再试一次 —— 拿着上一步到底哪里出错的完整上下文。
3. 物体分拣 + 操作员升级
一筐混合物按颜色分到不同容器。分类置信高的物品直接路由到"放进容器"节点。
对置信不够的模糊物品,条件边路由到一个人在回路节点:机器人暂停,操作员审批或修改,图从原地继续 —— 状态对象始终记得上一次读数。
4. 迷宫导航 / 寻宝
机器人要在未知迷宫里找一个特定颜色的目标。预先算好的路线没用,因为死胡同是路上才发现的。LangGraph 督导多个专门的子代理:导航 Agent 负责移动,视觉 Agent 盯着目标。导航 Agent 报告撞上意料外的墙,失败被写进共享状态,督导 LLM 在线重新规划路线,下一个移动命令反映新的地图。
踩坑点 —— 以及老手怎么绕开
1. LLM 规划延迟 vs 实时控制
前沿 LLM 每次决策周期 2-8 秒。机器人需要毫秒级反应才能在撞东西之前停下来。
解法:端-云分离。反射级控制(PID、IMU 稳定、防撞停)跑在 Pybricks hub 上,< 2 ms 完成。PC 上的 LangGraph 编排负责 500 ms - 2 s 范围的策略和再规划。永远不要让 LLM 直接"开"电机。2. BLE 掉线
BLE 链路(Nordic UART)偶尔会断 —— 信号干扰、电池低、蓝牙协议栈打嗝。
解法:给工具节点挂RetryPolicy,用指数退避自动重试,撑过短暂的故障。对更长时间的掉线,SQLite 检查点存着状态 —— 用同一个 thread_id 重启脚本,图从上一个完成的节点继续。
3. 传感器噪声和未建模动力学
颜色传感器读数随环境光变化;电机编码器有漂移;表面摩擦力不一样。
解法:"行动后立刻再观察"的回环。如果一个动作没产生期望的传感器变化,下一个规划周期的 LLM 调用就决定要重试、重新校准,还是换策略。4. 频繁 LLM 调用的成本
一次"观察 → 规划 → 行动 → 再观察"很容易就跑出 20+ 次 LLM 调用。Token 烧得快。
解法:硬性迭代上限(比如max_iterations = 8)当熔断器用。把简单子任务路由到更便宜的小模型(GPT-4o-mini、本地 Hermes 3:8B),只在便宜模型放弃时升级到旗舰模型。
5. 工具调用超时
LLM 工具调用立刻返回("已经驱动 50 cm"),但物理上机器人需要真实的 8 秒才走完 50 厘米。如果你的异步工具没把这件事算进去,下一个规划周期就会在电机还没停下来的时候打响。
解法:工具函数要等 hub 返回明确的 "ok" / "stall" 之后再返回。给ToolNode 加上 handle_tool_errors=True,让超时以文本形式回流给 LLM 推理,而不是让脚本崩。
⚠️ 安全 / 影响半径
一个为完成任务而被优化的 LLM 会愿意开下桌子去够目标,只要没有物理边界挡着。真实部署需要:
- Hub 上的硬件保险 —— 防撞停和倾斜守护本地运行,不理大脑指令。
- 人在回路中断 —— 对高风险动作编译时加
interrupt_before=["act"]。图先暂停,你批准,再继续。 - 用 Pydantic 类型约束工具参数 —— 距离封顶 100 cm,电机速度封顶安全值。LLM 没法跳出 schema。
入门项目 —— "Color-Seeker"(颜色追踪)代理
你有一个 SPIKE Prime hub(或 Robot Inventor / Technic Hub),一个 OpenAI key 或本地的 Hermes via Ollama,1.5 ~ 2 小时。这个项目小到能真做完,大到足以教会你整套架构。
物料清单
- 硬件
- 1× SPIKE Prime Hub(或兼容产品),刷上 Pybricks · 2× 中型电机做一个两轮驱动基座 · 1× 颜色传感器朝下安装 · 一台支持蓝牙的 PC 或 Mac
- 软件
pip install langgraph langchain langchain-openai pybricksdev· Pybricks 网页 IDE(code.pybricks.com)刷 hub · OpenAI key,或者本地 Ollama 跑hermes3:8b,全程离线也行
任务
"向前驱动,直到看到一条蓝线,然后停下来庆祝。"故意搞成一维问题 —— 没有迷宫、没有视觉模型、只有一个决策维度。机器人不断轮询颜色,把读数发给 LLM,等 LLM 决定要继续 "drive forward" 还是 "stop and beep"。
你要搭的两半
- 身体(Pybricks,跑在 hub 上)
- 一小段脚本监听
usys.stdin。收到color?就读传感器并打印结果。收到fwd 5就向前驱动 5 厘米并打印ok。就这样 —— 没有逻辑,只是个服务器。 - 大脑(LangGraph,跑在 PC 上)
- 三个节点的图 —— perception、plan、act —— 形状和上面代码讲解一模一样。工具包装
pybricksdevBLE 写入。条件边在 LLM 决定调用stop_and_celebrate时终止任务。
你实际上学到了什么(不写在课表里的那部分)
- 端-云分离 —— 不是读到,是感觉到
- 你会看着 LLM 用 4 秒钟"思考"的时候电机还在滑行。你会从骨子里明白为什么反射控制属于 hub 那一端。
- 跨周期的有状态记忆
- 你会看到同一份
messages列表在每个循环里增长,带着 LLM 用来决策的上下文。 - BLE 工具调用
- 你会把异步
pybricksdev包装成@tool,然后看着 LLM自己决定什么时候调用它。 - 有 agency 的错误恢复
- 跑到一半把颜色传感器拔下来。
handle_tool_errors=True接住失败,LLM 看到 "sensor unreachable" 的消息,然后决定停下机器人而不是让脚本崩。
从这个起点出发,下一步自然是:加第二个传感器(力或距离),让 LLM 自己决定该查哪个;加 HuskyLens 摄像头和一个 "identify object" 工具;或者把整张图用 interrupt_before=["act"] 包起来,让操作员在每次移动前批准。
来源 —— NotebookLM 调研引用的 26 份资料
NotebookLM 用一次深度调研把这些资料拉进来,主题是"用 LangGraph 控制乐高机器人,为 LEGO Mindstorms EV3、SPIKE Prime 和 Pybricks 编排 AI 代理"。综述 markdown 里的内联引用指向具体段落。
- LangGraph 基础 —— 官方文档、Building AI Agents with LangGraph — DEV Community、Building Smart AI Agents: A Practical Guide to LangGraph Design Patterns — Towards AI、LangGraph Multi-Agent in 20 Minutes — LaunchDarkly
- 乐高的 Python 生态 —— Pybricks v3.x 文档(其中 BLE / Nordic UART 部分引用最频繁)、pybricksdev v2.3.2 documentation — Bluetooth Low Energy、乐高教育 SPIKE Python 发布说明、ev3dev v2 文档
- 物理 AI / 机器人代理 —— Building intelligent physical AI: From edge to cloud with Strands,加上若干篇关于 LLM 驱动机器人延迟预算、重试策略、人在回路模式的 arXiv / 博客文章
- 安全 & 生产实践 —— AI Agent Security Best Practices and Tutorial — IBM
- 社区分享 —— Built a visual mission control for AI agents — Reddit,以及若干 maker 博客 demo LangGraph + 机器人组合的案例
完整的有据可查的答案(带脚注指向具体段落)和本页放在一起,路径是 HTML/it/ai/langgraph_lego_robots_research.md。来源 notebook 的 ID 是 5ae67007-acab-4e69-ae39-9baf503ac139;用 notebooklm use 5ae67007 + notebooklm ask "..." 可以复现或扩展这次调研。