Liking cljdoc? Tell your friends :D

im.ttalk.agent.agui.a2ui

A2UI —— 声明式生成式 UI(可选插件)。

移植自 @ag-ui/a2ui-middleware(CopilotKit 在 agent-utils.ts 里与 Open Generative UI 挂在同一个位置)。两者的分工值得先说清楚:

copilotkit.genui(Open Generative UI,在 examples 里)本 ns(A2UI)
模型生成什么一整块 HTML/CSS/JS组件树(只能用 catalog 里有的组件)
谁来渲染沙箱 iframe 执行模型写的代码前端按自己的组件库渲染
可控性任意 JS(所以要沙箱)白名单:模型编不出 catalog 之外的组件
交互回流Websandbox.connection.remote.*forwardedProps.a2uiAction(见 input-transform)

要「像素级自由」用 genui,要「设计系统一致 + 不执行任意代码」用 A2UI。

装

(rt/runtime {:agent-fn (tools/agent-fn (a2ui/with-tool spec))
             :event-transform (a2ui/event-transform)})

后台干什么

  1. 注入工具与用法(with-tool):render_a2ui 工具 + 一段用法提示词 + catalog(有哪些组件、各自什么属性)。catalog 是这条路的关键——它既是 给模型的词汇表,也是「模型编不出别的组件」的保证;

  2. 翻译事件(event-transform):模型调 render_a2ui,参数 {surfaceId, components, data} 翻成一条 ACTIVITY_SNAPSHOT:

    {:activityType "a2ui-surface"
     :messageId    "a2ui-surface-<toolCallId>"
     :replace      true
     :content {"a2ui_operations"
               [{:version "v0.9" :createSurface    {…}}
                {:version "v0.9" :updateComponents {…}}
                {:version "v0.9" :updateDataModel  {…}}]}}
    

    三条 op 的顺序是协议要求的(先建面,再给组件,最后灌数据);replace true 表示「整块换掉」——所以不需要 delta,每次都是完整的一份;

  3. 回流用户动作(input-transform):用户点了生成出来的按钮,前端把 forwardedProps.a2uiAction 发上来,翻成一句话喂回模型。

与上游的两处差异(都记在设计文档 §9.14)

  1. 上游从流式 tool-call 参数里增量抠出「已经完整的那几个组件」 (extractCompleteItems),一边生成一边往面上贴。我们的运行时不增量流式 工具参数(docs/agent-runtime-design.md §10 第 4 条),参数一次到齐,所以 一条快照发完——形状与顺序一致,少的是中间态;
  2. 上游把用户动作合成成 log_a2ui_event 的一次工具调用 + 工具结果塞进 历史。我们的历史是服务端权威(§7.3),客户端塞不进消息,所以翻成一句 用户消息(措辞与上游的 formatUserActionResult 一致)。
A2UI —— **声明式**生成式 UI(可选插件)。

移植自 `@ag-ui/a2ui-middleware`(CopilotKit 在 `agent-utils.ts` 里与
Open Generative UI 挂在同一个位置)。两者的分工值得先说清楚:

| | `copilotkit.genui`(Open Generative UI,在 examples 里) | 本 ns(A2UI) |
|---|---|---|
| 模型生成什么 | 一整块 HTML/CSS/JS | **组件树**(只能用 catalog 里有的组件) |
| 谁来渲染 | 沙箱 iframe 执行模型写的代码 | 前端按自己的组件库渲染 |
| 可控性 | 任意 JS(所以要沙箱) | **白名单**:模型编不出 catalog 之外的组件 |
| 交互回流 | `Websandbox.connection.remote.*` | `forwardedProps.a2uiAction`(见 `input-transform`) |

要「像素级自由」用 genui,要「设计系统一致 + 不执行任意代码」用 A2UI。

## 装

```clojure
(rt/runtime {:agent-fn (tools/agent-fn (a2ui/with-tool spec))
             :event-transform (a2ui/event-transform)})
```

## 后台干什么

1. **注入工具与用法**(`with-tool`):`render_a2ui` 工具 + 一段用法提示词 +
   **catalog**(有哪些组件、各自什么属性)。catalog 是这条路的关键——它既是
   给模型的词汇表,也是「模型编不出别的组件」的保证;
2. **翻译事件**(`event-transform`):模型调 `render_a2ui`,参数
   `{surfaceId, components, data}` 翻成一条 `ACTIVITY_SNAPSHOT`:

       {:activityType "a2ui-surface"
        :messageId    "a2ui-surface-<toolCallId>"
        :replace      true
        :content {"a2ui_operations"
                  [{:version "v0.9" :createSurface    {…}}
                   {:version "v0.9" :updateComponents {…}}
                   {:version "v0.9" :updateDataModel  {…}}]}}

   三条 op 的顺序是协议要求的(先建面,再给组件,最后灌数据);`replace true`
   表示「整块换掉」——所以不需要 delta,每次都是完整的一份;
