乐于分享
好东西不私藏

AI智能体100讲|第5讲:Claude Code 如何治理一次工具调用?

AI智能体100讲|第5讲:Claude Code 如何治理一次工具调用?

tools[] 只是 API 请求中的一面,真正关键的是调用前、调用中、调用后的完整控制链

前四讲,我们一直在研究 Claude Code 怎么管理模型的“脑子”。

第2讲 看的是 System Prompt:Agent 当前应该遵守什么行动规则。

第3讲 看的是 Durable Memory:哪些信息值得跨会话长期保存。

第4讲 看的是 Session Notes 和 Compact:长任务被压缩以后,怎样恢复工作现场。

这一讲,我们把视角从“怎么想”移到“怎么动手”。

先看一个很普通的请求:

请打开 src/auth.ts,找到登录失败的原因并修复它。

如果只是聊天模型,它可以告诉你可能的原因。

但 Coding Agent 要真正完成任务,至少要读取文件、定位代码、修改文件、运行测试,再根据失败结果继续修正。

这些动作不可能由 LLM 自己在电脑上完成。

Claude Code 的做法,是在每次 LLM Request 里加入一个 tools 参数,让模型能够提出结构化的工具调用意图。

模型可能返回:

{

  "type": "tool_use",

  "id": "toolu_01",

  "name": "Read",

  "input": {

    "file_path": "/project/src/auth.ts"

  }

}

注意,这只是一个调用意图,不是调用本身。模型只是提出“我想用 Read 读这个文件”;真正执行读取、把结果送回来的,都是 Claude Code。

真正值得研究的问题,也不是“模型会不会输出 tool_use”,而是:

哪些工具可以进入 tools[]?

几十、几百个工具会不会把上下文撑大?

参数不合法时,谁负责拦截?

Edit 为什么必须先 Read?

哪些操作需要权限确认?

多个工具能不能并发?

结果太长、执行失败或会话 compact 时怎么办?

tool_result 最后又放回下一次请求的哪里?

这些问题合起来,才是 Claude Code 的工具治理。

模型只负责提出结构化行动意图;Claude Code 负责决定这个意图是否可见、是否合法、是否获准、何时执行,以及执行结果怎样回到下一轮上下文。

这一讲沿着一次工具调用的生命周期,把工具治理拆成三段:调用前管“模型能看见什么、这次调用获不获准”;调用中管“由谁执行、能不能并发”;调用后管“结果怎样安全回到下一轮”。

边界说明:
 本文的“调用前 / 调用中 / 调用后”是为了理解源码建立的观察框架,不是源码中三个完全串行的模块;文中的“统一控制平面”“乐观并发控制”“Agent 与工作流的差异”和最后的落地清单,是基于源码行为作出的工程归纳,不是 Claude Code 源码中的模块名或官方设计规范。

全文约 10000 字,预计阅读 25 分钟。

如果时间有限,可以先看第二章的一次完整调用,和第七章的总架构与落地清单。


一、一个 Tool,两张面孔

一次 LLM Request 大致由四部分组成:system 装行动规则,messages 装对话历史,tools 装本轮可选的工具定义,model 指定本次使用的模型。

这一讲只盯住 tools。先把本章结论放在前面:同一个工具,模型和 Runtime 看到的是两张完全不同的面孔。

图 1 就是全文最重要的边界:左侧是 API 接收的 Tool Definition,帮助模型选择能力;右侧是 Runtime 掌握的完整执行契约,负责把行动真正管起来。后面所有 Tool Search、校验、权限和结果处理,都发生在这条边界周围。下面把两侧分别拆开。

一个送给 Anthropic Messages API 的工具定义,可以简化成:

{

  "name": "Read",

  "description": "Reads a file from the local filesystem...",

  "input_schema": {

    "type": "object",

    "properties": {

      "file_path": { "type": "string" },

      "offset": { "type": "number" },

      "limit": { "type": "number" }

    },

    "required": ["file_path"]

  }

}

模型靠三样东西理解一个工具:name 说明它叫什么,description 说明什么时候该用、什么时候不该用,input_schema 说明参数怎么写、哪些必填。

模型看不到 Claude Code 里真正执行文件读取的 TypeScript 函数。它只看到这份被转换后的协议。

但如果翻开 Claude Code 的 Tool 对象,会发现它远不止这三个字段。当前还原源码里的 Tool 接口,按顶层成员统计有 47 个字段或生命周期方法:是否启用、是否延迟加载、输入怎样校验、权限怎样判断、能否并发、收到新消息时是否中断、真实 call() 怎样执行、结果怎样映射给模型、超大结果怎样持久化、失败和进度怎样展示……

这说明 Claude Code 对“工具”有两个完全不同的观察面。

API 接收的是 Tool Definition。 除了 namedescriptioninput_schema,还可能带 strictdefer_loading 等协议字段。启用 Tool Search 时,尚未发现的 deferred 工具不会进入当前请求的 tools[];模型先通过 ToolSearch 得到 tool_reference,Claude Code 再根据消息历史中的发现状态,把目标工具的完整 schema 加入后续请求。此时该定义可以带 defer_loading: true,由服务端结合 reference 展开给模型。

Runtime 管的是完整 Tool。 注册与启用、输入与业务校验、风险与权限元数据、Hooks 与人工确认、并发与中断策略、call() 真实执行、结果映射与体积治理、UI 与审计恢复——这是一份执行契约,负责回答“这个工具本轮能不能出现、参数真的合法吗、动作是否允许执行、结果怎样进入下一轮上下文”。

这两层不能混在一起。

description
 写得再严格,也只是给模型看的软规则。真正不能被绕过的限制,要放在 Runtime 的校验、权限和执行层。

二、先跑通一次:Read 的完整闭环

在分段拆解之前,先用最简单的 Read 完整走一遍,建立全文的基准路径。

图 2 把外层协议压缩成六步:模型只提出 tool_use,Runtime 执行真实动作,结果以 tool_result 回到下一轮 messages[]。下面按八个步骤把这条环真正走一遍。

用户说:打开 /project/src/auth.ts,看看登录校验是怎么实现的。

第一步:构造本轮工具池。 Claude Code 从内置工具、MCP 工具(通过 Model Context Protocol 接入的外部工具)和当前运行环境中组装候选工具,再按运行模式、功能开关、isEnabled()、可在请求前确定的整工具 blanket deny 规则和 MCP 连接状态过滤。只有本轮可能使用的工具,才有机会进入 API Request。

第二步:把 Read 转成 API schema。 从 Runtime Tool 中取出 nameprompt() 生成的模型可见 description、inputSchema 转换后的 JSON Schema,再根据模型能力和功能开关决定是否加入 strictdefer_loading 等协议字段。

第三步:模型输出 tool_use 模型不会真的读取文件,它只在 assistant message 里提出调用意图:

{

  "type": "tool_use",

  "id": "toolu_read_01",

  "name": "Read",

  "input": {

    "file_path": "/project/src/auth.ts"

  }

}

第四步:Runtime 查找并校验工具。 Runtime 先通过输入 schema 检查 file_pathoffsetlimit 和 pages 的结构与数值约束;随后 Read 的 validateInput 预检 PDF 页范围、deny 路径、二进制扩展和可能阻塞的设备路径。文件是否真实存在、能否读取等 I/O 错误,要等 Read.call() 访问文件系统时才能确定。

第五步:Hooks 和权限裁决。 调用进入 PreToolUse Hooks(执行前预留的自定义检查点,用户和组织都可以在这里挂上自己的脚本)和统一权限系统。Read 通常比写入工具风险低,但“只读”不等于“自动拥有权限”——受保护路径、权限规则或组织 Hook 仍然可以拒绝它。

第六步:执行 Read.call() 真正获准以后,Read 才访问文件系统,并按文件类型选择处理路径:普通文本、图片、Notebook、PDF 并不是同一种结果结构。

第七步:把内部结果映射成 tool_result 工具内部结果不会原样塞给模型。Read 的 mapper 把它转换成 Anthropic 协议需要的结果块,tool_use_id 指回第三步的调用 ID。

第八步:回填下一轮 LLM Request。 这条结果不进 system,也不追加到 tools[].description。它作为一条 user role message 里的 tool_result content block,和前面的 assistant tool_use 通过 ID 配对。下一轮模型看到文件内容以后,才决定直接回答、继续 Grep,还是进入 Edit / Write / Bash。

所以 Tool Loop 的完整闭环是:

LLM Request(tools[])

  -> assistant: tool_use

  -> Runtime 治理与执行

  -> user: tool_result

  -> 下一次 LLM Request

这条线,是后面所有复杂工具的共同底座。接下来要拆开的,是中间那一段 Runtime 内部究竟设置了多少道控制关口。


三、调用前(上):先控制模型能看见什么

很多人把工具治理理解成权限弹窗。

但 Claude Code 的第一道治理发生得更早:在模型生成 tool_use 之前,先控制它能看到哪些工具,以及每个工具以什么定义出现。

图 3 把调用前治理分成三层:模型生成前管“能看见什么、能生成什么”;调度层先按输入判断并发安全性并排队;每个开始执行的调用再进入完整的 schema、业务校验、Hooks 和权限防线。权限只是单次调用防线的一个控制点,不是全部工具治理。

3.1 工具池不是一张写死的数组

Claude Code 确实有一组基础工具,但最终进入当前会话的工具池,会经过多层裁剪:

基础内置工具(构建类型 / 环境开关)

  -> simple / REPL / coordinator 等专属处理

  -> blanket deny 预过滤

  -> tool.isEnabled()

当前 MCP 工具

  -> blanket deny 预过滤

两路合并

  -> built-in / MCP 分区稳定排序

  -> 按 name 去重,built-in 优先

  -> Agent 场景再走自己的专属过滤

这里有一个很重要的设计原则:

如果在请求构造阶段已经确定某个工具绝对不能使用,就没必要继续把它的名称、说明和 schema 暴露给模型。

这不仅减少误调用,也降低 prompt 体积。

