Liking cljdoc? Tell your friends :D

im.ttalk.agent.tool

工具函数定义宏

deftool 宏同时生成:

  1. 普通 Clojure 函数(可直接调用)
  2. 完整的 tool schema 元数据(用于 LLM Function Calling)

参数声明使用向量格式: [名称 类型 描述] 支持可选参数: [名称 类型 描述 :default 默认值] 支持参数约束: [名称 类型 描述 :enum [..] :pattern ".." :minimum 1 :maximum 9 :min-length n :max-length n :items {:type "string"} :min-items n :max-items n :properties {…} :required [..] :additional-properties false] (进 input_schema,模型看得到;调用前按 schema 完整校验,见 tool-args) 对象参数的属性表可写成 [[名称 schema] …] 键值对 vector——保序,字段多于 8 个时用这个写法 (map 字面量多于 8 键会被 Clojure reader 变成 hash map,键序不再跟源码文本一致)。 数组条目的对象(:items {:type "object" :properties …})与更深的嵌套对象都吃同一套写法。

schema 在命名空间加载期构造(不是常量):AOT 编译会把常量 map 重建(>8 键即 PersistentHashMap),参数声明顺序会丢;加载期构造则 eval 与 AOT 完全一致。 支持敏感标记: {:sensitive true} 支持只读 context: {:context true} 支持串行标记: {:serial true}(副作用工具,批内并行时整批退化为按序执行) 支持直接返回: {:return-direct true}(结果即最终答案,不再回灌 LLM)

状态读写(Tool 阶段 MapReduce 契约,见 docs/agent-loop-concurrency-design.md §9):

  • 读:{:context true} 工具收到只读的轮初 context 快照(ctx 变量自动绑定)
  • 写:任意工具(无论是否声明 :context)返回 {:result r :writes {k v}} 声明写意图;批次屏障处按 tool-call 原始序经槽级 reducer 折叠进 context。 同批工具互相看不到对方的写(快照隔离);失败/超时/拒绝的调用 writes 不生效。

使用示例:

;; 基本用法 (deftool get-weather "获取指定城市的天气信息" [[city :string "城市名称"]] (fetch-weather city))

;; 带可选参数 (deftool calculate "执行数学计算" [[expression :string "数学表达式"] [precision :int "小数精度" :default 2]] (compute expression precision))

;; 标记敏感工具 (deftool delete-file "删除文件" [[path :string "文件路径"]] {:sensitive true} (io/delete-file path))

;; 带 tags 的工具 (deftool get-weather "获取天气" [[city :string "城市名称"]] {:tags [:weather :external-api :read-only]} (fetch-weather city))

;; 读 context(只读快照)+ 写状态(:writes 声明写意图) (deftool save-note "保存笔记" [[content :string "内容"]] {:context true} ;; ctx 是自动绑定的只读 context 变量 (let [user (context/get-var ctx :user-id)] {:result (str "已保存(" user ")") :writes {:notes content}})) ;; 槽 :notes 的 reducer 决定如何合并

;; 无参数函数 (deftool get-time "获取当前时间" [] (str (java.time.LocalDateTime/now)))

工具函数定义宏

deftool 宏同时生成:
1. 普通 Clojure 函数(可直接调用)
2. 完整的 tool schema 元数据(用于 LLM Function Calling)

参数声明使用向量格式: [名称 类型 描述]
支持可选参数: [名称 类型 描述 :default 默认值]
支持参数约束: [名称 类型 描述 :enum [..] :pattern ".." :minimum 1 :maximum 9
              :min-length n :max-length n :items {:type "string"} :min-items n :max-items n
              :properties {…} :required [..] :additional-properties false]
              (进 input_schema,模型看得到;调用前按 schema 完整校验,见 tool-args)
对象参数的属性表可写成 [[名称 schema] …] 键值对 vector——**保序**,字段多于 8 个时用这个写法
(map 字面量多于 8 键会被 Clojure reader 变成 hash map,键序不再跟源码文本一致)。
数组条目的对象(`:items {:type "object" :properties …}`)与更深的嵌套对象都吃同一套写法。

