Hi 这里! 👋

欢迎来到我的博客

影刀使用小记

在平时处理影刀RPA问题的时候,会找到一些好用的技巧,在这篇文章中做个记录,也方便以后自己查找。 网页 获取网页弹窗(JavaScript Alerts)的内容 测试页面(需要翻墙) 类似下图这样的弹窗,这种事 JS 原生的 Alert 弹窗,不再 HTML 标签里,无法选中弹窗的 DOM 对于获取这类弹窗的内容,可以用以下思路解决 向页面注入JS劫持 window.alert 方法,将 msg 保存到全局变量 RPA点击按钮触发弹窗,然后关闭弹窗 向页面注入JS,读取全局变量中的 msg 劫持 windw.alert 的 js function (element, input) { // 保存原始 alert window._originalAlert = window.alert; // 重写 alert,劫持弹窗内容 window.alert = function (msg) { window._lastAlertMsg = msg; // 存到全局变量 console.log("捕获到弹窗:", msg); // 调试打印 window._originalAlert(msg); // 继续弹出原弹窗 }; return null; } 读取页面全局变量的 js function (element, input) { // 读取劫持到的弹窗文本 let msg = window._lastAlertMsg || ""; console.log("最终弹窗内容:", msg); return msg; } 在影刀中的使用如下图 ...

2026年6月2日

在 Mac 上使用 launchtcl 部署服务

1. 前言 最近在公司做了个内部提效的飞书机器人,公司有台公共的mac mini,之前一直是手动打包好代码上传到mac mini上用 uv run python main.py 运行,可以说是非常不优雅了。 以前都是在 Linux 上用 systemctl 部署服务,没在 mac 弄过,查了一下 mac 系统上有个 launchd 用于管理服务,这篇文档记录一下第一次在 mac 上用 launchd 部署服务 2. launchctl 与 systemctl 的区别 功能 systemctl launchctl 主要配置文件 .service .plist 启动服务 systemctl start [name] launchctl bootstrap gui/$(id -u) [path] 停止服务 systemctl stop [name] launchctl bootout gui/$(id -u) [path] 开机自启 systemctl enable [name] 只要 .plist 文件放到特定目录,默认加载 查看状态 systemctl status [name] launchctl list [label] 服务配置文件位置 macOS 使用 Property List (.plist) 文件来定义服务。根据你想让服务以什么权限运行,存放路径也不同: ...

2026年4月9日

使用 OpenClaw + Lume 搭建 MacOS 自动化环境

OpenClaw 最近很火,正好公司有台空闲的 Mac Mini,可以在上面捣鼓一下这个新东西。 1. 安装 Lume Lume 是 MacOS 上的原生虚拟化框架,让 OpenClaw 在虚拟环境下运行不会搞乱我们的系统。 1.1 安装 Lume 方式一:脚本安装 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/lume/scripts/install.sh)" 方式二:Homebrew 安装 brew tap trycua/lume brew install lume 验证安装 使用以下命令验证是否安装成功: lume --version 1.2 下载 MacOS 镜像 lume 安装成功后,需要获取 MacOS 镜像,使用下面指令将镜像下载到 Downloads 文件夹中: curl -L -o ~/Downloads/macos.ipsw "$(lume ipsw 2>/dev/null | tail -1)" 1.3 运行虚拟机 使用下面指令即可运行虚拟机 lume create openclaw --os macos --ipsw ~/Downloads/macos.ipsw 2. 安装 OpenClaw openclaw 中文文档 openclaw 的安装方式在其官方文档中已经很清楚了,我这里只列出几个我安装时遇到的问题 2.1 踩坑记录 2.1.1 对接飞书频道 安装飞书插件 ...

2026年2月3日

AI驱动验证码自动化解决方案