3.2 prompt() 和 description() 不是一回事

Tool 接口里同时存在 prompt(...) 和 description(...),名字接近,用途不同。

prompt() 生成 API tools[].description,告诉模型什么时候用、参数怎么传、有哪些限制。例如 Edit 的模型说明会强调:先读取文件,old_string 必须精确匹配,不要在未理解上下文时修改。

description(input) 根据本次具体参数生成给人看的说明,用在权限确认和工具详情里。同一次 Edit,在权限 UI 里给人看的更像一句“修改 /project/src/auth.ts”。

一个面向模型选择,一个面向人类判断。

3.3 strict: true 到底严格在哪里?

当功能开关、模型和 API provider 都支持时,Claude Code 会在工具定义里加入 strict: true

它作用在 API / 模型生成阶段:当功能开关、工具声明和模型能力都满足时,Claude Code 在工具定义中发送 strict: true,请求 API 更严格地遵循工具说明和参数 schema。至于服务端对具体 JSON Schema 子集提供哪些逐字段保证,属于 API 契约,不能只从这份客户端源码推导。

但 strict 不负责判断文件是否存在、old_string 是否能匹配、命令是否危险、用户是否授权。这些都在它的能力范围之外。

所以正确的分层关系是:

strict

  请求 API 在受支持的 schema 子集内更严格遵循参数结构。

inputSchema.safeParse

  在客户端执行前重新检查结构和类型。

validateInput

  检查工具自己的业务条件和当前状态。

permissions

  判断这个动作能不能执行。

strict
 请求的是更严格的 schema 遵循,不负责业务状态和动作授权;Claude Code 仍会在本地执行 inputSchema.safeParse、工具业务校验和权限检查。

3.4 工具太多时,Tool Search 自己也是一个工具

连接多个 MCP server 以后,把所有工具的完整 description 和 schema 都放进首轮请求,会占用大量上下文,也让模型在过多低频能力中更难选对工具。

Claude Code 的答案是 Tool Search。它不是藏在请求构造器里的普通搜索函数,而是通过统一 Tool 接口注册的标准工具:输入 query,只读、可并发,结果是能够展开工具定义的 tool_reference

它不执行目标工具,也不联网安装新工具,只在当前已组装好的 deferred(延迟加载)工具池中搜索匹配项。

围绕延迟加载有三个容易混淆的字段:

shouldDefer  内置工具参与延迟判定的信号之一;

alwaysLoad   强制工具保持首轮加载,优先于默认延迟策略;

searchHint   只参与 Tool Search 的关键词匹配和排序。

shouldDefer: true 不表示工具被禁用;alwaysLoad: true 优先级更高,适合必须首轮完整可见的关键工具;searchHint 只提供检索关键词。三个字段都不负责权限。

一次完整的发现过程分两步:

第一步:ToolSearch(query) -> tool_reference(WebSearch)

第二步:下一轮请求恢复发现状态 -> 模型才能生成 tool_use(WebSearch)

它不是权限审批,也不是二级执行器。目标工具真正执行时,仍然要重新经过自己的 schema、业务校验、Hooks 和权限链路。

这里补一句背景:Prompt Cache 是 API 的前缀复用机制——本次请求开头那部分内容和上一轮完全相同时,API 可以直接复用之前的计算结果,更快也更便宜。所以工具定义最怕每一轮变来变去。

Claude Code 通过 session 内 schema 缓存和稳定排序减少基础工具定义的抖动;Tool Search 的发现状态来自消息历史,命中的 deferred 工具会在后续请求中加入 tools[]。因此请求并非只在消息尾部变化,但未发现的低频 schema 不必预先进入每一轮请求,稳定前缀仍有机会复用缓存。Tool Search 的目标不是“请求永远不变”,而是避免把所有低频工具 schema 都预先塞进请求。

3.5 Edit 必须先 Read,这个顺序谁来保证?

Edit 和覆盖已有文件的 Write 都要求先 Read,很容易据此推测存在 Edit.dependsOn = Read 这样的配置。

但当前 Tool 接口里并没有 dependsOnrequiresTool 这类依赖声明,也没有一张预先画好的工具依赖图。Claude Code 用两层方式处理依赖:

Prompt 负责让模型走正确顺序;

Runtime 状态负责强制关键前置条件。

Tool Prompt 告诉模型“修改前先读取”;Read 在会话状态里记录已读文件和时间信息,Edit / Write 在 validateInput 阶段检查这份状态。没有先 Read,或者 Read 之后文件又被外部修改,写入都会被拒绝。

由这条消息生成—执行顺序还可以推得一个约束:同一条 assistant message 里的多个 tool_use,后一个在生成时看不到前一个的结果。如果工具 B 的参数必须依赖工具 A 的输出,模型只能跨轮调用——第一轮拿到 A 的 tool_result,第二轮再构造 B 的 input。

从工程视角看,可以把这里体现出的差异概括为:传统工作流引擎通常把步骤依赖预先画成固定流程,Agent 的后续动作则可以由模型结合上一步结果动态决定,关键前置条件再交给运行时状态兜底。


