☰
Kimi Code FetchURL 网页获取工具深度解析:从 URL 契约、安全边界到登录墙语义
2026/9/28 21:38:05 网站建设 项目流程
  • AI Agent
  • 代码智能体
  • 人工智能
  • 大模型
  • CLI

【免费下载链接】kimi-code

Kimi Code CLI — The Starting Point for Next-Gen Agents

项目地址:https://gitcode.com/gh_mirrors/ki/kimi-code
点击查看免费下载

Kimi Code 的 Agent 在需要读取具体网页内容时,会调用名为FetchURL的内置工具。它负责把 URL 对应的页面内容取回给模型,是 Agent 感知外部 Web 世界的核心通道。本文以官方工具说明文档 fetch-url.md 为主体,结合 fetchUrlTool.ts、local-fetch-url.ts、moonshot-fetch-url.ts 等源码与配套测试,完整讲解该工具的输入输出契约、内容提取模式、网络与安全边界、登录墙语义,以及底层提供商选择与错误处理链路,帮助你理解 Agent 抓取网页的能力边界与正确使用方式。

一、工具定位:Agent 读取网页的专用通道

FetchURL是注册在 agent-core-v2 工具体系中的一个 Web 域工具,注册声明位于 fetchUrlTool.ts:registerAgentToolService(IFetchURLTool, FetchURLTool, { name: 'FetchURL', domain: 'web' })。它的设计意图非常明确——当 Agent 需要阅读某个特定网页(而不只是搜索或浏览摘要)时,用它把页面内容取回来。

官方文档给出了工具的一句话定位:

Fetch content from a URL. The content is returned either as the main text extracted from the page, or as the full response body verbatim; a note at the top of the result states which of the two you received, so you can judge how complete it is. Use this when you need to read a specific web page.

翻译过来即:从 URL 获取内容,结果要么是页面提取出的正文文本,要么是完整响应体原样返回;结果顶部会有一行说明标注你拿到的是哪种,据此可以判断内容的完整程度。需要读取特定网页时使用本工具。

这意味着该工具存在两种内容交付模式,下文将逐一拆解。

二、输入契约:极简的单参数 URL 工具

FetchURL的输入定义在 fetch-url.ts,是一个仅含单一字段的 Zod schema:

export const FetchURLInputSchema = z.object({ url: z.string().describe('The URL to fetch content from.'), });

工具的parameters通过toInputJsonSchema(FetchURLInputSchema)自动从 Zod schema 生成 JSON Schema 并暴露给模型,见 fetchUrlTool.ts。也就是说,Agent 只需提供一个字符串 URL,工具便会完成后续的请求、校验、解析与提取。

在工具执行前,resolveExecution会构造一次“执行预览”:

  • accesses: ToolAccesses.none()——该工具不声明任何本地资源访问权限(不读写文件、不执行命令);
  • description: \Fetching: ${preview}``——当 URL 超过 50 个字符时,预览会截断为前 50 字符加省略号,避免把超长 URL 展示给审批者;
  • display: { kind: 'url_fetch', url: args.url }——在界面/日志中以url_fetch类型呈现;
  • approvalRule: literalRulePattern(this.name, args.url)与matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.url)——审批规则与规则匹配均基于 URL 字面量/通配符模式,使用者可以通过配置允许/拒绝特定的 URL 模式,实现对抓取目标的细粒度管控。

三、输出契约:两种内容交付模式与顶部注释

工具执行的核心逻辑在 fetchUrlTool.ts 的execution方法中:调用webFetch.getUrlFetcher().fetch(url, ...)得到{ content, kind },然后根据kind在结果顶部追加一行说明,再通过ToolOutputAccumulator组装成最终输出。

两种kind(定义见 fetch-url-types.ts)分别对应:

kind含义顶部注释原文
passthrough完整响应体原样返回(verbatim)The returned content is the full response body, returned verbatim.
extracted从页面提取出的主文本The returned content is the main text extracted from the page.

无论哪种模式,结果顶部还会追加一行引用提醒(citeReminder):

If you use it in your answer, cite this page as a markdown link, e.g. title.

这要求 Agent 在回答中引用该页面内容时,必须以 Markdown 链接(标题)形式标注来源。这两行注释由 fetchUrlTool.ts 中的note与citeReminder组合生成,模型据此刻意判断“我拿到的是完整原文还是提取后的正文”,从而评估内容完整度。

此外有两个边界情形:

  • 若响应体为空(!content),工具返回The response body is empty.,且不视为错误(isError: false);
  • 若内容过长或提取失败,则可能返回extracted类型但内容不完整——这是文档强调“注意顶部注释以判断完整度”的原因。

