50行Python代碼帶你打造一個(gè)桌面AI Agent
說真的,現(xiàn)在熱榜 6 條里 4 條是 OpenClaw,我心想:這玩意真有這么神?
下載試了一下——800MB 客戶端、Electron 套殼、內(nèi)存占用 1.2G 起步。就為了讓 AI 幫我點(diǎn)點(diǎn)鼠標(biāo)?
于是我花了一個(gè)下午,用 Python 寫了個(gè) 50 行的"窮人版桌面 Agent"。核心原理其實(shí)很簡單:截圖 → 大模型看圖理解 → 執(zhí)行鼠標(biāo)鍵盤操作 → 循環(huán)。跑起來之后我自己都沒想到效果還不錯(cuò)。
先說結(jié)論
| 對比項(xiàng) | OpenClaw | 自己搭(本文方案) |
|---|---|---|
| 安裝體積 | 800MB+ | pip install 三個(gè)包 |
| 內(nèi)存占用 | 1.2GB+ | ~50MB |
| 自定義程度 | 插件系統(tǒng) | 代碼隨便改 |
| 模型選擇 | 綁定特定模型 | 想用啥用啥 |
| 上手難度 | 開箱即用 | 需要會 Python |
| 穩(wěn)定性 | 成熟 | 自己兜底 |
如果你只是想理解桌面 Agent 的原理,或者有特定的自動(dòng)化需求不想裝個(gè)大家伙,往下看。
核心原理:截圖-思考-行動(dòng) 循環(huán)
所有桌面 Agent,不管是 OpenClaw 還是 Anthropic 的 Computer Use,核心都是一個(gè) loop:
while True:
screenshot = 截屏()
action = 大模型看圖分析(screenshot, "用戶的指令")
執(zhí)行操作(action) # 點(diǎn)擊、輸入、滾動(dòng)...
if action == "done":
break
就這么簡單。大模型的 Vision 能力負(fù)責(zé)"看懂屏幕",pyautogui 負(fù)責(zé)"動(dòng)手操作"。
動(dòng)手:50 行代碼實(shí)現(xiàn)
環(huán)境準(zhǔn)備
pip install pyautogui openai pillow
完整代碼
import pyautogui
import base64
import io
import json
from openai import OpenAI
# 初始化客戶端(兼容 OpenAI 協(xié)議的接口都行)
client = OpenAI(
api_key="your-api-key",
base_url="https://api.ofox.ai/v1" # 支持 50+ 模型的聚合接口
)
SYSTEM_PROMPT = """你是一個(gè)桌面操作助手。用戶會給你一張屏幕截圖和一個(gè)任務(wù)。
你需要分析截圖,返回下一步操作。
返回 JSON 格式:
{"action": "click", "x": 100, "y": 200, "reason": "點(diǎn)擊搜索框"}
{"action": "type", "text": "hello world", "reason": "輸入搜索詞"}
{"action": "scroll", "direction": "down", "reason": "向下滾動(dòng)查看更多"}
{"action": "hotkey", "keys": ["command", "c"], "reason": "復(fù)制選中內(nèi)容"}
{"action": "done", "reason": "任務(wù)完成"}
只返回 JSON,不要其他內(nèi)容。"""
def screenshot_to_base64():
"""截屏并轉(zhuǎn)為 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"任務(wù):{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):
"""執(zhí)行大模型返回的操作"""
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):
"""主循環(huán)"""
print(f"?? 開始執(zhí)行: {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("? 任務(wù)完成!")
return True
pyautogui.sleep(1) # 等 UI 響應(yīng)
print("?? 達(dá)到最大步數(shù),停止執(zhí)行")
return False
# 用法
if __name__ == "__main__":
run_agent("打開瀏覽器,搜索今天的天氣")
實(shí)測效果
我拿這個(gè)腳本試了幾個(gè)場景:
場景 1:自動(dòng)搜索
run_agent("打開 Chrome,在百度搜索 Python 3.13 新特性")
執(zhí)行過程:
?? 開始執(zhí)行: 打開 Chrome,在百度搜索 Python 3.13 新特性
Step 1: 點(diǎn)擊 Dock 欄的 Chrome 圖標(biāo)
Step 2: 點(diǎn)擊地址欄
Step 3: 輸入 baidu.com
Step 4: 按回車鍵
Step 5: 點(diǎn)擊搜索框
Step 6: 輸入搜索詞
Step 7: 按回車執(zhí)行搜索
? 任務(wù)完成!
7 步搞定,全程大概 15 秒(主要是等 API 返回)。
場景 2:文件整理
run_agent("把桌面上所有 .tmp 文件拖到廢紙簍")
這個(gè)任務(wù)模型理解得不錯(cuò),但執(zhí)行拖拽操作時(shí)翻車了——pyautogui 的 drag 在 macOS 上有些時(shí)候不太靈。后來換成 hotkey + delete 的方式才搞定。
場景 3:填表單
run_agent("打開公司的 OA 系統(tǒng),填寫今天的日報(bào),內(nèi)容寫'完成了桌面 Agent 的技術(shù)調(diào)研'")
這個(gè)效果最好,因?yàn)楸韱雾撁娼Y(jié)構(gòu)清晰,模型識別準(zhǔn)確率很高。
踩坑記錄
坑 1:分辨率問題
pyautogui 返回的坐標(biāo)是邏輯像素,但 Retina 屏截圖是物理像素。大模型看到的是 2x 分辨率的圖,返回的坐標(biāo)也是 2x 的。
# 修復(fù):截圖時(shí)縮放到邏輯分辨率
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:模型選擇很關(guān)鍵
試了幾個(gè)模型的表現(xiàn):
| 模型 | 識別準(zhǔn)確率 | 響應(yīng)速度 | 適合場景 |
|---|---|---|---|
| Claude Sonnet 4.6 | ????? | 1.5s | 復(fù)雜頁面 |
| GPT-5.4 | ????? | 2s | 通用場景 |
| Gemini 3 Flash | ???? | 0.8s | 簡單任務(wù)(快!) |
| Qwen-VL-Max | ??? | 1.2s | 中文界面 |
我一般簡單任務(wù)用 Gemini 3 Flash(便宜快),復(fù)雜任務(wù)用 Claude Sonnet。通過聚合 API 切換模型就改一行代碼的事。
坑 3:JSON 解析失敗
大模型偶爾會在 JSON 外面包一層 markdown 代碼塊,加個(gè)容錯(cuò)處理:
import re
def parse_action(text):
"""容錯(cuò)解析 JSON"""
# 去掉可能的 markdown 代碼塊標(biāo)記
text = re.sub(r'```json?\s*', '', text)
text = re.sub(r'```\s*$', '', text)
return json.loads(text.strip())
坑 4:安全防護(hù)必須加
pyautogui 有個(gè) FAILSAFE 機(jī)制——鼠標(biāo)移到屏幕左上角會觸發(fā)異常停止。千萬別關(guān)掉它:
pyautogui.FAILSAFE = True # 默認(rèn)就是 True,別改成 False!
另外建議加個(gè)確認(rèn)機(jī)制,敏感操作前先問一下:
def execute_action(action):
act = action["action"]
# 危險(xiǎn)操作確認(rèn)
if act == "hotkey" and "delete" in str(action.get("keys", [])):
confirm = input(f"?? 即將執(zhí)行刪除操作: {action['reason']},確認(rèn)? (y/n) ")
if confirm != 'y':
return True # 跳過這步
# ... 其余執(zhí)行邏輯
進(jìn)階:加上記憶和多輪對話
基礎(chǔ)版只看當(dāng)前截圖,沒有"記憶"。加上對話歷史后,Agent 會聰明很多:
def ask_model(image_b64, task, history=None):
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
if history:
# 只保留最近 5 輪,避免 token 爆炸
messages.extend(history[-10:])
# ... 其余邏輯不變
還可以加個(gè)"反思"機(jī)制——每次操作后截圖對比,看有沒有變化:
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 比到底差在哪
說實(shí)話,OpenClaw 之所以火是有道理的——它的 Skill 生態(tài)、權(quán)限管理、多 Agent 協(xié)作這些東西,50 行代碼肯定搞不定。
但如果你的需求是:
- 自動(dòng)化一些固定流程(日報(bào)、數(shù)據(jù)抓取、定時(shí)截圖)
- 想深入理解桌面 Agent 原理
- 不想裝一個(gè) 800MB 的客戶端
- 需要自定義模型和 prompt
那自己搭完全夠用。核心就是 pyautogui + Vision API,剩下的都是工程細(xì)節(jié)。
小結(jié)
桌面 Agent 說到底就是個(gè)"截圖-看圖-操作"的循環(huán),原理一點(diǎn)不復(fù)雜。OpenClaw 的價(jià)值在于它把這個(gè)循環(huán)做得很工程化、很穩(wěn)定,但底層邏輯跟我們這 50 行代碼沒本質(zhì)區(qū)別。
我現(xiàn)在日常用這個(gè)腳本跑一些小任務(wù)——定時(shí)截圖監(jiān)控、自動(dòng)填表單、批量文件操作。比起 OpenClaw 的全家桶,我更喜歡這種能完全掌控的方式。
代碼已經(jīng)貼全了,復(fù)制就能跑。如果你也想試試,建議從最簡單的"打開瀏覽器搜索"開始,慢慢加功能。
對了,代碼里 API 調(diào)用用的是兼容 OpenAI 協(xié)議的聚合接口,一個(gè) key 能調(diào)幾十個(gè)模型,切換模型改一行 model 參數(shù)就行,省得每家單獨(dú)注冊。
到此這篇關(guān)于50行Python代碼帶你打造一個(gè)桌面AI Agent的文章就介紹到這了,更多相關(guān)Python AI Agent內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
100?個(gè)?Python?小例子(練習(xí)題四)
這篇文章主要給大家分享100?個(gè)?Python?小例子,前文分享了一二三,本文的四十最后一篇了,這篇就把100道python小練習(xí)全分享完了,感興趣的小伙伴也可以去練習(xí)前幾期內(nèi)容,洗碗給這幾篇文章給你的學(xué)習(xí)帶來幫助2022-01-01
Python多線程批量采集圖片的代碼實(shí)現(xiàn)
這篇文章主要給大家介紹了Python多線程批量采集圖片的代碼實(shí)現(xiàn),文中通過代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-05-05
Python編程itertools模塊處理可迭代集合相關(guān)函數(shù)
本篇博客將為你介紹Python函數(shù)式編程itertools模塊中處理可迭代集合的相關(guān)函數(shù),有需要的朋友可以借鑒參考下,希望可以有所幫助2021-09-09
python解析json內(nèi)容存入數(shù)據(jù)庫方式
這篇文章主要介紹了python解析json內(nèi)容存入數(shù)據(jù)庫方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-05-05
一文教會你用Python實(shí)現(xiàn)pdf轉(zhuǎn)word
python實(shí)現(xiàn)pdf轉(zhuǎn)word,支持中英文轉(zhuǎn)換,轉(zhuǎn)換精度高,可以達(dá)到使用效果,下面這篇文章主要給大家介紹了關(guān)于用Python實(shí)現(xiàn)pdf轉(zhuǎn)word的相關(guān)資料,需要的朋友可以參考下2023-01-01
python3實(shí)現(xiàn)磁盤空間監(jiān)控
這篇文章主要為大家詳細(xì)介紹了python3實(shí)現(xiàn)磁盤空間監(jiān)控,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06