**schema 在命名空间加载期构造**(不是常量):AOT 编译会把常量 map 重建(>8 键即
PersistentHashMap),参数声明顺序会丢;加载期构造则 eval 与 AOT 完全一致。
支持敏感标记: {:sensitive true}
支持只读 context: {:context true}
支持串行标记: {:serial true}(副作用工具,批内并行时整批退化为按序执行)
支持直接返回: {:return-direct true}(结果即最终答案,不再回灌 LLM)

状态读写(Tool 阶段 MapReduce 契约,见 docs/agent-loop-concurrency-design.md §9):
- 读:{:context true} 工具收到只读的轮初 context 快照(ctx 变量自动绑定)
- 写:任意工具(无论是否声明 :context)返回 {:result r :writes {k v}}
  声明写意图;批次屏障处按 tool-call 原始序经槽级 reducer 折叠进 context。
  同批工具互相看不到对方的写(快照隔离);失败/超时/拒绝的调用 writes 不生效。

使用示例:

;; 基本用法
(deftool get-weather
  "获取指定城市的天气信息"
  [[city :string "城市名称"]]
  (fetch-weather city))

;; 带可选参数
(deftool calculate
  "执行数学计算"
  [[expression :string "数学表达式"]
   [precision :int "小数精度" :default 2]]
  (compute expression precision))

;; 标记敏感工具
(deftool delete-file
  "删除文件"
  [[path :string "文件路径"]]
  {:sensitive true}
  (io/delete-file path))

;; 带 tags 的工具
(deftool get-weather
  "获取天气"
  [[city :string "城市名称"]]
  {:tags [:weather :external-api :read-only]}
  (fetch-weather city))

;; 读 context(只读快照)+ 写状态(:writes 声明写意图)
(deftool save-note
  "保存笔记"
  [[content :string "内容"]]
  {:context true}
  ;; ctx 是自动绑定的只读 context 变量
  (let [user (context/get-var ctx :user-id)]
    {:result (str "已保存(" user ")")
     :writes {:notes content}}))   ;; 槽 :notes 的 reducer 决定如何合并

;; 无参数函数
(deftool get-time
  "获取当前时间"
  []
  (str (java.time.LocalDateTime/now)))
raw docstring

build-tool-schemaclj

(build-tool-schema fn-name description parsed-params)

构建 Anthropic 兼容的 tool schema

参数:

  • fn-name: 函数名(keyword)
  • description: 函数描述
  • params: 解析后的参数列表

返回: {:name "函数名" :description "描述" :input_schema {:type "object" :properties {...} :required [...]}}

构建 Anthropic 兼容的 tool schema

参数:
- fn-name:     函数名(keyword)
- description: 函数描述
- params:      解析后的参数列表

返回:
{:name         "函数名"
 :description  "描述"
 :input_schema {:type "object" :properties {...} :required [...]}}
sourceraw docstring

call-with-timeoutclj

(call-with-timeout timeout-ms f)

在虚拟线程上跑 f,最多等 timeout-ms 毫秒。

返回 [:ok v] | [:err throwable] | [:timeout]——不替调用方决定结果 形状,由调用点自行翻译。这是超时机制的唯一实现,消费者是 chat-client/invoke-tool(强制「工具声明 > 引擎缺省」的生效超时)。

**语义如实声明:超时 = 放弃等待,不是终止执行。**JVM 没有强杀原语 (Thread.stop 已移除),到点只能 interrupt:

  • Thread/sleep、阻塞 IO(socket read 等)会被真正打断——虚拟线程上 JDK 关闭 socket 并抛 SocketException: Closed by interrupt(JEP 353 起 java.net.Socket 基于 NIO;实测 JDK 25 确认)。故「工具卡在外部 API 上」 这个最常见的形态能干净取消,副作用不落地;
  • 但不检查中断标志的代码打不断(纯 CPU 循环为主,另有 native 调用、吞掉 InterruptedException 的代码)——它会继续跑完,其副作用可能在超时之后 才落地。这是残余风险,框架消不掉。