四、调用前(下):参数合法,不等于动作已经获准

模型输出一个或多个 tool_use 以后,Runtime 先进行并发分类与排队,再让每个开始执行的调用进入自己的完整防线。源码里的两层关系更接近:

assistant: tool_use(s)

  -> 调度层:findTool + safeParse(用于并发判定)

  -> isConcurrencySafe(input)

  -> concurrency batch / serial queue

每个开始执行的调用:

  -> findToolByName

  -> inputSchema.safeParse

  -> validateInput(若工具实现)

  -> 输入清洗与兼容字段回填

  -> PreToolUse Hooks

  -> 权限裁决 / 必要时人工确认

  -> tool.call()

这里的第一次 safeParse 只服务于调度层的保守分类;进入单次执行链以后,Runtime 仍会重新解析输入并做完整治理。下面逐层拆开单次调用防线。

4.1 名称解析与结构校验

Runtime 先根据 tool_use.name 找工具定义,也可能处理兼容 alias(同一个工具的旧名或别名)。名称不存在时,系统不会随便猜一个工具执行,而是生成与原 tool_use.id 配对的错误结果,让模型下一轮修正。

找到工具后执行 inputSchema.safeParse(tool_use.input),检查字段、类型、enum 和对象结构。即使 API 已经启用 strict,这一步仍然保留——模型输出、第三方 provider、历史恢复和协议兼容都可能带来异常输入。

4.2 工具业务校验

schema 只知道“参数长得像不像”,不知道“当前能不能做”。所以工具还有自己的 validateInput(...)

Read:PDF 页范围、deny 路径、二进制扩展和阻塞设备路径等前置条件;文件存在性与可读性留到 call() 的 I/O 阶段处理;

Edit:old_string 是否唯一匹配,文件是否先被读取;

Write:目标已存在时是否满足先 Read 的条件;

Bash:命令、超时和运行方式是否符合限制;

可以把两层校验理解为:schema 判断“这是一把钥匙吗”,validateInput 判断“这把钥匙现在能开这扇门吗”。

4.3 PreToolUse Hooks:执行前的自定义检查点

进入权限系统之前,Claude Code 还留了一个可编程的扩展点:PreToolUse Hooks。

它的机制是:用户或组织在 settings 里按工具名注册外部命令。每次调用走到这一步,Claude Code 会在继续执行工具前运行匹配的 Hook,把本次的工具名和参数副本传给它们,并等待结果,再决定放行、拦截,还是使用 Hook 返回的更新输入继续。

这和提示词约束有本质区别。在 CLAUDE.md 里写“不要动 .env 文件”,是给模型看的软规则,模型可能忘、可能理解偏;写成 Hook,就变成一道程序化的门禁——不管模型怎么想,不合规的调用到这里直接被拦下,拦截原因还会作为配对的错误结果返回给模型,让它换方案或向用户解释。

典型用法:拦截危险命令、保护敏感文件、记录审计日志,或把调用接入组织的外部审批系统。

但 Hook 不是拥有最高权力的后门。即使 Hook 返回 allow,更高优先级的 deny、ask 和安全检查仍然可以阻止调用——收紧容易,放宽有上限。这个不对称设计保证了扩展点不会变成绕过安全边界的通道。

4.4 权限不是一个布尔值

Claude Code 的权限结果至少有四种语义:

allow        工具级判断允许,可以带 updatedInput;

deny         明确拒绝;

ask          需要进入人工确认;

passthrough  工具自己不下结论,交给统一权限系统继续判断。

权限规则可以来自用户设置、项目设置、组织策略、命令行参数、当前 session,以及用户刚刚在权限弹窗里保存的选择。

规则还可以只匹配某类内容,而不是粗暴地允许整个工具:只允许某类 Bash 命令、只允许某个目录、只允许某个域名、对某类修改始终询问。

4.5 什么情况下需要人介入

最终结果是 ask 时,系统把调用交给用户确认。用户不只是“同意 / 不同意”,还可以仅本次允许、拒绝并给模型反馈原因、修改参数后允许、保存一条以后复用的规则,甚至中止整个任务。

人类介入也是工具协议的一部分:它既是权限裁决,也是给模型提供纠偏信息的渠道。

如果后台 Agent 没有可用的交互界面,Claude Code 不会假装已经得到授权。它先看 PermissionRequest Hooks 能否给出决定;没有决定,就自动拒绝。

这是典型的 fail-closed:

没有人能确认,不等于默认获得权限。

顺带说清一个边界:权限模式可以改变默认行为,例如 acceptEdits 给安全文件编辑更快的允许路径,plan 限制真实执行。但即使进入 bypassPermissions,显式 deny、内容级 ask、必须交互的工具和某些安全检查仍然可以拦截调用。“跳过普通确认”和“关闭所有安全边界”不是一回事。

4.6 多个 tool_use 怎样并发

一个 assistant message 可以一次返回多个 tool_use。Claude Code 不会简单地全部并行,也不会一律串行,而是让每个工具通过 isConcurrencySafe(input) 根据本次参数判断能否并发:

连续的 concurrency-safe 调用

  -> 组成并发 batch。

非 concurrency-safe 调用

  -> 单独执行,前后调用都要等它跑完。

参数解析失败或并发判断异常

  -> 保守地按不可并发处理。

注意 isReadOnly 描述的是副作用风险,isConcurrencySafe 描述的是能否和相邻调用同时运行——只读不必然等于并发安全。

并发结果不靠返回顺序配对,而是靠 tool_use_id。即使某个并行调用被取消,系统也会代填一条 synthetic error result(合成的错误结果),保证每个调用都有下文,协议不断裂。

调度器让某个调用开始运行后,只有当它自己的这些关口全部通过,才轮到真正的 tool.call()


五、调用中:tool.call() 背后不止一种执行方式

Claude Code 统一的是外层治理协议:tool_use 进、校验和权限居中、tool_result 出。

但进入 tool.call() 以后,Runtime 可以把动作交给完全不同的执行后端。按“真正干活的是谁”来分类,当前还原源码里至少可以看到这样一张地图:

tool.call()

  ├─ 进程内本地执行:Read / Glob / Grep

  ├─ 本地协议适配与 Language Server 后端:LSP

  ├─ 带状态前置条件的本地写入:Edit / Write / NotebookEdit

  ├─ 子进程与后台任务:Bash / PowerShell / Monitor

  ├─ 能力发现与动态加载:ToolSearch / DiscoverSkills

  ├─ 直接网络访问与二次处理:WebFetch

  ├─ 嵌套 API 与服务端工具:WebSearch

  ├─ 外部协议适配:MCP tools / 远程连接

  ├─ 递归 Tool Loop:Agent / Team

  ├─ Prompt 展开与复合工具:Skill / Workflow / REPL

  └─ 人工交互与内部控制面:AskUserQuestion / Plan / Todo / Task

这张地图上有三个值得停下来的观察。

其一,一次工具调用不一定直接返回最终结果。Bash 可以转成后台任务,先返回一个 task id,让后续工具继续查询、监控或停止同一个任务。

其二,“真实执行”不一定发生在本地。WebSearch 的执行器在 Anthropic 服务端,MCP 工具的执行器在外部 server,Agent 工具的内部是子 Agent 自己的多轮模型请求。

其三,工具不只用于操作外部世界。暂停下来问用户一个问题、更新任务清单、切换运行模式,也都通过同一套 Tool Loop 被治理。

本篇不逐类展开这张地图,只从中挑四个代表性分支讲透:文件状态依赖、工具发现、服务端二级调用和 Agent 内部状态。

图 4 预览了这四条路径的差异:都从 tool_use 进入、以 tool_result 离开,但 Runtime 中间可以是文件状态约束、二级发现、服务端模型请求,也可以是纯内部状态更新。每个分支都沿着同一组问题看:模型为什么选择它、Runtime 在执行前拦什么、真正调用发生在哪里、结果或失败怎样回到下一轮。

5.1 Edit / Write:依赖不是 prompt 里的建议

模型准备修改已有文件时,正确路径是:

Read

  -> 记录文件内容和修改时间(mtime)

  -> Edit 或 Write

  -> validateInput 检查是否先读过

  -> 检查 Read 后文件是否被外部改变

  -> 权限裁决

  -> 安全写盘

  -> 更新 readFileState

这里最值得借鉴的是“软规则 + 硬状态”组合:Tool Prompt 先告诉模型应该 Read,Runtime 再通过会话状态拒绝不满足前置条件的写入。

如果文件在 Read 以后被其他进程修改,Edit / Write 也不能拿旧认知覆盖新内容。思路是:先读取并记录版本,写入前确认版本没变,变了就让模型重新读取和规划。这可以类比为数据库领域的乐观并发控制。

它比给工具接口增加一个 dependsOn: Read 更有价值——真正需要保护的不是调用顺序本身,而是模型执行写入时,对目标文件的认知是否仍然新鲜。

模型选择:

  Edit 提交 file_path / old_string / new_string;

  Write 提交 file_path / content。

执行前:

  schema 检查参数形状;

  validateInput 检查是否 Read、内容是否匹配、文件是否变化;

  写入权限仍可能进入 ask。

执行中:

  Edit 做精确字符串替换;

  Write 创建新文件或完整覆盖已有文件。

执行后:

  成功结果更新 readFileState;

  失败结果告诉模型重新 Read、修正匹配内容或换一种方案。

它代表的是“带版本前置条件的本地写入型工具”。

5.2 Tool Search:调用一个工具,是为了发现另一个工具

Tool Search 是元工具:一个专门用来发现其他工具的工具。它的 call() 不访问文件、不搜索网页,也不执行 MCP server,只在当前 deferred 工具池中匹配名称、searchHint 和 prompt,把匹配项映射为 tool_reference

关键约束是:Tool Search 的 allow,不等于目标工具的 allow。

发现能力和授权能力必须分开。否则“知道系统里有一个工具”,就会错误地等价于“已经允许执行它”。

