PT Agent:一个能交给 AI Agent 使用的 PT Free 刷流工具

项目地址:https://github.com/daniellauyu/pt-agent
起因:一个大包差点让我把号玩废
前段时间在论坛求了一个馒头💊,进了馒头之后猛刷了两天,做种做到 1T 左右,感觉一切都很顺。
然后出事了。
有一个 Free 的大包,我下到一半,Free 到期了。qB 那边毫无察觉,继续吭哧吭哧地下。等我反应过来的时候,分享率已经从好好的掉到了 0.7。
当时是真慌。刚求到邀请,我要是这么把号玩废了,实在说不过去。
冷静下来之后我想明白一件事:问题不在于我下得多快,而在于我根本不知道 Free 什么时候到期。
qB 里那个任务,看上去和别的任务没有任何区别。名字、大小、进度、速度,全都正常。唯独没有一个字告诉我:这个东西再过两小时就要开始计费了。
找现成工具:能用,但不解决我的问题
问了几个论坛,得到的答案基本一致:用 vertex,或者 ptool。
两个我都装了、部署了。说实话,配置是真的麻烦。规则、筛选器、站点适配、各种参数,我研究了半天没研究明白,最后是丢给 AI 让它帮我配完的。
配完之后各下了一些,感觉还是不太行。倒不是说工具不好,而是我最在意的那件事它们没解决:
免费到期之后,我从 qB 里还是看不出来。
我要的其实很简单——我不需要一个能配置一百种规则的引擎,我需要一个新手保护装置:帮我挑一挑值得下的,然后在 Free 快到期的时候,把下不完的那些自动清理掉,别让我再被爆一次分享率。
既然没有,那就自己写一个。
核心思想
整个工具的逻辑可以用一段话讲完:
定时(或随机间隔)通过馒头的 API Token 拉取种子列表 → 只筛出 Free 的 → 用一个简单的算法算出「推荐」 → 推送到 qB → Free 到期前,把还没下完的自动删掉。
推荐的判断标准也很朴素:
- Free 剩余时间足够长(默认至少 12 小时)
- 下载人数 > 做种人数(说明有需求,上传才有得跑)
就这两条撑起了主干。剩下的都是围绕「别把号玩废」加的保险。
两个版本
我做了两个版本,功能定位不同,但决策逻辑是同一份代码。
Chrome 插件版:自己手动刷
装在浏览器里。点一下扫一次,把当前所有 Free 资源按推荐 / 风险 / 拒绝分好,每行显示评分、体积、做种比、Free 剩余和绝对截止时间。看中哪个点哪个,或者「一键下载推荐」。
适合刚开始用的时候——你可以先看看它的判断标准合不合你的胃口,再决定要不要放手让它自己跑。
终端守护进程版:无人值守
跑在 NAS 或者常开的小主机上,不需要浏览器。按随机间隔(默认 40–90 分钟)自动扫描、自动推送、自动清理。
为什么是随机而不是固定周期?两个原因:固定周期在站点侧是很明显的机器人特征;另外随机化也能错开抢种的高峰。两个值填成一样就退化成固定周期。
自带一个和插件同款视觉的 WebUI,默认只绑 127.0.0.1:7788。
原本还打算做个 Docker 版本,后来想了想——其实 Dockerfile 和 docker-compose.yml 直接写进去就完事了,没必要单独算一个版本。群晖上直接 docker compose up -d 就能跑。
关键设计:把 Free 截止时间写进标签
这是整个项目的地基,也是我觉得最值的一个设计。
问题的根源是下载器不知道 Free 什么时候结束——这个信息只存在于站点那边。所以入队的时候,我给每个任务打两个标签:
ptagent
ptagent-free-end=2026-07-30T18:00:00+08:00
- 第一个标记「这是本工具加的」
- 第二个是 Free 截止时间,保留时区的 ISO 8601
有了这个,后面的一切就都成立了:
- qB 里能直接看见。任务列表里那个原本什么都不说的任务,现在明明白白写着什么时候到期。
- 保护检查有了依据。守护进程每分钟回读一次这些标签,在到期前 10 分钟(可配)把还没下完的任务连文件一起删掉。
- 不误伤。没有
ptagent标签的任务一律不碰——那是你自己加的,跟我无关。
顺带做了个「回填」功能:你在装这个工具之前手动加的那些馒头任务,可以按名称和字节体积精确匹配回查一遍,把截止标签补上。补上之后它们也受保护了。
删文件这件事,边界写死在代码里
一个会自动删文件的工具,最重要的不是它删得多准,而是它绝对不会删什么。这几条我是写死在代码里、并且有测试守着的:
- 只删带
ptagent标签的任务 —— 你自己加的、别的工具加的,一概不碰 - 已完成的任务不删 —— 都下完了当然要留着做种,Free 过期也不影响
- 打了
pt_agent_keep/pt_agent_nodel的不删 —— 你说留就留 - 关掉自动删除时只告警不动手 —— 默认行为可以改
所有删除动作都进审计日志。哪天你发现文件少了,ptagent audit 一查就知道是什么时候、因为什么被删的。
两道关,而不是一道
发种子到下载器之前要过两关,这个分法是踩过坑之后才改的。
第一关,决策引擎(扫描时逐个判定):不是明确 Free 的拒绝、已到期的拒绝、HR 的拒绝、0 做种的判风险、下载人数不高于做种人数的不进推荐。通过的按 2xFree、Free 时长、做种供给、供需比、体积算分,满分 100。
对只有 1–2 个做种但下载需求极高的稀缺资源还有个额外的机会模型:Free 至少 6 小时、下载人数至少 20、下载/做种比至少 10、算下来所需均速不超过 512 KiB/s 的,允许推荐。能不能下完由「所需均速」判断,比拍一个固定的 GB 上限准得多。
第二关,本地安全准入(点下载的那一瞬间):评分够不够、体积超没超、Free 还剩多久、分享率达不达标。
为什么要分成两步?因为并发数、分享率这类条件是会变的,扫描那一刻的结论到点下载的时候可能已经不成立了,只能在入队瞬间重新判断。
有一条我特意改过:并发数只提示不拦截。一开始我让它超过上限就拦下,后来发现这是错的——下载器自己有队列,超出的任务会排队,不会丢。拦下来反而让人以为发送失败了。
踩过的坑
vibecoding 的过程不是一路顺风。有几个坑我觉得挺有代表性,说说。
1. 外网访问 qB 一直 403
在家用内网地址好好的,出门用公网地址就全是 403。查了半天是 qB 的 CSRF 保护:它会校验请求的 Origin / Referer。浏览器插件发出去的请求带的是插件自己的 origin,qB 认为这是跨站攻击。
解决办法是用 declarativeNetRequest 在发出前把这两个头去掉。但这里有个陷阱我第一版就掉进去了:如果用常驻规则,规则是按目标 URL 匹配的,那么任何网页对你 qB 发的请求都会被剥掉 Origin——等于把 qB 的 CSRF 保护全局关掉了。后来参考 PT-depiler 的做法改成了会话级规则 + 每次请求前装、请求后卸 + excludedTabIds 排除掉除自己以外的所有标签页。
2. 我自己把自己的 IP 封了
有一天突然连不上 qB,提示「身份认证失败次数过多,您的 IP 地址已被封禁」。查下来是我自己写的:代码里有 8 处无条件 login(),后台守护又每分钟跑一次,密码只要有一次不对,就是一场登录风暴。
改成了:平时不登录,只有真的因为没有会话被拒(403)时才登录一次并重试一次;识别到封禁消息就绝不再试——因为继续撞只会把封禁窗口无限续期,永远等不到自动恢复。
3. 明明添加成功了,却报失败
这个坑连着踩了四次,最后发现是同一个根因:把对方的正常响应当成失败。
createCategory在分类已存在时返回 409 Conflict → 我当成错误,直接中断了整次入队,种子根本没提交torrents/add在种子已存在时也返回 409 → 我报「添加失败」,但其实 qB 里明明有- 登录响应体被反向代理改写成空 → 我要求严格等于
"Ok.",于是成功的登录被判成失败
修完之后总结了三条原则,后面一直照着办:
以状态码为准;只在对方明确说失败时才判失败;幂等操作的冲突即成功。
4. 报错内容全是 [object Object]
排查上面那些问题的时候,我截了一堆图发给 AI,结果 Chrome 的错误页面上全是 [object Object]——因为 console.error("xxx", data) 的第二个参数在那个页面上不会被展开。
真正的教训不是这个 bug 本身,而是我意识到排查花的时间大部分耗在「看不到真相」上。后来专门做了一轮改造:所有报错统一进日志、未捕获异常和 Promise 拒绝也抓、Service Worker 休眠时日志有兜底、错误详情直接内联进消息字符串。之后再遇到问题,排查速度完全不是一个量级。
配合 AI Agent 用
守护进程的每条命令都支持 --json,日志和审计是 JSONL(一行一条 JSON),可以逐行解析。
我自己是搭配 hermes 用的:在 hermes 里起一个 watchDog,配置一下就能做进程监控。挂了自动拉起,出错了推给我。
另外一个我觉得挺实用的点:如果嫌配置麻烦,可以直接把整个项目丢给 AI agent,让它帮你配。
我在仓库里专门写了一份 AGENTS.md,告诉 agent 该看哪些接口、日志怎么聚合(同一轮扫描的所有日志共享一个 operationId,按它聚合就能还原完整因果链)、哪些操作是安全的(--dry-run 保证零副作用)、哪些需要先问过用户(--force 会跳过安全准入)。
配置流程大概就是:ptagent doctor 体检 → 哪项红了改哪项 → ptagent scan --dry-run 空跑一轮看看它会挑什么 → 满意了再 ptagent start。这套流程 agent 自己就能跑完。
一点技术上的选择
决策逻辑只有一份。 两个版本的评分、决策、准入、Free 保护、qB 客户端是同一套代码——插件是源头,守护进程带一份逐字节副本。副本用 sha256 记在 MANIFEST.json 里,有测试确认两边一致。
这么做是因为:有两份副本,最大的风险不是它们不同步,而是不同步了却没人发现。插件那边改了评分权重,终端版还按老规则跑,你根本看不出来。
内外网自动切换。 Chrome 扩展拿不到 WiFi SSID,所以我没去判断「现在连的是哪个网」,而是直接探测「哪台下载器现在连得上」。配置里下载器的顺序就是探测优先级,内网地址放前面——在家秒通,出门超时后自动落到公网那条。效果等价,还不依赖任何系统权限。
终端版有个天然优势。 馒头的下载地址会 302 跳到 CDN,浏览器里要靠 host 权限才能跨域取回种子字节,取不到就只能退化成让 qB 自己去抓(成功率低,而且失败常常是静默的)。Node 里没有同源策略,种子文件永远能拿到字节再上传。
测试。 插件 177 个,守护进程 100 个。核心链路是用一个同时扮演馒头和 qBittorrent 的假网络跑完整流程的,不是只测单个函数——因为真正出问题的从来不是单个函数,而是它们串起来的边界:会话过期、409 冲突、种子字节取不到时的降级。
现状与限制
老实说清楚:
- 站点只支持馒头(API 模式)。站点适配层是按多站点设计的,接新站点补一个适配器就行,但我暂时没有别的站可测。
- 下载器只支持 qBittorrent。Transmission / Deluge 的适配层位置留好了,
implemented打开就能接。
最后
写这个东西的初衷特别朴素:我不想再被一个 Free 大包干到 0.7 一次。
如果你也是刚拿到药的新人,也在为「Free 到期了我怎么知道」发愁,也许它对你有点用。
项目地址:https://github.com/daniellauyu/pt-agent
MIT,随便用。有问题提 issue。
免责:自动化下载是否符合站点规则请自行确认。这个工具不绕过任何站点限制,只是把你本来就会手动做的判断和操作自动化。用它导致的账号问题由使用者自负。