用虚拟线程而非 clojure.core/future(send-off 平台线程池)的两个理由: (1) 阻塞 IO 因此真的可取消(平台线程会无视 interrupt 把请求读完); (2) 被放弃的执行只占几 KB 栈,且不破坏 ToolCallingManager 各引擎的线程模型。

bound-fn* 包装:调用方的动态绑定对 f 照常可见(与 future 的传导语义一致)—— 见设计原则 §3「边界内一致」。

在**虚拟线程**上跑 f,最多等 timeout-ms 毫秒。

返回 `[:ok v]` | `[:err throwable]` | `[:timeout]`——**不**替调用方决定结果
形状,由调用点自行翻译。这是超时机制的**唯一实现**,消费者是
`chat-client/invoke-tool`(强制「工具声明 > 引擎缺省」的生效超时)。

**语义如实声明:超时 = 放弃等待,不是终止执行。**JVM 没有强杀原语
(`Thread.stop` 已移除),到点只能 interrupt:
- `Thread/sleep`、阻塞 IO(socket read 等)**会被真正打断**——虚拟线程上 JDK
  关闭 socket 并抛 `SocketException: Closed by interrupt`(JEP 353 起
  `java.net.Socket` 基于 NIO;实测 JDK 25 确认)。故「工具卡在外部 API 上」
  这个最常见的形态能干净取消,副作用不落地;
- 但**不检查中断标志的代码打不断**(纯 CPU 循环为主,另有 native 调用、吞掉
  `InterruptedException` 的代码)——它会继续跑完,其副作用可能在超时**之后**
  才落地。这是残余风险,框架消不掉。

用虚拟线程而非 `clojure.core/future`(send-off 平台线程池)的两个理由:
(1) 阻塞 IO 因此真的可取消(平台线程会无视 interrupt 把请求读完);
(2) 被放弃的执行只占几 KB 栈,且不破坏 ToolCallingManager 各引擎的线程模型。

`bound-fn*` 包装:调用方的动态绑定对 f 照常可见(与 `future` 的传导语义一致)——
见设计原则 §3「边界内一致」。
sourceraw docstring

check-timeout!clj

(check-timeout! label t)

校验 timeout 值,非法则抛 ex-info。label 是出错时点名的对象(如 "工具 :foo")。

DRY:此前 4 处(chat_client.clj validate-tool-timeouts! var/inline + build-chat-client manager + react.clj check-timeout-opt!)各自手抄 :timeout 必须为正整数毫秒 消息串——消息串被两个模块的测试用正则钉住,改一处漏一处即出问题。

校验 timeout 值,非法则抛 ex-info。label 是出错时点名的对象(如 "工具 :foo")。

**DRY**:此前 4 处(chat_client.clj validate-tool-timeouts! var/inline + build-chat-client
manager + react.clj check-timeout-opt!)各自手抄 `:timeout 必须为正整数毫秒`
消息串——消息串被两个模块的测试用正则钉住,改一处漏一处即出问题。
sourceraw docstring

context-tool?clj

(context-tool? v)

检查 tool function 是否需要(只读)context

参数:

  • v: var 引用

返回: boolean

检查 tool function 是否需要(只读)context

参数:
- v: var 引用

返回: boolean
sourceraw docstring

deftoolcljmacro

(deftool name description params & body)
(deftool name description params opts-map & body)

定义工具函数

同时生成带 tool schema 元数据的 Clojure 函数。

用法: (deftool 函数名 "描述" [[参数1 :类型 "描述"] [参数2 :类型 "描述" :default 默认值]] 函数体...)

(deftool 函数名 "描述" [[参数1 :类型 "描述"]] {:sensitive true} ; 选项 map(可选) 函数体...)

