简介:本文通过一个完整的股票查询示例,系统讲解面向Agent的声明式UI协议中Agent服务端与客户端的实现方法。读者将掌握UI模板设计、数据绑定、按钮交互、工具函数注册、事件回调处理以及客户端渲染流程,并学会结果验证与常见问题排查,能够独立搭建可运行的端到端演示。
在前一篇文章中,我们介绍了面向 Agent 的声明式 UI 协议的基本理念:由 Agent 生成结构化的 UI 描述(通常为 JSON),客户端负责解析并渲染为原生界面,从而让大模型驱动的应用具备可控、可预测、可复用的交互界面。本篇进入实践环节,通过一个完整的股票查询 Demo,把协议从概念落到代码,重点讲解 Agent 服务端与客户端的实现过程。
本教程要完成的任务是:搭建一个可运行的端到端演示,用户在前端输入股票代码并提交,Agent 服务端调用大模型与工具函数获取行情数据,按预定义模板生成声明式 UI JSON,客户端解析后渲染为原生界面,并支持“刷新”“加入自选”等按钮交互回调。
适合阅读本文的读者包括:正在探索大模型应用界面生成方案的开发者、需要为 Agent 设计可控输出结构的技术负责人,以及希望理解“服务端生成 UI + 客户端原生渲染”这一架构模式的运维与前端工程师。阅读前建议具备 Python 基础、了解大模型工具调用(Function Calling)的基本概念,并对移动端或跨端 UI 开发有初步认识。
适用场景包括:需要由模型动态决定界面内容的智能助手、表单与卡片类信息展示、多轮交互中界面随状态变化的场景,以及希望避免让模型直接输出前端代码、从而降低安全与可维护性风险的产品。
Demo 由三部分组成:Agent 服务端、协议层、客户端。数据流如下:
这一架构的关键在于职责分离:模型负责“决定展示什么”,模板负责“约束怎么展示”,客户端负责“如何渲染”。模型不直接产出可执行代码,安全边界清晰,界面风格也保持一致。
开始动手前,请准备以下环境与素材:
建议先在本地以 Mock 数据跑通全链路,再替换为真实数据源,避免联调阶段同时排查网络与逻辑问题。
示例 Agent 采用按职责拆分文件的方式组织,结构如下:
stock_lookup/├── __main__.py # 启动入口,配置服务与路由├── agent.py # Agent 定义,注入 Prompt 与工具├── agent_executor.py # 事件处理,处理按钮回调├── a2ui_examples.py # UI 模板(股票卡片、成功页)├── prompt_builder.py # Prompt 构建,包含模板规则├── tools.py # 工具函数(行情查询)└── stock_data.json # Mock 数据
各文件职责说明:
__main__.py:创建服务实例,注册 Agent 执行器与路由,是进程启动点;agent.py:声明 Agent 的身份、系统提示词与可用工具集合;agent_executor.py:接收客户端事件,区分首次查询与按钮回调两类输入;a2ui_examples.py:集中存放 UI 模板常量,便于复用与校验;prompt_builder.py:把模板规则、可用组件与数据路径约定拼装进提示词;tools.py:实现具体业务能力,供模型按需调用;stock_data.json:离线数据,保证 Demo 可独立运行。拆分的价值在于:模板与提示词解耦、工具与 Agent 定义解耦、事件处理独立,后续替换数据源或新增模板时改动范围可控。
先在 a2ui_examples.py 中定义模板。推荐先用可视化编排工具生成基础 JSON 结构,再手工改造三处关键内容。
第一处是数据绑定。把静态文本改为路径引用,让同一模板可复用于不同数据:
{"id": "stockCode", "component": {"Text": {"text": {"path": "symbol"}}}}
第二处是按钮交互。为按钮添加动作名与上下文,客户端点击时据此回传:
{"id": "addWatchlistBtn","component": {"Button": {"child": "btnText","action": {"name": "addToWatchlist","context": [{"key": "symbol", "value": {"path": "symbol"}},{"key": "name", "value": {"path": "name"}}]}}}}
第三处是结构完整性。股票卡片应包含代码、名称、价格、涨跌幅等文本节点,底部放置“刷新”与“加入自选”两个按钮;成功页模板同理,包含确认图标、提示文本与后续操作按钮。
设计时注意:组件 id 在同一模板内必须唯一;路径引用要与后续数据结构字段名严格一致;动作名要使用稳定的英文标识,便于服务端分支处理。模板一旦确定,就应作为“契约”写入提示词,避免模型自由发挥导致客户端解析失败。
在 tools.py 中实现行情查询函数,例如接收股票代码、返回名称、价格、涨跌幅等字段。工具函数应满足三点:入参简单明确、返回结构稳定、异常时返回可读错误信息而非抛出未捕获异常。稳定的返回结构是模板数据绑定能正确解析的前提。开发阶段可先读取本地 Mock 数据文件,保证无外部依赖即可跑通。
prompt_builder.py 负责把以下内容拼装为系统提示词:Agent 的角色定位、可用的 UI 模板及其组件结构、数据路径与字段的对应关系、按钮动作的命名约定、以及“只输出符合模板的 JSON”这类格式约束。提示词写得越具体,模型输出越稳定。建议在提示词中直接嵌入模板示例,并明确要求不得新增未定义的组件类型。
在 agent.py 中创建 Agent 实例,注入上一步构建的提示词,并注册工具函数。注册后,模型在需要行情数据时会自动发起工具调用,服务端执行函数并把结果回填给模型,模型再据此生成带真实数据的 UI JSON。
agent_executor.py 是交互闭环的关键。它需要区分两类输入:首次查询(用户输入的股票代码)与按钮回调(动作名 + 上下文)。对于回调,按动作名分支处理:refresh 重新查询行情并返回更新后的卡片模板;addToWatchlist 执行加入自选逻辑后返回成功页模板。处理完成后,把生成的 UI JSON 作为响应返回给客户端。
在 __main__.py 中创建服务实例、注册执行器、配置监听地址与端口后启动。启动成功后在日志中确认路由已注册,再用最简单的请求验证服务可达,之后再接入客户端。
客户端职责包括:提供输入界面、发送请求、解析 UI JSON、按组件类型映射为原生控件、绑定按钮动作。渲染器建议实现为一个递归解析函数:遇到 Text 渲染文本控件并解析 path 取值,遇到 Button 渲染按钮并绑定动作回调。解析时要做好防御:组件类型未知时跳过并记录日志,路径取值失败时显示占位内容,避免整页崩溃。
客户端点击按钮时,把动作名与上下文组装为事件请求发回服务端,收到新 UI JSON 后替换当前界面。至此形成“输入—生成—渲染—交互—再生成”的完整闭环。
验证可分三层:其一,服务端单独请求,确认返回的是符合模板结构的 JSON,字段路径与模板一致;其二,客户端渲染,确认卡片正确显示代码、名称、价格与涨跌幅,按钮可见可点;其三,交互验证,点击“刷新”后价格更新,点击“加入自选”后切换到成功页。若三层均通过,说明端到端链路正常。
path 与实际字段名;性能上,可对模板与提示词做缓存,减少重复构建;安全上,服务端应对模型输出做结构与白名单校验,拒绝未定义组件;稳定性上,为工具调用设置超时与降级数据;可维护性上,把模板纳入版本管理并为每个模板编写校验用例;成本上,控制提示词长度,避免每次请求都携带全部模板全文。
本文以股票查询 Demo 为主线,完成了从模板设计、工具实现、提示词构建、Agent 注册、事件处理到客户端渲染与回调的完整实践。核心要点是:模板即契约,数据用路径绑定,交互用动作名与上下文描述,服务端负责生成与校验,客户端负责解析与渲染。掌握这一链路后,你可以把同一模式迁移到更多信息卡片与多轮交互场景中,并继续在模板校验、渲染容错与交互扩展上深入打磨。