3. **回流用户动作**(`input-transform`):用户点了生成出来的按钮,前端把
   `forwardedProps.a2uiAction` 发上来,翻成一句话喂回模型。

## 与上游的两处差异(都记在设计文档 §9.14)

1. 上游从**流式** tool-call 参数里增量抠出「已经完整的那几个组件」
   (`extractCompleteItems`),一边生成一边往面上贴。我们的运行时不增量流式
   工具参数(docs/agent-runtime-design.md §10 第 4 条),参数一次到齐,所以
   **一条快照发完**——形状与顺序一致,少的是中间态;
2. 上游把用户动作合成成 `log_a2ui_event` 的一次**工具调用 + 工具结果**塞进
   历史。我们的历史是服务端权威(§7.3),客户端塞不进消息,所以翻成一句
   **用户消息**(措辞与上游的 `formatUserActionResult` 一致)。
raw docstring

activity-typeclj

activity 消息的类型标签。前端按它挑 renderer。

activity 消息的类型标签。前端按它挑 renderer。
sourceraw docstring

basic-catalogclj

(basic-catalog)

A2UI v0.9 基础 catalog(18 个组件)。见文件末尾 basic-components。

A2UI v0.9 基础 catalog(18 个组件)。见文件末尾 `basic-components`。
sourceraw docstring

basic-catalog-idclj

A2UI v0.9 基础 catalog 的 id。CopilotKit 的 a2ui-renderer 默认实现的就是它。

A2UI v0.9 基础 catalog 的 id。CopilotKit 的 `a2ui-renderer` 默认实现的就是它。
sourceraw docstring

basic-componentsclj

生成的,别手改:由 @a2ui/web_core 的 src/v0_9/schemas/catalogs/basic/catalog.json 摘出来(组件名 + 属性 + 属性描述 + 枚举 + 必填),去掉了 $ref 与公共部分——那些是给校验器看的, 模型只需要词汇表。要换成你自己的组件库,给 with-tool 传 :catalog。

**生成的**,别手改:由 `@a2ui/web_core` 的
`src/v0_9/schemas/catalogs/basic/catalog.json` 摘出来(组件名 + 属性 +
属性描述 + 枚举 + 必填),去掉了 `$ref` 与公共部分——那些是给校验器看的,
模型只需要词汇表。要换成你自己的组件库,给 `with-tool` 传 `:catalog`。
sourceraw docstring

descriptionclj

工具描述——逐句移植自上游的 RENDER_A2UI_TOOL。

工具描述——逐句移植自上游的 `RENDER_A2UI_TOOL`。
sourceraw docstring

event-transformclj

(event-transform)
(event-transform {:keys [tool-name catalog-id]
                  :or {tool-name tool-name catalog-id basic-catalog-id}})

runtime 的 :event-transform:render_a2ui 的调用 → surface 快照。

与 genui 的一处不同:不扣住 :tool/started。上游 A2UI 也不扣—— 工具卡片先出、面随后贴上,是它接受的顺序(genui 那边扣住是因为沙箱 UI 一闪的观感问题,两边取舍不同,照各自的来)。

`runtime` 的 `:event-transform`:`render_a2ui` 的调用 → surface 快照。

与 `genui` 的一处不同:**不扣住 `:tool/started`**。上游 A2UI 也不扣——
工具卡片先出、面随后贴上,是它接受的顺序(genui 那边扣住是因为沙箱 UI
一闪的观感问题,两边取舍不同,照各自的来)。
sourceraw docstring

guidelinesclj

(guidelines tool)

用法提示词——移植自上游的 RENDER_A2UI_TOOL_GUIDELINES。

这是提示词不是文档:flat 组件数组、root 必须是布局组件、数据绑定语法、 重复渲染语法、按钮 action 的形状,模型全靠这段学会。

用法提示词——移植自上游的 `RENDER_A2UI_TOOL_GUIDELINES`。

**这是提示词不是文档**:flat 组件数组、root 必须是布局组件、数据绑定语法、
重复渲染语法、按钮 action 的形状,模型全靠这段学会。
sourceraw docstring

input-transformclj

(input-transform)

routes/start! 的 :input-transform:把 A2UI 的用户动作接进本轮输入。