(deftool 函数名 "描述" [[参数1 :类型 "描述"]] {:context true} ; 需要只读 context ;; ctx 变量自动可用(只读快照);写状态用 :writes {:result "..." :writes {:key val}})

生成的 var 元数据:

  • :tool/schema Anthropic 格式 tool schema(加载期构造,eval / AOT 一致)
  • :tool/params 参数定义列表
  • :tool/sensitive 是否为敏感工具
  • :tool/serial 是否串行工具(批内并行时整批退化)
  • :tool/timeout 超时毫秒(可选)。经 chat-client 调用时开箱即生效(缺省不超时; 优先级:本声明 > 引擎 {:timeout ms})。语义为「放弃等待」而非 「终止执行」——超时后工具可能仍在后台跑完并产生外部副作用 (见 call-with-timeout 文档)
  • :tool/category 工具分类
  • :tool/context 是否需要(只读)context
  • :tool/tags 标签集合(用于过滤)
  • :tool/function 标记为 tool function
定义工具函数

同时生成带 tool schema 元数据的 Clojure 函数。

用法:
(deftool 函数名
  "描述"
  [[参数1 :类型 "描述"] [参数2 :类型 "描述" :default 默认值]]
  函数体...)

(deftool 函数名
  "描述"
  [[参数1 :类型 "描述"]]
  {:sensitive true}      ; 选项 map(可选)
  函数体...)

(deftool 函数名
  "描述"
  [[参数1 :类型 "描述"]]
  {:context true}        ; 需要只读 context
  ;; ctx 变量自动可用(只读快照);写状态用 :writes
  {:result "..." :writes {:key val}})

生成的 var 元数据:
- :tool/schema    Anthropic 格式 tool schema(**加载期**构造,eval / AOT 一致)
- :tool/params    参数定义列表
- :tool/sensitive 是否为敏感工具
- :tool/serial    是否串行工具(批内并行时整批退化)
- :tool/timeout   超时毫秒(可选)。经 chat-client 调用时**开箱即生效**(缺省不超时;
                  优先级:本声明 > 引擎 {:timeout ms})。语义为「放弃等待」而非
                  「终止执行」——超时后工具可能仍在后台跑完并产生外部副作用
                  (见 call-with-timeout 文档)
- :tool/category  工具分类
- :tool/context   是否需要(只读)context
- :tool/tags      标签集合(用于过滤)
- :tool/function  标记为 tool function
sourceraw docstring

get-schemaclj

(get-schema v)

获取 tool function 的 tool schema

参数:

  • v: var 引用

返回: tool schema map 或 nil

获取 tool function 的 tool schema

参数:
- v: var 引用

返回: tool schema map 或 nil
sourceraw docstring

get-tagsclj

(get-tags v)

获取 tool function 的标签集合

参数:

  • v: var 引用

返回: 标签集合(set)或 nil

获取 tool function 的标签集合

参数:
- v: var 引用

返回: 标签集合(set)或 nil
sourceraw docstring

has-any-tag?clj

(has-any-tag? v tags)

检查 tool function 是否包含任意一个指定标签

参数:

  • v: var 引用
  • tags: 标签集合

返回: boolean

检查 tool function 是否包含任意一个指定标签

参数:
- v:    var 引用
- tags: 标签集合

返回: boolean
sourceraw docstring

invokeclj

(invoke v args)
(invoke v args context)

执行 tool function 并返回标准结果

参数:

  • v: tool function 的 var 引用
  • args: 参数 map
  • context: (可选) Context 对象({:context true} 工具的只读快照)

返回: {:success bool :result any :error string (:writes {k v})} 工具返回含 :writes 的 map({:result r :writes {k v}})时拆出写意图, 由批次屏障处的 reducer 折叠进共享 context(工具本身不改 context)。

执行 tool function 并返回标准结果

参数:
- v:       tool function 的 var 引用
- args:    参数 map
- context: (可选) Context 对象({:context true} 工具的只读快照)

返回:
{:success bool :result any :error string (:writes {k v})}
工具返回含 :writes 的 map({:result r :writes {k v}})时拆出写意图,
由批次屏障处的 reducer 折叠进共享 context(工具本身不改 context)。
sourceraw docstring

