ByteNoteByteNote

字节笔记本

2026年6月26日

用 Kimi 兼容接口驱动 cua-agent 做屏幕自动化:一次完整的 computer use 踩坑实录

API中转
¥120

最近想用 Kimi 的编程模型驱动屏幕自动化,顺手拿 cua-agent 这个框架试了试。整个过程比预想的曲折不少——接口能通,模型能聊,但真到要它"动手"操作屏幕时却卡住了。这篇记一下完整排查过程,以及最后怎么让它跑起来的。

核心结论先放这儿:Kimi 的 kimi-for-coding 接口能通过环境变量无缝挂到 Anthropic SDK 上,但模型本身不会主动调用 computer use 工具,所以原生那条循环走不通,得换 OMNI loop 靠客户端解析坐标。下面展开说。

先搞清楚这套东西在干什么

很多人第一眼看到这种命令会懵:

bash
ANTHROPIC_BASE_URL=https://api.kimi.com/coding/ \
ANTHROPIC_AUTH_TOKEN=sk-kimi-xxxx \
ANTHROPIC_MODEL=kimi-for-coding \
python3 agent.py "做点什么" --provider env

它其实就干了三件事:

  1. ANTHROPIC_BASE_URL 把 SDK 默认指向的 api.anthropic.com 换成 Kimi 的兼容接口地址
  2. ANTHROPIC_AUTH_TOKEN 用 Kimi 的 key 替代 Anthropic 的 key 做鉴权
  3. ANTHROPIC_MODEL 告诉 SDK 透传哪个模型名给后端

--provider env 的意思是别在代码里硬编码用哪家的 API,而是从环境变量里读 base URL 和 token。本质上 Kimi 这个接口实现的是 Anthropic Messages API 的格式,所以能直接替换,SDK 感知不到差异。

理解了这一层,后面所有的坑都好解释了。

第一步:验证接口本身能通

没急着上手写 agent,先用最原始的 curl 探一下连通性。这一步很重要,把"网络/鉴权/路由"这类问题先排除掉,后面出问题就知道锅不在网络层。

bash
curl -X POST https://api.kimi.com/coding/v1/messages \
  -H "x-api-key: sk-kimi-xxxx" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"kimi-for-coding","max_tokens":100,"messages":[{"role":"user","content":"回复pong两个字"}]}'

返回 200,body 里规规矩矩带着 "text":"pong",说明接口格式、鉴权、模型路由全都没问题。基础连通性这一关过了。

第二步:测 computer use 工具——这里就翻车了

连通性 OK 不代表能做屏幕自动化。computer use 的关键在于:接口得能接受 computer_20250124 这种工具定义,而且模型得在回复里吐出 tool_use 类型的 block(里面带 action 和坐标),agent 循环才能据此去点击、截图。

于是照着协议构造了带工具定义的请求,顺手塞了一张截图进去:

bash
curl -X POST https://api.kimi.com/coding/v1/messages \
  -H "x-api-key: sk-kimi-xxxx" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model":"kimi-for-coding",
    "max_tokens":1024,
    "tools":[{"type":"computer_20250124","name":"computer",
              "display_width_px":32,"display_height_px":32,"display_number":1}],
    "messages":[{"role":"user","content":[
      {"type":"text","text":"请截图看看屏幕上有什么"},
      {"type":"image","source":{"type":"base64","media_type":"image/png","data":"<截图base64>"}}
    ]}]
  }'

这一步有个坑得提一句。最早我图省事,塞了一张从网上扒来的 base64 PNG,结果接口直接回了 400,报错 failed to decode image: invalid or unsupported image format。第一反应是"工具定义不被支持",差点下错结论。后来用 Python 现场生成了一张干净的 PNG 重测,才知道是图本身坏了,跟工具兼容性没关系。

所以排查这类问题时,别用来源不明的 base64,要么自己生成,要么从真实截图编码,免得被图片问题误导判断方向。

换上合法截图再测,这次返回 200,工具定义是被接受了。但模型的回复让人哭笑不得——它完全无视了那张图,也没发任何 tool_use,而是回了句:

我无法截图或查看您的屏幕。我是一个纯文字的人工智能,没有视觉功能。

到这儿,真正的瓶颈才算定位:接口接受工具定义,但模型既不看图,也不会主动调用 computer 工具。这意味着原生那条依赖 model 自发 tool_use 的循环,在这条链路上是死路。

为什么 ANTHROPIC loop 走不通,OMNI loop 可以

cua-agent 里有两类循环,理解它们的区别就能明白问题出在哪。

ANTHROPIC loop 走的是 Anthropic 原生 computer use 协议。它假定模型本身懂 computer 这个工具,会自己决定"现在该截图""该点 (x, y) 这个位置",然后吐出 tool_use。整个循环的节奏由模型主导。Kimi 这条路前面已经验证过——模型不吐 tool_use,所以循环一开始就卡死,根本转不起来。

