第3B篇:LLM 闪耀的地方——以及智能体会毁掉的 90%(下)

系列:我们构建了一条几万行代码的流水线。智能体为何做不到。

上一篇:第3A篇。
Read the English version.

每个问题看起来都很简单,直到它在生产环境里把你的预算烧穿。

上篇讲了 LLM 闪耀的地方和编排的 90%。模糊分类是 LLM 不可替代的 10%,域名判断同样需要语义理解。剩下 90% 全是编排。能写 if 的地方,就别问 LLM。

但编排到底面对哪些具体问题?智能体能做好吗?

想象一下你搬进了一间新公寓。签合同、拿钥匙,大事搞定了。然后你发现:淋浴水管漏水,厨房插座接触不良,衣柜门合不上,楼下邻居的 Wi-Fi 干扰你的蓝牙音箱。没有哪一件是“难”的,但每一件都得花半天。三周后你回头看,搬家本身只花了一天,收拾这些“小事”花了一个月。

生产级采集流水线就是这样。上一篇讲的是大脑,分类系统,也就是签合同和拿钥匙。这一篇讲的是搬进去之后的那一个月。六个“小问题”,每个都觉得半天能搞定,每个最后都折腾了好几周。

简而言之:六个看起来简单的问题——链接提取、查询参数、停止规则、PDF 处理、PDF 推迟策略、robots.txt 合规——每一个在真实网站上都以意想不到的方式崩掉。修复靠的是逐家公司翻车后积累的经验。一旦修好,就写进代码一劳永逸。

智能体每次运行都重新踩坑。流水线只踩一次,然后永不再犯。

问题 1:链接提取

“把页面上所有链接找出来”有多难?

你去超市买东西。货架上摆着的,你一眼就能看到。但有些商品在仓库,有些在冷柜后面,有些印在传单上,只有问店员才知道。网页上的链接也是这样:有些明明白白写在页面上,有些藏在你根本想不到的地方。

智能体当然可以调用现成的 HTML 解析库。问题是:哪个能直接用?

流水线一开始用的是最朴素的解析方式:找到所有 <a href> 标签,完事。

然后碰到一家企业,导航栏全是 JavaScript 渲染的。静态解析器看到的是一个空页面,就像你走进一家店,发现货架是空的,因为所有商品要扫二维码才会从后台送出来。于是先加无头浏览器渲染,然后再解析 HTML。

然后另一家公司把下载链接藏在 <link><area> 标签里。这些不是常规链接标签,通常不会有人在里面放下载链接,但现实不管常规。加上。

然后有公司用了 <base> 标签,改变页面里所有相对链接的基准地址,搞坏了相对 URL 的解析。修。

然后发现光解析原始 HTML 还不够,于是加了一路 Markdown 转换,因为标准化后的文本提取 URL 更方便。但 Markdown 路线处理不了所有边界情况,所以另一条更复杂的原始 HTML 解析路线也得保留。两边结果合并去重。

看到规律了吗?每次以为“这下该齐了”,下一个网站就教你做人。

然后是去重问题。/page/page//page/index.html 是同一个页面。/reports?lang=en&sort=date/reports?sort=date&lang=en 也是。但 /reports?lang=en/reports?lang=fr 是不同页面。去重逻辑必须理解哪些差异有意义。

javascript: 链接、mailto: 链接、tel: 链接、纯锚点链接,全部过滤。Cookie 同意弹窗遮挡了真实导航,处理。下载链接和菜单链接,分开追踪。

几百行规范化代码。每一行都追溯到某个真实网站上出过的问题。

讽刺的是,智能体做好这件事最可能的路径是什么?有人把实战检验过的提取流水线开源,有人包装成工具接口,智能体调用。很好。但现在智能体调的是工程师写的确定性代码,它变成了流水线的自然语言外壳。

智能体是界面。代码是引擎。

这正是本系列主张的组合模式。

没有哪个库能在几千个企业网站上把链接提取做对。答案是几百个修复,每一个来自一次真实翻车。

问题 2:查询参数

如果说链接提取是“找到所有门”,查询参数就是“发现很多门通向同一个房间”。

查询参数就是 URL 里问号后面那串东西,比如 ?page=2&lang=en。企业网站靠它们生成无限的 URL 变体:

/reports?page=1
/reports?page=2
/reports?lang=en
/reports?lang=fr
/reports?sort=date
/reports?sort=date&page=3&lang=en

每个组合在技术上都是唯一的 URL。

第一反应:直接去掉所有查询参数。URL 干净了,没重复了。

马上翻车。?lang=fr 可能是一份不存在英文版的报告的法语版。?year=2024 可能是到达当前披露的唯一途径。全部去掉,丢文件。

