01

一句话,给 AI 装上互联网

从"帮我看看小红书上这个品的口碑"这句抓瞎的话,到它真的能看。这一章我们看清楚:Agent Reach 到底是个什么东西。

你大概遇到过这个尴尬

你的 AI Agent 已经能写代码、改文档了。但你让它上网找点东西,它就卡住了:

📺
"帮我总结这个 YouTube 教程"

看不了 —— 拿不到字幕

🐦
"搜一下推特上大家怎么评价这个产品"

搜不了 —— Twitter 官方接口要付费

📕
"看看小红书上这个品的口碑"

打不开 —— 必须登录才能看

每个平台都有自己的门槛:要付费的 API、要绕过的封锁、要登录的账号、要清洗的乱码数据。一个一个踩坑下来,光让 Agent 读个推特就得折腾半天。

Agent Reach 把这件事压缩成一句话

你不用自己装工具、配账号。你只要把下面这句话,复制粘贴给你的 Agent:

你发给 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 Reach 更像公司的"入职培训 + IT 部门"

新员工(你的 Agent)来了,IT 部门不替他干活。IT 做的是:把电脑装好、账号开好、发一本《工作手册》,然后退到一边。之后员工自己拿着工具干活。Agent Reach 就是这个 IT 部门 —— 它选型、安装、体检,但真正读推特的是它装好的上游工具,Agent 直接调用,中间没有它。

官方管这叫 能力层(capability layer)。记住这句话,整个项目的设计都是从它推导出来的:

选型 替你挑当下最稳的那个工具(推特用哪个?B站用哪个?)
安装 自动装好这些工具和它们的依赖
体检 告诉你每个平台现在通不通、走的哪条路
发手册 给 Agent 装一份 SKILL.md,让它知道该调什么

跟着数据走一遍:那句话之后发生了什么

点"下一步",看你粘贴那句话之后,幕后到底跑了哪些环节。注意最后一步 —— Agent Reach 自己退场了。

🧑
🤖
Agent
🛠️
agent-reach CLI
🌐
上游工具
点"下一步"开始
💡
"没有包装层"是什么意思

安装完之后,Agent 读推特是直接调 twitter-cli,不经过 Agent Reach。这跟"包装库"完全相反 —— 包装库会永远夹在中间,多一层就多一层出错和维护的成本。Agent Reach 装完就退场,这是个很聪明的设计选择,第 5 章我们会看到它带来的好处。

这个 CLI 本体,其实很小

既然真正读取的活都外包给了上游工具,那 Agent Reach 自己的代码就很轻。它的"主心脏"这个类,短到出乎意料:

core.py 的真实代码
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),本质上是什么?

02

认识主角们

上一章我们看到 CLI 装完就退场。这一章走进后台,认识真正干活的五个角色 —— 以后你说"这个功能坏了",就能猜到该去看谁。

一个剧组,五个角色

Agent Reach 的代码分工很干净,像一个小剧组。每个文件夹/文件都是一个专职角色,各管一摊:

🎬
CLI(cli.py)—— 前台接待

你敲的每条命令都先到它这儿。它听懂你要干嘛,再分派给别人

🗄️
Config(config.py)—— 保险柜管理员

你的 Cookie、Token 存哪、怎么存得安全,归它管

🩺
Doctor(doctor.py)—— 体检医生

挨个问每个平台"你还好吗",汇总成一张体检报告

📡
Channels(channels/)—— 15 个平台专员

每个平台一个文件:推特专员、B站专员、小红书专员……

🔬
Probe(probe.py)—— 验货员

不听工具"我在"的口头汇报,真的敲一下命令看它到底能不能跑

在代码里,他们长这样

如果你打开这个项目的文件夹,会看到这样的结构。别怕,重点就那么几个:

agent_reach/ 整个工具的代码都在这
cli.py 前台接待:install / doctor / configure 等命令都定义在这
config.py 保险柜管理员:读写 ~/.agent-reach/config.yaml
doctor.py 体检医生:收集所有平台的健康状态
probe.py 验货员:真的执行命令来验证工具可用
channels/ 15 个平台专员,一个平台一个文件
base.py所有专员的"岗位说明书"(父类)
twitter.py推特专员
xiaohongshu.py小红书专员
bilibili.pyB站专员
🧭
给你一个找 bug 的直觉

"小红书搜不出来" → 去看 channels/xiaohongshu.py。"存的 Cookie 不见了" → 去看 config.py。"doctor 报告显示错的" → 去看 doctor.py文件名就是路标。这种"按职责拆文件"的习惯,工程师叫它 关注点分离

15 个专员,共用一份岗位说明书

推特专员、小红书专员各不相同,但公司要求他们都会答同样几个问题。这份"统一要求"写在 base.py 里,叫 父类

channels/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 章要深挖的体检动作。

💡
为什么这么设计?—— 一个能帮你指挥 AI 的洞察

