Catalogs、内置组件与自定义组件
catalog 是你的 Agent 和 client 之间的 contract:它列出 renderer 被允许 实例化的 component、可以执行的 function,并携带 Agent 在 handshake 时读取的 可选 JSON schema。这篇指南覆盖关于组合这份 contract 的一切——从一行的最小写法, 到内置组件集合,再到发布你自己的组件并生成它们的 schema。
如果你只想快速浏览一节,请看内置组件和basic-catalog functions:那是你的 Agent 能使用的「词汇表」。
catalog 是什么
一个 catalog 有两类 entry:
- Component——把一个协议名(
"Text"、"Card"、你的"MyChart")映射到 client 渲染的 ReactLynx 组件。 - Function——把一个协议 function 名(
"formatDate"、"required")映射到 客户端实现,renderer 在解析 props、actions 和 validation checks 时调用它。
你用 defineCatalog 构建它,然后要么把它(或一个原始 input 数组)传给
<A2UI catalogs={…}> 用于渲染,要么用 serializeCatalog 把这份 contract
告知你的 Agent。
一个组件的协议名来自 displayName ?? component.name,除非你把它和 manifest
配对——这时 manifest 的顶层 key 是权威的。Agent 只引用这个名字,所以保持它稳定
很重要(见下面的 minifier 警告)。
从小开始:renderer-only components
如果你的应用只需要渲染,直接传 bare components 即可。不需要 schema,也没有任何 仪式感。
你也可以把同一个数组直接传给 <A2UI> 而不必自己调用 defineCatalog——组件会在
内部组合它:
打包器会 tree-shake 掉未使用的组件:引入 Text 不会把 Button、Card 或
其他任何内置组件拖进你的 bundle。
⚠️ 生产环境 minifier 会改写 function 声明名,这会破坏
component.name这个回退。为了生产安全,请给每个自定义组件设置显式displayName(字符串 字面量能在 minify 后保留),或用 tuple 形式把组件和它的catalog.jsonmanifest 配对——manifest 的 key 是权威的,也不受 minify 影响。
内置组件
这个包提供 20 个 A2UI v0.9 basic-catalog renderer。每个都是独立、可 tree-shake
的导出,既可从根导入,也可从 @lynx-js/genui/a2ui/catalog/<Name> 导入。
布局与容器
内容
输入与动作
数据可视化
反馈
要了解每个组件接受的确切 props,请阅读它的 manifest
@lynx-js/genui/a2ui/catalog/<Name>/catalog.json——那份 JSON 就是 Agent 看到的
同一份 schema。
为 Agent handshake 加入 manifests
如果你希望 serializeCatalog(...) 为每个组件输出 JSON Schema(让 Agent 知道该
发哪些 props),用 tuple 形式把每个组件和 dist/catalog/<Name>/catalog.json
生成的 JSON 配对:
协议名作为顶层 key 存在 JSON 里,所以运行时从不重复它。没有 manifest 就注册的
组件照样能渲染——它们只是序列化成 { name },告诉 Agent 该组件存在,但不描述
它的 props。
basic-catalog functions
A2UI messages 可以在 dynamic props、action payload 和 validation checks 里内嵌
function call——例如 { call: 'formatDate', args: { … } }。这些在客户端的渲染
时刻运行。要让它们可用,把 ...basicFunctions 展开进同一个 catalog input 列表:
basicFunctions 是一组现成 entry 的数组,它们的实现直接来自上游
@a2ui/web_core basic catalog,所以 wire contract 能免费地与 A2UI v0.9 spec
保持一致。它覆盖 25 个 function:
注意大小写不统一——比较/文本类用
snake_case(not_equals、starts_with), 而格式化类用camelCase(formatDate、openUrl)。这些是上游 A2UI v0.9 的 名称;在 message 里请原样使用。
只要你的 Agent 可能发出其中任何一个,就加入 ...basicFunctions。如果某条
message 引用了 catalog 里没有的 function,那次调用会解析为 undefined 而不是
抛错。
如果你构建自己的 renderer 而不用 <A2UI>,调用一次 registerBasicFunctions()
就能把这些相同的实现注册进共享的 functionRegistry。
为什么没有 catalog/all
这个包刻意不提供 all-in-one 的 catalog 常量,也没有
@lynx-js/genui/a2ui/catalog/all 导出。一个引用所有内置组件的顶层数组会破坏
tree-shaking——任何用到这个聚合的消费者都会打包全部组件,即使它们从不渲染其中
某些。组合是逐组件的,bundle 成本在 import 处保持可见。
可粘贴的「全部内置」配方
当你确实想要它们全部时,请把列表保留在接入点。下面这段组合了每个内置组件及其 manifest,外加 basic functions:
对任何你不需要把 schema 发给 Agent 的组件,去掉它的 manifest 导入和 tuple
形式即可——defineCatalog([Text, Button]) 完全有效。如果你的 messages 用到
function call,就保留 ...basicFunctions。
自定义组件
catalog 组件可以是_任何_接收单个 props 对象并返回 ReactNode 的东西。它的
function 名——或它的 displayName——就是 Agent 会使用的协议名:
自定义组件从 protocol stream 收到运行时形状的 props。对于任何超出叶子组件的
情况,请使用 @lynx-js/genui/a2ui/react——这是自定义组件接入的 contract:
useDataBinding——把一个 bound 值({ path })针对 surface 的 data model 求值,并拿回一个 setter,这样输入组件就能把用户编辑写回 model。useResolvedProps——一次性解析整个 prop 包,把 data binding 和 function call 展开成具体值。useAction——把一个协议 action 变成你在点击/提交时触发的sendAction回调;结果会通过<A2UI onAction>回流出去。useChecks——求值 validation checks(由required/email等 basic function 构建),并以 pass/fail 加消息的形式报告。NodeRenderer——针对同一个 surface 渲染 child component id,方式和内置 组件渲染自己的 children 完全一样。
为自定义组件生成 manifest
如果 Agent 需要知道某个自定义组件的 props,生成一个 manifest,并像内置组件那样 把它配对。
-
把 props 描述为一个 TypeScript
interface,并用@a2uiCatalog <ComponentName>JSDoc 标签标注它,让 extractor 能识别它。 -
运行公开 CLI 输出 JSON:
-
把生成的 JSON 与组件配对:
npx @lynx-js/genui a2ui generate catalog 是面向用户的命令;
@lynx-js/genui/a2ui-catalog-extractor 是它背后由 TypeDoc 驱动的引擎。两条约束
能让抽取顺利进行:组件文件夹名必须与导出的 function 名一致
(src/catalog/MyChart/index.tsx 导出 function MyChart),并且框架级 props
会被排除在产出的 schema 之外。详情见
extractor README。
Catalog API 参考
下面这些都从 @lynx-js/genui/a2ui(以及 /catalog subpath)导出。
defineCatalog(inputs)——构建运行时 catalog。inputs是一个数组,可以 混合 bare components、[component, manifest]tuple、已解析的 entry(例如来自mergeCatalogs),以及 function entries。同一类里的重名会被拒绝。function entry 会立即把它的 impl 注册进functionRegistry,所以defineCatalog之后 的任何executeFunctionCall都能路由到它们。mergeCatalogs(...catalogs)——合并多个 catalog,重名时后写覆盖前写。 适合分层:page catalog 覆盖 brand catalog,brand catalog 覆盖内置。serializeCatalog(catalog)——为 Agent handshake 输出 JSON manifest。没有 附带 schema 的组件序列化成{ name };function 在有 schema 时带上其参数 schema 一起序列化。resolveCatalog(catalog)——返回一个name → component的 map。renderer 内部用它来解析{ component: 'Text' };供高级场景暴露。defineFunction(impl, manifest?)——把一个 function 实现包装成 catalog entry。带 manifest 时,名字来自 manifest 的 key,schema 会被告知 Agent;不带 时,名字来自impl.displayName ?? impl.name,Agent 就看不到参数 schema。
下一步
- 概览与架构——一条 message 如何变成 UI、职责划分,以及 export 映射。
- System Prompts——生成让 Agent 与你的 catalog 配套的模型指令。
- 打开 A2UI playground——在线体验。