模型选择:

  输入 query,按名称或能力关键词寻找 deferred 工具。

执行前:

  ToolSearch 自身是只读工具;

  输入只负责搜索定义,不携带目标工具的执行参数。

执行中:

  call() 只在 deferred 工具池中做匹配。

执行后:

  mapper 返回 tool_reference;

  找不到时模型可以改写 query;

  找到后下一轮再调用目标工具,并重新经过目标工具的权限链。

它代表的是“能力发现型元工具”。

5.3 WebSearch:外层 Tool Loop 里还有一次内层模型请求

WebSearch 最容易被误解成一个本地搜索 SDK。当前还原源码里的路径更接近:

主模型

  -> 外层 tool_use(name=WebSearch)

  -> Claude Code 本地 WebSearch.call(...)

  -> 再发起一次独立的 Anthropic Messages API 请求

  -> 内层请求携带 server tool: web_search_20250305

  -> 内层模型生成 server_tool_use

  -> Anthropic 服务端执行搜索

  -> 返回 web_search_tool_result 和文本

  -> 本地包装成外层普通 tool_result

  -> 回给主模型

这里同时存在两套工具协议:外层是 Claude Code 的 Tool Loop(tool_use / tool_result),内层是 Anthropic 服务端工具调用(server_tool_use / web_search_tool_result)。外层主模型决定“当前任务需要搜索”,内层模型负责组织真正的搜索请求。所谓 server tool,就是由 Anthropic 在服务端托管执行的工具——搜索动作既不发生在模型里,也不发生在 Claude Code 本地。

这也解释了为什么某些 provider 即使支持普通 tool_use,也不一定能直接使用这个内建 WebSearch——它们还需要支持内层服务端搜索协议。

allowed_domains 也不能替代外层权限:前者约束内层搜索范围,Claude Code 仍要先决定这次 WebSearch 是否允许执行。

模型选择:

  外层输入 query,可选 allowed_domains 或 blocked_domains。

执行前:

  validateInput 检查 query 非空;

  allowed_domains 和 blocked_domains 不能同时出现;

  外层 WebSearch 权限独立裁决。

执行中:

  call() 创建独立内层 Messages API 请求;

  内层模型驱动 web_search_20250305 服务端工具。

执行后:

  内层结果被整理成外层 tool_result;

  provider 不支持、权限拒绝或内层搜索失败都是可恢复的错误路径。

它代表的是“本地包装 + 服务端托管执行型工具”。

5.4 TodoWrite:没有操作外部世界,也完整走 Tool Loop

TodoWrite 不读文件、不写业务代码、不执行命令,只更新当前 Agent 的结构化任务状态。但它仍然是一个标准工具:tools[] 里有定义,模型输出 tool_use,Runtime 通过 inputSchema.safeParse 校验每一项,call() 更新 AppState.todos(Claude Code 进程内的应用状态,不落到你的项目文件里),mapper 生成简短 tool_result。TodoWrite 没有单独实现 validateInput;列表结构、非空文本和状态 enum 由 TodoListSchema 在本地 schema 校验阶段处理。

TodoWrite 每次提交的是整张列表快照,不是增量 patch。所以在会话恢复路径里(尤其 SDK / 非交互场景),系统可以从后往前找到最后一次 TodoWrite tool_use,取出 input.todos 重建当前状态。这也是为什么完整列表存在于 assistant tool_use.input 中,而成功的 tool_result 可以非常短。

它还体现了三条输出通道的分离:AppState 保存结构化状态,终端 UI 展示成独立面板,LLM tool_result 只返回模型继续工作所需的简短确认。主 Agent 和子 Agent 使用不同的 todo key,避免共享一张清单相互污染。

模型选择:

  多步骤任务需要显式跟踪时,提交完整 todos[] 快照。

执行前:

  strict 请求更严格的服务端 schema 遵循,本地 inputSchema.safeParse 再检查列表结构和状态;

  todo 更新本身不需要外部世界权限确认。

执行中:

  call() 按 sessionId 或 agentId 更新 AppState.todos。

执行后:

  UI 展示完整列表,模型只收到简短确认;

  全部完成时清空活动状态,恢复时可从最后一次 tool_use 重建。

它代表的是“Agent 内部状态管理型工具”。

四条路径合起来,验证了这一章的结论:入口和出口统一,内部执行路径不同。Bash 的子进程、MCP 的协议桥接、Agent 的递归 Tool Loop,同属这张地图上尚未展开的分支。


六、调用后:结果不是原样塞回模型

工具执行完成,只代表真实动作结束。要让 Agent 继续工作,还要把结果治理成合法、可控、可恢复的下一轮上下文。这里不能把所有工具压成一条完全相同的固定顺序:

普通内置工具成功:

  call -> mapper -> 单结果体积处理 -> tool_result message

       -> PostToolUse 附件 / 审计 / continuation 控制

MCP 工具成功:

  call -> PostToolUse

       -> 可选 updatedMCPToolOutput

       -> mapper -> 单结果体积处理 -> tool_result message

