ChatClient 核心 - 中央编排器
ChatClient 构建使用声明式 map:
(build-chat-client {:chat-model my-chat-model :tools [#'get-weather #'get-time] :filters [memory-filter logging-filter] :settings {:max-tool-iterations 10}})
Tool 定义(支持 tags): (deftool get-weather "获取天气" [[city :string "城市"]] {:tags [:weather :read-only]} (str city ": 25°C"))
Invoke API - 调用函数/LLM: (invoke-tool chat-client :get-weather {:city "北京"} context) (invoke-chat chat-client messages opts) (invoke chat-client messages opts) (invoke chat-client messages {:tags [:weather]}) ;; 只用带 :weather tag 的工具 (invoke chat-client messages {:exclude-tags [:dangerous]}) ;; 排除危险工具
Query API - 查询 ChatClient 状态: (tool-registry/tool-schemas chat-client) ;; 所有 tool schemas (list-functions chat-client) ;; 所有函数名 (find-function chat-client :get-weather)
ChatModel 格式: ChatModel 是一个 map,定义 LLM 调用接口: {:chat-fn (fn [messages opts] -> {:text "..." :tool-calls [...]})}
返回值格式: invoke-tool: {:value v (:writes {k v})} ;; context 只读,写意图经 :writes invoke-chat: {:response r :context ctx} invoke: {:response r :context ctx :tool-calls-made [...]}
ChatClient 核心 - 中央编排器
ChatClient 构建使用声明式 map:
(build-chat-client {:chat-model my-chat-model
:tools [#'get-weather #'get-time]
:filters [memory-filter logging-filter]
:settings {:max-tool-iterations 10}})
Tool 定义(支持 tags):
(deftool get-weather
"获取天气"
[[city :string "城市"]]
{:tags [:weather :read-only]}
(str city ": 25°C"))
Invoke API - 调用函数/LLM:
(invoke-tool chat-client :get-weather {:city "北京"} context)
(invoke-chat chat-client messages opts)
(invoke chat-client messages opts)
(invoke chat-client messages {:tags [:weather]}) ;; 只用带 :weather tag 的工具
(invoke chat-client messages {:exclude-tags [:dangerous]}) ;; 排除危险工具
Query API - 查询 ChatClient 状态:
(tool-registry/tool-schemas chat-client) ;; 所有 tool schemas
(list-functions chat-client) ;; 所有函数名
(find-function chat-client :get-weather)
ChatModel 格式:
ChatModel 是一个 map,定义 LLM 调用接口:
{:chat-fn (fn [messages opts] -> {:text "..." :tool-calls [...]})}
返回值格式:
invoke-tool: {:value v (:writes {k v})} ;; context 只读,写意图经 :writes
invoke-chat: {:response r :context ctx}
invoke: {:response r :context ctx :tool-calls-made [...]}(build-chat-client {:keys [chat-model tools tool-vars filters settings
state-slots eligibility-fn tool-manager]
:or {tools [] filters [] settings {}}})构建 ChatClient 实例
参数 opts map:
返回: ChatClient record
示例: (build-chat-client {:chat-model cm :tools [#'get-weather #'get-time] :filters [memory-filter retry-filter] :settings {:max-tool-iterations 10}})
构建 ChatClient 实例
参数 opts map:
- :chat-model ChatModel map(必需)
- :tools tool var 向量(如 [#'get-weather]),也可混入内联工具 map
(含 :handler,由 delegate-tool 等动态构建)。**工具名必须
唯一**——var 与内联共用一个命名空间,重名装配期即抛
- :filters filter 向量(如 [memory-filter logging-filter]),注册顺序即执行顺序
- :settings 额外设置(可选),如 {:max-tool-iterations 10}
- :state-slots 状态槽声明(可选){k {:init v0 :reduce (fn [old new] merged)}}——
工具批次 :writes 的合并语义(未声明的槽默认 last-writer)
- :eligibility-fn 循环续跑判据(可选)(fn [response context] -> boolean)——
响应带 tool-call 时是否**真的**执行并续跑;返回 false 则
该响应按最终答案收尾(工具不执行)。缺省恒真,即「有
tool-call 就跑」。对标 Spring AI ToolExecutionEligibilityChecker
返回:
ChatClient record
示例:
(build-chat-client {:chat-model cm
:tools [#'get-weather #'get-time]
:filters [memory-filter retry-filter]
:settings {:max-tool-iterations 10}})(invoke-chat chat-client messages opts)发送 Chat Completion 请求(经 chat filter 洋葱链,不含工具调用循环)
组装 ChatRequest → 预编译的 :chat 链包裹 → terminal 调 LLM。 memory 等「读历史 / 改写请求 / 加工响应」能力以 chat filter 形态注入; filter 可 around(短路 / 重试 / 计时),详见 chat-client.filter。
参数:
返回:
ChatClientResponse{:response ChatResponse :context updated-ctx}
发送 Chat Completion 请求(经 chat filter 洋葱链,不含工具调用循环)
组装 ChatRequest → 预编译的 :chat 链包裹 → terminal 调 LLM。
memory 等「读历史 / 改写请求 / 加工响应」能力以 chat filter 形态注入;
filter 可 around(短路 / 重试 / 计时),详见 chat-client.filter。
参数:
- chat-client: ChatClient 实例(需已配置 chat-model)
- messages: 消息列表(本轮 delta)
- opts: {:tools 工具 schema 列表 / :tool-choice / :system-prompt / :context}
除 :context 外的键全部进 ChatRequest 的 :options(provider 私有
参数照样穿得过去——不筛白名单)
返回:
`ChatClientResponse`{:response ChatResponse :context updated-ctx}(invoke-chat-async chat-client messages opts)invoke-chat 的异步版:返回 deferred<ChatClientResponse>,调用线程不阻塞。
ChatModel 实现 IAsyncChatModel 时走原生异步;否则虚拟线程兜底
(cm/call-async*)——任何 provider 都可用。
⚠️ 走这条路时 :chat filter 的 (chain req) 真的返回 deferred,响应侧
必须用 flt/fmap / flt/fbind((let [r (chain req)] …) 不报错,但拿到的是
future——静默错)。
`invoke-chat` 的异步版:返回 **deferred<`ChatClientResponse`>**,调用线程不阻塞。 ChatModel 实现 `IAsyncChatModel` 时走原生异步;否则虚拟线程兜底 (`cm/call-async*`)——任何 provider 都可用。 ⚠️ 走这条路时 `:chat` filter 的 `(chain req)` **真的返回 deferred**,响应侧 必须用 `flt/fmap` / `flt/fbind`(`(let [r (chain req)] …)` 不报错,但拿到的是 future——静默错)。
(invoke-chat-stream chat-client messages opts)invoke-chat 的流式版本:terminal 调 chat-model 的 :stream-fn,token 经 on-token 实时回调。
与 invoke-chat 共用同一条 chat filter 洋葱链(memory 等照常生效);on-token 经 request 透传到 terminal。:stream-fn 在流结束时返回最终归一化响应,故 memory-filter 落库的是 完整 assistant 消息——与同步路径历史不分叉。
filter 的 :token-xform(transducer)在 terminal 内组合成出站 token 变换链: provider 原始 token → xform 链(注册顺序,靠前者先见原始 token)→ on-token。 正常完流调 completion arity(缓冲 flush),stream-fn 抛异常则不 flush; 下游 reduced(如 take)后不再喂 token,completion 照常。只变换交付流, 不改最终 :response。设计见 docs/token-stream-filter-design.md。
参数:
返回: ChatClientResponse{:response ChatResponse :context ctx}
invoke-chat 的流式版本:terminal 调 chat-model 的 :stream-fn,token 经 on-token 实时回调。
与 invoke-chat 共用同一条 chat filter 洋葱链(memory 等照常生效);on-token 经 request
透传到 terminal。:stream-fn 在流结束时返回最终归一化响应,故 memory-filter 落库的是
**完整** assistant 消息——与同步路径历史不分叉。
filter 的 :token-xform(transducer)在 terminal 内组合成出站 token 变换链:
provider 原始 token → xform 链(注册顺序,靠前者先见原始 token)→ on-token。
正常完流调 completion arity(缓冲 flush),stream-fn 抛异常则不 flush;
下游 reduced(如 take)后不再喂 token,completion 照常。**只变换交付流,
不改最终 :response**。设计见 docs/token-stream-filter-design.md。
参数:
- opts: 同 invoke-chat,外加 :on-token (fn [token-data] ...)
返回: `ChatClientResponse`{:response ChatResponse :context ctx}(invoke-chat-stream-async chat-client messages opts)invoke-chat-stream 的异步版:返回 deferred<ChatClientResponse>。
:token-xform 的组装位置与同步版完全一致(chat 链之后、terminal 之内),
flush 挪进 fmap 的续延——正常完流才 flush,异常(error channel)不 flush,
与同步版的 try 语义逐字相同。
⚠️ on-token 的调用线程不保证(见 cm/stream-call-async*)。
`invoke-chat-stream` 的异步版:返回 deferred<`ChatClientResponse`>。 `:token-xform` 的组装位置与同步版完全一致(chat 链之后、terminal 之内), flush 挪进 fmap 的续延——**正常完流才 flush,异常(error channel)不 flush**, 与同步版的 try 语义逐字相同。 ⚠️ on-token 的调用线程不保证(见 `cm/stream-call-async*`)。
(invoke-tool chat-client fn-name args context)调用 ChatClient 中注册的函数(经 tool filter 洋葱链)
组装 ToolRequest {:function :args :context} → 预编译的 :tool 链包裹 → terminal 执行函数。filter 可改写 args、短路(不调 chain,如审批拒绝/熔断/ 限流/安全策略)、around(超时计时)。:context 是请求侧只读字段:filter 与 工具都不改写它;工具的写意图经返回值 :writes 声明,由批次屏障处的 reducer 折叠(见 context/apply-writes)。
参数:
返回: {:value result (:writes {k v}) (:error {:class :message})}
错误: 抛 ex-info(仅函数未找到;执行异常被 terminal 捕获为错误结果字符串 + :error 分类信息 {:class :semantic|:transient|:environment},供屏障路由; 失败调用无 :writes——写意图不生效)
调用 ChatClient 中注册的函数(经 tool filter 洋葱链)
组装 ToolRequest {:function :args :context} → 预编译的 :tool 链包裹
→ terminal 执行函数。filter 可改写 args、短路(不调 chain,如审批拒绝/熔断/
限流/安全策略)、around(超时计时)。**:context 是请求侧只读字段**:filter 与
工具都不改写它;工具的写意图经返回值 :writes 声明,由批次屏障处的
reducer 折叠(见 context/apply-writes)。
参数:
- chat-client: ChatClient 实例
- fn-name: 函数名(关键字或字符串)
- args: 参数 map
- context: Context 对象(只读快照)
返回:
{:value result (:writes {k v}) (:error {:class :message})}
错误:
抛 ex-info(仅函数未找到;执行异常被 terminal 捕获为错误结果字符串 +
:error 分类信息 {:class :semantic|:transient|:environment},供屏障路由;
失败调用无 :writes——写意图不生效)cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |