尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

把网站变成CLI:让AI Agent读网页的Token成本降低百倍

把网站变成CLI:让AI Agent读网页的Token成本降低百倍 很多做过 AI Agent 开发的人应该都有同感让智能体去“看”一个网页成本高得让人心疼。随便打开一个常见的技术博客页面源码动辄几十 KB如果把内联脚本、样式、评论区和各种统计代码都算上上到一两百 KB 也不奇怪。而主流大模型是按 token 计费的粗略估算1 个 token 大约对应 3 到 4 个英文字符一个完整的网页转成 token往往就是几千甚至上万。更麻烦的是agent 每次要获取最新内容都得重新抓一遍几轮对话下来单单“读网页”这一个动作就会消耗掉大量上下文。所以当 Hacker News 上出现“Show HN: Turn any website into a CLI for AI agents”这个项目时它能引起关注一点都不意外。项目标题里最有冲击力的信息是这套方案声称比直接读 HTML 少用 142 倍的 token。先不管这个数字在多大范围内成立至少它指出了一个非常本质的问题——HTML 是给人看的不是给模型读的。与其让 agent 去忍受一堆视觉和交互相关的噪音不如先把网页转成紧凑、结构化、命令式的接口让 agent 像调用命令行一样去获取信息。这篇文章不打算只复述这个标题而是想把这个思路彻底拆开它到底解决了什么问题、背后的架构是什么、和现在大热的 MCP / Codex CLI / Claude Code CLI 有什么关系、你自己怎么用几十行代码做一个最小版本以及这套方案在真实工程里的边界在哪里。如果你正在做 AI 编程工具、Agent 工作流或者只是心疼自己账单里的 token 消耗这篇文章应该能给你一些可落地的参考。1. 为什么 AI Agent 读网页时会“吃”掉大量 Token很多人第一次调通 AI Agent 时都会有一个困惑明明模型能力很强为什么让它从网页里提取几条信息效果却这么差、花费却这么高问题往往不在模型而在输入格式。HTML 是一种为浏览器渲染设计的文档格式它包含大量与语义无关的噪音。一个典型网页里真正承载信息的正文可能只占 10% 到 20%剩下的全是导航菜单、侧边栏、推荐位、社交分享按钮、内联 CSS、JavaScript 变量定义、埋点脚本、JSON-LD 结构化数据和层层嵌套的 DIV 容器。这些噪音对 token 消耗的影响是叠加的。首先是体积问题。一个看起来不算复杂的商业页面浏览器保存下来的完整 HTML 经常超过 100 KB这在 token 层面就是两三万 token。如果是英文内容一个 token 大约对应 3 到 4 个字符100 KB 的 HTML 大约在 2 万 token 以上如果是中文为主的混合页面token 数量还要再乘上一个系数。其次是重复问题。Agent 做信息提取时往往要多次访问同一个站点的不同页面或者在同一页面反复请求最新内容每一次都会重新计算整页 HTML 的 token。另一个容易被忽视的因素是上下文长度。上下文窗口不是无限大的即使模型支持百万元素的窗口窗口里塞满了 HTML 标签和脚本真正留给推理和工具调用的空间就被压缩了。Agent 在回答一个问题时需要同时容纳浏览器返回的原始页面、当前对话历史、系统提示词、中间推理结果。页面的冗余部分挤占了大量上下文空间导致 agent 能记住的有效信息更少更容易在长任务中出现遗漏或前后不一致。更进一步看动态渲染的页面还会加剧成本。很多现代网站使用前端框架真正的内容由 JavaScript 在浏览器端渲染直接在 HTML 里拿不到。为了让 agent 读到这类页面开发者往往要引入无头浏览器一个完整的 Chromium 实例在内存和 CPU 上的开销姑且不提它抓回来的 HTML 同样带着大量运行时产生的节点和样式标记token 消耗只会更高。所以这里的核心结论是让 agent 直接读 HTML本质上是在给模型布置一道“从工程文件里找用户可见内容”的逆向题目。模型确实能做但效率极低、成本极高、稳定性还差。与其提高模型的解析能力不如换一个思路在输入端就把网页转换成 agent 真正需要的那种结构化信息。这也是“把网站变成 CLI”这个方案最值得讨论的地方。2. 把网站变成 CLI核心思路与整体架构“把网站变成 CLI”这句话看起来很直接但它并不只是“写一个爬虫脚本再套上命令行参数”那么简单。它的核心增量在于改变了数据消费方。传统爬虫抓下来的数据要展示给人看开发者会设计排版、分页、筛选逻辑而这里的 CLI 是给 AI Agent 调用的输出必须足够紧凑、足够稳定、足够容易被解析。可以做个类比。一个普通网站的访问方式就像走进一家餐厅你要看菜单、等服务员、理解桌号和特色推荐整个过程充满视觉和人机交互设计而面向 agent 的 CLI 方案就像把这家餐厅的“点菜协议”直接变成一组标准命令比如 list 看菜品、detail 看做法、order 下单。虽然最终吃到的东西一样但 agent 不需要理解环境只需要执行命令、读取返回值。从架构上看这个方案的链路并不复杂核心步骤可以概括为抓取、解析、暴露、调用。抓取是获得目标 URL 的原始 HTML这里可以直接用 HTTP 请求也可以用无头浏览器处理动态页面解析是把 HTML 中真正有价值的信息抽取出来通常依赖 BeautifulSoup、XPath 或 Readability 这类工具暴露是指把解析结果封装成一组有固定语义的命令比如 list、detail、search调用则是让 agent 通过 subprocess 或函数调用去执行这些命令拿到标准的文本或 JSON 输出。相比把整页 HTML 塞给模型CLI 方案最直接的收益是 token 数量大幅下降。原因很简单CLI 输出只包含目标数据字段不包含任何装饰性标记。同样是看一篇文章列表HTML 需要把整个 DOM 树带着样式和脚本提供给模型而 CLI 的 list 命令可能只返回“标题、链接、发布时间、摘要”四个字段。指令本身也很短agent 不需要理解页面结构它只需要知道这个 CLI 暴露了哪些命令然后按命令去取数。这里有一个容易混淆的地方就是 CLI 和 MCP 的关系。很多读者看到“让 agent 调用工具”会想到 MCP两者其实在不同的抽象层。MCP 是模型上下文协议它定义的是“大模型如何发现并调用外部工具”的协议规范而 CLI 是一个程序暴露给操作系统的命令入口。网站转 CLI 方案里可以用 MCP 做工具发现也可以让 CLI 命令被 MCP 服务包装一层反过来MCP 服务内部也可能只是执行了一个命令行程序。所以它们不是替代关系而是可以组合的关系。理解这个区别对后续搭建 agent 应用非常有帮助。关于标题里的 142 倍我需要多说一句。这个数字是项目作者在特定网站上测试得到的结果可以用来建立直觉但不应该被当作普适保证。从工程上说节省 10 倍、50 倍还是 100 倍取决于两个因素原始页面的 HTML 冗余度以及 CLI 输出的抽象程度。如果一个网站页面极其复杂、导航和脚本占了 95%而 CLI 只暴露一个“新闻标题列表”那节省 142 倍完全有可能。如果一个页面本身已经接近纯文本CLI 又要求返回完整正文那节省空间就有限。理解了这一点你在评估方案时就不会被单个数字误导。3. 当前技术背景CLI 正在成为 AI 编程的新入口把网站变成 CLI 这个想法放在两年前可能只是一个普通的效率工具但放在现在的技术节点上它踩中了一个更大的趋势CLI 正在从“开发者手里的高级工具”变成“AI Agent 的标准交互界面”。如果近期关注过 AI 编程工具应该能注意到 Codex CLI、Claude Code CLI 这类终端型产品的热度。它们和传统 IDE 插件最大的不同是把整个编程助手做成了可以直接在终端里运行、可以被脚本驱动、输出天然结构化的程序。这个变化的意义在于AI Agent 不只是需要理解自然语言还需要一个能稳定调用外部能力的“操作层”。终端 CLI 恰好具备几个非常适合 agent 调用的特性它输入简洁通常一个命令加几个参数就能表达完整意图它输出可控标准输出和错误输出分开它天然可组合可以在 Shell 脚本里串起来形成更复杂的流程。当 agent 要通过代码和系统交互时CLI 往往是成本最低、兼容性最好的方式。网站转 CLI 的方案正好是这一趋势在信息获取侧的复刻。AI 编程工具已经把“怎么写代码”的入口变成了 CLI那么“怎么获取网页信息”的入口也有机会变成 CLI。一个 agent 如果既能在终端里操作代码又能通过一组命令读取外部网站的数据它就能完成更多端到端的任务而不是每走一步都要把搜索结果、网页原文一股脑塞进上下文。对普通开发者来说这个趋势带来的启示是如果你正在设计一个面向 AI 的应用除了 REST API不妨把 CLI 也当作一等公民来考虑。API 适合服务与服务之间调用格式严谨但要定义完整的请求和响应约束CLI 更适合 agent 在终端环境里快速试探和使用。给产品加一个薄薄的 CLI 层成本不高却能显著降低 agent 接入的门槛。尤其在内部工具、个人自动化脚本、DevOps 运维这类场景里CLI 的灵活性和可观测性往往比一个完整的 Web 服务更好。当然CLI 也不是万能的。它不适合需要图形操作界面、需要实时视觉反馈、或者参数极其复杂的场景。但用来做“信息查询型”的网页封装它几乎是最优解。这也是为什么这个 HN 项目能在众多 Agent 基础设施里脱颖而出它没有发明新协议也没有引入重型框架只是把一个一直存在的问题换了一个方式解决而且解决得足够简洁。4. 环境准备与前置条件为了让你真正理解这个方案我们用一个最小示例跑通“网页转 CLI”的完整流程。示例采用 Python 实现因为 Python 在 HTML 解析和命令行工具开发两个方向都有非常成熟的生态也适合快速验证想法。如果你用的是其他语言思路完全一致不需要照搬代码。运行环境方面建议使用 Python 3.9 或更高版本操作系统不限Windows、macOS、Linux 都可以。示例依赖三个库requests 用来发送 HTTP 请求、beautifulsoup4 用来解析 HTML、click 用来构建命令行接口。这些库都很常见安装命令如下pip install requests beautifulsoup4 click为了不引入外部网站的不确定性也为了让你能立刻跑通我不会直接用某个线上页面做演示而是在本地准备一个模拟的 HTML 文件然后在同一个项目里编写解析器和 CLI。这样整个实验不依赖网络环境也不会涉及抓取授权问题你可以先把流程跑通再替换成自己真实需要的数据源。项目目录结构建议如下website-cli-demo/ ├── pages/ │ └── tech_news.html ├── site_cli.py └── agent_call_demo.pypages 目录存放要解析的 HTML 文件site_cli.py 是转出来的 CLI 主入口agent_call_demo.py 用来演示一个 AI Agent 如何调用这个 CLI 并拿到结构化结果。下面我们会依次创建这些文件。如果你暂时不想用 click 这类第三方库也可以用 Python 标准库里的 argparse 替代但对多命令工具来说 click 的可读性好很多所以我这里选择 click。5. 完整示例把一个网页改造成 Agent 可调用的 CLI5.1 准备示例 HTML 文件先创建一个模拟的“技术早报”页面内容刻意保持简单方便观察解析逻辑。文件路径为 pages/tech_news.html内容如下!DOCTYPE html html langzh-CN head meta charsetUTF-8 title技术早报/title stylebody { font-family: sans-serif; }/style /head body article h2a href/posts/1AI Agent 入门指南/a/h2 p classmeta发布时间2025-01-10/p p classsummary本文讲解 Agent 的基本概念、工具调用与工作流设计。/p /article article h2a href/posts/2CLI 工具设计最佳实践/a/h2 p classmeta发布时间2025-01-11/p p classsummary稳定输出、错误处理和可组合性是 CLI 工具设计的核心。/p /article article h2a href/posts/3大模型 Token 计费指南/a/h2 p classmeta发布时间2025-01-12/p p classsummary了解 tokenizer 的工作方式才能有效控制 AI 应用成本。/p /article /body /html这个文件里既有标题、链接、正文摘要也包含样式和少量嵌套结构足够用来演示 HTML 噪音如何被剥离。实际开发中你需要把这里的选择器替换成目标网站的真实节点。5.2 编写 CLI 主程序接下来创建 site_cli.py这是整个示例的核心。它读取 HTML 文件用 BeautifulSoup 提取文章信息然后通过 click 暴露三个命令list 列出文章列表、detail 查看单篇文章、后续可以继续扩展 search 搜索。输出支持 text 和 json 两种格式json 格式是为了让 agent 更容易解析。# 文件路径site_cli.py import json from pathlib import Path import click from bs4 import BeautifulSoup PAGE_PATH Path(__file__).parent / pages / tech_news.html def load_articles(): if not PAGE_PATH.exists(): raise FileNotFoundError(f找不到页面文件{PAGE_PATH}) soup BeautifulSoup(PAGE_PATH.read_text(encodingutf-8), html.parser) articles [] for idx, item in enumerate(soup.select(article), start1): link item.select_one(h2 a) meta item.select_one(.meta) summary item.select_one(.summary) articles.append({ id: idx, title: link.get_text(stripTrue) if link else , url: link.get(href, ) if link else , date: meta.get_text(stripTrue).replace(发布时间, ) if meta else , summary: summary.get_text(stripTrue) if summary else , }) return articles def render(articles, fmt): if fmt json: return json.dumps(articles, ensure_asciiFalse, indent2) lines [] for a in articles: lines.append(f[{a[id]}] {a[title]} | {a[date]}) lines.append(f {a[summary]}) return \n.join(lines) click.group() def cli(): 将网页转换为 AI Agent 可调用的 CLI 接口。 pass cli.command(list) click.option(--limit, default10, show_defaultTrue, help最多返回多少条) click.option(--fmt, typeclick.Choice([text, json]), defaulttext) def list_articles(limit, fmt): 列出文章列表。 articles load_articles()[:limit] click.echo(render(articles, fmt)) cli.command(detail) click.argument(article_id, typeint) click.option(--fmt, typeclick.Choice([text, json]), defaulttext) def detail_article(article_id, fmt): 查看单篇文章摘要。 articles load_articles() target next((a for a in articles if a[id] article_id), None) if target is None: raise click.ClickException(f不存在 id{article_id} 的文章) click.echo(render([target], fmt)) if __name__ __main__: cli()这份代码有几点值得解释。load_articles 是整个转换流程的“解析层”它从 HTML 文件中读取原始页面然后用 soup.select 定位文章节点再用 get_text 和 get 提取文本与链接。format 函数把文章对象渲染成不同格式text 适合人看json 适合程序解析。list 和 detail 是两个命令参数通过 click 的 option 和 argument 定义这样 agent 调用时语义非常清楚。5.3 让 Agent 调用这个 CLICLI 本身写好后Agent 可以用多种方式调用它。最直接的方式就是通过 subprocess 执行命令然后解析标准输出。下面这个 agent_call_demo.py 演示了如何调用 site_cli.py并把 JSON 输出解析成 Python 对象# 文件路径agent_call_demo.py import json import subprocess def call_site_cli(*args): result subprocess.run( [python, site_cli.py, *args], capture_outputTrue, textTrue, encodingutf-8, checkFalse, ) if result.returncode ! 0: raise RuntimeError(fCLI 调用失败{result.stderr}) return result.stdout if __name__ __main__: output call_site_cli(list, --limit, 2, --fmt, json) articles json.loads(output) for item in articles: print(f标题{item[title]}链接{item[url]}摘要{item[summary]})在实际的 Agent 应用中这段 subprocess 调用逻辑通常会被封装成一个 function calling 工具或 MCP 工具工具的 description 可以写成“调用站点 CLI 获取技术文章信息”然后让模型在需要时调用。这里的关键是CLI 已经成为 agent 和网站之间的稳定接口模型不需要看 HTML 源码只需要知道命令有哪些、参数是什么、返回值结构是什么。6. 运行结果与 Token 消耗对比示例写完以后可以实际运行验证。先看文本输出cd website-cli-demo python site_cli.py list --limit 2预期输出如下[1] AI Agent 入门指南 | 2025-01-10 本文讲解 Agent 的基本概念、工具调用与工作流设计。 [2] CLI 工具设计最佳实践 | 2025-01-11 稳定输出、错误处理和可组合性是 CLI 工具设计的核心。再验证 JSON 输出它更适合 agent 解析python site_cli.py detail 3 --fmt json预期输出如下[ { id: 3, title: 大模型 Token 计费指南, url: /posts/3, date: 2025-01-12, summary: 了解 tokenizer 的工作方式才能有效控制 AI 应用成本。 } ]到这里整个“网页转 CLI”的最小链路已经跑通。现在我们来做一个直观的 token 消耗对比。pages/tech_news.html 文件大小大约 3.6 KB而 list 命令输出两条记录时文本模式只有 180 字节左右JSON 模式也只有大约 300 字节。不同模型 tokenizer 不一样但按英文语料约 4 字符/token 的粗略标准看HTML 原文大约对应 900 tokenCLI 输出大约对应 50 到 80 token节省了一个数量级以上。这个演示页面本身并不复杂所以还没有达到 142 倍那样的量级。如果你把方案套到一个真实的大型商业网站上页面 HTML 可能超过 100 KB而 CLI 只返回标题和摘要缩小幅度会非常可观。节省 token 的同时还有一个容易被忽略的收益因为输入变小了agent 的上下文窗口被释放出来它可以一次查看更多候选内容或者在同一轮对话中调用更多工具整体任务的成功率往往会提高。要判断这个方案是否值得你用我建议不要只看别人的测试数字。你可以在自己的目标页面上做一次对比实验把原始 HTML 保存下来再把 CLI 输出保存下来分别统计字符数再换算成 token 数看差距是多少。这个差距就是你的真实收益。页面动态渲染程度越高、导航脚本越多、CLI 抽象越精简收益越大。7. 常见问题与排查思路在实际运用“网站转 CLI”的方案时你会碰到不少具体问题。我把最常见的几类整理成表格方便遇到问题时快速定位。问题现象可能原因排查方式解决方案抓取不到正文内容页面由 JavaScript 动态渲染用 curl 查看原始 HTML对比浏览器渲染结果改用无头浏览器或等待渲染完成后再抓取中文内容乱码页面编码识别错误查看响应头中的 charset在解析前根据 charset 显式指定编码示例中使用 encodingutf-8解析不到目标节点页面结构改版或选择器写错在浏览器开发者工具中重新检查 DOM 节点更新 BeautifulSoup 的 select 路径做好选择器配置化agent 调用的结果不稳定CLI 输出格式不固定对比多次输出检查是否夹杂日志或错误信息强制输出 JSON错误信息写入 stderr不要污染标准输出请求频繁被限流短时间内请求过多检查网站返回状态码和响应头增加缓存、设置超时和重试必要时降低请求频率需要登录才能查看内容目标页面在鉴权之后确认是否有公开访问权限为 CLI 增加 Cookie 或 Token 注入能力但必须先确认授权这里我想展开讲两个最常见的问题。第一个是动态渲染。很多现代网站虽然 URL 看起来是普通路径但真正内容是通过 JavaScript 异步请求拿到的直接请求 URL 拿到的 HTML 只是空壳。遇到这种情况不要急着上无头浏览器先观察页面是否包含 JSON 数据接口很多框架会把初始数据放在 script 标签里的NEXT_DATA或 window.INITIAL_STATE中解析这些字段通常比重启一个 Chromium 实例便宜得多。只有数据缺口无法用静态请求补齐时才考虑无头浏览器方案。第二个问题是输出格式污染。很多命令行程序会把调试日志、警告信息混在标准输出里这对人来说不碍事但对 agent 来说会直接破坏 JSON 解析。一个可靠的 CLI 工具应该做到正常数据只输出到 stdout日志和错误只输出到 stderr如果命令执行失败返回非零退出码。agent 调用端也应该养成检查 returncode 的习惯而不是无脑解析 stdout。8. 最佳实践与工程建议把网页转成 CLI 在技术上并不复杂但要想在真实项目里稳定运行还要考虑很多工程细节。第一个要强调的就是合规与授权。抓取外部网站前先确认网站的 robots.txt 是否允许访问目标路径是否要求附加条款涉及登录态、个人数据、付费内容的页面没有明确授权就不要处理更不要做成公开服务。这个方案真正适合的场景是你自己有权限的内容、公司内部系统、公开授权接口的包装、或者通过正规 API 获取数据后的再加工。CLI 命令设计方面建议遵循“动词 对象 参数”的命名习惯list、detail、search、export 这类动词容易让 agent 从 description 里理解用途。每个命令的输出字段要保持稳定不能今天返回 title明天改成 heading。为了向前兼容新增字段比改名更安全。输出格式上建议同时支持 text 和 json但让 agent 默认使用 json因为结构化的键值对能减少模型在文本里搜索信息的开销。缓存在这个方案里非常重要。网页内容和新闻标题可能有分钟级延迟但 10 秒内的重复请求完全可以用内存缓存挡住。设计缓存时要给每个页面设置合理的 TTL并在 CLI 里增加一个 --no-cache 参数用于强制刷新。这样既降低目标网站的请求压力也降低 token 消耗。可以说缓存是“少买 token”之外最容易获得收益的优化手段。如果你打算把多个网站转成同一套 CLI 提供给 agent我建议把这些 CLI 脚本统一包装成工具描述暴露为 MCP 工具或 function calling 工具。工具的描述文本要写清楚“这个工具返回什么数据、适合什么场景、可能有什么限制”比如“返回最近 5 条文章标题和链接适合用于生成技术早报不支持搜索历史数据”。描述越清楚模型在复杂任务里就越不会误调用。这里需要注意的是CLI 和 MCP 不要重复造轮子CLI 负责执行和输出MCP 负责工具发现和约束两者职责分离。最后说一下什么时候不适合这个方案。如果你要处理的是需要视觉理解的任务比如“看页面顶部广告位是哪个客户的”、需要拖动登录滑块、需要实时双向交互的图表页面CLI 封装就不是最合适的方案。CLI 适合的是信息查询型、结构化程度较高、人类通过点击几下就能获得数据的场景。页面越接近“数据展示页面”CLI 方案越有价值页面越接近“交互应用”越不适合用这种方式强行抽象。9. 总结与下一步这篇文章从“AI Agent 读网页太费 token”这个实际问题出发说了三层意思。第一层直接给模型读 HTML 是一种高成本、低稳定的方案问题不在模型而在输入格式第二层把网站封装成 CLI能够让 agent 用极小的 token 代价获取结构化信息而且这个思路和 Codex CLI、Claude Code CLI 推动的“CLI 作为 AI 原生交互入口”是同一个方向第三层这个方案落地成本很低几十行代码就能做出来但真正用好它还需要在授权、缓存、输出稳定性和工具封装上下工夫。如果你看完想动手实践建议的下一步是找一个你自己经常访问、信息结构清晰、允许抓取的网页用上面示例里的思路写一个最小 CLI先跑通 list 和 detail 两个命令再在 Agent 工作流里通过 subprocess 或 MCP 调用它。跑通之后你有两个可以继续深入的方向一个是把解析层做扎实支持更多页面类型和节点配置另一个是把 CLI 服务化部署成一个供团队多个 agent 共享的工具接口。最后再提醒一次项目标题里的 142 倍 token 节省是作者在特定页面上的测试结果你可以把它当作一个值得验证的假设但不要直接拿它去写汇报材料。真正可靠的做法是在你的目标页面上自己做一次字符量级对比用数据判断这个方案值不值得投入。如果你也在做 Agent 相关的工作不妨现在就试试把一个你经常手工复制给模型的网页转成一个 50 行的 CLI。
返回列表