Agent Skills 中 Read URL 工具的设计与实践:面向 LLM-as-Judge 研究链的结构化网页内容提取
【免费下载链接】Agent-Skills-for-Context-EngineeringA comprehensive collection of Agent Skills for context engineering, multi-agent architectures, and production agent systems. Use when building, optimizing, or debugging agent systems that require effective context management.项目地址: https://gitcode.com/GitHub_Trending/ag/Agent-Skills-for-Context-Engineering
导读
readUrl是 llm-as-judge-skills 示例项目中 Research 工具族(webSearch、readUrl、extractClaims、verifyClaim、synthesize)的核心成员之一,负责把给定 URL 的网页内容提取为带来源元数据的结构化文本,供研究型 Agent 做深入阅读与后续的 claim 提取、交叉验证与综合归纳。本文以 tools/research/read-url.md 为骨架,结合仓库中 webSearch 工具规范、Research Agent 定义 以及 TypeScript 工具实现模式,完整讲解该工具的输入/输出 Schema、内容类型处理策略、错误码设计、实现注意事项,以及它在多 Agent 研究流水线中的实际调用位置。读完你可以直接照搬这套工具定义搭建自己的网页阅读工具,并将其接入 Agent 的检索—阅读—验证工作流。
一、工具定位:Research 工具链中承上启下的一环
在 llm-as-judge-skills 的 Research Agent 中,一个完整的研究任务被拆解为五步:webSearch(检索)→readUrl(精读)→extractClaims(抽取论断)→verifyClaim(交叉验证)→synthesize(综合成文)。readUrl恰好处于"检索命中"与"深入分析"的衔接点:
- 上游:
webSearch返回带 snippet 的结果列表(标题、URL、摘要、域名、相关度分数),只解决"有哪些相关来源"的问题; - 下游:
extractClaims需要的是"干净可读的正文文本",而不是掺杂导航、广告、侧边栏的原始 HTML——这正是readUrl的输出职责。
工具的设计目的在 read-url.md 中被明确为:"Extract and parse content from a given URL. Returns structured text content with metadata about the source."(从给定 URL 提取并解析内容,返回带来源元数据的结构化文本)。其 description 字段还特别强调"Use after webSearch to get full content from relevant results"(在 webSearch 之后使用,以获取相关结果的完整内容),说明这两个工具是配套使用的。
二、Tool 定义:AI SDK + Zod 的声明式工具结构
readUrl采用 Vercel AI SDK 的tool()工厂函数配合 Zod 描述参数,完整定义如下(出自 read-url.md):
import { tool } from "ai"; import { z } from "zod"; export const readUrl = tool({ description: `Read and extract content from a URL. Returns the main text content, stripped of navigation and ads. Use after webSearch to get full content from relevant results.`, parameters: z.object({ url: z.string().url() .describe("The URL to read"), contentType: z.enum(["auto", "article", "documentation", "paper", "code"]).default("auto") .describe("Hint for content type to optimize extraction"), maxLength: z.number().min(1000).max(50000).default(10000) .describe("Maximum characters to return"), extractSections: z.boolean().default(true) .describe("Whether to identify and label sections"), includeMetadata: z.boolean().default(true) .describe("Include author, date, and other metadata") }), execute: async (input) => { return extractUrlContent(input); } });这段定义透露出三层设计信息,与仓库的 工具设计模式 相互印证:
- description 是给模型看的"路由信号":
tools/index.md指出工具的 description 要"清晰描述工具做什么"。readUrl 的 description 不仅说明功能,还写明了使用时机(webSearch 之后)和收益(去掉导航与广告),帮助模型在webSearch、readUrl、verifyClaim等多个工具间做出正确选择。 - Zod 负责类型安全与默认值:所有可选参数都通过
.default()提供默认值,模型不传参也能安全执行;z.string().url()在入口处就拦截非法 URL。这与仓库中src/tools/evaluation/direct-score.ts用DirectScoreInputSchema定义输入、z.infer推导类型的做法完全一致。 - execute 只做一件事:把经过校验的输入转交给
extractUrlContent内部实现。execute与"参数校验"、"提取逻辑"的职责分离,是 tools/index.md 中"Standard Tool Structure"推荐的写法。
三、输入参数详解(Input Schema)
read-url.md 给出的输入参数表如下,结合 Tool 定义可以补充取值约束与默认值:
| 字段 | 类型 | 必填 | 说明 | 约束 / 默认值 |
|---|---|---|---|---|
| url | string | 是 | 要读取的 URL | 必须通过z.string().url()校验,非法 URL 在进入 execute 前即被拒绝 |
| contentType | enum | 否 | 内容类型提示,用于优化提取策略 | auto(默认)/article/documentation/paper/code |
| maxLength | number | 否 | 返回的最大字符数 | 默认10000,范围1000 ~ 50000(z.number().min(1000).max(50000)强制约束) |
| extractSections | boolean | 否 | 是否识别并标记章节 | 默认true,输出中对应content.sections字段 |
| includeMetadata | boolean | 否 | 是否包含作者、日期等元数据 | 默认true,输出中对应metadata字段 |
参数设计中值得注意的两个工程细节:
- maxLength 的上下限:最小 1000、最大 50000 字符的约束既防止模型给出过小的截断值(导致正文不完整),也限制单次返回过大(避免撑爆上下文窗口)。这体现了"工具输出要服务于上下文工程"的思想——在 context-fundamentals 技能中,"有效管理上下文"是核心原则,控制读取长度正是其落地手段。
- contentType 是"提示"而非"开关":它只是 hint(提示),用于让提取器选择更合适的解析策略,即便模型猜错类型,
auto模式下的自动探测仍然兜底。
四、输出结构详解(Output Schema)
readUrl返回结构化结果,完整接口如下(出自 read-url.md):
interface ReadUrlResult { success: boolean; url: string; title: string; content: { full: string; sections?: { heading: string; level: number; // h1=1, h2=2, etc. content: string; }[]; }; metadata?: { author?: string; publishedDate?: string; lastModified?: string; description?: string; keywords?: string[]; source: string; }; stats: { totalCharacters: number; truncated: boolean; sectionsFound: number; }; error?: { code: string; message: string; }; }这个输出结构是刻意面向"研究型 Agent"设计的,几个字段各有用途:
| 输出区块 | 字段 | 对下游研究流程的意义 |
|---|---|---|
| 顶层 | success/url/title | 快速判断读取成败、确认来源身份,便于引用溯源 |
content.full | 去除导航广告后的正文 | 直接作为extractClaims的输入文本 |
content.sections | 带 heading 与 level 的章节数组 | 支持按章节检索,Agent 可只针对命中章节做细读 |
metadata | author / publishedDate / lastModified / description / keywords / source | 供研究综合(synthesis)阶段评估来源权威性与时效性 |
stats | totalCharacters / truncated / sectionsFound | 暴露截断状态,防止 Agent 误把不完整内容当全文引用 |
error | code / message | 机器可读的错误码,供上层做重试或降级决策 |
其中metadata和stats与 Research Agent 的质量标准直接呼应:该 Agent 的指令要求"note the recency and authority of sources"(记录来源的时效性与权威性),publishedDate、source字段正是为此提供数据;而 research-synthesis-prompt.md 在综合阶段要求标注每条 finding 的来源、日期与类型,同样依赖这里的 metadata。
五、完整使用示例
官方使用示例如下(出自 read-url.md):
const content = await readUrl.execute({ url: "https://eugeneyan.com/writing/llm-evaluators/", contentType: "article", maxLength: 15000, extractSections: true, includeMetadata: true }); // Result: // { // success: true, // url: "https://eugeneyan.com/writing/llm-evaluators/", // title: "Evaluating the Effectiveness of LLM-Evaluators", // content: { // full: "LLM-evaluators, also known as LLM-as-a-Judge...", // sections: [ // { // heading: "Key considerations before adopting an LLM-evaluator", // level: 2, // content: "Before reviewing the literature..." // }, // ... // ] // }, // metadata: { // author: "Eugene Yan", // publishedDate: "2024-06-15", // source: "eugeneyan.com" // }, // stats: { // totalCharacters: 15000, // truncated: true, // sectionsFound: 8 // } // }示例选用的 eugeneyan.com 这篇文章正是本示例项目 README 所依据的 LLM-Evaluators 研究来源,用它做演示既真实又贴合"研究素材阅读"的场景。注意示例中totalCharacters: 15000与truncated: true:当maxLength设为 15000 而正文实际更长时,工具会截断并明确标记,下游 Agent 需要感知这一信号,避免把片段当全文。
在实际的 Agent 调用链中,这个工具通常不是被直接execute,而是由 Research Agent 通过tools注册表调用:
tools: { webSearch: researchTools.webSearch, readUrl: researchTools.readUrl, extractClaims: researchTools.extractClaims, verifyClaim: researchTools.verifyClaim, synthesize: researchTools.synthesize }(见 research-agent.md)。模型会在webSearch拿到结果列表后,挑选高相关度 URL 调用readUrl获取全文,再交给extractClaims。
六、Content Type Handling:五种内容类型的提取策略
针对不同网页形态,readUrl提供不同的提取优化策略(出自 read-url.md):
| 类型 | 优化策略 |
|---|---|
| article | 优先提取正文,跳过侧边栏(Prioritize main content, skip sidebars) |
| documentation | 保留代码块,维持文档结构(Preserve code blocks, keep structure) |
| paper | 抽取摘要、章节与参考文献(Extract abstract, sections, references) |
| code | 保留格式与语法高亮(Preserve formatting, syntax highlighting) |
| auto | 从内容中自动探测类型(Detect type from content) |
这五种策略对应的核心诉求是**"按内容形态调整信息保真度"**:
- 文章(article)场景下,导航、侧边栏、推荐位是噪声,应尽可能剥离;
- 技术文档(documentation)场景下,代码块与层级结构是信息主体,必须原样保留——这与仓库 context-fundamentals 中"保留代码结构供模型理解"的原则一致;
- 论文(paper)场景下,摘要、章节、参考文献是后续 claim 验证的重要锚点;
- 代码(code)场景下,格式与高亮直接影响可读性。
模型只需提供 contentType hint,具体解析逻辑由实现方(示例中为extractUrlContent)分派。从仓库结构看,readUrl属于"工具规范文档(MD)+ 实际 TypeScript 实现"双轨模式:规范在 tools/research/read-url.md,而同类工具的落地方案可参考 src/tools/evaluation/direct-score.ts 中tool({...})+execute的写法。
七、错误处理:面向重试与降级的错误码设计
readUrl定义了六种机器可读错误码(出自 read-url.md):
const errorCodes = { "URL_NOT_FOUND": "Page does not exist (404)", "ACCESS_DENIED": "Page requires authentication (401/403)", "TIMEOUT": "Request timed out", "BLOCKED": "Access blocked by robots.txt or rate limit", "INVALID_CONTENT": "Content could not be parsed", "UNSUPPORTED_TYPE": "Content type not supported (e.g., binary)" };这套错误码设计遵循 tools/index.md 中定义的ToolError模式:
interface ToolError { code: string; // Machine-readable error code message: string; // Human-readable message retryable: boolean; // Whether retry might help details?: object; // Additional context }每种错误的"可重试性"不同,Agent 的应对策略也应不同:
| 错误码 | 含义 | 建议策略 |
|---|---|---|
URL_NOT_FOUND | 页面 404 不存在 | 不重试,报告来源失效,改选其他候选 URL |
ACCESS_DENIED | 需要认证(401/403) | 不重试,跳过该来源或换镜像/缓存版本 |
TIMEOUT | 请求超时 | 可重试(配合退避),或换更短 maxLength 再试 |
BLOCKED | 被 robots.txt 或限流拦截 | 不立即重试,放慢频率或更换 UA 后隔段时间再试 |
INVALID_CONTENT | 内容无法解析 | 不重试,该来源 HTML 结构异常,换源 |
UNSUPPORTED_TYPE | 内容类型不支持(如二进制) | 不重试,改请求 PDF/文本版本接口 |
在 Research Agent 的质量标准里,"Never fabricate information or sources"(绝不编造信息或来源)与"clearly indicate when information is uncertain"(明确标示不确定信息)是硬性要求,而 readUrl 的error.code正是让 Agent 能够准确表达"这个来源读不到/读不全"的依据,避免把失败伪装成成功结果。
八、实现注意事项:做一名礼貌且可靠的网络读者
read-url.md 的 Implementation Notes 给出六条生产级实现规范,逐条解读如下:
- Respect robots.txt:读取并遵守目标站点的 robots.txt 指令。这既是法律与道德义务,也是避免被封禁的前提——与错误码
BLOCKED直接相关。 - Rate Limiting:不要对同一域名发起高频请求("Don't hammer the same domain")。建议按域名维护请求队列与最小间隔,配合滑动窗口限流。
- User Agent:使用合适的 UA 字符串。清晰标识自己是"研究型 Agent 的抓取工具"并附带联系方式,比伪装浏览器更合规,也更不容易被 WAF 误伤。
- Timeouts:设置合理超时(10~30 秒)。超时过长会拖慢整个研究流水线,过短又容易误杀慢速站点,10~30s 是文档给出的经验区间,超时后返回
TIMEOUT错误码。 - JavaScript Rendering:对 JS 重渲染的站点(如 SPA),纯 HTTP 抓取拿不到正文,需要评估使用 headless browser(如 Playwright/Puppeteer)渲染后再提取。代价是更高延迟与资源开销,建议仅在
auto探测失败或显式标记时启用。 - Caching:对重复读取做内容缓存。研究流水线中同一 URL 可能被多次访问(如多轮验证),缓存正文与 metadata 能显著降低延迟与对源站的打扰,与
webSearch工具的缓存建议保持一致(见 web-search.md 的 Implementation Notes 第 2 条)。
从仓库现状看,这六条是工具规范的约定层内容,具体落地取决于实际实现;当把readUrl接入生产环境时,建议同时为extractUrlContent补充日志与指标(单次提取耗时、截断比例、错误码分布),以便持续调优。
九、在流水线中与其他工具协同
readUrl的价值只有在完整研究流水线中才能最大化。以下是 Research Agent 文档中定义的五步工作流(research-agent.md):
readUrl服务于E(Deep Reading)环节,其前后衔接如下:
- C → D(Initial Search → Source Selection):
webSearch按 query 返回结果与relevanceScore,Agent 据此筛选要精读的 URL 清单; - E(Deep Reading):对每个入选 URL 调用
readUrl,得到干净正文 + 章节 + metadata; - F(Claim Extraction):把
content.full与sections交给extractClaims,抽取带置信度的论断; - G(Cross-Verification):论断与
metadata.publishedDate、source一起参与verifyClaim的交叉验证,时效性与权威性成为置信度评估的输入; - H(Synthesis):最终由 research-synthesis-prompt.md 综合,其模板要求每条 finding 携带 source/date/type——恰好是
readUrl输出的metadata与stats所提供的字段。
也就是说,readUrl输出的结构化程度直接决定了整条流水线后续环节的质量:content.sections支撑章节级引用、metadata支撑来源评估、stats.truncated防止误引不完整内容、error.code支撑失败降级。这也是它在 tools/index.md 的 Research 工具类别中被列为webSearch之后首选工具的原因("Find information → webSearch, readUrl")。
十、结语:把 readUrl 的设计思想迁移到自己的 Agent 系统
回顾readUrl这个工具,其值得复用的设计要点可以总结为四条:
- 用 description 承载路由语义:一句话说清"做什么、什么时候用、带来什么好处",让模型在多工具场景下正确选择;
- 用 Zod 承载约束与默认值:非法输入在入口拦截,可选参数全部有安全默认值,
maxLength的 min/max 限制从源头控制上下文占用; - 用结构化输出承载下游需求:正文、章节、元数据、统计、错误码分而治之,每个下游环节(抽取、验证、综合)都能拿到恰好需要的字段;
- 用错误码承载失败语义:可重试与不可重试的错误清晰区分,让 Agent 能做重试、换源、降级等理性决策。
如果你正在构建自己的研究型 Agent(例如 LLM-as-a-Judge 评测前的资料收集阶段),可以直接照搬 read-url.md 的工具定义与错误码约定,配合 web-search.md 完成"检索—精读"闭环,再接入 research-synthesis-prompt.md 完成带引用的综合报告。更完整的工程落地方案(含真实 TypeScript 实现与测试)可继续查阅 src/tools/evaluation/direct-score.ts 与 tests 目录,作为扩展同族工具时的参照。
【免费下载链接】Agent-Skills-for-Context-EngineeringA comprehensive collection of Agent Skills for context engineering, multi-agent architectures, and production agent systems. Use when building, optimizing, or debugging agent systems that require effective context management.项目地址: https://gitcode.com/GitHub_Trending/ag/Agent-Skills-for-Context-Engineering
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考