前端点按钮时发的那一轮可能没有用户消息(就是个动作),所以只在 :message 空的时候顶上;两者都有就把动作附在后面(用户既打了字又点了按钮)。

`routes/start!` 的 `:input-transform`:把 A2UI 的用户动作接进本轮输入。

前端点按钮时发的那一轮**可能没有用户消息**(就是个动作),所以只在
`:message` 空的时候顶上;两者都有就把动作附在后面(用户既打了字又点了按钮)。
sourceraw docstring

operations-keyclj

content 里装 op 数组的键。字符串键——它要原样出现在 JSON 里。

`content` 里装 op 数组的键。**字符串键**——它要原样出现在 JSON 里。
sourceraw docstring

parametersclj

JSON Schema,对齐上游 RENDER_A2UI_TOOL.parameters。

没有 catalogId 参数:catalog 由宿主定,不能让模型自己编一个前端没注册的。

JSON Schema,对齐上游 `RENDER_A2UI_TOOL.parameters`。

**没有 catalogId 参数**:catalog 由宿主定,不能让模型自己编一个前端没注册的。
sourceraw docstring

render-toolclj

(render-tool)
(render-tool name)

内联工具 map。

handler 不画任何东西——UI 就是参数本身,后台只负责翻译成 activity 事件; handler 只给模型一个「贴上去了」的回执好让它继续说话(与 genui 同款取舍)。

内联工具 map。

**handler 不画任何东西**——UI 就是参数本身,后台只负责翻译成 activity 事件;
handler 只给模型一个「贴上去了」的回执好让它继续说话(与 `genui` 同款取舍)。
sourceraw docstring

schema-context-descriptionclj

catalog 注入模型上下文时的抬头(上游的 A2UI_SCHEMA_CONTEXT_DESCRIPTION 同位)。

catalog 注入模型上下文时的抬头(上游的 `A2UI_SCHEMA_CONTEXT_DESCRIPTION` 同位)。
sourceraw docstring

surface-eventclj

(surface-event tool-call-id args catalog-id)

一条 :activity/snapshot。:replace true = 整块换掉,所以不需要 delta。

一条 `:activity/snapshot`。`:replace true` = 整块换掉,所以不需要 delta。
sourceraw docstring

surface-opsclj

(surface-ops {:keys [surfaceId components data]} catalog-id)

render_a2ui 的参数 → A2UI v0.9 op 数组。

顺序是协议要求的:先 createSurface 建面,再 updateComponents 给组件, 最后(有 data 才发)updateDataModel 灌数据。少一条或反了,前端要么空白要么 报「surface 不存在」。

`render_a2ui` 的参数 → A2UI v0.9 op 数组。

**顺序是协议要求的**:先 `createSurface` 建面,再 `updateComponents` 给组件,
最后(有 data 才发)`updateDataModel` 灌数据。少一条或反了,前端要么空白要么
报「surface 不存在」。
sourceraw docstring

tool-nameclj

source

user-action-messageclj

(user-action-message {:keys [name surfaceId sourceComponentId context]})

forwardedProps.a2uiAction.userAction → 一句喂回模型的话。

措辞与上游 formatUserActionResult 一致(它把这句话作为 log_a2ui_event 的工具结果塞进历史;我们的历史是服务端权威,客户端塞不进消息,所以作为 用户消息进来——语义相同,路径不同)。

`forwardedProps.a2uiAction.userAction` → 一句喂回模型的话。

措辞与上游 `formatUserActionResult` 一致(它把这句话作为 `log_a2ui_event`
的工具结果塞进历史;我们的历史是服务端权威,客户端塞不进消息,所以作为
**用户消息**进来——语义相同,路径不同)。
sourceraw docstring

with-toolclj

(with-tool spec)
(with-tool spec
           {:keys [catalog tool-name]
            :or {catalog (basic-catalog) tool-name tool-name}})

把 render_a2ui 工具、用法提示词、catalog 挂进一份 create-agent 配置。

  • :catalog {:catalogId … :components {名字 → JSON Schema}},缺省用 basic-catalog。传你前端真正注册的那份——catalog 是模型的 词汇表,报了前端没有的组件,渲染就是空白;
  • :tool-name 自定义工具名(上游允许,默认 render_a2ui)。
把 `render_a2ui` 工具、用法提示词、catalog 挂进一份 `create-agent` 配置。

- `:catalog`   `{:catalogId … :components {名字 → JSON Schema}}`,缺省用
               `basic-catalog`。**传你前端真正注册的那份**——catalog 是模型的
               词汇表,报了前端没有的组件,渲染就是空白;
- `:tool-name` 自定义工具名(上游允许,默认 `render_a2ui`)。
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