params->json-schemaclj

(params->json-schema parsed-params)

将解析后的参数列表转换为 JSON Schema

properties 按参数声明顺序写出(>8 个参数也保序,进 prompt cache 的稳定前缀)。 :properties / :items 里的嵌套 map 原样透传;嵌套属性表(含 :items 里数组条目的对象、 更深的嵌套对象)也可写成键值对 vector([[:字段 {…}] …])以保序——map 字面量 >8 键受 Clojure reader 限制,键序不是源码文本序。

返回: {:type "object" :properties {:param1 {:type "string" :description "..."} ...} :required ["param1" ...]}

将解析后的参数列表转换为 JSON Schema

properties 按参数声明顺序写出(>8 个参数也保序,进 prompt cache 的稳定前缀)。
`:properties` / `:items` 里的嵌套 map 原样透传;嵌套属性表(含 `:items` 里数组条目的对象、
更深的嵌套对象)也可写成键值对 vector(`[[:字段 {…}] …]`)以保序——map 字面量 >8 键受
Clojure reader 限制,键序不是源码文本序。

返回:
{:type       "object"
 :properties {:param1 {:type "string" :description "..."} ...}
 :required   ["param1" ...]}
sourceraw docstring

retry-specclj

(retry-spec v)

读取 tool function 的 :retry 声明(deftool 选项,幂等工具 opt-in)。

返回: nil(未声明)| true | {:max-retries n :initial-delay-ms ms}

读取 tool function 的 :retry 声明(deftool 选项,幂等工具 opt-in)。

返回: nil(未声明)| true | {:max-retries n :initial-delay-ms ms}
sourceraw docstring

return-direct-tool?clj

(return-direct-tool? v)

检查 tool function 是否声明 :return-direct(结果即最终答案,不再回灌 LLM)

参数:

  • v: var 引用

返回: boolean

检查 tool function 是否声明 :return-direct(结果即最终答案,不再回灌 LLM)

参数:
- v: var 引用

返回: boolean
sourceraw docstring

serial-tool?clj

(serial-tool? v)

检查 tool function 是否声明 :serial(副作用工具; 批内并行时含 serial 工具的整批退化为按序执行)

参数:

  • v: var 引用

返回: boolean

检查 tool function 是否声明 :serial(副作用工具;
批内并行时含 serial 工具的整批退化为按序执行)

参数:
- v: var 引用

返回: boolean
sourceraw docstring

timeout-specclj

(timeout-spec v)

读取 tool function 的 :timeout 声明(deftool 选项,毫秒)。

与 :serial / :retry / :return-direct 同款——经 chat-client 调用时开箱即生效, 由 chat-client/invoke-tool 强制。缺省不超时;引擎可给整体缺省 (…-tool-calling-manager {:timeout ms}),本声明恒优先。

内联工具的同名声明经 chat-client/tool-timeout 读取(本函数只管 var)。 语义注意:JVM 上超时 = 放弃等待 ≠ 终止执行,详见 call-with-timeout。

返回: nil(未声明)| 正整数毫秒

读取 tool function 的 :timeout 声明(deftool 选项,毫秒)。

与 `:serial` / `:retry` / `:return-direct` 同款——经 chat-client 调用时**开箱即生效**,
由 `chat-client/invoke-tool` 强制。缺省不超时;引擎可给整体缺省
`(…-tool-calling-manager {:timeout ms})`,本声明恒优先。

内联工具的同名声明经 `chat-client/tool-timeout` 读取(本函数只管 var)。
语义注意:JVM 上超时 = 放弃等待 ≠ 终止执行,详见 `call-with-timeout`。

返回: nil(未声明)| 正整数毫秒
sourceraw docstring

tool-function?clj

(tool-function? v)

检查 var 是否为 tool function

