说真的,打开掘金看到热榜 6 条里 4 条是 OpenClaw,我心想:这玩意真有这么神?
下载试了一下------800MB 客户端、Electron 套壳、内存占用 1.2G 起步。就为了让 AI 帮我点点鼠标?
于是我花了一个下午,用 Python 写了个 50 行的"穷人版桌面 Agent"。核心原理其实很简单:截图 → 大模型看图理解 → 执行鼠标键盘操作 → 循环。跑起来之后我自己都没想到效果还不错。
先说结论
| 对比项 | OpenClaw | 自己搭(本文方案) |
|---|---|---|
| 安装体积 | 800MB+ | pip install 三个包 |
| 内存占用 | 1.2GB+ | ~50MB |
| 自定义程度 | 插件系统 | 代码随便改 |
| 模型选择 | 绑定特定模型 | 想用啥用啥 |
| 上手难度 | 开箱即用 | 需要会 Python |
| 稳定性 | 成熟 | 自己兜底 |
如果你只是想理解桌面 Agent 的原理,或者有特定的自动化需求不想装个大家伙,往下看。
核心原理:截图-思考-行动 循环
所有桌面 Agent,不管是 OpenClaw 还是 Anthropic 的 Computer Use,核心都是一个 loop:
ini
while True:
screenshot = 截屏()
action = 大模型看图分析(screenshot, "用户的指令")
执行操作(action) # 点击、输入、滚动...
if action == "done":
break
就这么简单。大模型的 Vision 能力负责"看懂屏幕",pyautogui 负责"动手操作"。
动手:50 行代码实现
环境准备
bash
pip install pyautogui openai pillow
完整代码
python
import pyautogui
import base64
import io
import json
from openai import OpenAI
# 初始化客户端(兼容 OpenAI 协议的接口都行)
client = OpenAI(
api_key="your-api-key",
base_url="https://api.ofox.ai/v1" # 支持 50+ 模型的聚合接口
)
SYSTEM_PROMPT = """你是一个桌面操作助手。用户会给你一张屏幕截图和一个任务。
你需要分析截图,返回下一步操作。
返回 JSON 格式:
{"action": "click", "x": 100, "y": 200, "reason": "点击搜索框"}
{"action": "type", "text": "hello world", "reason": "输入搜索词"}
{"action": "scroll", "direction": "down", "reason": "向下滚动查看更多"}
{"action": "hotkey", "keys": ["command", "c"], "reason": "复制选中内容"}
{"action": "done", "reason": "任务完成"}
只返回 JSON,不要其他内容。"""
def screenshot_to_base64():
"""截屏并转为 base64"""
img = pyautogui.screenshot()
buffer = io.BytesIO()
img.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode()
def ask_model(image_b64, task, history=None):
"""让大模型看图决策"""
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
if history:
messages.extend(history)
messages.append({
"role": "user",
"content": [
{"type": "text", "text": f"任务:{task}\n请分析截图,返回下一步操作。"},
{"type": "image_url", "image_url": {
"url": f"data:image/png;base64,{image_b64}"
}}
]
})
resp = client.chat.completions.create(
model="anthropic/claude-sonnet-4-6", # 也可以换 gpt-5.4、gemini-3-flash 等
messages=messages,
max_tokens=300,
temperature=0
)
return json.loads(resp.choices[0].message.content)
def execute_action(action):
"""执行大模型返回的操作"""
act = action["action"]
if act == "click":
pyautogui.click(action["x"], action["y"])
elif act == "type":
pyautogui.write(action["text"], interval=0.05)
elif act == "scroll":
pyautogui.scroll(-3 if action["direction"] == "down" else 3)
elif act == "hotkey":
pyautogui.hotkey(*action["keys"])
elif act == "done":
return False
return True
def run_agent(task, max_steps=10):
"""主循环"""
print(f"🤖 开始执行: {task}")
history = []
for step in range(max_steps):
img_b64 = screenshot_to_base64()
action = ask_model(img_b64, task, history)
print(f" Step {step+1}: {action.get('reason', '...')}")
history.append({"role": "assistant", "content": json.dumps(action)})
if not execute_action(action):
print("✅ 任务完成!")
return True
pyautogui.sleep(1) # 等 UI 响应
print("⚠️ 达到最大步数,停止执行")
return False
# 用法
if __name__ == "__main__":
run_agent("打开浏览器,搜索今天的天气")
实测效果
我拿这个脚本试了几个场景:
场景 1:自动搜索
python
run_agent("打开 Chrome,在百度搜索 Python 3.13 新特性")
执行过程:
vbnet
🤖 开始执行: 打开 Chrome,在百度搜索 Python 3.13 新特性
Step 1: 点击 Dock 栏的 Chrome 图标
Step 2: 点击地址栏
Step 3: 输入 baidu.com
Step 4: 按回车键
Step 5: 点击搜索框
Step 6: 输入搜索词
Step 7: 按回车执行搜索
✅ 任务完成!
7 步搞定,全程大概 15 秒(主要是等 API 返回)。
场景 2:文件整理
python
run_agent("把桌面上所有 .tmp 文件拖到废纸篓")
这个任务模型理解得不错,但执行拖拽操作时翻车了------pyautogui 的 drag 在 macOS 上有些时候不太灵。后来换成 hotkey + delete 的方式才搞定。
场景 3:填表单
python
run_agent("打开公司的 OA 系统,填写今天的日报,内容写'完成了桌面 Agent 的技术调研'")
这个效果最好,因为表单页面结构清晰,模型识别准确率很高。
踩坑记录
坑 1:分辨率问题
pyautogui 返回的坐标是逻辑像素,但 Retina 屏截图是物理像素。大模型看到的是 2x 分辨率的图,返回的坐标也是 2x 的。
python
# 修复:截图时缩放到逻辑分辨率
def screenshot_to_base64():
img = pyautogui.screenshot()
# macOS Retina 需要缩放
logical_size = (img.width // 2, img.height // 2)
img = img.resize(logical_size)
buffer = io.BytesIO()
img.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode()
坑 2:模型选择很关键
试了几个模型的表现:
| 模型 | 识别准确率 | 响应速度 | 适合场景 |
|---|---|---|---|
| Claude Sonnet 4.6 | ⭐⭐⭐⭐⭐ | 1.5s | 复杂页面 |
| GPT-5.4 | ⭐⭐⭐⭐⭐ | 2s | 通用场景 |
| Gemini 3 Flash | ⭐⭐⭐⭐ | 0.8s | 简单任务(快!) |
| Qwen-VL-Max | ⭐⭐⭐ | 1.2s | 中文界面 |
我一般简单任务用 Gemini 3 Flash(便宜快),复杂任务用 Claude Sonnet。通过聚合 API 切换模型就改一行代码的事。
坑 3:JSON 解析失败
大模型偶尔会在 JSON 外面包一层 markdown 代码块,加个容错处理:
python
import re
def parse_action(text):
"""容错解析 JSON"""
# 去掉可能的 markdown 代码块标记
text = re.sub(r'```json?\s*', '', text)
text = re.sub(r'```\s*$', '', text)
return json.loads(text.strip())
坑 4:安全防护必须加
pyautogui 有个 FAILSAFE 机制------鼠标移到屏幕左上角会触发异常停止。千万别关掉它:
python
pyautogui.FAILSAFE = True # 默认就是 True,别改成 False!
另外建议加个确认机制,敏感操作前先问一下:
python
def execute_action(action):
act = action["action"]
# 危险操作确认
if act == "hotkey" and "delete" in str(action.get("keys", [])):
confirm = input(f"⚠️ 即将执行删除操作: {action['reason']},确认? (y/n) ")
if confirm != 'y':
return True # 跳过这步
# ... 其余执行逻辑
进阶:加上记忆和多轮对话
基础版只看当前截图,没有"记忆"。加上对话历史后,Agent 会聪明很多:
python
def ask_model(image_b64, task, history=None):
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
if history:
# 只保留最近 5 轮,避免 token 爆炸
messages.extend(history[-10:])
# ... 其余逻辑不变
还可以加个"反思"机制------每次操作后截图对比,看有没有变化:
python
def run_agent_with_reflection(task, max_steps=10):
prev_img = None
for step in range(max_steps):
curr_img = screenshot_to_base64()
if prev_img and curr_img == prev_img:
print("⚠️ 屏幕没变化,可能操作没生效,重试...")
# ...
prev_img = curr_img
跟 OpenClaw 比到底差在哪
说实话,OpenClaw 之所以火是有道理的------它的 Skill 生态、权限管理、多 Agent 协作这些东西,50 行代码肯定搞不定。
但如果你的需求是:
- 自动化一些固定流程(日报、数据抓取、定时截图)
- 想深入理解桌面 Agent 原理
- 不想装一个 800MB 的客户端
- 需要自定义模型和 prompt
那自己搭完全够用。核心就是 pyautogui + Vision API,剩下的都是工程细节。
小结
桌面 Agent 说到底就是个"截图-看图-操作"的循环,原理一点不复杂。OpenClaw 的价值在于它把这个循环做得很工程化、很稳定,但底层逻辑跟我们这 50 行代码没本质区别。
我现在日常用这个脚本跑一些小任务------定时截图监控、自动填表单、批量文件操作。比起 OpenClaw 的全家桶,我更喜欢这种能完全掌控的方式。
代码已经贴全了,复制就能跑。如果你也想试试,建议从最简单的"打开浏览器搜索"开始,慢慢加功能。
对了,代码里 API 调用用的是兼容 OpenAI 协议的聚合接口,一个 key 能调几十个模型,切换模型改一行 model 参数就行,省得每家单独注册。