1. 前言 在之前尝试过 AI 驱动的RPA程序完成给定的任务,但是整个运行下来给人的感觉就是太慢了,当时跑一个淘宝搜索产品并获取产品信息的任务跑了快10分钟,总结下来慢的原因无非下面几点。 AI返回内容慢,通过OpenRouter调用gemini模型,一个请求要1-2分钟才能返回结果。 要实现程序 感知-决策-在感知-判断 就要调用多次模型,模型调用次数越多就越慢。 所以,之前AI驱动RPA属于对技术的探索,但没有实际意义, 这次我尝试用AI来处理在自动化中会遇到的一些复杂验证码问题 下面是这次需要解决的验证码的例子,这三个验证码都来自Temu平台。 2. 核心思路 整体思路很简单,把验证码截图给AI,让AI按要求返回坐标,程序解析坐标并点击 整体的代码设计如下图,首先是三个抽象类下面逐个解释一下。 Agent:用于调用AI接口,封装了图片解析、AI结果处理等功能,需要子类实现具体的AI接口 Capability:表示一种能力,就是给AI的提示词。 Handle:使用AI返回的结果处理问题 Agent有3个子类,分别是调用OpenAI规范的接口、调用影刀内置AI接口和调用影刀AIPower接口。 Capability的3个子类分别表示点选验证码处理、拖动验证码处理以及复选框处理 在做软件自动化时很容易遇到复选框无法判断是否勾选,这种情况就可以借助AI Handle有2个子类分表用于,处理点击操作和处理拖动操作 3. 代码实现 ability.py from abc import ABC, abstractmethod class Capability(ABC): """ 表示一种能力, AI能做什么操作 """ @property @abstractmethod def prompt(self): pass class ClickVerifier(Capability): @property def prompt(self): prompt = """ 请严格按照图片中给出的操作要求执行。 图片中已经明确标注了需要点击的目标及点击顺序, 你只需要根据图片内容,依次返回每一步需要点击的位置坐标。 规则: 1. 只返回图片中明确要求点击的内容,不要增加或省略步骤 2. 坐标基于当前输入图片的像素坐标 3. 坐标原点为图片左上角 (0, 0),x 向右,y 向下 4. 每个坐标应尽量位于对应目标的可点击区域中心 5. 如果图片中某一步无法明确定位,请不要猜测,在 summary 中说明 输出要求: - 仅返回 JSON,不要输出任何多余文本 - 不要使用 markdown - JSON 结构必须严格如下: { "positions": [ { "x": xxx, "y": xxx }, { "x": xxx, "y": xxx } ], "summary": "简要说明这些坐标如何对应图片中的点击要求" } """ return prompt class DragVerifier(Capability): @property def prompt(self): prompt = """ 图片中已经明确标注了需要如何拖拽内容 你只需要根据图片内容,依次返回开始拖拽和结束拖拽的位置坐标 规则: 1. 坐标基于当前输入图片的像素坐标 2. 坐标原点为图片左上角 (0, 0),x 向右,y 向下 3. 每个坐标应尽量位于对应目标的可点击区域中心 4. 如果图片中某一步无法明确定位,请不要猜测,在 summary 中说明 输出要求: - 仅返回 JSON,不要输出任何多余文本 - 不要使用 markdown - JSON 结构必须严格如下: { "positions": [ { "start": { "x": xxx, "y": xxx, }, "end": { "x": xxx, "y": xxx, } }, { "start": { "x": xxx, "y": xxx, }, "end": { "x": xxx, "y": xxx, } }, ], "summary": "简要说明理由" } """ return prompt class CheckboxProcess(Capability): @property def prompt(self): prompt = """ 给你一张界面截图,你需要根据任务要求返回需要点击的复选框坐标 如果一个复选框已经被点击则跳过该复选框 规则 1. 坐标基于当前输入图片的像素坐标 2. 坐标原点为图片左上角 (0, 0),x 向右,y 向下 3. 每个坐标应尽量位于对应目标的可点击区域中心 4. 如果图片中某一步无法明确定位,请不要猜测,在 summary 中说明 输出要求: - 仅返回 JSON,不要输出任何多余文本 - 不要使用 markdown - JSON 结构必须严格如下: { "positions": [ { "x": xxx, "y": xxx }, { "x": xxx, "y": xxx } ], "summary": "简要说明这些坐标如何对应图片中的点击要求" } """ return prompt agent.py ...

