一句话,给 AI 装上互联网
从"帮我看看小红书上这个品的口碑"这句抓瞎的话,到它真的能看。这一章我们看清楚:Agent Reach 到底是个什么东西。
你大概遇到过这个尴尬
你的 AI Agent 已经能写代码、改文档了。但你让它上网找点东西,它就卡住了:
看不了 —— 拿不到字幕
搜不了 —— Twitter 官方接口要付费
打不开 —— 必须登录才能看
每个平台都有自己的门槛:要付费的 API、要绕过的封锁、要登录的账号、要清洗的乱码数据。一个一个踩坑下来,光让 Agent 读个推特就得折腾半天。
Agent Reach 把这件事压缩成一句话
你不用自己装工具、配账号。你只要把下面这句话,复制粘贴给你的 Agent:
帮我安装 Agent Reach:
https://raw.githubusercontent.com/
Panniantong/agent-reach/main/docs/install.md
"去读这个网址里的安装说明书。"
Agent 会自己打开这份说明,照着一步步做。
几分钟后,它就能读推特、搜 Reddit、看 YouTube、刷小红书了。
那份 install.md 不是普通网页,是写给 AI 看的操作手册。Agent 读完就知道该敲哪些命令。这是本章要拆开看的核心:真正干活的其实是一个叫
命令行工具
的程序 —— agent-reach。
最关键的一个心智模型
大多数人以为 Agent Reach 是个"读取工具"—— 你调它,它去读推特还你数据。错。
新员工(你的 Agent)来了,IT 部门不替他干活。IT 做的是:把电脑装好、账号开好、发一本《工作手册》,然后退到一边。之后员工自己拿着工具干活。Agent Reach 就是这个 IT 部门 —— 它选型、安装、体检,但真正读推特的是它装好的上游工具,Agent 直接调用,中间没有它。
官方管这叫 能力层(capability layer)。记住这句话,整个项目的设计都是从它推导出来的:
选型
替你挑当下最稳的那个工具(推特用哪个?B站用哪个?)
安装
自动装好这些工具和它们的依赖
体检
告诉你每个平台现在通不通、走的哪条路
发手册
给 Agent 装一份 SKILL.md,让它知道该调什么
跟着数据走一遍:那句话之后发生了什么
点"下一步",看你粘贴那句话之后,幕后到底跑了哪些环节。注意最后一步 —— Agent Reach 自己退场了。
安装完之后,Agent 读推特是直接调 twitter-cli,不经过 Agent Reach。这跟"包装库"完全相反 —— 包装库会永远夹在中间,多一层就多一层出错和维护的成本。Agent Reach 装完就退场,这是个很聪明的设计选择,第 5 章我们会看到它带来的好处。
这个 CLI 本体,其实很小
既然真正读取的活都外包给了上游工具,那 Agent Reach 自己的代码就很轻。它的"主心脏"这个类,短到出乎意料:
class AgentReach:
"""Give your AI Agent eyes to see
the entire internet.
For reading/searching, use the
upstream tools directly."""
def doctor(self):
# Check all channel availability.
from agent_reach.doctor import check_all
return check_all(self.config)
这是整个工具的主入口"类"(一段代码的组织单位)。
连它自己的注释都写着:读取和搜索请直接用上游工具。
它对外几乎只提供一个动作 —— doctor(体检)。
体检干嘛?挨个检查每个平台通不通。真正读内容的活,它一个字都不碰。
一个号称"给 AI 装上整个互联网"的工具,主类却只干体检 —— 这不是偷懒,是它想清楚了自己的定位。接下来四章,我们就把这个定位拆开看。
想一想
安装完成后,你让 Agent "搜一下推特"。数据实际是怎么流动的?
你粘贴给 Agent 的那个链接(install.md),本质上是什么?
认识主角们
上一章我们看到 CLI 装完就退场。这一章走进后台,认识真正干活的五个角色 —— 以后你说"这个功能坏了",就能猜到该去看谁。
一个剧组,五个角色
Agent Reach 的代码分工很干净,像一个小剧组。每个文件夹/文件都是一个专职角色,各管一摊:
你敲的每条命令都先到它这儿。它听懂你要干嘛,再分派给别人
你的 Cookie、Token 存哪、怎么存得安全,归它管
挨个问每个平台"你还好吗",汇总成一张体检报告
每个平台一个文件:推特专员、B站专员、小红书专员……
不听工具"我在"的口头汇报,真的敲一下命令看它到底能不能跑
在代码里,他们长这样
如果你打开这个项目的文件夹,会看到这样的结构。别怕,重点就那么几个:
"小红书搜不出来" → 去看 channels/xiaohongshu.py。"存的 Cookie 不见了" → 去看 config.py。"doctor 报告显示错的" → 去看 doctor.py。文件名就是路标。这种"按职责拆文件"的习惯,工程师叫它
关注点分离。
15 个专员,共用一份岗位说明书
推特专员、小红书专员各不相同,但公司要求他们都会答同样几个问题。这份"统一要求"写在 base.py 里,叫
父类:
class Channel(ABC):
name: str = "" # "youtube"
description: str = "" # "YouTube 视频和字幕"
backends: List[str] = [] # 候选后端列表
tier: int = 0 # 0=零配置 2=要登录
@abstractmethod
def can_handle(self, url):
# 这个网址归我管吗?
def check(self, config):
# 我的上游工具装好了吗?
每个专员都得填四张名片:我叫什么、干什么的、我能走哪几条路、我难不难配。
tier=0 是"装好即用",tier=2 是"得先登录"—— 这决定了它在体检报告里排哪一栏。
@abstractmethod 是"这题必答":每个专员必须回答"这个网址归我管吗"。
还得会回答"我的工具好使吗"(check)—— 这就是第 4 章要深挖的体检动作。
因为所有专员形状一样,体检医生就能不用认识任何具体平台,用同一套流程对待全部 15 个。想加第 16 个平台?照着说明书填一个新文件就行,别处一个字都不用改。这叫 可扩展性。以后你让 AI 加功能时,可以明确要求它"照现有模式加一个模块,别改动其它文件"。
听他们对一次话:你跑了 doctor
你敲下 agent-reach doctor。看这五个角色怎么配合 —— 一条一条点开:
体检医生"什么都不懂",反而是优点
看医生的核心代码 —— 它真的不认识任何平台,只会做一件事:挨个喊、挨个记:
for ch in get_all_channels():
try:
status, message = ch.check(config)
except Exception as e:
# 一个专员崩了,不能拖垮整张报告
status, message = "error", f"体检异常:{e}"
results[ch.name] = { "status": status, ... }
挨个遍历所有专员(channel),让每人自己做体检。
try/except 是安全网:万一某个专员体检时崩溃了……
……就把它记成"error",而不是让整个程序跟着崩。
最后把每个人的结果记进一张表。医生自己完全不懂推特和小红书的区别。
如果没有它,只要 15 个平台里有 1 个体检时抛错,整个 doctor 命令就崩了 —— 你连另外 14 个的状态都看不到。这种"一个环节别拖垮全局"的防御,是区分"能跑的代码"和"靠谱的代码"的关键。以后 AI 给你写循环处理一批东西时,可以问它一句:"其中一个失败了,会不会连累其它的?"
想一想
用户报告"小红书搜索出来的结果乱码"。基于这一章,你会第一个去看哪个文件?
你想让 AI 帮你加一个"知乎"平台。基于这套架构,最干净的做法是?
首选 + 备选:会换代的能力
互联网平台天天变:接口被封、工具停更。这一章看 Agent Reach 最聪明的一招 —— 每个平台不是绑死一个工具,而是一串"有先后顺序的候选人"。
先看一个真实翻车现场
2026 年 6 月,一件事真的发生了:B站升级了 风控,把原来一直用来下 B站视频的工具 yt-dlp 彻底拦死了 —— 每次请求都返回 412 错误。
那一天起,所有用户的 B站功能全挂。要修,就得改代码、发新版本、等每个用户更新。用户在这期间只能干瞪眼。这是绝大多数"绑死一个工具"的项目的宿命。
但 Agent Reach 的用户当天零操作,B站照样能用。秘密就在它对待每个平台的方式。
把每个平台想成一支救火队
救火队有队长、副队长、替补,排好了顺序。队长受伤了,副队长顶上;副队长也不行,替补上。任务照常完成,不用临时改组织架构。
Agent Reach 里每个平台就是这样一支队伍 —— 一串 后端候选人,排好了先后:
class BilibiliChannel(Channel):
name = "bilibili"
description = "B站视频、字幕和搜索"
backends = ["bili-cli",
"OpenCLI",
"B站搜索 API"]
这是 B站专员。
它有三个候选后端,写成一个有顺序的列表。
bili-cli 是队长(首选),OpenCLI 是副队长,搜索 API 是替补。
yt-dlp?已经从这份名单里被除名了——它现在只服务 YouTube。
换接入方式 = 调整这个列表的顺序,不是重写代码。B站翻车那天,维护者做的事就是把 yt-dlp 从列表里拿掉、bili-cli 提到首位。改的是一行"名单",不是一堆逻辑。这种把"会变的东西"抽出来单独放的思路,是好架构的标志。
怎么从名单里选出当值的那个?
光有名单还不够 —— 得真的挨个试,第一个"完整可用"的当选。看 B站专员体检时的核心逻辑:
for backend in self.ordered_backends(config):
if backend == "bili-cli":
result = self._check_bili_cli()
elif backend == "OpenCLI":
result = self._check_opencli()
else:
result = self._check_search_api()
if result is None:
continue # 这个没装,试下一个
按名单顺序,从队长开始一个个点名。
是队长 bili-cli?去真检查它能不能跑。
是副队长 OpenCLI?检查浏览器登录态那套。
否则就是替补:试试那个免登录的搜索接口。
如果 result 是 None(这个后端根本没装)——continue,跳过,问下一个。第一个真能用的就当选。
看一次"故障转移"的完整过程
假设 bili-cli 没装、OpenCLI 也没装,会怎样?点"下一步"看队长副队长挨个落选、替补顶上:
这种"首选挂了自动换下一个"的机制,工程师叫它 故障转移。它是让系统"打不死"的核心手段。
用户想插队指定?也留了后门
万一你就想强制用某个后端呢?名单顺序也支持被你覆盖。看这段"插队"逻辑,它藏着一个很老练的安全细节:
candidates = list(self.backends)
override = config.get(f"{self.name}_backend")
if override:
for i, b in enumerate(candidates):
if b == override or b.startswith(override):
candidates.insert(0, candidates.pop(i))
break # 找到了才插队;找不到就忽略
先复制一份名单(不动原始的)。
看你有没有指定"我就要用某个后端"。
如果指定了,就在名单里找它,找到就提到第一位。
关键在最后:只有确实存在的后端才提前。你要是拼错了名字,代码会忽略它、继续用原顺序——而不是把名单搞乱、让所有后端都用不了。
你打错一个后端名字,最坏结果只是"你的插队没生效",而不是"整个平台崩了"。这种优雅降级的心态,是靠谱代码和脆皮代码的分水岭。以后让 AI 写"接受用户输入"的功能时,值得追问一句:"如果用户输入了无效值,会发生什么?"
想一想
某天 twitter-cli 突然被封了。基于这套设计,维护者最省力的修复是?
你想强制 B站走 OpenCLI,但配置里把名字拼成了 "opencIi"(把 l 打成大写 I)。会发生什么?
真探测 vs 假存在
"命令存在"不等于"命令能用"。这一章拆开验货员 Probe —— 它揭穿了一个连很多程序员都会踩的坑,也是 doctor 报告能被你信任的原因。
一个会骗人的常用检查
程序想知道某个工具装没装,最常见的做法是喊一句
which:
"系统里有 bili 这个命令吗?"有就返回它的位置。
问题是:它只看到了一个门牌号,没敲门看里面有没有人。
很多工具(用 pipx / uv 装的)本质是一个小小的"快捷方式",它指向某个 Python 解释器。哪天你升级了系统 Python,那个解释器就换地方了 —— 快捷方式还在,但它指向的目标没了。这时候 which 照样说"在!",你一运行却报错。门牌还挂着,屋里早就搬空了。
如果 doctor 只用 which 检查,它会信誓旦旦告诉你"B站可用",然后你一用就崩。这种报告还不如没有。
验货员分得清三种"不对劲"
Probe 的思路是海关验货:不看报关单,直接开箱。它能区分三种截然不同的状态 —— 而 which 把它们全看成"在":
missing 没装
门牌都没有。工具根本不在系统里。处方:去装它。
broken 装了但跑不了
门牌在,屋里空。就是上一页那个"幽灵命令"。处方:重装修复。
timeout 能跑但耍脾气
屋里有人,但半天不应门。可能是临时抽风,值得再试一次。
"没装"和"装了但坏了"是两种病,药方完全不同。which 把它们混为一谈,只能给你一句"没找到"。Probe 把它们分开,才能对每种情况给出能直接照做的修复命令。这就是为什么 doctor 报告不只说"坏了",还告诉你"怎么修"。
"开箱验货"这一步长什么样
关键就一步:不光问 which,还真的执行这个命令,看它跑不跑得起来。看 probe.py 里揭穿幽灵命令的那几行:
path = shutil.which(cmd)
if not path:
return ProbeResult("missing")
try:
r = subprocess.run([path, *args], timeout=timeout)
except FileNotFoundError:
# which 找到了它,但执行失败:
# 快捷方式指向的解释器没了
return ProbeResult("broken", hint=reinstall_hint(package))
先用 which 找门牌号。
连门牌都没有?那就是"没装",直接返回。
有门牌,那就真去敲门——执行这个命令(subprocess.run)。
如果敲门时报"文件找不到"——这正是幽灵命令的特征:门牌指向一个已经不存在的屋子。
这时不慌,判定为"broken",并附上一句重装处方。这就是 which 永远给不了你的信息。
你来当验货员:抓出偷懒的那行
下面是一段"想检查工具是否可用"的代码。它犯了本章开头那个经典错误。点你觉得有问题的那一行:
哪一行让这个检查变得不可信?
def is_tool_ready(cmd):
if shutil.which(cmd): # 找到门牌就当能用
return "ok"
return "missing"
AI 写"检查某工具装没装"的代码时,十有八九给你 if which(cmd): return True —— 又快又看着对,但会放过幽灵命令。学会这一课后,你可以直接对 AI 说:"别只用 which,真的执行一下这个命令确认它能跑。"一句话,就把一个脆弱的检查变成靠谱的检查。
重试也有讲究:别把没救的再试一遍
网络抽风值得重试,但"没装"和"坏了"重试一百遍也不会自己变好。Probe 想清楚了这点:
for _ in range(retries + 1):
last = _run_once(path, args, timeout, package)
if last.ok:
return last
# missing/broken 不会自己好——别浪费重试
if last.status in ("missing", "broken"):
return last
按允许的次数反复试。
一旦某次成功了,立刻返回,不用再试。
但如果失败原因是"没装"或"坏了"——
立刻放弃,不再重试。因为这两种病重试再多次也不会自愈,多试只是白白拖慢体检。只有网络超时这种"临时抽风"才值得再给一次机会。
无脑重试所有失败,是个很常见的坏习惯 —— 它让程序在没救的情况下卡半天。会区分"可恢复"和"不可恢复"的错误,是成熟代码的标志。这也是你可以对 AI 提的要求:"重试前先判断这个错误是不是重试能解决的。"
想一想
用户升级了系统 Python 后,doctor 显示某工具"可用",但一用就报"文件找不到"。这最可能是哪种状态?
Probe 只对某类失败重试。为什么"没装"和"坏了"重试是浪费?
体检报告与大局观
最后一章,我们学会读那张体检报告——它是你排查一切问题的第一站——然后把前四章串成一张完整的地图。
出问题时,先问医生
"小红书搜不出来了"、"推特没反应"—— 别急着猜。agent-reach doctor 这一条命令,就是你的仪表盘。它把 15 个平台此刻的真实状态摆在你面前:
✅ ok
通了,能用,还告诉你现在走的是哪个后端
[!] warn
装了但需要配置或登录 —— 差临门一脚
[X] off / error
没装或坏了 —— 报告里会附上修复处方
任何"某平台不工作"的问题,第一步都是跑 doctor。它会直接告诉你是"没装"、"没登录"还是"坏了",每种都附带下一步该干嘛。这比你盯着代码瞎猜快十倍。给 Agent 用的 SKILL.md 里甚至写死了一条铁律:动手前先跑 doctor --json,按报告选命令。
给人看 vs 给机器看:同一份体检,两种语言
doctor 有个小开关 —— 加一个
标志
--json,输出就从"给人读的报告"变成"给机器读的数据":
results = check_all(config)
if getattr(args, "json", False):
print(json.dumps(results, ensure_ascii=False))
return
rprint(format_report(results)) # 给人看的彩色报告
先做完体检,拿到原始结果。
如果用户加了 --json 这个标志……
……就吐出结构化数据(JSON),方便程序解析。这是给 Agent 看的。
否则,排版成彩色的、带 ✅ 的报告。这是给你看的。
同一份体检数据,两副面孔——人和 AI 各取所需。
好工具会同时照顾"人读"和"程序读"两种场景。你要写脚本自动处理,就用 --json;你自己看,就用默认报告。以后让 AI 做命令行工具时,这是个值得主动提的需求:"加一个 --json 选项,输出结构化数据。"
顺手学一课:你的 Cookie 存得安全吗
需要登录的平台,你的 Cookie 会存进本地一个配置文件。这里有个容易被忽视、但很关键的安全细节 —— 存的时候怎么防止别人偷看:
# 一开始就用严格权限创建文件,
# 避免有一瞬间凭据是"人人可读"的
fd = os.open(
str(self.config_path),
os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
stat.S_IRUSR | stat.S_IWUSR, # 0o600 只有你能读写
)
注释说清了动机:不能让凭据有哪怕一瞬间是"全系统可读"的。
所以创建文件时,一步到位设好权限。
0o600 的意思是:只有文件主人(你)能读能写,同机器的其他用户碰都碰不到。
要点:权限是"创建的同时"就设好的,不是"先建再改"——那样中间会有个危险的空档。
如果先用默认权限建文件、再改成 0o600,那中间这一瞬,文件是敞开的 —— 万一正好被别的程序读到,凭据就泄了。这种"两步之间的危险空档"叫 竞态条件。 这属于 AI 很容易写漏的细节,你若在意安全,可以点名要求"创建文件时就设好权限,别分两步"。
大局观:五个角色如何拼成一张图
把前四章串起来。你敲一条命令后,整台机器是这样转的 —— 拖拽下面的角色,看你还记不记得谁管什么:
听懂你敲的命令,分派任务
挨个喊每个平台报告状态,汇总成报告
一个平台一个,管着自己的"首选+备选"名单
真的执行命令,揭穿"幽灵命令"
安全地存取你的 Cookie 和 Token
带走这四个能指挥 AI 的直觉
这门课真正的收获不是"读懂了 Agent Reach",而是四个可迁移的判断力 —— 你可以直接用来指挥和检查 AI:
按职责拆文件
"照现有模式加一个模块,别改动其它文件。"—— 让 AI 写出好扩展的代码。
把易变的抽成数据
"把可能会换的选项做成一个有顺序的列表,别写死在逻辑里。"
验证要动真格
"别只检查它在不在,真的运行一下确认它能用。"
追问失败路径
"如果这一步失败/输入无效,会怎样?会不会连累别的?"
回到第一章那个心智模型:它是能力层 —— 选型、安装、体检、发手册,然后退场。它自己不读一个字的推特。理解了这个定位,你就理解了它每一个设计决策。这种"想清楚自己该干什么、不该干什么"的克制,才是它最值得学的地方。
最后想一想
你的 Agent 突然报告"Reddit 搜不出东西"。基于这门课,你的第一步是?
你想写个脚本,每天自动检查所有平台状态、把挂掉的记进日志。你会用 doctor 的哪种输出?
一句话概括:安装完成后,Agent Reach 在你"读推特"这件事里扮演什么角色?
你现在能看懂 Agent Reach 的架构、能读它的核心代码、更重要的是 —— 带走了四个能帮你更好地指挥 AI、更快地排查问题的直觉。这,就是把代码变成超能力的样子。