因为所有专员形状一样,体检医生就能不用认识任何具体平台,用同一套流程对待全部 15 个。想加第 16 个平台?照着说明书填一个新文件就行,别处一个字都不用改。这叫 可扩展性。以后你让 AI 加功能时,可以明确要求它"照现有模式加一个模块,别改动其它文件"。

听他们对一次话:你跑了 doctor

你敲下 agent-reach doctor。看这五个角色怎么配合 —— 一条一条点开:

体检医生"什么都不懂",反而是优点

看医生的核心代码 —— 它真的不认识任何平台,只会做一件事:挨个喊、挨个记:

doctor.py 真实代码
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",而不是让整个程序跟着崩。

最后把每个人的结果记进一张表。医生自己完全不懂推特和小红书的区别。

🛡️
那个 try/except 是新手最容易漏的

如果没有它,只要 15 个平台里有 1 个体检时抛错,整个 doctor 命令就崩了 —— 你连另外 14 个的状态都看不到。这种"一个环节别拖垮全局"的防御,是区分"能跑的代码"和"靠谱的代码"的关键。以后 AI 给你写循环处理一批东西时,可以问它一句:"其中一个失败了,会不会连累其它的?"

想一想

用户报告"小红书搜索出来的结果乱码"。基于这一章,你会第一个去看哪个文件?

你想让 AI 帮你加一个"知乎"平台。基于这套架构,最干净的做法是?

03

首选 + 备选:会换代的能力

互联网平台天天变:接口被封、工具停更。这一章看 Agent Reach 最聪明的一招 —— 每个平台不是绑死一个工具,而是一串"有先后顺序的候选人"。

先看一个真实翻车现场

2026 年 6 月,一件事真的发生了:B站升级了 风控,把原来一直用来下 B站视频的工具 yt-dlp 彻底拦死了 —— 每次请求都返回 412 错误。

💥
如果代码里写死了"B站 = yt-dlp"会怎样?

那一天起,所有用户的 B站功能全挂。要修,就得改代码、发新版本、等每个用户更新。用户在这期间只能干瞪眼。这是绝大多数"绑死一个工具"的项目的宿命。

但 Agent Reach 的用户当天零操作,B站照样能用。秘密就在它对待每个平台的方式。

把每个平台想成一支救火队

救火队有队长、副队长、替补,排好了顺序。队长受伤了,副队长顶上;副队长也不行,替补上。任务照常完成,不用临时改组织架构。

Agent Reach 里每个平台就是这样一支队伍 —— 一串 后端候选人,排好了先后:

bilibili.py 真实代码
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站专员体检时的核心逻辑:

bilibili.py 的 check() 真实代码
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 也没装,会怎样?点"下一步"看队长副队长挨个落选、替补顶上:

🥇
bili-cli 队长
🥈
OpenCLI 副队
🥉
搜索 API 替补
📋
体检报告
点"下一步"开始

这种"首选挂了自动换下一个"的机制,工程师叫它 故障转移。它是让系统"打不死"的核心手段。

用户想插队指定?也留了后门

万一你就想强制用某个后端呢?名单顺序也支持被你覆盖。看这段"插队"逻辑,它藏着一个很老练的安全细节:

base.py 的 ordered_backends() 真实代码
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)。会发生什么?

04

真探测 vs 假存在

"命令存在"不等于"命令能用"。这一章拆开验货员 Probe —— 它揭穿了一个连很多程序员都会踩的坑,也是 doctor 报告能被你信任的原因。

一个会骗人的常用检查

程序想知道某个工具装没装,最常见的做法是喊一句 which: "系统里有 bili 这个命令吗?"有就返回它的位置。

问题是:它只看到了一个门牌号,没敲门看里面有没有人。

👻
"幽灵命令"是怎么产生的

很多工具(用 pipx / uv 装的)本质是一个小小的"快捷方式",它指向某个 Python 解释器。哪天你升级了系统 Python,那个解释器就换地方了 —— 快捷方式还在,但它指向的目标没了。这时候 which 照样说"在!",你一运行却报错。门牌还挂着,屋里早就搬空了。

如果 doctor 只用 which 检查,它会信誓旦旦告诉你"B站可用",然后你一用就崩。这种报告还不如没有。

验货员分得清三种"不对劲"

Probe 的思路是海关验货:不看报关单,直接开箱。它能区分三种截然不同的状态 —— 而 which 把它们全看成"在":

🚫

missing 没装

门牌都没有。工具根本不在系统里。处方:去装它。

👻

broken 装了但跑不了

门牌在,屋里空。就是上一页那个"幽灵命令"。处方:重装修复。

⏱️

timeout 能跑但耍脾气

屋里有人,但半天不应门。可能是临时抽风,值得再试一次。

💡
分得越细,处方越准

"没装"和"装了但坏了"是两种病,药方完全不同。which 把它们混为一谈,只能给你一句"没找到"。Probe 把它们分开,才能对每种情况给出能直接照做的修复命令。这就是为什么 doctor 报告不只说"坏了",还告诉你"怎么修"。