四、安全边界:只抓公开的 http/https,绝不触碰内网

官方文档明确了两条硬性限制:

Only fully-formed publichttp/httpsURLs are supported; other schemes and private or loopback addresses are not fetched. Very large pages may be truncated or refused.

即:只支持格式完整、面向公网的http/httpsURL;其他协议(scheme)以及私有地址、回环地址一律不被抓取;超大页面可能被截断或拒绝。

这些限制在本地抓取实现 local-fetch-url.ts 中有非常完整的落地:

1. 协议白名单。resolveSafeFetchTarget在发起请求前用new URL(url)解析目标,任何非http:/https:的协议(如file:、ftp:、data:)都会抛出Unsupported URL scheme错误(local-fetch-url.ts)。

2. 私有地址黑名单。内置一张覆盖 IPv4/IPv6 的BlockList(local-fetch-url.ts):包括0.0.0.0/8、10.0.0.0/8、100.64.0.0/10、127.0.0.0/8、169.254.0.0/16、172.16.0.0/12、192.168.0.0/16、IPv6 回环::1、站点本地fc00::/7、链路本地fe80::/10等。如果目标主机名本身就是 IP 且命中黑名单,直接拒绝;若主机名是localhost或以.localhost结尾,也直接拒绝。

3. DNS 解析后二次校验。对域名形式的主机,会先用dns/promises的lookup(host, { all: true })解析出全部地址,逐一检查是否命中私网黑名单;只要有一个地址是私有地址,就拒绝抓取(local-fetch-url.ts)。

4. DNS 固定(pinning)防重绑定。校验通过后,抓取时会用pinnedLookup构造一个自定义 lookup 函数,把连接固定到前面已校验过的 DNS 解析结果上,配合Agent({ connect: { lookup } })使用,防止 TOCTOU 时间差导致的 DNS 重绑定攻击(local-fetch-url.ts)。当系统配置了代理且该主机不在no_proxy名单内时,则走代理、不做 pinning。

5. 重定向受限。请求使用redirect: 'manual'手动跟踪重定向,仅允许 301/302/303/307/308 状态码,最多跟随 10 跳(MAX_REDIRECT_HOPS = 10),超限即报Too many redirects错误(local-fetch-url.ts)。

6. 大小上限。默认DEFAULT_MAX_BYTES = 10 * 1024 * 1024(10 MiB)。响应头中的content-length与实际读取后的字节数都会被检查,任一超过上限即拒绝返回,报Response body too large(local-fetch-url.ts)。这正是文档所说“超大页面可能被截断或拒绝”的实现依据。

上述安全机制均有配套测试覆盖,可参考 local-fetch-url.test.ts 与 fetch-url.test.ts。

五、内容提取流水线:正文提取与直通两种路径

抓取到响应后,readResponse(local-fetch-url.ts)按以下规则决定返回模式:

  1. 状态码 ≥ 400 时直接抛HttpFetchError(带状态码与状态文本),不返回页面内容;
  2. 检查大小上限;
  3. 根据Content-Type判断:若以text/plain或text/markdown开头,则把响应体原样返回,kind为passthrough;
  4. 其余 HTML 内容走extractMainContent提取正文,kind为extracted。

提取主文本的实现(local-fetch-url.ts)分两级:

  • 首选方案:用linkedom解析 HTML,再用@mozilla/readability的Readability.parse()提取文章正文与标题,输出形如# 标题\n\n正文的 Markdown 风格文本;
  • 兜底方案:若 Readability 解析失败或正文为空,则依次回退到页面<title>+<article>/<main>/<body>的textContent;
  • 若兜底仍为空,则报错Failed to extract meaningful content from the page. The page may require JavaScript to render.——提示该页面可能需要 JS 渲染才能产生内容,而本工具不做浏览器渲染。

这套流水线说明:passthrough主要用于纯文本/Markdown 资源(如 API 返回的文本、README 文件),而普通 HTML 页面则被压缩成“主文本”,大幅削减噪声,同时顶部注释让模型知道内容经过提取。

六、认证与登录墙语义:无会话抓取的边界

官方文档特别强调了认证边界:

The fetch carries no login or session for the target site, so pages behind authentication (private repositories, internal dashboards) return a login page or an error instead of the real content — if the text you get back looks like a generic landing or sign-in page, treat that as the login wall, not the answer, and reach the content through a credentialed route (an authenticated CLI or MCP tool) instead.

要点如下:

  • FetchURL的请求不携带任何目标站点的登录态或会话 Cookie(本地实现仅设置User-Agent头,见 local-fetch-url.ts),因此需要认证的页面(私有仓库、内部面板)只会返回登录页或错误;
  • 当返回内容看起来像通用着陆页或登录页时,模型应将其识别为登录墙(login wall),而非问题的答案;
  • 此时应改走有凭据的通道——例如已认证的 CLI 或 MCP 工具——去获取真实内容,而不是反复重试FetchURL。

这是一条关键的 Agent 行为准则:把“抓不到”正确归因为“认证墙”,而不是内容不存在。

七、底层提供商选择链路:本地抓取与托管抓取

FetchURL本身不直接发请求,而是通过IWebFetchService获取一个UrlFetcher实现。服务装配在 webService.ts:

getUrlFetcher(): UrlFetcher { return this.fromServicesConfig() ?? this.fromManagedOAuth() ?? this.localFetcher; }

选择优先级为:

  1. fromServicesConfig(显式托管配置):读取services.moonshotFetch配置节,若配置了baseUrl,则构造MoonshotFetchURLProvider,凭据可来自oauth配置解析出的 token provider、或apiKey,并可附带自定义请求头(webService.ts);
  2. fromManagedOAuth(受管 OAuth 提供商):若当前提供商是 OAuth 目录厂商且具备 OAuth 配置,则把提供商baseUrl拼接/fetch作为托管抓取端点(webService.ts);
  3. localFetcher(本地兜底):默认的LocalFetchURLProvider,即上文描述的本地抓取实现。

托管提供商 moonshot-fetch-url.ts 的工作方式与本地实现不同:

  • 以POST向baseUrl发送 JSON 体{ "url": url },请求头携带Authorization: Bearer <token>、Accept: text/markdown、Content-Type: application/json,若提供了toolCallId还会附带X-Msh-Tool-Call-Id头(moonshot-fetch-url.ts);
  • 托管服务返回的内容一律标记为extracted模式;
  • 失败自动降级:当托管抓取抛出错误时(非 abort),会记录web_fetch_fallback遥测事件并回落到本地localFallback抓取(moonshot-fetch-url.ts),保证功能可用性。

八、错误语义与模型可读的失败信息

execution中的错误处理(fetchUrlTool.ts)把底层异常翻译成对模型友好的文本:

  • 请求被中止(signal.aborted)时直接向上抛错,交由外层处理;
  • HttpFetchError(定义于 fetch-url-types.ts,携带 HTTP 状态码)→ 输出Failed to fetch URL. Status: <code>. <message>;
  • 其他网络类异常 → 输出Failed to fetch URL due to network error: <url>. <message>。

这些信息让 Agent 能区分“HTTP 层拒绝”“网络不可达”“URL 非法/私有地址”等不同失败原因,从而采取不同应对策略(如换 URL、换凭据通道或放弃)。

九、总结:FetchURL 的能力边界速览

维度规则
输入单个字符串url(Zod schema 校验)
输出extracted(页面主文本)或passthrough(响应体原样),顶部注释标明模式并提示 Markdown 引用
协议仅http/https,其余 scheme 拒绝
地址私网/回环/链路本地地址与 DNS 解析出的私网地址一律拒绝,支持 DNS pinning 防重绑定
大小默认上限 10 MiB,超限拒绝;重定向最多 10 跳
认证无登录态;登录墙页面只返回登录页/错误,需通过有凭据的 CLI 或 MCP 工具获取
抓取通道显式托管配置 → 受管 OAuth 托管 → 本地抓取,托管失败自动回落本地

在 Kimi Code 的 Agent 工具集中,FetchURL是读取公开网页内容的轻量通道:它刻意不携带会话、不做浏览器渲染、严格隔离内网,以换取安全与可控;而模型侧需要理解顶部注释、登录墙语义与失败分类,才能正确使用返回结果。如需进一步研究实现细节,可继续阅读 fetchUrlTool.ts、local-fetch-url.ts 及其测试 local-fetch-url.test.ts、web-fetch-service.test.ts。

  • AI Agent
  • 代码智能体
  • 人工智能
  • 大模型
  • CLI

【免费下载链接】kimi-code

Kimi Code CLI — The Starting Point for Next-Gen Agents

项目地址:https://gitcode.com/gh_mirrors/ki/kimi-code
点击查看免费下载

相关推荐

上一篇:如何使用LHM实现MySQL在线无锁迁移:完整实战指南
下一篇:oh-my-hermes AI Agent自动安装协议:INSTALL_FOR_AGENTS.md完整教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询