执行失败:

  catch -> PostToolUseFailure

        -> is_error tool_result + hook messages

三条路径随后进入消息规范化、配对修复和下一次 LLM Request。

6.1 模型结果和终端结果不是同一份内容

每个 Tool 都可以实现 mapToolResultToToolResultBlockParam(...),负责把内部 data 转成模型能看到的 tool_result;终端 UI 则通过另一组 render 方法展示进度、成功、失败、权限拒绝和可展开详情。

两个通道服务的对象不同:给模型看的结果追求协议合法、信息足够、上下文成本可控;给用户看的结果追求可读、可审查。TodoWrite 的独立任务面板、Bash 的运行进度,都说明 UI 没必要机械复刻模型上下文。

工具成功后还可以运行 PostToolUse Hooks,用于审计、附加提醒或控制后续流程。对 MCP 工具,Hook 还可以通过 updatedMCPToolOutput 改写输出后再映射;普通内置工具的结果已经先完成映射和体积处理,不能把“过滤输出”概括成所有 PostToolUse Hook 的通用能力。call() 抛错则进入 PostToolUseFailure Hooks,系统会尽量把失败转换成 is_error: true 的匹配结果。

6.2 错误也必须生成 tool_result

Claude Code 会尽量给每一个 tool_use 生成一个可配对的结果,包括:

工具名不存在;

schema 校验失败;

validateInput 失败;

权限被拒绝;

PreToolUse Hook 阻止;

真实执行抛错;

并行 sibling 被取消;

用户中止调用。

为什么失败也要返回结果?因为下一轮模型需要区分:参数写错了要重试、前置条件不满足要先 Read、权限不允许要换方案、执行环境失败要诊断错误。错误如果只出现在终端日志里,模型看不到,就无法恢复。

6.3 大结果不能无限占用上下文

一条 Bash 命令可能打印几万行日志,MCP 工具可能返回大型 JSON。Claude Code 有一层常规的单结果体积治理:结果超过工具的有效阈值时,完整内容写入当前会话目录下的 tool-results 文件,模型只收到大约 2,000 bytes 的预览和完整文件路径。

在相应功能开关启用、contentReplacementState 可用时,还会执行第二层消息级聚合预算:同一条 API user message 里的多个结果合计过大时,系统继续选择较大的结果做持久化替换,直到总量回到预算内。

空字符串和纯空白结果也会被规范成类似 (ToolName completed with no output) 的占位文本,避免模型把空工具结果误认为对话边界。

顺带一个边界:Tool 接口里可以有 outputSchema,但当前还原源码的主执行链里,没有看到所有普通工具都统一执行 outputSchema.safeParse。当前可以直接确认的消费点主要是 MCP entrypoint 的结构暴露,以及部分 UI 恢复和渲染路径的安全解析;声明它不等于自动完成结果治理。

6.4 tool_result 最后拼接到 request 的哪里

这是整篇最容易记错的地方。

图 5 里最关键的不是 role=user 这个表面名称——它只是 Anthropic 工具协议中的消息结构,不表示用户亲手输入了这段结果——而是两端共享同一个调用 ID。没有这层配对,模型就无法确定结果属于哪一次行动意图。

落到 API 请求里,tool_result 不进入 system prompt,不进入 tools[],也不追加在 assistant 文本尾部,而是进入下一次 LLM Request 的 messages

[

  {

    "role": "assistant",

    "content": [

      {

        "type": "tool_use",

        "id": "toolu_123",

        "name": "Read",

        "input": {

          "file_path": "/project/src/auth.ts"

        }

      }

    ]

  },

  {

    "role": "user",

    "content": [

      {

        "type": "tool_result",

        "tool_use_id": "toolu_123",

        "content": "...result..."

      }

    ]

  }

]

发出下一次请求前,Claude Code 还会做最后一道整理。先合并清理本地消息结构(normalizeMessagesForAPI);再专门检查配对完整性(ensureToolResultPairing):有调用没结果的,代填一条错误结果;有结果找不到调用的,直接删掉;最后剥离当前模型不支持的内容块和多余媒体。

所以 tool_use_id 不只是一个方便调试的字段。它是工具执行结果能够安全进入下一轮推理的协议主键。

6.5 Compact / resume 后怎样保持工具历史合法

工具历史也是上下文治理的一部分。会话 compact 或恢复以后,Claude Code 需要继续保持 tool_use / tool_result 配对、Tool Search 已发现工具集合、大结果已被替换成文件预览的状态。

这和第4讲的结论一致:

Compact 不是简单压短文本,还要恢复一个符合 API 协议、能够继续执行的现场。

七、总架构与落地清单

到这里,可以把整条链路放回一张图。

图 6 可以压缩成四个观察面。前三个沿着一次 Tool Loop 展开,第四个贯穿整个会话生命周期。

1. 调用前|选择、校验与授权

治理工具暴露、按需发现、并发分类与排队、schema、业务校验、调用前 Hooks、权限和人工确认。

2. 调用中|启动、执行与中断

治理已排队调用的启动与中断、tool.call(),以及本地或服务端的真实执行。

3. 调用后|映射、配对与回填

治理结果映射、错误配对、体积控制、tool_result 回填和恢复。

4. 横向能力|稳定、扩展与恢复

治理缓存稳定性、Hooks 扩展、审计,以及 compact / resume。

最重要的边界可以浓缩成四句话:

发现不等于授权;strict 不等于业务校验;只读不等于必然免审批;tool_result 不进入 System Prompt。

自己构建 Agent 时,工具治理怎么落地

如果你在构建自己的 Agent,想把这套工具治理搬过去,可以直接用下面这份清单——自己照着做,或者整段丢给编程助手(Codex、Claude Code),让它帮你实现。

下面是基于前述源码实现归纳的最小可行版本,不是 Claude Code 源码中的规范、模块划分或逐行复刻:

目标:

  为一个 Agent 搭建位于模型和真实世界之间的工具控制平面。

核心模块:

  1. 双层工具契约

     模型侧:name、description、input schema。

     Runtime 侧:enable、validate、permission、schedule、call、result mapper。

  2. 工具池构造

     每轮请求前按运行模式、开关和权限过滤候选工具;

     确定不可用的工具不进入 tools[];

     工具按稳定顺序排列,保护 prompt 缓存前缀。

  3. 两层输入校验

     schema 校验结构和类型;

     业务校验检查当前状态,例如文件是否读过、目标是否被外部修改。

  4. 权限裁决器

     规则 = 工具名 + 内容模式 + allow/deny/ask + 来源;

     deny 优先;没有明确允许时进入 ask;

     无人可确认时自动拒绝,fail-closed。

  5. 并发调度器

     isConcurrencySafe(input) 按本次参数判断能否并发;

     不确定就保守串行;

     被取消的调用也要生成配对的错误结果。

  6. 结果治理

     每个 tool_use 都有配对的 tool_result,失败也不例外;

     给模型的结果和给用户的展示分成两条通道;

     超大结果落盘存档,模型只收预览和文件路径。

  7. 恢复协议

     会话压缩或恢复后,保持调用与结果配对、

     已发现工具集合和大结果替换关系。

关键约束:

  - 安全底线写在 Runtime 校验和权限里,不能只写在工具说明里。

  - 工具被看见、被发现、被授权,是三个独立判断。

  - 错误只出现在日志里,模型就无法恢复;必须回到 tool_result。

  - 给模型和给用户的内容不共用同一份渲染。

如果只做最低可行版,可以先实现四件事:

1. 双层契约:schema + call + result mapper 分开。

2. 两层输入校验:结构一层,业务状态一层。

3. deny 优先、fail-closed 的权限裁决。

4. 每个调用必有配对结果,失败也不例外。

做到这些,工具系统才不只是“能调用”,而是能够在长任务里持续、可控地运行。


结尾:工具能力的核心,不是“能调用多少工具”

这一讲从 LLM Request 的 tools[] 出发,看了 Claude Code 如何治理一次工具调用。

Read 和 Edit 说明,工具之间的关键前置条件要由 Runtime 状态兜底。

Tool Search 说明,发现能力和执行权限必须分开。

WebSearch 说明,一个外层工具内部还可能启动另一套模型与服务端工具协议。

TodoWrite 说明,即使不操作外部世界,Agent 内部状态也可以通过标准 Tool Loop 被管理、展示和恢复。

从工程视角,可以把 Claude Code 的这些机制概括为模型与真实世界之间的一套统一控制平面。

模型负责决定下一步想做什么,工具治理系统负责确保这一步看得见、说得清、验得过、批得下、跑得稳、收得回来。

在本文的工程归纳中,它是 Agent 从“会回答”走向“能可靠行动”的重要边界之一。

当然,这套控制平面里还有不少值得单独拆开的话题:每种执行后端由谁真正执行、权限规则怎样精确关联到具体工具、Hooks 怎样承接组织策略。这个系列会继续沿着 Claude Code 的源码,把它们一个个讲清楚。

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-20 01:59:02 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/866222.html
  2. 运行时间 : 0.556323s [ 吞吐率:1.80req/s ] 内存消耗:4,818.77kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=43200a02fe05b1a7cee546f4b88ae90e
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000916s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001414s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.040739s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.026327s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001859s ]
  6. SELECT * FROM `set` [ RunTime:0.000650s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001843s ]
  8. SELECT * FROM `article` WHERE `id` = 866222 LIMIT 1 [ RunTime:0.045532s ]
  9. UPDATE `article` SET `lasttime` = 1784483942 WHERE `id` = 866222 [ RunTime:0.025943s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.001360s ]
  11. SELECT * FROM `article` WHERE `id` < 866222 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.037178s ]
  12. SELECT * FROM `article` WHERE `id` > 866222 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.095608s ]
  13. SELECT * FROM `article` WHERE `id` < 866222 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.109893s ]
  14. SELECT * FROM `article` WHERE `id` < 866222 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.040647s ]
  15. SELECT * FROM `article` WHERE `id` < 866222 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.029566s ]
0.557956s