- AI Agent
- 代码智能体
- 人工智能
- 大模型
- CLI
【免费下载链接】kimi-code
Kimi Code CLI — The Starting Point for Next-Gen Agents
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 public
http/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)按以下规则决定返回模式:
- 状态码 ≥ 400 时直接抛
HttpFetchError(带状态码与状态文本),不返回页面内容; - 检查大小上限;
- 根据
Content-Type判断:若以text/plain或text/markdown开头,则把响应体原样返回,kind为passthrough; - 其余 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; }选择优先级为:
fromServicesConfig(显式托管配置):读取services.moonshotFetch配置节,若配置了baseUrl,则构造MoonshotFetchURLProvider,凭据可来自oauth配置解析出的 token provider、或apiKey,并可附带自定义请求头(webService.ts);fromManagedOAuth(受管 OAuth 提供商):若当前提供商是 OAuth 目录厂商且具备 OAuth 配置,则把提供商baseUrl拼接/fetch作为托管抓取端点(webService.ts);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
相关推荐
IronClaw Telegram 扩展 delete_message 工具深度解析:精确删除语义、unknown_message 错误契约与安全边界
IronClaw Telegram 扩展 delete_message 工具深度解析:精确删除语义、unknown_message 错误契约与安全边界 本篇指南
人工智能AI 应用交互助手AI AgenttheHarvester 5.0.0 语义契约深度解析:产品边界、执行模型与证据领域语言
theHarvester 5.0.0 语义契约深度解析:产品边界、执行模型与证据领域语言 本文围绕仓库根目录下的 CONTEXT.md https://link
网络安全渗透测试qwen-code ACP Skill 管理模块深度解析:源码获取、原子安装与安全边界
qwen code ACP Skill 管理模块深度解析:源码获取、原子安装与安全边界 导读 本文聚焦 qwen code(一个运行在终端中的开源 AI 编码代
人工智能AI Agent代码智能体工具调用交互助手CLIQwen
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考