langgraph-cua 是什么:用 LangGraph 编排 CUA 的 Python 实战入门

📅 发布时间:2026/10/18 21:10:30
langgraph-cua 是什么:用 LangGraph 编排 CUA 的 Python 实战入门
1. 从 LangGraph 状态图视角理解 langgraph-cua 是什么如果你写过 LangGraph应该熟悉那种把任务拆成节点、用边串起来的感觉。langgraph-cua 就是把这套思路搬到了「让 AI 操作电脑」这件事上。它是什么一句话一个基于 LangGraph 的 Python 库用来编排 CUAComputer Use Agent计算机使用代理让模型能像人一样在浏览器里点击、输入、翻页、填表。适合谁适合已经会写 Python、想把手里的重复网页操作交给代理跑、又不想自己从零搭状态机的开发者。CUA 这个词这两年出现得很多但真正落地时会发现难点不在模型而在「怎么把一次任务拆成可观测、可中断、可恢复的步骤」。langgraph-cua 的价值就在这里它把 CUA 的执行流程抽象成一张状态图每个节点负责一件事比如创建虚拟机、执行动作、判断是否完成。你不需要自己写 while 循环去轮询浏览器状态图本身会帮你推进。它默认配合 Scrapybara 沙箱使用。Scrapybara 提供的是远程虚拟机环境里面跑着浏览器代理的所有点击和输入都发生在那个隔离环境里不会碰到你本机的桌面。这个配合场景很关键本地跑浏览器自动化容易污染环境、留下登录态、甚至误操作真实账号放到沙箱里任务结束直接销毁干净利落。从 LangGraph 视角看langgraph-cua 的图大致长这样入口节点接收 messages然后 create_vm_instance 节点拉起沙箱接着进入一个循环——模型决定下一步动作动作在沙箱里执行执行结果回灌给模型直到模型认为任务完成。这个循环在 LangGraph 里就是条件边加状态累积。你如果读过 LangGraph 的 ReAct 示例会发现结构非常像只是「工具」变成了浏览器操作。我试过把它接进一个查价格的流程最直观的感受是以前写 Selenium 要处理等待、元素定位、异常重试现在这些都被代理的决策层吸收了你更多是在调图和看流式输出。当然代价是你要接受模型偶尔点错所以状态图的可中断设计就很重要——你可以在某个节点后插入人工确认这也是 LangGraph 的强项。理解它是什么之后下一步就是把它跑起来。下面从环境依赖开始一步步给你可复制的配置。2. TaoToken 前置准备与 langgraph-cua 环境依赖安装在真正写图之前先把两件事准备好模型调用的入口以及 Python 环境。langgraph-cua 本身不绑定某一家模型服务它需要一个兼容 OpenAI 接口的 endpoint。这里我用 TaoToken 作为统一入口原因是它同时提供对话模型和编码场景的额度后面如果你想把这个 CUA 流程接到更长的自动化任务里不用再换一套鉴权。先拿 Key。打开 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 登录后创建一个 API Key复制出来。这个 Key 就是后面环境变量里的 OPENAI_API_KEY。注意不要把它写进代码提交到仓库用 .env 管理。接着是 Scrapybara 的 Key。langgraph-cua 默认用 Scrapybara 做沙箱你需要去它的控制台注册并拿到 SCRAPYBARA_API_KEY。这一步不能省因为 create_vm_instance 节点会真的去拉起一台远程虚拟机。免费额度通常够跑几次验证正式用再考虑升级。Python 环境建议 3.10 以上3.11 更稳。用 venv 隔离python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install --upgrade pip pip install langgraph-cua python-dotenv装完之后确认一下版本pip show langgraph-cua你会看到版本号目前公开的是 0.0.1 这个早期版本接口可能还会变所以下面给的配置以「能跑通」为目标不追求覆盖所有参数。然后建一个 .env 文件放在项目根目录OPENAI_API_KEY你的_taotoken_key OPENAI_BASE_URLhttps://taotoken.net/api SCRAPYBARA_API_KEY你的_scrapybara_key这里有个容易踩的点langgraph-cua 内部调用模型时读的是 OPENAI_API_KEY 和 OPENAI_BASE_URL如果你只设了 Key 没设 Base URL它会默认打到官方地址而你的 Key 是 TaoToken 的结果就是 401。所以 Base URL 一定要显式写。如果你更习惯用配置文件而不是环境变量也可以在代码里显式传参但环境变量方式对 dotenv 最友好。装依赖这一步看起来简单实际报错大多出在 Python 版本和依赖冲突上下一节我会把配置片段和常见冲突一起讲。3. 可复制的 langgraph-cua 最小状态图配置现在进入核心部分写一个最小的 CUA 图并跑起来。langgraph-cua 暴露的入口是 create_cua()它返回一个编译好的 LangGraph 图对象。你不需要自己定义 State库内部已经封装好了 messages 的累积逻辑。先看完整的最小可运行脚本保存为 cua_demo.pyimport asyncio import os from dotenv import load_dotenv from langgraph_cua import create_cua load_dotenv() # 确认环境变量已加载避免 401 assert os.getenv(OPENAI_API_KEY), 缺少 OPENAI_API_KEY assert os.getenv(OPENAI_BASE_URL), 缺少 OPENAI_BASE_URL assert os.getenv(SCRAPYBARA_API_KEY), 缺少 SCRAPYBARA_API_KEY cua_graph create_cua() messages [ { role: system, content: Youre an advanced AI assistant with browser access., }, { role: user, content: 打开 example.com读出页面标题并返回。, }, ] async def main(): stream cua_graph.astream( {messages: messages}, stream_modeupdates, ) async for update in stream: for node_name, node_output in update.items(): print(f[节点] {node_name}) if node_name create_vm_instance: stream_url node_output.get(stream_url) print(f[沙箱] stream_url {stream_url}) if messages in node_output: last node_output[messages][-1] print(f[消息] {getattr(last, content, last)}) if __name__ __main__: asyncio.run(main())这段代码里有几个关键点值得展开。第一stream_modeupdates 让你能看到每个节点执行后的增量输出这对调试 CUA 特别重要因为代理的决策是逐步产生的一次性等最终结果你根本不知道它卡在哪。第二create_vm_instance 节点的输出里带 stream_url这是沙箱的实时画面地址你可以用浏览器打开看代理在干什么排查点击错位时非常有用。第三messages 的 system 提示词决定了代理的行为风格官方示例用的是「advanced AI assistant with browser access」你可以按任务改但别删掉 browser access 这个语义否则模型可能不知道自己有浏览器。如果你想把模型 ID 显式指定而不是用默认值可以在 create_cua 里传参。不同版本的参数名可能不同稳妥做法是先看签名import inspect from langgraph_cua import create_cua print(inspect.signature(create_cua))打印出来你会看到它接受哪些关键字参数常见的有 model、zdr_enabled 等。zdr_enabledTrue 表示零数据保留模式每次请求都把完整消息历史传给模型而不是复用之前的响应 ID适合对隐私敏感的场景代价是 token 消耗更高。关于 Scrapybara 沙箱的配合这里再补一句create_vm_instance 默认拉起的是一台带浏览器的 Ubuntu 实例。你不需要在本地装 Chrome 或 Playwright所有操作都在远端。这也是它和本地 Selenium 最大的区别——环境是临时的、隔离的任务跑完可以销毁不会在你机器上留下 cookie 和缓存。配置写好后先别急着跑复杂任务用上面那个「读页面标题」的小任务验证链路。下一节讲怎么确认它真的成功了。4. 本地运行验证与流式输出结果解读运行命令很简单python cua_demo.py第一次跑会慢一些因为要等沙箱创建。你会在终端看到类似这样的输出顺序[节点] create_vm_instance [沙箱] stream_url https://... [节点] call_model [消息] ... [节点] execute_action [消息] ... [节点] call_model [消息] 页面标题是 Example Domain这个顺序就是状态图的推进过程。create_vm_instance 完成后图进入 call_model模型根据当前 messages 决定下一步动作比如「导航到 example.com」然后 execute_action 节点在沙箱里执行这个动作执行结果作为新消息回灌再次进入 call_model直到模型输出最终答案。怎么判断成功看两个信号。一是最后一条消息里包含了你要求的信息比如「Example Domain」这个标题。二是整个流程没有卡在某个节点反复循环。如果 call_model 和 execute_action 来回超过十几次还没结束通常是任务描述太模糊模型在反复试探。把 stream_url 复制到浏览器打开你能看到沙箱的实时画面。这个画面是验证代理行为最直接的方式。有时候模型说「已点击按钮」但画面上根本没动那就是动作映射出了问题可能是坐标偏移或者页面还没加载完。看到画面你就能判断是模型决策错还是执行层错。流式输出里还有一个细节node_output 的结构在不同节点不一样。create_vm_instance 返回的是沙箱信息call_model 返回的是 messagesexecute_action 返回的可能是动作执行结果。所以遍历 update.items() 时要做类型判断不能假设每个节点都有 messages 字段。上面脚本里用 getattr 做了兜底就是这个原因。如果你跑通了这个小任务可以试着把 user 消息换成稍微复杂一点的比如「打开 example.com找到页面上的 More information 链接读出它的 href」。这会触发多步操作导航、定位元素、读取属性。观察它需要几轮 call_model 才完成你就能对代理的能力边界有个直观感受。验证通过后说明你的 Key、Base URL、沙箱三件套都配对了。接下来把常见报错过一遍省得你遇到时抓瞎。5. langgraph-cua 常见报错排查401、local proxy failed 与 reading choices这一节按真实会遇到的报错来。第一个401 Unauthorized。这个几乎都是 Key 或 Base URL 的问题。检查顺序.env 里 OPENAI_API_KEY 是不是 TaoToken 的 KeyOPENAI_BASE_URL 是不是写成了 https://taotoken.net/api注意结尾不要多加 /v1除非文档明确要求。如果你在代码里手动传了 api_key 参数确认它没覆盖环境变量。还有一种情况是 dotenv 没加载成功load_dotenv() 要在读取环境变量之前调用且 .env 文件要在当前工作目录。第二个local proxy failed 或连接超时。这个报错通常出现在沙箱创建阶段说明你的网络到 Scrapybara 的连接不稳定或者 SCRAPYBARA_API_KEY 无效。先确认 Key 没写错再去 Scrapybara 控制台看额度是否用完。如果额度正常重试一次往往就好了沙箱服务偶发波动是正常的。注意这里不要试图用任何网络代理工具去「解决」问题一般出在 Key 或服务端不在你的本地网络配置。第三个reading choices 相关的报错完整信息可能是 Cannot read properties of undefined (reading choices) 或者 Python 侧的 KeyError: choices。这个错误的本质是模型返回体不符合预期最常见原因是 Base URL 指向了一个不兼容 OpenAI 接口的地址或者模型 ID 写错了。langgraph-cua 内部按 OpenAI 的响应格式解析 choices 字段如果你的 endpoint 返回的是别的结构就会在这里炸。解决办法确认 OPENAI_BASE_URL 是 https://taotoken.net/api确认你用的模型 ID 在 TaoToken 的模型列表里存在。可以在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 查可用模型。第四个OAuth 或鉴权跳转类报错。如果你在任务里让代理登录某个网站可能会遇到 OAuth 回调失败。这不是 langgraph-cua 的 bug而是沙箱环境和目标站点的交互问题。处理方式是尽量用不需要登录的页面做验证或者用 Scrapybara 的持久化会话功能保存登录态。早期版本对复杂登录流程支持有限别一上来就挑战硬骨头。第五个图跑起来但一直不结束。前面提过这通常是任务描述模糊。把 user 消息改得更具体比如加上「只做一步」「完成后立即返回」这类约束。另外可以在 create_cua 时设置最大迭代次数如果版本支持防止无限循环。排查时有个通用技巧把 stream_mode 改成 values看完整状态快照而不是增量。这样你能看到 messages 数组的全貌定位是哪一步的消息导致了异常。两种模式配合用调试效率会高很多。6. 把 langgraph-cua 接入你的自动化流程从验证到长期编码跑通最小示例后你大概能判断它适不适合自己的场景了。如果你的需求是「偶尔跑一次的网页信息收集」那直接用脚本调 create_cua 就够了不需要更复杂的编排。但如果你要把它做成长期运行的服务或者和编码任务结合那就值得考虑更稳定的额度方案。langgraph-cua 的图是可以嵌套的。你可以把它作为一个子图嵌进更大的 LangGraph 应用里。比如外层图负责调度多个 CUA 任务内层图负责单个网页操作。这种组合方式在 LangGraph 里很自然因为图本身就是可组合的。实际写的时候把 create_cua() 返回的对象当作一个节点加进外层图即可状态通过 messages 传递。对于需要长期跑编码和代理任务的场景TaoToken 的 Coding Plan 更合适额度模型和按次调用不同适合持续性的工作负载。你可以在这里了解https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。如果你的 CUA 任务需要频繁调用模型做决策用包月方案比按 token 计费更可控。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有 Base URL、鉴权方式、模型列表的完整说明。遇到接口层面的疑问先查文档比在代码里猜要快。最后给一个实用建议把沙箱的 stream_url 记录下来配合日志一起存。CUA 的调试成本主要在「不知道代理为什么这么做」有了画面回放和节点日志复盘会轻松很多。你可以写个小函数在每个节点输出时把 node_name 和时间戳打到文件里跑几次之后就能看出哪些任务容易卡住。如果你还没决定要不要深入先用本文的最小示例跑三个不同类型的任务读页面信息、填一个简单表单、点击多步导航。跑完你心里就有数了。需要开始的话先去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 拿 Key把环境变量配好剩下的就是调图的事了。