第二反应:全部保留。现在 1000 个 URL 变成 10000 个,甚至永远爆炸下去。每个参数组合技术上都是“新”URL,采集器陷入无限循环。

真正的答案:逐类参数区别对待。

流水线维护一套两层系统。第一层,一个固定的垃圾参数屏蔽列表:广告追踪参数、分析参数、会话 ID、临时 token。无条件砍掉。

然后是一个启发式分类器,把剩余参数分门别类:分页类、排序类、显示设置类、搜索分面类、区域类、资源 ID。分页和排序去掉,因为它们制造循环。区域和资源 ID 保留,因为它们指向不同内容。

这套分类不是设计出来的。是云账单预警逼出来的。

有一天收到一封超额预警邮件:LLM 调用费用飙升,远超预期。排查后发现,采集引擎在一家金融机构的网站上陷入了死循环。查询参数的不同组合生成了成千上万个“不同”的 URL。?sort=date&page=3?page=3&sort=date 技术上是两个 URL,但指向同一页。

采集器不知道。分类器不知道。每个“新”URL 都老老实实调一次 LLM,连续跑了好几天都停不下来。就像一个人在旋转门里转圈,每转一圈都觉得自己到了新地方。

账单就这么滚上去了。

顺便一提,发现这个 bug 的不是日志监控系统,是财务邮件。

智能体看到两个参数顺序不同的 URL,很容易把它们当作两个不同页面。它跟踪每个变体,在无法识别的重复项上烧完 token 预算。等它“意识到”自己在转圈,如果它能意识到的话,预算已经用完了。

“哪些参数重要?”听起来一句话就能答。实际上,它是一套从真实失败里攒出来的分类系统。

1000 个 URL 和无限爆炸之间,就差一层参数规范化。而做对和做错之间,差的是好几周迭代。

问题 3:什么时候该停

你在自助餐厅吃饭。前面几道菜是你专门来吃的。然后你看到甜点台、水果台、饮料区。每样单独看都“值得拿”,但如果什么都拿,盘子堆成山,最后大部分倒进垃圾桶。

智能体不知道什么时候该收手。

每个企业网站都有数百篇新闻文章。许多提到了 sustainabilityclimate,技术上 ESG 相关。但核心报告、政策、披露文件到手之后,再收 500 篇新闻就是在堆废纸。盘子已经满了。

这花了好几周才搞明白。早期的流水线像个新来的实习生,看到什么都往袋子里塞,不管有没有用。某家大型跨国公司,几千篇新闻页面、新闻稿、博客文章。磁盘上堆满噪音。下游分析被低价值内容淹没。

所以流水线学会了做预算。新闻页面硬上限 200,代码里写死。ESG 核心内容,真正的报告、政策、披露文件,不受上限约束。PDF 下载推迟到页面采集完成后,因为页面产生新链接,PDF 不会。在采集过程中下载一份 200 页报告会阻塞链接发现循环。

自适应阈值在线索充裕时收紧,稀缺时放宽。逻辑很简单:好内容找得到的时候,挑剔一点;线索快枯竭的时候,把网撒大一点。

这些都不是预先设计的。每条规则都来自观察采集器在新闻存档上超支、在巨大 PDF 上卡住、在跨国公司上过度探索、在小公司上探索不足。

智能体会一路跑到预算见底。没有上限,没有推迟策略,没有自适应收紧。做个私人助理任务,人们已经嫌贵了。想象一下在 5000 个企业网站上不加约束地采集。

人类分析师知道什么时候该停下来。流水线从观察分析师学到:200 篇新闻够了,先把正式报告搞到手。

有意思的是,人类只需要看几十个网站就能形成这种直觉。流水线的做法是把人类从有限观察里提炼出的规律写成代码,然后在 5000 家公司上无损复制。

“什么时候够了”是一个工程判断。流水线把它写死在代码里。智能体连这个问题的存在都未必意识得到。

前三个问题,你可能觉得:好吧,细节多了点,但道理能想通。

下面这个不一样。下面这个,即使你想通了道理也没用,因为坑不在逻辑里,在现实的混乱里。

问题 4:PDF

PDF 下载听起来很简单。点击链接。保存文件。八个字。

实际上,这是整条自动化采集里最不可预测的环节。

先说文件名,光这一件事就够折腾三轮。

/content/dam/doc-847293.pdf 这样的 PDF 链接什么都没告诉你,就像收到一个快递,外包装上只写了一串流水号,里面装的什么全靠猜。

好消息是,服务器有时候会告诉你真名。它可能发一个 Content-Disposition 头,也就是服务器告诉浏览器“这个文件叫什么名字”的信号:attachment; filename="sustainability-report-2024.pdf"。这才是真名字。

但有些文件分发层不发这个头。那你就查重定向后的最终 URL,也许重定向链会落在一个更有描述性的端点。两者都没有的话,只能退回去用原始 URL 的路径段碰运气。

流水线按优先级依次尝试这三种来源,因为没有任何一种可靠。

然后是下载本身。这里的每一种失败方式都够写一个 bug 报告:

  1. 下载对话框:自动化浏览器可以拦截,但处理方式因浏览器版本和操作系统而异。
  2. 新标签页打开:浏览器把 PDF 渲染出来而不是下载。人能看到,代码看不到。自动化代码在那儿等文件落地事件,等到天荒地老。
  3. 重定向链:三四个跳转才到达实际文件。HTTP 客户端跟着第一个重定向,结果认证 cookie 没带上,收到 403。或者重定向跨了域名,Referer 头破坏了认证。
  4. 错误的 Content-Type:服务器说“这是一个网页”,其实是 PDF。浏览器信了。
  5. PDF 门禁:输入邮箱以下载、接受条款、完成人机验证。流水线得做门禁检测、模式匹配、回退策略。绕不过去的,留日志给人工审。

好不容易下载成功了?保存照样能出事。生产机器上的安全软件可能在文件写到一半时锁住它。流水线使用原子文件保存:先写到临时文件,确认完整写入磁盘后再重命名为正式文件名。重命名失败,最多重试五次。没有这个机制,你会得到写了一半的损坏 PDF。

这些都不是智能。是从几百个边缘案例里硬磕出来的工程。

一个点击 PDF 链接就指望文件乖乖落地的智能体,不是在采集,是在许愿。

最危险的不是失败,而是静默失败:没有报错,只是少了文件。智能体浑然不觉。它继续往下跑,以为自己“采到了”那份报告。

而那份报告还安安静静躺在它早已关掉的浏览器标签页里,从未被下载。

“点击链接,保存文件”是人类对 PDF 的理解。流水线的版本是几百上千行代码,把能出错的地方全堵上。

光 PDF 处理就能搞垮一个智能体。而它只是流水线已经解决的几十个“简单”问题之一。

问题 5:PDF 推迟

问题 4 讲的是单个 PDF 怎么下载。这个问题更上一层:什么时候下载,甚至该不该现在就下载。

打个比方:你搬家到一座新城市。第一天你不会立刻把所有家具买齐。你先四处逛逛,搞清楚哪条街有什么店,然后再决定去哪里买什么。先探路,再搬家。

早期的流水线没有这个觉悟。策略六个字:见一个下一个。碰到 PDF 就下载,不问值不值。听起来很合理,链接在手,趁热打铁。

然后跑了几轮迭代,问题浮出水面。

有些公司的网站上挂着几百份 PDF:年报、季报、新闻通稿、投资者演示文稿、政策文件、治理框架。分类器设计原则是“宁可多收不可漏掉”,所以大部分都通过了 ESG 过滤。头几轮迭代还没采完页面,光 PDF 就已经下载了 500 多份,预算直接打爆。

更糟的是,PDF 是死胡同。

一个网页采完之后能提取出几十个新链接,进入下一轮分类循环。这是链接发现的核心驱动力。但 PDF 不会。一份 PDF 下载完就完了,不给你带来任何新的 URL。

在采集过程中途停下来下载一份 200 页报告,就像探路走到一半停下来搬家具。你还没搞清楚城市布局,就已经把卡车堵在路上。页面采集,真正能发现新链接的环节,停在那里干等。一份大 PDF 占着连接槽和带宽,其他请求全在排队。

智能体会想到这一层吗?

三个教训逼出了推迟策略:

第一,PDF 是死胡同,所以先采页面。页面产生新链接,链接产生新的分类轮次,新的分类轮次发现更多内容。这个循环是整条流水线的核心。PDF 不参与这个循环,所以放到最后。

第二,推迟之后可以重新排序。如果在发现 PDF 的那一刻就立刻下载,先来的先下,跟相关性无关。但如果把所有 PDF 链接先攒着,等页面采集全部完成后再统一处理,就可以按相关性重新排序。出现在 ESG 专题页上的 PDF 排在前面,出现在新闻列表里的排在后面。最相关的 PDF 先下载,预算花在刀刃上。

第三,批量下载有专门策略。PDF 下载的失败模式跟页面采集完全不同,所以混在一起处理只会互相干扰。推迟后可以用专门的并发策略、重试机制、超时设置来处理 PDF,不会搞乱页面采集的节奏。

智能体没有“推迟”这个概念。它看到 PDF 链接就会尝试下载,因为它的指令是“采集 ESG 内容”,眼前这个 PDF 看起来就是 ESG 内容。它不知道现在下载这份 PDF 意味着放弃发现更多链接的机会。它不知道 500 份 PDF 里可能只有 50 份真正重要。它不知道把 PDF 攒到最后可以重新排序。

“先采页面,再下 PDF。”六个字的策略。背后是好几轮迭代吃过的亏。

推迟不是偷懒。是用正确的顺序做正确的事。

问题 6:robots.txt

前五个问题都是工程问题。这个不一样:这是组织内部约束。跟代码质量无关,跟模型能力无关。法务说了“不行”,那就是不行。

采集引擎开发到一定程度,合规和 IT 审查介入了。问题很直接:这东西合法吗?自动化访问企业网站,有没有法律风险?

实际上,采集引擎的行为跟人类分析师手动浏览没有本质区别:每个域名固定间隔限速,不比人用浏览器点链接快多少。它不暴力请求,不绕认证,不伪装身份。数据用途是公开信息的 ESG 分析,不是竞争情报,不是数据倒卖。这些解释得通。

但 IT 那边还有一个硬性要求:必须遵守 robots.txtrobots.txt 是网站根目录下的文本文件,用来告诉自动化程序哪些路径可以访问,哪些别碰。不管你的采集多温和,如果网站明确说“别来”,那就不能去。这不是技术选择,是合规底线。

于是这变成了一个新的工程问题。

robots.txt 的规范看似简单,就像交通标志:红灯停,绿灯行。解析 User-agentDisallow,能有多难?

但真实世界里的 robots.txt 像一座老城的交通系统:有的路口有灯,有的只有手写标语,有的标志互相矛盾,有的干脆是坏的。有的用通配符,有的针对不同爬虫设不同规则,有的格式本身就是错的。

每个 URL 在发出请求之前都得先检查:这个路径被 robots.txt 禁止了吗?

这个功能迭代了几轮才稳定。第一版只处理基本规则,能跑,但碰到通配符就漏判。第二版加了通配符匹配和 Crawl-delay 支持。第三版处理了 AllowDisallow 的优先级冲突。每一轮都是拿真实网站的 robots.txt 测试,发现边缘情况,再迭代。

现在每个 URL 在进入采集队列之前都要过 robots.txt 检查。被禁止的路径直接跳过,记日志,不请求。这是一个纯确定性的前置过滤,跟分类无关,跟内容无关,只跟合规有关。

智能体不会主动想到去查 robots.txt。即便有人在提示词里写了“遵守 robots.txt”,智能体也得自己去获取、解析、对每个 URL 做匹配,而且每次运行都重新来一遍。

更关键的是:合规不是“尽量做到”的事情,是必须做到的事情。一个概率性系统去处理一个零容忍约束,这本身就是矛盾。

法务和 IT 不关心你的模型多聪明。他们关心的是:你能不能证明每一个请求都检查过 robots.txt?流水线能。智能体不能。

合规约束不需要智能。它需要的是每次都一样、可审计、零遗漏的执行。

六个问题,一个模式

回头看这六个问题:链接提取、查询参数、停止规则、PDF 处理、PDF 推迟策略、robots.txt 合规。它们长得一模一样:

  1. 外面看很简单:“提取一下链接能有多难?”
  2. 放到真实网站上就崩,而且以你想不到的方式崩。
  3. 修复靠翻车经验:盯着流水线在某家公司上出事,然后修。
  4. 一旦修好,写成代码一劳永逸。不需要聪明,只需要不忘。

智能体每跑一次都重新踩坑。工程师修一次,写进代码,永不再犯。

还记得开头搬公寓的比方吗?签合同是大事,但真正让你住得舒服的,是修好每一个漏水的水管和接触不良的插座。这六个问题就是那些水管和插座。不聪明。不简单。但修好了就修好了。

这就是那 90%。已经解决了,而且明天不需要重新解决一遍。

外面的人觉得智能体包治百病,这可以理解。但即便在开发团队内部,对 AI 的应用方向也分成了两派:一派认为 AI 是通用底座,搭好基础设施、把一切包上去就行;另一派认为人的判断仍然关键,该投入的恰恰是领域经验和前沿知识。这不是外行和内行的分歧,而是两种工程哲学的分歧。

选择智能体路线,隐含的信念是:通用工具能搞定一切,本质上在押注通用智能已经到了。选择定制流水线,承认的是:人类经验、前沿知识、以及那些语言还装不下的信息传递方式,今天仍然不可替代。

这个差距会随时间缩小。AI 确实在一步步吞噬人类知识的边界。但“一步步”不等于“今天”。

说了这么多,有没有数字?同样的任务,流水线和智能体到底差多少?

下一篇:第 4 篇——诚实的对比:流水线 vs. 智能体,用数字说话。


Originally published externally: source article.