OMNI loop 的思路完全不同。它不指望模型懂 computer use,而是在客户端用 OmniParser 把截图标注成一堆带编号的方框(Set-of-Marks),再把这些"几号框是什么"以文本形式喂给模型。模型只需要做它擅长的事——看图说话、选个编号,客户端拿到编号再换算成坐标去点击。

换句话说,OMNI loop 把"理解界面、定位坐标"这个最难的部分从模型身上卸下来,挪到了客户端。模型只负责"理解意图",这正是 Kimi 这种会聊天但不发 tool_use 的模型能胜任的。所以接入非 Anthropic 的模型时,OMNI 几乎是默认选择。

cua-agent 的 API 变了,别照着老博客抄

确定走 OMNI 之后写接入代码,结果上来就栽了个跟头:照着网上一些资料写的 LLMLLMProviderAgentLoopComputerAgent(computer=...) 这些,在新版里根本不存在,直接 ImportError。

没敢继续凭印象写,把当时的 wheel 包解出来逐行看了源码,才发现 0.5.x 这套签名已经彻底改了。新的用法是这样:

python
from agent import ComputerAgent       # 注意顶层包名是 agent
from computer import Computer         # Computer 来自单独的 computer 包

async with Computer(os_type="macos") as computer:
    agent = ComputerAgent(
        model="omni+anthropic/kimi-for-coding",  # loop 和 provider 都编进了模型名
        tools=[computer],                         # 是 tools,不是 computer=
        api_key="sk-kimi-xxxx",
        api_base="https://api.kimi.com/coding",
        telemetry_enabled=False,
    )
    async for resp in agent.run("打开计算器算一下 7 乘 6"):
        print(resp)

几个关键点:

  • 顶层包名是 agent,不是 cua_agent,所以自己写的脚本千万别叫 agent.py,否则 from agent import ... 会把自己当成库导入,报一个特别误导人的错。这点我亲历,排查了好一会儿。
  • loop 和 provider 不再是独立参数,而是编进模型名字符串里,用 +/ 分隔。omni+ 选 OMNI loop,anthropic/ 让底层的 litellm 走 Anthropic 兼容路径,后面才是真正的模型名。
  • 凭据既能在构造器传 api_key/api_base,也能靠环境变量。但要留意 litellm 认的变量名是 ANTHROPIC_API_BASEANTHROPIC_API_KEY,跟早期文档里的 ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN 不是一回事,混用会静默不生效。

拿计算器跑一遍

理论清楚了,做个端到端验证:让 Kimi 通过 OMNI loop 操作 macOS 自带的计算器,算个 7 × 6,看它能不能点对按钮、读出结果。

依赖装好(cua-agentcomputer、还有 OMNI loop 必需的 cua-som)之后,核心就是上面那段代码。跑起来它会真实接管鼠标键盘——这点得提醒,运行期间千万别碰电脑,否则坐标全乱。

实际跑的时候,OmniParser 会先截图、标注元素,再把"这是几号框、大概是啥"告诉 Kimi;Kimi 回一句"点 7 号框",客户端换算坐标去点,循环往复,直到它判断任务完成。中间任何一步出错(比如标注没识别准、模型选错框),都可能导致算出错误结果或卡在某一步,这也是 OMNI loop 相比原生 computer use 的代价——多了一层坐标换算,精度依赖 OmniParser 的识别质量。

跑完人工核对一下计算器显示屏是不是 42 就行。这一步的意义不在于"算得准不准",而在于验证整条链路(Kimi 接口 → OMNI loop → 坐标换算 → 真实点击)是通的,这才是接入非 Anthropic 模型时最该先确认的事。

几个值得记一笔的坑

把整个排查过程里反复绊到人的几个点单独拎出来,省得下次再踩。

坏图会被误判成工具不兼容。 接口报 failed to decode image 时,问题十有八九在图片本身,而不是工具定义。排查 computer use 兼容性,一定用自己生成的干净截图,别用来源不明的 base64。

环境变量在进程启动时就定格了。 它是进程创建时的一次性快照,运行中在 shell 里改 export,对已经跑起来的 agent 没有任何影响。改了发现不生效,直接重启进程,别浪费时间在别处找原因。

变量名要认准 litellm 那一套。 接 cua-agent 用的是 ANTHROPIC_API_BASE / ANTHROPIC_API_KEY,不是更早那两个带 BASE_URL / AUTH_TOKEN 的。名字写错不会报错,只是静默不读,特别难发现。

文件别叫 agent.py 和库的顶层模块重名,from agent import 会导入自己,报错信息还看不出是命名冲突,新手特别容易在这上面耗时间。

小结

把 Kimi 这类兼容接口接到 cua-agent,真正卡人的不是"怎么配环境变量"——那三行 export 谁都会抄。难的是判断模型到底支不支持 computer use,以及在不支持的时候知道还有 OMNI loop 这条退路。

实操下来一个体会:接非官方模型做 agent,先别急着写业务,先用最小请求探清楚模型的能力边界。一个 curl 就能知道它会不会发 tool_use,这比把整套 agent 跑起来再发现卡死省事得多。能发就用原生循环,不能发就走 OMNI 让客户端干脏活,两条路总有一条走得通。

分享: