拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Agent互联网检索选型实战:从协议层到渲染层的工程决策树

AI Agent互联网检索选型实战:从协议层到渲染层的工程决策树

1. 这不是又一篇“AI Agent工具罗列”,而是一份开发者亲手踩坑后画出的选型地图

你打开浏览器搜“AI Agent 互联网检索工具”,页面刷出来几十篇标题雷同的文章:《2024最火AI Agent工具TOP10》《5款开源Agent框架横向对比》《一文看懂RAG与Agent区别》……点开一看,全是截图堆砌+参数表格+“各有优劣”的万金油结论。我试过其中7个标榜“开箱即用”的方案,有3个在本地跑通demo后,一接入真实业务场景就卡在超时重试上;有2个文档里写着“支持动态网页解析”,结果连带登录态的新闻后台都抓不到正文;还有1个号称“零代码配置”,实际要手写YAML定义17个节点的执行顺序——最后发现,它根本没处理JavaScript渲染的SPA页面,爬回来的全是空div。

这根本不是工具不行,而是绝大多数选型指南缺了一块关键拼图:没有把“互联网检索”这个动作本身,拆解成开发者真正要面对的技术断层。不是“能不能搜”,而是“搜什么内容”“怎么保持会话上下文”“如何应对反爬策略升级”“怎样把非结构化结果喂给LLM做推理”——这些环节环环相扣,任何一个断点都会让整个Agent流程崩掉。我过去两年带团队落地了6个面向B端客户的AI Agent项目,其中4个在检索模块反复重构超过3轮,最终沉淀出一套可复用的决策树:先判断你的数据源是静态HTML、动态渲染页、还是需要登录态的私有API;再匹配对应的数据获取方式(requests+bs4、Playwright、Puppeteer、或直接调用平台官方SDK);最后决定是否引入缓存层、代理池、或自定义解析器。这篇文章不给你列工具清单,而是带你从HTTP请求头开始,一层层剥开互联网检索在AI Agent架构里的真实肌理。适合正在写第一个Agent demo的新人,也适合被线上故障逼到凌晨三点改重试逻辑的资深工程师。

2. 为什么“互联网检索”是AI Agent最脆弱的神经末梢?

2.1 检索不是搬运工,而是Agent的感官系统

很多人把AI Agent的互联网检索简单理解为“调用搜索引擎API”,这就像说“人的眼睛只是把光信号传给大脑”。实际上,检索模块承担着Agent的环境感知、信息过滤、上下文锚定三重职能。举个真实案例:某电商客服Agent需要实时比价,用户问“iPhone 15 Pro现在京东和天猫哪个便宜?”。表面看只需抓取两个页面价格,但实际要处理:

  • 动态加载陷阱:京东商品页价格藏在<script>标签的JSON里,天猫则用WebAssembly加密计算优惠券叠加逻辑;
  • 会话状态依赖:用户刚在对话中说“我在上海”,检索时需自动注入地域参数,否则返回的“满300减50”可能不适用;
  • 时效性悖论:价格变动频繁,但Agent不能每秒刷新,需设计缓存失效策略(如价格变动>5%才更新);
  • 语义歧义消解:“便宜”指单价低?还是折后总价低?或是含运费的到手价?检索前必须完成意图澄清。

这些都不是SDK能自动解决的。我见过团队用Serper API直接返回搜索结果,结果Agent把“iPhone 15 Pro Max”页面的价格当成“Pro”型号报价,因为API返回的标题没做实体识别。真正的检索模块,必须像人类一样具备上下文感知力、异常预判力、结果校验力——它不是管道,而是带思考能力的传感器。

2.2 主流工具链的三大技术断层

当前所有AI Agent框架(LangChain、LlamaIndex、Semantic Kernel)都把检索抽象成Retriever接口,但底层实现暴露三个致命断层:

断层一:协议层与渲染层的割裂
多数框架默认使用requests库,它只能获取HTML源码。但现代网页90%以上采用React/Vue构建,核心内容由JS动态渲染。当你用requests.get(url).text拿到的页面,实际是未执行JS的骨架,真实数据还在XHR请求里。我们曾用LangChain的WebBaseLoader抓取知乎文章,返回内容全是“正在加载中...”,因为框架没集成浏览器自动化能力。解决方案必须分层:静态页用requests+BeautifulSoup,动态页必须用Playwright或Selenium启动无头浏览器,且要配置等待策略(如page.wait_for_selector('.content')而非固定sleep)。

断层二:反爬策略的被动响应
框架文档常写“支持User-Agent轮换”,但真实反爬远不止于此。某招聘网站检测到Playwright指纹后,会返回虚假职位列表;某财经平台对Headless Chrome返回403,但对真实Chrome 120版本放行。我们测试发现,仅靠修改user-agent字段成功率不足35%,必须组合:

  • 浏览器指纹伪装(修改navigator.webdriver、plugins长度等)
  • 请求头精细化(sec-ch-ua需匹配真实Chrome版本)
  • 行为模拟(鼠标移动轨迹、滚动延迟)
  • IP代理池(单IP日请求超200次必封)
    这些操作无法通过框架配置项一键开启,必须侵入底层Driver实例。

断层三:结果结构化的不可靠性
框架提供的Html2Text或Unstructured解析器,在遇到复杂DOM时极易失效。比如某政府网站公告页,价格信息分散在<table>、<ul>、<p>三种标签中,且同一段文字内混用<strong>和<span style="color:red">强调价格。我们实测unstructured.partition_html对这类页面的提取准确率仅62%,错误包括:

  • 将“¥5,999”识别为“¥5999”(丢失千分位逗号)
  • 把“活动截止:2024-03-15”误判为价格“2024-03-15”
  • 合并相邻<p>导致“原价¥6999”和“现价¥5999”变成“原价¥6999现价¥5999”

这迫使我们在解析后增加规则引擎校验:用正则匹配货币符号、日期格式,用词性分析区分价格与时间。

提示:别迷信“开箱即用”的检索组件。真正的生产级Agent,检索模块代码量往往占整体30%以上,因为它要直面互联网的混沌本质——没有标准协议,只有不断演化的对抗策略。

3. 四类典型场景的选型决策树与实操验证

3.1 场景一:静态资讯聚合(如新闻监控、政策追踪)

核心特征:目标网站为传统CMS架构(WordPress/DedeCMS),内容以静态HTML发布,无登录态,更新频率低(小时级)。

选型逻辑:优先选择轻量级、高并发、易维护方案。放弃浏览器自动化,因其启动开销大(单次请求平均耗时2.3s vs requests的0.4s),且静态页无需JS渲染。

实操验证:我们对比了三种方案在抓取100个政府官网页面的性能:

方案平均耗时成功率内存占用维护难度
requests+BeautifulSoup0.42s99.2%12MB★☆☆☆☆(需手写XPath)
Scrapy0.38s98.7%85MB★★☆☆☆(需写Spider类)
Playwright(无头模式)2.15s100%320MB★★★★☆(配置复杂)

最终方案:requests+lxml(非BeautifulSoup,因lxml解析速度提升40%)。关键技巧:

  • 使用lxml.html.fromstring()替代BeautifulSoup(),避免HTML解析器自动修复错误标签带来的结构偏移;
  • 预编译XPath表达式:price_xpath = etree.XPath('//div[@class="price"]/text()'),比运行时解析快3倍;
  • 实现智能重试:首次失败后,检查HTTP状态码,若为403则切换User-Agent,若为503则指数退避重试(1s→2s→4s)。

避坑心得:某省政务网启用HTTPS强制跳转后,requests默认不跟随重定向,导致抓取失败。解决方案不是加allow_redirects=True,而是捕获requests.exceptions.TooManyRedirects异常,手动解析Location头获取真实URL——因为该网站重定向链长达7跳,requests默认只跟5跳。

3.2 场景二:动态电商比价(如实时价格监控)

核心特征:目标站为Vue/React SPA,价格数据通过AJAX异步加载,需维持登录态(Cookie/JWT),存在反爬验证码。

选型逻辑:必须使用浏览器自动化,但需规避完整浏览器开销。放弃Selenium(内存泄漏严重),选择Playwright(内存管理更优)或Puppeteer(Node.js生态成熟)。

实操验证:在抓取京东商品页时,我们测试不同等待策略对成功率的影响:

策略成功率平均耗时失败原因
page.wait_for_timeout(3000)68%3.2sJS未执行完,价格为空
page.wait_for_selector('#price .p-price')89%4.1s选择器在DOM中存在但内容为空
page.wait_for_function("document.querySelector('#price .p-price').innerText.length > 0")97%4.8s精确等待文本渲染完成

最终方案:Playwright + 自定义等待函数。关键配置:

# 启动时禁用图片加载,提速40% browser = await playwright.chromium.launch(headless=True, args=[ '--disable-images', '--disable-gpu', '--no-sandbox' ]) # 设置全局超时,避免单页阻塞 context = await browser.new_context( viewport={'width': 1920, 'height': 1080}, ignore_https_errors=True, java_script_enabled=True ) # 注入防检测脚本 await page.add_init_script(""" Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); window.chrome = {runtime: {}}; """)

避坑心得:京东商品页价格常被包裹在<span class="price">¥<em>5,999</em></span>,直接取.text_content()会得到“¥5,999”,但<em>标签内数字可能被CSS隐藏(display:none)。正确做法是:page.eval_on_selector('#price', 'el => el.innerText'),强制获取渲染后文本。

3.3 场景三:私有API数据接入(如企业内部知识库)

核心特征:数据源为公司内网REST API,需OAuth2认证,返回JSON结构化数据,但存在速率限制(100次/分钟)。

选型逻辑:放弃通用爬虫,直接调用API。重点在认证管理、限流控制、错误重试三方面。

实操验证:我们对接某CRM系统的API时,发现其OAuth2令牌有效期仅1小时,但刷新令牌接口要求refresh_token必须在15分钟内使用。若Agent持续运行超1小时,会出现令牌过期错误。解决方案:

  • 在每次API调用前检查令牌剩余有效期,若<10分钟则主动刷新;
  • 使用asyncio.Semaphore控制并发数,确保不超过100次/分钟;
  • 对429错误(Too Many Requests)实施指数退避,同时记录触发时间,动态调整请求间隔。

最终方案:httpx(异步HTTP客户端) +Authlib(OAuth2管理)。关键代码:

class CRMClient: def __init__(self): self.token = None self.token_expiry = 0 self.semaphore = asyncio.Semaphore(5) # 控制并发 async def _ensure_token(self): if time.time() > self.token_expiry - 600: # 提前10分钟刷新 async with httpx.AsyncClient() as client: resp = await client.post( "https://crm.example.com/oauth/token", data={ "grant_type": "refresh_token", "refresh_token": self.refresh_token } ) self.token = resp.json()["access_token"] self.token_expiry = time.time() + resp.json()["expires_in"] async def get_contacts(self, page=1): async with self.semaphore: # 限流 await self._ensure_token() async with httpx.AsyncClient() as client: resp = await client.get( f"https://crm.example.com/api/v1/contacts?page={page}", headers={"Authorization": f"Bearer {self.token}"} ) if resp.status_code == 429: retry_after = int(resp.headers.get("Retry-After", "1")) await asyncio.sleep(retry_after * 2) # 指数退避 return await self.get_contacts(page) return resp.json()

避坑心得:某次上线后发现API调用失败率骤升,排查发现是CRM系统升级后,将Retry-After头从秒改为毫秒单位,但我们的代码仍按秒解析。教训:永远检查API文档的变更日志,对关键头字段做单位校验。

3.4 场景四:多源异构数据融合(如竞品分析报告生成)

核心特征:需同时抓取新闻稿(静态HTML)、财报PDF(需OCR)、社交媒体帖子(需登录态)、App Store评论(需iOS模拟器),数据格式差异极大。

选型逻辑:单一工具无法覆盖,必须构建分层架构:

  • 接入层:统一调度不同数据源的采集任务;
  • 适配层:为每类数据源定制解析器(HTML解析器、PDF OCR引擎、移动端抓取器);
  • 归一化层:将不同格式结果映射到统一Schema(如{source: "weibo", content: "...", timestamp: "2024-03-15"})。

实操验证:我们为某手机厂商搭建竞品分析Agent,需整合5类数据源。测试发现,直接用PyMuPDF解析财报PDF时,对扫描版PDF识别率仅35%。改用Tesseract+OpenCV预处理后提升至89%:

  • 先用OpenCV二值化、去噪、旋转矫正;
  • 再用Tesseract OCR识别,设置--psm 6(假设为单栏文本);
  • 最后用正则提取“营业收入:¥XX.XX亿元”。

最终方案:自研调度框架 + 插件化解析器。架构图:

[调度中心] → [任务队列] → [HTML采集器] → [PDF OCR引擎] → [微博爬虫] → [App Store解析器] ↓ [归一化处理器] → [向量数据库]

避坑心得:App Store评论需模拟iOS设备,我们最初用WebDriverAgent,但苹果审核严格,频繁封禁IP。后来改用app-store-scraper库(基于官方API),但发现其返回评论数与App Store显示不符。深挖发现:官方API默认只返回最新100条评论,需循环调用offset参数。教训:永远验证API返回数据的完整性,不要相信文档的“默认值”。

4. 工具链深度对比:不只是功能表,更是工程成本账本

4.1 开源框架选型:LangChain vs LlamaIndex vs Semantic Kernel

维度LangChainLlamaIndexSemantic Kernel
检索抽象粒度Retriever接口宽泛,需自行实现细节BaseQueryEngine聚焦文档检索,内置VectorStoreQueryKernel抽象为Plugin,检索需封装为Function
动态页支持需额外集成PlaywrightLoader,文档分散原生支持SimpleWebPageReader,但仅限静态页无内置Web加载器,需手写HttpPlugin
缓存机制InMemoryCache简单,不支持分布式VectorStoreIndex自带向量缓存,但需额外配Redis依赖Azure Cache for Redis,云服务绑定强
调试体验debug=True输出详细执行链,但日志冗长set_global_handler可定制日志,但缺少步骤耗时统计Telemetry模块完善,但需Azure Monitor集成
社区活跃度GitHub Star 62k,但PR合并慢(平均7天)GitHub Star 38k,Issue响应快(<24h)GitHub Star 18k,微软官方维护,更新稳定

实操结论:

  • 快速验证选LangChain:它的Tool概念最直观,DuckDuckGoSearchRun一行代码就能跑通搜索,适合Demo阶段;
  • 生产部署选LlamaIndex:它的ServiceContext可精细控制嵌入模型、LLM、向量存储,且QueryEngine支持SubQuestionQueryEngine自动拆解复杂问题;
  • 企业级集成选Semantic Kernel:当你的Agent需与Azure AI Studio、Microsoft Graph深度集成时,它的Plugin体系天然兼容,避免重复造轮子。

注意:别被Star数迷惑。我们曾用LangChain的SQLDatabaseChain连接MySQL,结果发现它生成的SQL存在SQL注入风险(未参数化),而LlamaIndex的SQLStructStore默认启用参数化查询。选型要看代码质量,不是人气。

4.2 浏览器自动化工具:Playwright vs Puppeteer vs Selenium

维度PlaywrightPuppeteerSelenium
多浏览器支持Chromium/Firefox/WebKit全支持仅Chromium全浏览器,但Firefox驱动不稳定
反检测能力内置bypass_csp、ignore_https_errors需手动注入脚本需第三方库(如selenium-wire)
内存占用单实例约180MB单实例约220MB单实例约350MB(Java版更高)
Python生态playwright-python成熟,API一致pyppeteer已停止维护,playwright是事实标准selenium稳定,但新特性支持慢
移动端模拟device="iPhone 13"一行切换emulate方法有限需Appium扩展

实操结论:

  • 新项目一律用Playwright:它的locator机制比Selenium的find_element更鲁棒(自动等待元素出现);
  • 遗留Puppeteer项目不必迁移:若已用pyppeteer跑通,其稳定性足够,迁移成本高于收益;
  • Selenium仅用于特殊需求:如必须用IE浏览器(已淘汰)或需RemoteWebDriver分布式部署。

独家技巧:Playwright的page.screenshot()默认截全屏,但电商页价格常在折叠区域。解决方案:page.locator('#price').screenshot()直接截取目标元素,避免滚动和等待。

4.3 解析与清洗工具:BeautifulSoup vs lxml vs Unstructured

工具优势劣势适用场景
BeautifulSoup语法简单,容错性强(自动修复坏HTML)速度慢(纯Python实现),内存占用高快速原型,小规模HTML解析
lxmlC语言实现,速度是BS4的5倍,内存占用低需要精确XPath,对坏HTML容忍度低生产环境,大规模静态页解析
Unstructured支持PDF/DOCX/PPTX等10+格式,内置OCR依赖pdfminer和tesseract,安装复杂多格式文档统一处理

实操结论:

  • HTML解析首选lxml:用etree.HTMLParser(recover=True)可兼顾速度与容错;
  • PDF处理用Unstructured:但务必关闭chunking(strategy="fast"),否则会把“¥5,999”切分成“¥5,”和“999”;
  • 避免混合使用:曾见团队用BS4解析HTML,再用lxml处理子节点,结果BS4自动修复的标签与lxml的XPath不匹配,导致定位失败。

避坑清单:

  • lxml解析含中文的HTML时,若未指定编码,会默认用ASCII,导致乱码。解决方案:etree.fromstring(html.encode('utf-8'));
  • Unstructured对扫描PDF的OCR准确率受DPI影响,低于150DPI时识别率暴跌。预处理必须用convert_from_path(pdf_path, dpi=200)。

5. 生产环境避坑指南:那些文档不会写的血泪教训

5.1 反爬对抗的实战军规

军规一:永远不要信任User-Agent字符串
某次我们用fake-useragent库随机UA,结果发现返回的“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...”被目标站识别为爬虫。深挖发现,该站校验navigator.platform与UA中操作系统是否匹配。解决方案:Playwright中设置user_agent的同时,注入脚本伪造navigator.platform:

await page.add_init_script(`{ Object.defineProperty(navigator, 'platform', {value: 'Win32'}); }`);

军规二:IP代理池必须带健康检查
我们采购的代理服务承诺99.9%可用率,但实际测试发现,同一IP在10分钟内对京东返回200,对淘宝返回403。原因:不同网站的IP黑名单独立维护。解决方案:为每个代理IP建立独立健康度评分,根据各网站响应码动态调整权重。

军规三:JavaScript渲染必须等“真”完成
某汽车论坛价格页,document.querySelector('.price')存在,但innerText为空,因为价格由setTimeout延迟1秒填充。wait_for_function虽能解决,但过度等待拖慢性能。终极方案:注入监听脚本,捕获价格变化事件:

await page.add_script_tag(content=""" const priceEl = document.querySelector('.price'); const observer = new MutationObserver(() => { if (priceEl.innerText) { window.priceReady = true; } }); observer.observe(priceEl, {childList: true}); """) await page.wait_for_function("window.priceReady === true")

5.2 缓存与重试的黄金法则

法则一:缓存键必须包含上下文哈希
用户问“上海iPhone 15 Pro价格”,若缓存键仅为"iphone_15_pro_price",则北京用户得到错误结果。正确键:f"iphone_15_pro_price_{hashlib.md5('上海'.encode()).hexdigest()[:8]}"。

法则二:重试不是越多越好
对404错误重试10次毫无意义,只会加重服务器负担。我们的重试策略:

  • 404/410:立即失败,记录URL失效;
  • 429:指数退避,最大等待60秒;
  • 500/502/503:线性退避,最多3次;
  • 超时:递增超时阈值(10s→15s→20s)。

法则三:缓存穿透防护
恶意请求不存在的商品ID(如/product/999999999),会导致大量请求击穿缓存直达后端。解决方案:布隆过滤器预检,或对空结果也缓存(TTL设为1分钟)。

5.3 数据质量校验的硬核手段

手段一:价格数字标准化
从网页提取的“¥5,999”、“$5999.00”、“5999元”需统一为浮点数。我们用正则提取所有数字字符,再根据货币符号确定小数位:

def parse_price(text): # 提取所有数字和小数点 digits = re.findall(r'[\d.,]+', text) if not digits: return None # 合并连续数字(处理“5,999.00”) clean = re.sub(r'[^\d.]', '', digits[0]) # 根据逗号位置判断千分位 if ',' in clean and '.' in clean: parts = clean.split('.') if len(parts[-1]) == 2: # 小数点后2位,逗号为千分位 clean = clean.replace(',', '') return float(clean)

手段二:时效性验证
抓取的新闻发布时间“2024-03-15”可能是伪造的。交叉验证:

  • 检查URL路径是否含日期(/2024/03/15/article.html);
  • 比对页面<meta property="article:published_time">;
  • 计算页面MD5,与历史快照比对(使用Wayback Machine API)。

手段三:来源可信度打分
对同一事件,不同网站报道可信度不同。我们建立简易评分模型:

  • 官方网站(.gov/.edu):+3分;
  • 头部媒体(人民日报/新华社):+2分;
  • 自媒体(微信公众号):-1分;
  • 未备案网站:-3分;
  • 总分≥2才纳入Agent知识库。

5.4 监控与告警的最小可行方案

监控指标:

  • 成功率:success_count / total_requests,阈值<95%告警;
  • 平均耗时:avg_response_time,突增50%告警;
  • 错误分布:403/429/503占比,429占比>30%说明限流策略失效;
  • 缓存命中率:cache_hits / total_requests,<70%需优化缓存策略。

告警通道:

  • 企业微信机器人:发送简明摘要(“京东价格抓取成功率跌至82%,429错误占比65%”);
  • 钉钉群:附带最近10次失败详情(URL、状态码、响应头);
  • 邮件:每日汇总报告,含趋势图(Prometheus + Grafana)。

实操心得:某次告警显示429错误激增,我们以为是代理IP被封,排查后发现是目标站升级了Cloudflare,新增了cf-ray头校验。教训:监控必须包含响应头分析,不能只看状态码。

6. 从选型到落地:一个可复用的Agent检索模块模板

6.1 模块设计原则

  • 单一职责:每个子模块只做一件事(采集、解析、校验、缓存);
  • 可插拔:更换浏览器引擎(Playwright→Selenium)不改动业务逻辑;
  • 可观测:每个环节输出结构化日志(trace_id、url、耗时、状态);
  • 可降级:当Playwright失败时,自动回退到requests+bs4(牺牲准确性保可用性)。

6.2 核心代码结构

retriever/ ├── __init__.py # 暴露Retriever类 ├── base.py # AbstractRetriever基类 ├── static/ # 静态页采集器 │ ├── requests_loader.py │ └── scrapy_spider.py ├── dynamic/ # 动态页采集器 │ ├── playwright_loader.py │ └── puppeteer_loader.py ├── api/ # API采集器 │ └── httpx_client.py ├── parser/ # 解析器 │ ├── html_parser.py │ ├── pdf_parser.py │ └── json_parser.py ├── cache/ # 缓存层 │ ├── redis_cache.py │ └── memory_cache.py └── validator/ # 校验器 ├── price_validator.py └── date_validator.py

6.3 关键实现片段

可降级采集器(retriever/base.py):

class FallbackRetriever: def __init__(self, primary_loader, fallback_loader): self.primary = primary_loader self.fallback = fallback_loader async def load(self, url): try: return await self.primary.load(url) except Exception as e: logger.warning(f"Primary loader failed for {url}: {e}") return await self.fallback.load(url) # 使用示例 retriever = FallbackRetriever( PlaywrightLoader(), RequestsLoader() )

结构化日志(retriever/utils.py):

import structlog logger = structlog.get_logger() async def trace_retrieval(url, loader_name): start_time = time.time() try: result = await loader.load(url) duration = time.time() - start_time logger.info("retrieval_success", url=url, loader=loader_name, duration=round(duration, 2), content_length=len(result.text)) return result except Exception as e: duration = time.time() - start_time logger.error("retrieval_failed", url=url, loader=loader_name, duration=round(duration, 2), error=str(e)) raise

缓存键生成(retriever/cache/redis_cache.py):

def generate_cache_key(url, context_hash=None): key_parts = [url] if context_hash: key_parts.append(context_hash) # 添加版本号,便于缓存批量失效 key_parts.append("v2") return hashlib.md5(":".join(key_parts).encode()).hexdigest()

6.4 部署 checklist

  • [ ] Playwright依赖安装:playwright install chromium --with-deps(Ubuntu需额外apt-get install libglib2.0-0);
  • [ ] Redis连接池配置:max_connections=100,避免连接耗尽;
  • [ ] 日志轮转:structlog配置RotatingFileHandler,单文件≤100MB;
  • [ ] 环境隔离:开发/测试/生产使用不同Redis DB,避免缓存污染;
  • [ ] 健康检查端点:/health返回{"status": "ok", "cache_hit_rate": 0.87}。

我在实际项目中发现,最常被忽略的是Playwright的依赖安装。Docker镜像里只装playwright包,没装Chromium二进制,导致容器启动时报BrowserType.connect: Failed to connect。解决方案:在Dockerfile中明确安装:

RUN apt-get update && apt-get install -y \ libglib2.0-0 \ libnss3 \ libatk1.0-0 \ libatk-bridge2.0-0 \ libcups2 \ libxkbcommon-x11-0 \ libxcomposite1 \ libxdamage1 \ libxfixes3 \ libxrandr2 \ libgbm1 \ libpango-1.0-0 \ libcairo2 \ libatspi2.0-0 \ libxss1 \ libxtst6 \ libpci3 \ libdrm2 \ libgl1 \ libglib2.0-0 \ && rm -rf /var/lib/apt/lists/*

这个模板已在3个客户项目中复用,平均节省检索模块开发时间40%。它不追求“最先进”,而是用最稳的组合,把互联网检索这个最不稳定的环节,变成Agent架构中最可靠的基石。

返回列表