参数:

  • v: var 引用(如 #'get-weather)

返回: boolean

检查 var 是否为 tool function

参数:
- v: var 引用(如 #'get-weather)

返回: boolean
sourceraw docstring

tool-metaclj

(tool-meta fn-name description params)

运行时构建 deftool 的元数据片段:{:tool/schema … :tool/params […]}

为什么不能把 schema 当常量:deftool 过去把 schema 作为常量塞进 var 元数据。 AOT 编译时 Clojure 会把常量 map 重建(>8 键即 PersistentHashMap),参数声明顺序 全丢——单测在 eval 下是绿的,发出去的包里模型看到的顺序却是乱的(见 feedbacks/2026-11-11-deftool-order-lost-under-aot.md)。

改为命名空间加载期构造:eval 与 AOT 走同一条路径,顺序由 ordered-map 钉住。

参数:

  • fn-name: 工具名(keyword)
  • description: 工具描述
  • params: 参数声明向量(用户写法;参数名是符号)

返回: {:tool/schema … :tool/params […]}

运行时构建 deftool 的元数据片段:{:tool/schema … :tool/params […]}

**为什么不能把 schema 当常量**:deftool 过去把 schema 作为常量塞进 var 元数据。
AOT 编译时 Clojure 会把常量 map 重建(>8 键即 PersistentHashMap),参数声明顺序
全丢——单测在 eval 下是绿的,发出去的包里模型看到的顺序却是乱的(见
feedbacks/2026-11-11-deftool-order-lost-under-aot.md)。

改为**命名空间加载期**构造:eval 与 AOT 走同一条路径,顺序由 `ordered-map` 钉住。

参数:
- fn-name:     工具名(keyword)
- description: 工具描述
- params:      参数声明向量(用户写法;参数名是符号)

返回: {:tool/schema … :tool/params […]}
sourceraw docstring

valid-retry?clj

(valid-retry? r)

:retry 声明是否合法:nil(未声明)| true(opt-in 用默认)| 正整数 map。 与 valid-timeout? 对称——:retry 此前零校验,{:max-retries "3"} 在 运行期 (long ...) 抛 CCE 且被收敛成指向工具的 :semantic 错误——症状偏离病因。

`:retry` 声明是否合法:nil(未声明)| true(opt-in 用默认)| 正整数 map。
与 `valid-timeout?` 对称——`:retry` 此前零校验,`{:max-retries "3"}` 在
运行期 `(long ...)` 抛 CCE 且被收敛成指向工具的 :semantic 错误——症状偏离病因。
sourceraw docstring

valid-timeout?clj

(valid-timeout? t)

:timeout 声明是否合法:nil(未声明)或正整数毫秒。

非正整数一律拒绝——"5s" 会在 deref 处抛 ClassCastException,-1 会让该 工具每次调用都立刻超时(静默、无从排查),2.7 会被静默截断。由 chat-client/build-chat-client 在装配期校验(var 与内联工具汇合、尚未开始执行的最早时点)。

`:timeout` 声明是否合法:nil(未声明)或正整数毫秒。

非正整数一律拒绝——`"5s"` 会在 deref 处抛 ClassCastException,`-1` 会让该
工具每次调用都立刻超时(静默、无从排查),`2.7` 会被静默截断。由
`chat-client/build-chat-client` 在装配期校验(var 与内联工具汇合、尚未开始执行的最早时点)。
sourceraw docstring

validate-argsclj

(validate-args v args)

按 var 元数据中的 input_schema 完整校验工具参数(type / enum / pattern / min·max / minLength / items / required …,一次报全,见 tool-args)。

参数:

  • v: tool function 的 var 引用
  • args: 参数 map

返回: {:valid bool :errors [string]}——每处错误一行(带路径与期望)

按 var 元数据中的 input_schema **完整**校验工具参数(type / enum / pattern /
min·max / minLength / items / required …,一次报全,见 `tool-args`)。

参数:
- v:    tool function 的 var 引用
- args: 参数 map

返回:
{:valid bool :errors [string]}——每处错误一行(带路径与期望)
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close