用LLM+浏览器自动化打造GitHub智能代理:Clawless实战解析

发布时间:2026/10/20 5:06:22

用LLM+浏览器自动化打造GitHub智能代理:Clawless实战解析 1. 项目背景与需求拆解1.1 为什么我给 GitHub 操作装了一个“智能代理脑”做开发这些年GitHub 上的日常操作其实非常规整看 issue、关 issue、拉分支、提交 PR、检查 CI 状态、回复评论。这些操作本身不难难的是“一天要重复几十次”。尤其是维护几个活跃仓库的时候很多时间都耗在点按钮、复制链接、粘贴模板上面。我一直在想要不要写一套自动化脚本可是脚本这东西有个毛病——写死了。今天想按仓库名搜 issue明天想按标签过滤后天可能又要导出某段时间所有 PR 的评论。每换一个需求就要改代码维护成本比手动操作还高。后来我意识到真正需要的不是一套固定流程而是一个“听得懂人话”的中间层。用户用自然语言说一句话比如“把 A 仓库最近两天没人回复的 issue 列出来顺便生成一份摘要”系统能理解这个意图自己决定要打开哪些页面、点哪些按钮、读取哪些数据最后把结果汇总成报告。这就是 Clawless 的核心定位——它不是又一个 GitHub API 封装库而是一个基于 LLM 与浏览器自动化的智能代理。1.2 为什么不直接用 GitHub API 搞定一切很多人的第一反应可能是GitHub 官方 API 那么全为什么还要让浏览器自动化去点页面这里有几个实际原因。第一个原因是“未登录态和权限边界”。GitHub API 确实很完善但在某些场景下比如你想看某个组织私有仓库的报表或者模拟用户在网页端的操作API 的权限模型和网页端并不完全一致。用浏览器自动化相当于“所见即所得”你能在页面上做到什么代理就能做什么不需要额外申请一堆 token。第二个原因是“页面本身就是最高效的信息容器”。很多数据在 API 里要多次调用才能拼出来但在网页端一次渲染就全部展示了。比如某个 issue 的整个讨论串包括相关人员、标签、里程碑、引用关系API 要拆分好几个端点才能拉全而浏览器直接抓取渲染后的 DOM 就能一次性拿到整块内容。第三个原因更实际——我们想让 LLM 的输出直接作用于真实界面。LLM 擅长理解复杂的上下文和模糊指令浏览器自动化擅长执行确定性的 UI 操作。两者结合LLM 负责“思考下一步做什么”浏览器自动化负责“把思考变成点击动作”。如果只走 API那 LLM 的很多优势体现不出来因为 API 调用本身是结构化的少了一个“让模型自己决定怎么查”的中间层。这不是说 API 没用。Clawless 里实际上也用了 GitHub API用于获取稳定的结构化数据比如仓库元信息、commit 状态。而浏览器自动化主要处理那些需要“看懂页面”的场景。两套手段互补才是这个项目的核心思路。2. 项目整体设计与架构解析2.1 模块划分LLM、浏览器控制、任务队列三件套Clawless 的整体架构并不复杂核心可以拆成三个模块意图理解模块、浏览器自动化执行模块、结果整理与反馈模块。意图理解模块负责接收自然语言指令通过大模型将指令解析成一系列可执行的操作步骤浏览器自动化执行模块负责驱动真实浏览器环境在 GitHub 页面上完成这些步骤结果整理模块负责把中间抓取到的页面内容喂回给大模型生成最终的用户可读汇报。这三个模块并非简单的流水线而是一个带反馈的循环。比如用户说“看一下这个 PR 有没有冲突”代理会先打开 PR 页面观察是否有“This branch has conflicts”的提示如果有再进一步查看冲突涉及哪些文件。这个过程需要多次“观察-决策-行动”循环而不是一次性把所有步骤规划完。我把这种执行方式称为“半自主循环”。为什么是半自主因为在大模型给出操作序列之前我会先定义一组可用的原语操作比如 open_repo、click_issue、extract_text、scroll_page 等等。大模型不能自由发挥去执行任意 JavaScript只能在原语集合里面选。这样既保留了灵活性又确保了安全性。毕竟浏览器自动化一旦失控可比 API 调用失控危险多了。2.2 关键技术选型Playwright 与 LLM 的配合浏览器自动化这一块我选择的是 Playwright。原因很直接它对现代浏览器的控制能力足够强支持页面快照、网络拦截、iframe 处理、文件下载而且自带多浏览器支持。相比老牌的自动化工具Playwright 的等待策略和对单页应用的友好程度要高很多。GitHub 的页面是典型的动态渲染大量的内容是异步加载的用传统工具很容易踩“元素找不到”的坑Playwright 的自动等待机制能省下不少事。LLM 部分我采用的是本地运行的开源量化模型加云端 API 混合策略。为什么会有这种混合因为意图识别对模型能力要求比较高尤其是理解“把 A 仓库最近两天没人回复的 issue 列出来”这种带有时间窗口、过滤条件和输出格式约束的句子小模型容易翻车。所以在关键决策节点我会调用能力更强的云端模型而在一些简单的文本分类、字段提取环节用本地小模型就足够了速度更快也省成本。整个系统通过一个事件总线来通信。LLM 输出的决策被包装成结构化指令浏览器自动化模块执行完以后把观察到的页面摘要发回到总线上再由 LLM 决定下一步动作。这有点像一个带眼睛和手的 Agent眼睛是 DOM 快照手是自动化操作。2.3 架构上的几个取舍与避坑思路第一版设计时我曾想过完全让 LLM 写 JavaScript 代码来操作页面后来发现不可行。原因是生成的代码错误率太高而且页面一旦改版生成的代码就失效了。改成“原语函数 参数化调用”之后模型只需要输出函数名和参数可靠性提升非常明显。另一个取舍是同步执行还是异步执行。早期的想法是每次指令都立刻执行但实际用下来发现很多任务执行时间较长比如批量导出几十个 issue中间还可能遇到页面加载慢、验证码等问题。现在我改成了任务队列模式用户提交指令后任务先排队代理逐个执行并通过日志实时推送进度。这样即使某一步卡住了也不会阻塞整个会话。架构里还有一个不可忽略的模块安全性控制。默认情况下所有读操作查看 issue、搜索代码、读取评论都是允许的但写操作创建 issue、关闭 PR、合并分支必须经过二次确认。这个二次确认不是简单的弹窗而是让 LLM 先把“即将执行的动作”翻译成自然语言描述用户确认后才执行。从实际体验看这个设计阻止了大量误操作。3. 核心实现细节与关键步骤3.1 环境搭建与依赖准备Clawless 使用 Python 作为主语言原因在于 LLM 生态和数据处理库都集中在 Python 上和 Playwright 的 Python 版本配合也没什么障碍。整个项目环境建议使用 Python 3.10 以上版本因为后面要用的某些类型注解特性在旧版本上解析会出问题。pip install playwright playwright install这两条命令做完后再用自带工具安装浏览器内核。如果是在服务器上跑还要额外装系统依赖。这一步最容易踩坑的是 Linux 环境缺库会导致浏览器启动时崩溃。解决办法是执行 Playwright 官方提供的系统依赖安装脚本或者根据报错补装对应的共享库。除了 Playwright还需要安装大模型相关的库。如果使用云端 API直接装官方 SDK 即可如果要在本地跑开源模型需要额外部署推理服务。我在本地测试时用的是量化到 4bit 的小参数模型占用的显存大约 6GB日常任务足够用但遇到稍微复杂的指令就会有点吃力。后来把核心推理切换到云端模型本地模型只用来做字段抽取整个系统的稳定性就上来了。环境配置中最容易忽略的是“并发控制”。浏览器自动化是重资源操作同时开太多页面容易把内存吃满。我在代码里加入了一个简单的信号量保证同一时间最多只有三个浏览器上下文在运行。如果任务队列里同时进来十个任务多出来的会排队等待。3.2 让 LLM 将自然语言指令转换成可执行计划这一节是整个项目的灵魂也是最难做好的部分。大模型本身并不会用浏览器我们需要教它“以什么样的格式输出决策”。我的做法是定义一套严格的工具调用协议。LLM 每次输出都会包含一个 JSON 对象字段说明如下{ thought: 简要说明当前观察到的情况和决策理由, action: 要调用的原语函数名, parameters: { arg1: value1 } }这个输出会被解析后交由执行器处理。为了确保模型不会输出格式之外的东西我在 prompt 里放了一个成功案例和两个失败案例。少样本提示的效果比单纯说“请以 JSON 格式输出”要好得多因为模型天生擅长模仿模式。任务拆分是另一个关键点。用户一句“帮我看看这个仓库的 health 情况”我们不可能靠一次浏览器动作拿到所有信息。我们会先把任务拆成几个子步骤打开仓库主页、读取 README、查看最近 issue 和 PR 数量、检查 CI 状态最后汇总。拆分子任务既可以在模型内部完成也可以通过一个独立的“规划器”来做。我选择让规划器调用一个固定的工具函数来生成步骤列表这个列表会被展示给用户确认用户只确认“大方向对”就行细节由代理自行调整。实际跑下来会发现LLM 在规划阶段容易出现“过度设计”。比如用户只想看一个 issue模型偏要打开仓库首页再点进去。这里可以用一个简单策略prompt 里明确要求“以最少步骤完成目标”。有了这条约束模型的规划会明显精简。3.3 浏览器自动化操作 GitHub 页面的实现细节Playwright 操作 GitHub 页面的语法并不复杂难的是等待条件。GitHub 很多元素是异步渲染的加载时间也不固定如果直接用 click 操作经常会碰到元素还未挂载的情况。我封装了一个 wait_for_selector 的函数并且结合文本匹配来定位元素。下面是一个典型的打开 issue 页并抓标题的实现片段async def open_issue(page, issue_number): await page.goto(fhttps://github.com/some/repo/issues/{issue_number}) await page.wait_for_selector(h1.gh-header-title, timeout10000) title await page.text_content(h1.gh-header-title) comments await page.query_selector_all(.timeline-comment) return {title: title.strip(), comment_count: len(comments)}这段代码用h1.gh-header-title定位标题用类名.timeline-comment定位评论块。这里有一个细节GitHub 的类名偶尔会调整所以不能写死一个选择器。更稳妥的做法是同时启用多个备选选择器例如先按类名找找不到再按 aria-label 找。浏览器自动化的核心思想是“不是把某个按钮点出来而是把事情办成”。对于需要登录的场景我更推荐直接注入 cookie 而不是每次都走登录流程。把 GitHub 的登录态 cookie 存到本地启动浏览器时通过context.add_cookies()添加进去这样页面会直接识别为已登录状态避免每次执行任务前都要手动扫码或者输密码。当然cookie 有有效期过期后会自动跳转到登录页。针对这种情况我在执行循环里加入了一个“登录检测”步骤如果页面出现登录表单就暂停任务通过邮件或者钉钉通知人工介入。3.4 代理策略让 LLM 决策与页面状态保持同步很多人做 LLM 加自动化的 demo 时会让 LLM 生成“一串”操作然后一次性执行。这在简单固定流程中没问题但在真实页面上会很脆弱。GitHub 的页面状态经常跟随用户操作发生变化比如点击一个标签后可能展开新的导航菜单滚动加载后才会出现更多评论。如果 LLM 的决策不是基于“当前页面”的状态那所有后续操作都会建立在错误的前提上。Clawless 采用的方法是“一次只走一步”。每一步开始前先从当前页面提取一个精简的状态摘要包括页面标题、当前 URL、可见的主要按钮文字、issue 或 PR 的标题列表。这个摘要通过一个上下文压缩函数处理去掉大量无关的导航和 DOM 噪音保留决策所需的信号。然后把这个摘要发给 LLM让它基于摘要输出下一次动作。这里有个值得分享的经验摘要不是越长越好。一开始我试图把整个页面的 body 文本都塞给模型结果不仅 token 消耗大模型还容易被 sidebar 里的无关链接带偏。后来我改成只提取“可交互元素”和“目标内容区域”的文本。比如在 issue 列表页只提取每个 issue 的标题、编号、标签和更新时间在 issue 详情页只提取正文、评论者、状态、时间线核心事件。这样模型的注意力集中准确率也高了。如果 LLM 输出的动作与页面实际可执行的操作不匹配比如点击了一个不存在的按钮执行器会返回一个“操作失败”标志并附上失败原因。这个原因会回传给 LLM让它重新判断。这种带反馈的循环是保证可靠性的关键也是 Clawless 和普通脚本自动化最大的不同。4. 常见问题与排查技巧实录4.1 页面元素定位失败频繁怎么处理用浏览器自动化做 GitHub 操作遇到最多的报错就是超时和元素找不见。GitHub 页面之所以难定位是因为其 DOM 结构在不同功能模块间非常不一致而且经常通过 A/B 测试调整样式。我刚开始写抓取脚本时经常今天能跑通的代码明天就挂了。排查思路第一是看选择器是否太严格。比如我一度用button[data-testidnew-issue-button]来定位新建 issue 按钮但这个属性只在部分页面存在。后来改成文本匹配优先、属性匹配兜底的策略先用get_by_text(New issue)实在不行再查button元素里的文本。Playwright 自带的get_by_role和get_by_text比裸query_selector要稳很多因为是语义化定位。第二是处理懒加载。GitHub 的评论和通知是分段加载的滚动到页面底部才会加载新内容。对于“导出所有评论”这种需求不能只抓一次就完事而应该循环执行滚动直到页面高度不再变化然后判断是否到底。我封装了一个scroll_until_end的函数每次滚动后等待 500 毫秒再比较新的滚动高度与旧值是否相同如果相同就说明已经到达底部。第三种常见情况是页面弹窗和悬浮层遮挡。GitHub 的导航栏有固定定位有时会挡住左侧的元素导致 Playwright 点击时提示元素不可见。解决办法是点击前先执行element.scroll_into_view_if_needed()然后再点击。这个操作大部分时间有效如果还是失败就换成 JavaScript 直接触发点击事件page.evaluate((el) el.click(), element)。4.2 LLM 指令理解偏差和优化技巧Clawless 使用 LLM 的最大痛点不在于模型能力而在于“提示词的表达能力”。有时候用户说“最近没人管的 issue”是什么意思没人回复超过 48 小时没有任何 label还是 assignee 为空这种语义歧义如果放到真实场景中会导致代理的理解和用户预期相悖。我的解决方式是定义“领域术语扩展表”。在把用户指令发给模型之前先经过一个预处理器把常见的模糊说法映射为具体的筛选逻辑。比如“没人管”统一映射为“没有 assignee 且最近 7 天没有评论”“没动静”映射为“30 天内没有活动”。这个映射规则可以放在配置文件里用户也能自己调整。预处理器本质上是一个文本分类模型用本地小模型就可以跑。另一个技巧是让 LLM 输出时附带上“假设”。例如它计划点击某个按钮前先描述“我准备点击‘Close issue’按钮因为该 issue 的用户已确认问题解决”。如果这个假设和实际页面不符执行器可以拦截并纠正。虽然多了一步验证但安全性提升很大尤其是涉及写操作的场景。如果发现 LLM 反复生成无效的重复动作比如不断尝试点击同一个已失效的按钮我会在 prompt 中加入“历史动作列表”让模型知道哪些操作已经尝试过且失败了。这相当于给模型一个短期记忆避免原地打转。循环超过三次之后代理会主动停止并询问用户是否继续防止死循环消耗资源。4.3 浏览器环境下的身份验证与安全边界GitHub 有时候会要求二次验证比如输入验证码或者确认设备。这类验证无法通过自动化代码直接绕过。我的做法是在执行流程中插入“人工辅助点”当检测到需要验证码时代理发消息给控制端附上当前页面的截图控制端人员查看截图后手动输入验证码代理继续执行后续任务。这个流程听起来笨重但实际用下来效率很高因为验证码不是每次都有。需要设置超时时间比如 5 分钟没有人工响应就放弃该任务进入队列尾并标记为需要人工处理。安全边界方面我在执行器中单独维护了一个“受限操作列表”。凡是在列表内的操作比如 force push、删除分支、修改仓库设置一律拒绝执行。即使 LLM 明确输出了对应动作执行器也会返回“operation not allowed”。这个保护机制不能写在 prompt 里因为 prompt 可以被绕过必须写在代码层作为绝对的规则。最后还要提一下 cookie 泄露的问题。所有自动化相关的凭证文件都不能提交到公开仓库里建议使用环境变量或者专用配置目录并且设置.gitignore。我在某个模拟项目里就见过有人把 cookie 文件传上去的还好及时发现不然账号就危险了。5. 实际运行效果与经验总结Clawless 完整跑通后我把几个高频场景放进去做了测试自动整理每周 issue 周报、批量筛选无人认领的 bug、生成 PR 变更摘要并发送到聊天工具。效果比预期好不少尤其是在“生成摘要”这个环节LLM 可以把页面上的零散信息整理成带逻辑顺序的短文省掉大量人工整理时间。但也有几个地方没有想象中完美。首先是执行速度。浏览器自动化比 API 调用慢得多一个简单的任务通常需要 10 到 30 秒复杂的甚至要几分钟。如果用户想要非常快速的反馈这套方案就不太合适。所以后来我在架构里加了一个“快速任务”分流——需要秒级响应的操作直接调 API只有理解成本高的任务才走 LLM 加浏览器这条路径。另一个是 token 开销。每次抓取页面摘要后发给 LLM长途请求的 token 消耗不小。为了控制成本我设置了一个页面摘要的压缩比例很多无关文本会被截断。如果摘要超过 4000 字符就只保留前 1500 字符和最后 800 字符因为关键信息通常集中在开头和结尾。我在实际使用中最深的一个体会是这类项目真正难的不是模型怎么选也不是自动化框架怎么用而是“信任边界”怎么设计。LLM 让系统看起来有了智慧但智慧的另一面是难以预测。只有把决策的范围限制在明确的原语函数集里把高风险操作挡在代码层才敢放心让代理去操作真实仓库。Clawless 走到今天最值钱的不是那几段代码而是这套边界设计。如果你也想做类似的东西我建议从一个非常窄的场景切入比如“自动整理某个 issue 列表并生成报告”不要一上来就想着覆盖所有 GitHub 操作。先把一条链路跑顺再扩展其他能力。同时建议尽早接入日志系统把所有 LLM 决策和浏览器动作记录下来这样出了问题可以回溯也方便调优提示词。给代理加一条“日志就是安全绳”的原则能省掉很多后期排查的麻烦。
延伸阅读

更多相关文章

2026/10/20 5:06:22

TensorFlow实战宝可梦图像分类:从数据清洗到模型部署全流程

1. 项目缘起与整体设计思路宝可梦图像分类这个题目,乍一看像是玩具项目,但真正动手做过的人都知道,它几乎涵盖了图像分类任务中所有典型的坑:类别数量多、类间差异小、类内差异大、数据分布不均衡、背景干扰严重。我当初选这个题目…

2026/10/20 5:06:22

YOLOv11轻量化改造:Slim-Neck特征融合原理与实战

YOLOv11出来之后,我身边的不少开发者都在聊同一个话题:模型越来越大,算力需求越来越高,可现实里的部署场景往往就那么点内存、那么点算力。尤其是在边缘设备、嵌入式平台、实时视频流分析这类环境里,跑大模型根本不现实…

2026/10/20 5:06:22

Codex赋能可视化大屏开发:高效工作流构建与实战指南

做可视化大屏开发这几年,我越来越确认一件事:真正拖慢进度的从来不是某个图表不会写,而是从业务需求到最终成品的反复拉扯。业务方丢来一句话需求,设计师给一版高保真,前端再照着还原,中间任何一环出现理解…

2026/10/20 6:06:26

ppt-master:用代码和配置自动化生成PPT的工程化方案

1. 项目概述:这不是一个PPT模板,而是一套可编程的幻灯片生成系统“ppt-master”这个名称乍一听像是某个网红PPT模板合集,但实际接触过的人很快会发现——它根本不是传统意义上的美化资源包。我第一次在某技术社区看到这个词时,也以…

2026/10/20 6:06:26

Unity AssetBundle安卓实战:从打包分组到热更新全链路避坑指南

1. 为什么打包成APK之后资源加载全乱套了如果你做过Unity移动端项目,大概率经历过这个场景:在编辑器里跑得好好的场景,打包成APK装到安卓手机上,要么贴图变成一片粉红,要么模型直接消失,要么音效播不出来。…

2026/10/20 6:06:26

i.MX6ULL交叉编译libmodbus 3.1.6实战与避坑指南

简介:本资源为libmodbus 3.1.6源码包,面向从事嵌入式开发、需要在ARM Cortex-A7架构的imx6ull平台上实现Modbus通信的工程师与学习者。它解决的是跨平台交叉编译与源码定制问题,涵盖RTU与TCP/IP两种模式,可作为工业自动化、物联网…

2026/10/20 6:06:26

多智能体分布式共识控制与动态事件触发的Matlab仿真

开头先交代一下背景。这类“多智能体系统”的复现项目,在控制方向的课题里出现频率很高,标题往往把三件事并在一起:固定拓扑与切换拓扑、分布式共识控制、动态事件触发机制,最后落在Matlab仿真实现上。说白了,就是让一…

2026/10/20 6:06:26

nanoMuse轻量生成框架实战:从部署到插件开发全解析

1. 从一条热搜说起:为什么大家都在聊 nanoMuse前几天刷技术社区的时候,一条标题反复出现在我的信息流里——“最近爆火的 Muse 浙大开源版 nanoMuse,来了”。说实话,第一眼看到这个标题,我的反应是:又是一个…

2026/10/20 6:01:26

PLMS自适应滤波器:脉冲噪声下的LMS鲁棒性改进原理与Matlab实现

前几天,一位做车载音频的工程师朋友跟我抱怨,说他的回声消除算法在实验室里测得好好的,一到实际路测就“抽风”:安静路段收敛得很干净,可一旦出现开关门、喇叭鸣笛这类突发噪声,滤波器的权值就像被弹弓弹了…

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/20 1:05:37

单片机C运行时从启动到多任务:中断安全与共享资源冲突

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

GCC编译四步详解:预处理、编译、汇编、链接实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

FIW线是什么?FEP注塑线与普通氟塑料线的区别及应用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