当前位置:首页>python>【有趣的python项目】OpenClaw没那么神,30行代码开发出自己的智能体.

【有趣的python项目】OpenClaw没那么神,30行代码开发出自己的智能体.

  • 2026-09-10 11:17:03
【有趣的python项目】OpenClaw没那么神,30行代码开发出自己的智能体.
最近智能体OpenClaw(龙虾)爆火,甚至出现600块帮部署的奇闻。首先声明,峰哥并没有部署OpenClaw,一是这玩意的部署即便对于程序员来说也很复杂;二是作为python程序员,想必大家曾都被各种自动化任务逼疯过,办公自动化(openpyxl、docx、pdfplumber)、键鼠自动化(pyautogui)、windows自动化(pywin32)、浏览器自动化(selenium)、图像处理自动化(pillow、opencv)……随便一个定向解决具体问题的小任务,都要花上数十小时时间,解决bug到深夜,才能勉强稳定运行,所以对于张口就说全盘掌控操作系统自动化完成所有任务的项目,我是打死不信的,其稳定性和正确率能达到什么程度,部署门槛有多高,要配置多少API,有多少BUG要应付,实在不敢想象。而且对于商业软件的自动化依赖开放接口,人家抵制都来不及,怎么可能开放接口给你用?例如,曾经有一个团队破解微信实现了微信收发消息的自动化,并开发出商用的微信专用管理工具,不久后腾讯就打官司赢了该团队,并且对其执行了一大笔赔偿。
虽然我没有部署,但是我也不是毫无根据地去评论它,写这篇文章之前,我也去知乎、B站、小红书阅读(观看)了很多高赞的测评,基本都印证了我的推测。
我想说的是其实智能体没有大家想象的那么神秘,底层原理也很简单,但是声称全能就有点不厚道了,既然大家在学习python了,也算半个程序员,不要被某些营销号忽悠瘸了,与其盲目追捧不如洞悉其原理,甚至自己动手开发出一些个简单的智能体。
所谓智能体,说来说去无非就是提交用户的需求到大模型,再结构化大模型返回的结果,然后形成具体指令去对接操作系统、办公软件、少量商业软件、浏览器、键鼠的某些API,实现一些自动化的操作。自动化对于python来说是传统强项,只不过大模型出来之后让指令的输入变得更加自由。
比如上一篇提到的自动发送邮件,也是智能体所标榜的功能之一,其实python完成也就20代码。如果包装成智能体,则只需要选取一个大模型API(比如deepseek),通过自然语言提交发送邮件的需求(例如,今晚8:00的时候给张二毛发送一封Email,请他周末来我家喝酒吃饭,顺便把xxx文件作为附件发给他。),然后让大模型返回规定的数据结构(比如JSON:{'email':xxx@163.com,'name':'张二毛','content':'张二毛,请你周日中午12:00到我家吃饭,我家地址xxxx','file':'xx/xxx/xxxx.pdf'}),如此一来拿给python解析就很方便。
这里以deepseek为例介绍如何对接大模型API:
提示词:我会提交给你一段自然语言,请你从中提取我要发送的邮件地址、要发给的人、发送的时间、发送的内容、还有可能提到发送附件,你把这些信息提取,并格式化成这样的json:{str(example)}返回给我。
from openai import OpenAIkey = 'sk-6d4ad9f4cc1b49fdbfa07da395b……'#需要去ds官方平台注册并申请。promot="今晚8:00的时候给张二毛发送一封Email,请他周末来我家喝酒吃饭,顺便把xxx文件作为附件发给他。"client = OpenAI(api_key=key, base_url="https://api.deepseek.com")example={'email':xxx@163.com,'name':'张二毛','content':'张二毛,请你周日中午12:00到我家吃饭,我家地址xxxx','file':'xx/xxx/xxxx.pdf'}user_prompt = f"我会提交给你一段自然语言,请你从中提取我要发送的邮件地址、要发给的人、发送的时间、发送的内容、还有可能提到发送附件,你把这些信息提取,并格式化成这样的json:{str(example)}返回给我。我提交的自然语言如下:{promot}}"response = client.chat.completions.create(                model="deepseek-chat",                messages=[{"role": "user", "content": user_prompt}],                response_format={"type": "json_object"})result = json.loads(response.choices[0].message.content)
上面这段代码是根据deepseek官方的json output示例修改的(什么是json output?在很多场景下,用户需要让模型严格按照 JSON 格式来输出,以实现输出的结构化,便于后续逻辑进行解析。DeepSeek 提供了 JSON Output 功能,来确保模型输出合法的 JSON 字符串。),result返回的json数据便包含了发送邮件的信息,后续如何发送邮件上一篇文章已经说清楚了,大家照着编写即可。
大家看到了,前后几十行代码就实现了一个发送邮件的极简智能体,高大上的智能体概念是不是又具象化了。结合大模型结构化输出,大家可以打开脑洞,集成一些其他智能体操作。
我首先想到的是丰富的win32 api,可以较为全面地控制windows系统:
# 基础系统操作 (win32api)import win32apiwin32api.MessageBox(0, "Hello", "Title", 1)     # 显示消息框win32api.GetSystemTime()                        # 获取当前系统时间win32api.GetCursorPos()                         # 获取鼠标当前位置win32api.SetCursorPos((100, 200))               # 设置鼠标位置win32api.GetComputerName()                      # 获取计算机名称win32api.GetUserName()                          # 获取当前用户名win32api.GetLastError()                         # 获取最后一个错误码win32api.Beep(1000, 500)                        # 发出蜂鸣声(频率,持续时间)# 窗口与界面管理 (win32gui)import win32guiwin32gui.FindWindow(None, "无标题 - 记事本")     # 通过标题查找窗口句柄win32gui.FindWindow("Notepad", None)           # 通过类名查找窗口句柄win32gui.GetWindowText(123456)                 # 获取指定窗口标题win32gui.SetWindowText(123456, "新标题")        # 设置指定窗口标题win32gui.ShowWindow(123456, 0)                 # 隐藏窗口(0隐藏,1显示,2最小化,3最大化)win32gui.EnumWindows(lambda hwnd, param: print(win32gui.GetWindowText(hwnd)), None)  # 枚举所有窗口win32gui.GetForegroundWindow()             # 获取当前激活窗口句柄win32gui.SetForegroundWindow(123456)      # 将窗口置前win32gui.MoveWindow(123456, 0, 0, 800, 600, True)   # 移动并调整窗口大小(x,y,宽,高,是否重绘)win32gui.IsWindowVisible(123456)      # 判断窗口是否可见win32gui.CloseWindow(123456)     # 关闭窗口win32gui.DestroyWindow(123456)   # 销毁窗口win32gui.GetWindowRect(123456)   # 获取窗口矩形区域(左,上,右,下)win32gui.GetClientRect(123456)   # 获取窗口客户区矩形win32gui.ScreenToClient(123456, (100, 200))# 屏幕坐标转客户区坐标win32gui.ClientToScreen(123456, (100, 200))# 客户区坐标转屏幕坐标win32gui.EnableWindow(123456, False) # 启用/禁用窗口(False禁用)win32gui.SetActiveWindow(123456) # 激活窗口# 文件与磁盘操作 (win32file)import win32filehandle = win32file.CreateFile("test.txt", 0x80000000, 0, None, 3, 0, None)  # 创建/打开文件win32file.ReadFile(handle, 1024)   # 读取文件(读取1024字节)win32file.WriteFile(handle, b"Hello World")  # 写入文件win32file.CloseHandle(handle)      # 关闭文件句柄win32file.GetFileSize(handle)    # 获取文件大小win32file.GetFileAttributes("test.txt")  # 获取文件属性win32file.SetFileAttributes("test.txt", 0x00000002) # 设置文件属性(2=隐藏)win32file.CopyFile("src.txt", "dst.txt", True) # 复制文件(True=如果目标存在则失败)win32file.MoveFile("old.txt", "new.txt") # 移动文件win32file.DeleteFile("test.txt") # 删除文件win32file.CreateDirectory("new_folder", None)  # 创建目录win32file.RemoveDirectory("new_folder")  # 删除目录win32file.GetLogicalDrives()  # 获取所有逻辑驱动器win32file.GetDiskFreeSpace("C:\\") # 获取磁盘剩余空间# 进程管理 (win32process)import win32processhandle, thread_id, pid, tid = win32process.CreateProcess(None, "notepad.exe", None, None, False, 0, None, None, 0)  # 创建进程pid_list = win32process.EnumProcesses()   # 枚举所有进程IDwin32process.TerminateProcess(handle, 0)  # 终止进程win32process.GetProcessTimes(handle)     # 获取进程时间信息win32process.GetCurrentProcessId()     # 获取当前进程IDwin32process.GetCurrentThreadId()    # 获取当前线程IDwin32process.SuspendThread(thread_id)  # 挂起线程win32process.ResumeThread(thread_id)    # 恢复线程win32process.GetExitCodeProcess(handle)    # 获取进程退出代码win32process.WaitForSingleObject(handle, 10000)  # 等待进程结束(10秒超时)# 注册表操作 (win32registry)import win32registrykey = win32registry.OpenKey(win32registry.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, 131097)  # 打开注册表键win32registry.QueryValueEx(key, "ProgramName")   # 查询注册表值win32registry.SetValueEx(key, "ProgramName", 0, 1, "C:\\test.exe")  # 设置注册表值(1=字符串类型)win32registry.EnumKey(key, 0)     # 枚举子键(索引0)win32registry.EnumValue(key, 0)     # 枚举值(索引0)win32registry.DeleteKey(key, "SubKey") # 删除子键win32registry.DeleteValue(key, "ValueName") # 删除值win32registry.CreateKey(win32registry.HKEY_CURRENT_USER, "Software\\MyApp")  # 创建键win32registry.CloseKey(key)   # 关闭注册表键# 剪贴板操作 (win32clipboard)import win32clipboardwin32clipboard.OpenClipboard()   # 打开剪贴板win32clipboard.EmptyClipboard()   # 清空剪贴板win32clipboard.SetClipboardText("Hello Clipboard")   # 设置剪贴板文本text = win32clipboard.GetClipboardText()  # 获取剪贴板文本win32clipboard.GetClipboardData(13) # 获取剪贴板数据(13=文本格式,1=位图格式)win32clipboard.SetClipboardData(13, "Data")  # 设置剪贴板数据win32clipboard.CloseClipboard()  # 关闭剪贴板win32clipboard.IsClipboardFormatAvailable(13) # 检查剪贴板是否有指定格式数据# 打印机操作 (win32print)import win32printwin32print.GetDefaultPrinter()   # 获取默认打印机名称win32print.EnumPrinters(2)     # 枚举所有打印机win32print.OpenPrinter("Printer Name")   # 打开打印机win32print.ClosePrinter(handle)  # 关闭打印机win32print.GetPrinter(handle) # 获取打印机信息
那么我是不是可以设计一个文件分类的智能体:当我电脑散落大量文档时,我可以向大模型提交工作文件分类的诉求:“我传给你一个我工作文件夹(含多级文件夹)的目录,是我对工作的分类。另外我再传给你我需要分类的文件列表,现在请你对它们进行重新分类,尽量在我提供的分类中进行分类,实在无法在我提供的分类中分类的文件,可以新建合理的分类。你重新分类的结果请返回给我json数据……”,除了提交文件名,还可以让大模型读取文档内容(注意保密问题),让大模型返回每个文档对应的分类,然后调用win32接口去移动这些文档或者创建文件夹。除此之外执行打印、操作窗口、修改注册表等操作也是可以的。
除此之外,还有强大的浏览器自动化(selenium):
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keysfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECimport time# 创建浏览器对象(以Chrome为例)driver = webdriver.Chrome()# ---------- 浏览器基本操作 ----------# 打开网页driver.get("https://www.baidu.com")  # 阻塞直到页面加载完成# 获取当前URLcurrent_url = driver.current_urlprint(f"当前URL: {current_url}")# 获取页面标题title = driver.titleprint(f"页面标题: {title}")# 浏览器窗口最大化driver.maximize_window()# 设置窗口大小driver.set_window_size(1024, 768)# 浏览器前进/后退driver.back()  # 后退time.sleep(1)driver.forward()  # 前进# 刷新页面driver.refresh()# ---------- 窗口和标签页管理 ----------# 打开新标签页driver.execute_script("window.open('');")# 切换到新标签页driver.switch_to.window(driver.window_handles[1])driver.get("https://www.google.com")# 获取所有窗口句柄handles = driver.window_handlesprint(f"窗口句柄: {handles}")# 切换到指定窗口driver.switch_to.window(handles[0])  # 切换回第一个标签页# 关闭当前标签页driver.close()# ---------- 截图 ----------# 截取当前页面driver.get_screenshot_as_file("screenshot.png")# 或者driver.save_screenshot("homepage.png")# 获取页面源码page_source = driver.page_source# 执行JavaScriptdriver.execute_script("window.scrollTo(0, document.body.scrollHeight);")  # 滚动到底部driver.execute_script("alert('Hello Selenium');")# 关闭浏览器(关闭所有窗口)driver.quit()
# 先定位一个搜索框元素search_box = driver.find_element(By.ID, "kw")# ---------- 基本交互 ----------# 输入文本search_box.send_keys("Selenium教程")# 清空输入框search_box.clear()# 重新输入并提交search_box.send_keys("Python自动化")search_box.submit()  # 提交表单# 点击元素search_button = driver.find_element(By.ID, "su")search_button.click()# ---------- 获取元素信息 ----------# 获取元素的文本内容text = search_button.textprint(f"按钮文本: {text}")# 获取元素的属性value = search_box.get_attribute("value")  # 获取输入框的值class_name = search_box.get_attribute("class")print(f"输入框的值: {value}")# 检查元素是否可见is_displayed = search_box.is_displayed()print(f"是否可见: {is_displayed}")# 检查元素是否可用is_enabled = search_box.is_enabled()print(f"是否可用: {is_enabled}")# 检查元素是否被选中(用于复选框、单选框)checkbox = driver.find_element(By.CSS_SELECTOR, "input[type='checkbox']")is_selected = checkbox.is_selected()print(f"是否选中: {is_selected}")# 获取元素的CSS属性font_size = search_button.value_of_css_property("font-size")color = search_button.value_of_css_property("color")print(f"字体大小: {font_size}, 颜色: {color}")# ---------- 键盘操作 ----------search_box.send_keys("Selenium")search_box.send_keys(Keys.CONTROL, 'a')  # 全选search_box.send_keys(Keys.CONTROL, 'c')  # 复制search_box.send_keys(Keys.CONTROL, 'v')  # 粘贴search_box.send_keys(Keys.ENTER)  # 回车# ---------- 鼠标操作 ----------from selenium.webdriver.common.action_chains import ActionChains# 创建动作链actions = ActionChains(driver)# 鼠标悬停menu = driver.find_element(By.LINK_TEXT, "设置")actions.move_to_element(menu).perform()# 右键点击actions.context_click(search_box).perform()# 双击actions.double_click(search_box).perform()# 拖拽source = driver.find_element(By.ID, "draggable")target = driver.find_element(By.ID, "droppable")actions.drag_and_drop(source, target).perform()# 按住并移动actions.click_and_hold(source).move_to_element(target).release().perform()
我们是不是可以向大模型提交一个链接,让它解析出html元素,从而直接使用selenium自动化操作网页呢。还没试过,大家可以尝试。

最新文章

随机文章