2026年1月19日

使用Hugo搭建博客

这篇文章也是用来记录我自己搭建博客博客的经历 1. 项目 1.1 实现链路 下面是整个博客的链路,因为我要考虑服务器迁移的问题,为了方便迁移所以会使用容器化方案。 最终实现我写好一篇文章,在本地使用 git push命令即可实现服务器自动更新内容。 1.2 文件结构 hugo-blog/ ├── blog/ │ ├── source/ │ ├── repo.git/ └── docker-compose.yaml source:存放hugo原始数据以及hugo编译后的public文件夹 repo.git:Git仓库 2. 博客搭建 2.1 创建Git仓库 在服务器上创建Git裸仓库,用于接收推送的代码 # 创建 git 文件夹 mkdir -p blog/repo.git cd blog/repo.git # 初始化裸仓库 git init --bare 配置自动化钩子,当本地终端通过git push推送文章时,服务器自动触发这个脚本 vim hooks/post-receive #!/bin/bash # 定义路径 GIT_DIR=$HOME/docker/hugo-blog/blog/repo.git SRC_DIR=$HOME/docker/hugo-blog/blog/source # 1. 检出源码到临时目录 git --work-tree=$SRC_DIR --git-dir=$GIT_DIR checkout -f main echo "迁出分支成功" # 2. 使用 Docker 运行 Hugo 构建 # --rm 表示构建完后自动删除容器 docker run --rm \ -u $(id -u):$(id -g) \ -v $SRC_DIR:/src \ hugomods/hugo:latest build echo "Hugo构建成功" 给脚本执行权限chmod +x hooks/post-receive ...

2026年1月13日

RPA Agent 探索

这次尝试的宗旨是 AI可替换,不和特定AI绑定 不考虑Token消耗 设计出通用的 AI 驱动框架,并不局限于 RPA 这个方面 1. 浅尝 最开始的想法是实现两个Agent分别是·、任务规划Agent和任务执行Agent 任务规划Agent根据用户需求列出步骤清单,任务执行Agent负责循环步骤清单执行步骤 1.1 任务规划Agent 我在实现完任务规划Agent后就发现,任务规划和执行分离不是一个好设计,规划的任务准确性会越来越低,所以这个方案我就没有继续实现执行Agent rask_planner_agent.py import base64 from langchain_deepseek import ChatDeepSeek from langchain_openai import ChatOpenAI from langchain.agents import create_agent from .output_schema import TaskSchema class TaskPlannerAgent: def __init__(self): self.llm = ChatOpenAI( openai_api_key="sk-or-v1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, openai_api_base="https://openrouter.ai/api/v1", model_name="google/gemini-3-flash-preview" ) # System prompt self.system_prompt = """ 你是一个专业的任务规划助手。用户将提供任务描述或图片,你需要结合这些信息将其拆解为结构化步骤。 """ self.agent = create_agent( model=self.llm, system_prompt=self.system_prompt, response_format=TaskSchema ) def plan(self, instruction: str, image_path: str = None) -> TaskSchema: content = [{"type": "text", "text": instruction}] if image_path: image_data = self._encode_image(image_path) content.append({ "type": "image_url", "image_url": { "url": f"data:image/jpeg;base64,{image_data}" } }) result = self.agent.invoke( {"messages": [{"role": "user", "content": content}]} ) return result["structured_response"] def _encode_image(self, image_path: str): """将本地图片转为 base64编码""" with open(image_path, 'rb') as image_file: return base64.b64encode(image_file.read()).decode('utf-8') def main(args): agent = TaskPlannerAgent() for step in agent.plan("搜索熊猫,并进入搜索结果的第一项内容", r"C:\Users\Levit\Desktop\1.jpg").steps: print(step) output_schema.py 规范Agent输出格式 ...

2025年12月23日