"开箱验货"这一步长什么样

关键就一步:不光问 which,还真的执行这个命令,看它跑不跑得起来。看 probe.py 里揭穿幽灵命令的那几行:

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 永远给不了你的信息。

你来当验货员:抓出偷懒的那行

下面是一段"想检查工具是否可用"的代码。它犯了本章开头那个经典错误。点你觉得有问题的那一行:

哪一行让这个检查变得不可信?

1 def is_tool_ready(cmd):
2 if shutil.which(cmd): # 找到门牌就当能用
3 return "ok"
4 return "missing"
🔍
这个直觉,能帮你抓 AI 的错

AI 写"检查某工具装没装"的代码时,十有八九给你 if which(cmd): return True —— 又快又看着对,但会放过幽灵命令。学会这一课后,你可以直接对 AI 说:"别只用 which,真的执行一下这个命令确认它能跑。"一句话,就把一个脆弱的检查变成靠谱的检查。

重试也有讲究:别把没救的再试一遍

网络抽风值得重试,但"没装"和"坏了"重试一百遍也不会自己变好。Probe 想清楚了这点:

probe.py 真实代码
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 只对某类失败重试。为什么"没装"和"坏了"重试是浪费?

05

体检报告与大局观

最后一章,我们学会读那张体检报告——它是你排查一切问题的第一站——然后把前四章串成一张完整的地图。

出问题时,先问医生

"小红书搜不出来了"、"推特没反应"—— 别急着猜。agent-reach doctor 这一条命令,就是你的仪表盘。它把 15 个平台此刻的真实状态摆在你面前:

✅ ok 通了,能用,还告诉你现在走的是哪个后端
[!] warn 装了但需要配置或登录 —— 差临门一脚
[X] off / error 没装或坏了 —— 报告里会附上修复处方
🩺
一个能救你很多时间的习惯

任何"某平台不工作"的问题,第一步都是跑 doctor。它会直接告诉你是"没装"、"没登录"还是"坏了",每种都附带下一步该干嘛。这比你盯着代码瞎猜快十倍。给 Agent 用的 SKILL.md 里甚至写死了一条铁律:动手前先跑 doctor --json,按报告选命令。

给人看 vs 给机器看:同一份体检,两种语言

doctor 有个小开关 —— 加一个 标志 --json,输出就从"给人读的报告"变成"给机器读的数据":

cli.py 真实代码
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 会存进本地一个配置文件。这里有个容易被忽视、但很关键的安全细节 —— 存的时候怎么防止别人偷看:

config.py 真实代码
# 一开始就用严格权限创建文件,
# 避免有一瞬间凭据是"人人可读"的
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 很容易写漏的细节,你若在意安全,可以点名要求"创建文件时就设好权限,别分两步"。

大局观:五个角色如何拼成一张图

把前四章串起来。你敲一条命令后,整台机器是这样转的 —— 拖拽下面的角色,看你还记不记得谁管什么:

CLI 前台
Doctor 医生
Channel 专员
Probe 验货员
Config 保险柜

听懂你敲的命令,分派任务

拖到这

挨个喊每个平台报告状态,汇总成报告

拖到这

一个平台一个,管着自己的"首选+备选"名单

拖到这

真的执行命令,揭穿"幽灵命令"

拖到这

安全地存取你的 Cookie 和 Token

拖到这

带走这四个能指挥 AI 的直觉

这门课真正的收获不是"读懂了 Agent Reach",而是四个可迁移的判断力 —— 你可以直接用来指挥和检查 AI:

🎬

按职责拆文件

"照现有模式加一个模块,别改动其它文件。"—— 让 AI 写出好扩展的代码。

🔌

把易变的抽成数据

"把可能会换的选项做成一个有顺序的列表,别写死在逻辑里。"

🔬

验证要动真格

"别只检查它在不在,真的运行一下确认它能用。"

🛡️

追问失败路径

"如果这一步失败/输入无效,会怎样?会不会连累别的?"

🎓
Agent Reach 全程只做了一件事

回到第一章那个心智模型:它是能力层 —— 选型、安装、体检、发手册,然后退场。它自己不读一个字的推特。理解了这个定位,你就理解了它每一个设计决策。这种"想清楚自己该干什么、不该干什么"的克制,才是它最值得学的地方。

最后想一想

你的 Agent 突然报告"Reddit 搜不出东西"。基于这门课,你的第一步是?

你想写个脚本,每天自动检查所有平台状态、把挂掉的记进日志。你会用 doctor 的哪种输出?

一句话概括:安装完成后,Agent Reach 在你"读推特"这件事里扮演什么角色?

🎉
课程完成

你现在能看懂 Agent Reach 的架构、能读它的核心代码、更重要的是 —— 带走了四个能帮你更好地指挥 AI、更快地排查问题的直觉。这,就是把代码变成超能力的样子。