面向Agent的声明式UI协议实战:服务端与客户端实现

作者:rousong2026.11.04 11:32浏览量:0

简介:本文通过一个完整的股票查询示例,系统讲解面向Agent的声明式UI协议中Agent服务端与客户端的实现方法。读者将掌握UI模板设计、数据绑定、按钮交互、工具函数注册、事件回调处理以及客户端渲染流程,并学会结果验证与常见问题排查,能够独立搭建可运行的端到端演示。

agent-ui-agent-">面向 Agent 的声明式 UI 协议实战:Agent 服务端与客户端实现

在前一篇文章中,我们介绍了面向 Agent 的声明式 UI 协议的基本理念:由 Agent 生成结构化的 UI 描述(通常为 JSON),客户端负责解析并渲染为原生界面,从而让大模型驱动的应用具备可控、可预测、可复用的交互界面。本篇进入实践环节,通过一个完整的股票查询 Demo,把协议从概念落到代码,重点讲解 Agent 服务端与客户端的实现过程。

一、教程目标与适用场景

本教程要完成的任务是:搭建一个可运行的端到端演示,用户在前端输入股票代码并提交,Agent 服务端调用大模型与工具函数获取行情数据,按预定义模板生成声明式 UI JSON,客户端解析后渲染为原生界面,并支持“刷新”“加入自选”等按钮交互回调。

适合阅读本文的读者包括:正在探索大模型应用界面生成方案的开发者、需要为 Agent 设计可控输出结构的技术负责人,以及希望理解“服务端生成 UI + 客户端原生渲染”这一架构模式的运维与前端工程师。阅读前建议具备 Python 基础、了解大模型工具调用(Function Calling)的基本概念,并对移动端或跨端 UI 开发有初步认识。

适用场景包括:需要由模型动态决定界面内容的智能助手、表单与卡片类信息展示、多轮交互中界面随状态变化的场景,以及希望避免让模型直接输出前端代码、从而降低安全与可维护性风险的产品。

二、整体架构与数据流

Demo 由三部分组成:Agent 服务端、协议层、客户端。数据流如下:

  1. 客户端采集用户输入(如股票代码),通过协议层发送请求;
  2. Agent 服务端接收请求,交由大模型结合工具函数处理;
  3. 工具函数返回结构化行情数据;
  4. 服务端按模板规则生成声明式 UI JSON;
  5. 客户端解析 JSON,渲染为原生 UI;
  6. 用户点击按钮时,客户端回传动作与上下文,服务端处理事件并返回新的 UI 描述。

这一架构的关键在于职责分离:模型负责“决定展示什么”,模板负责“约束怎么展示”,客户端负责“如何渲染”。模型不直接产出可执行代码,安全边界清晰,界面风格也保持一致。

三、前置准备

开始动手前,请准备以下环境与素材:

  • 具备 Python 运行环境的开发机,能够安装依赖包;
  • 一个可调用的大模型服务(通过通用 API 方式接入),并具备工具调用能力;
  • 客户端的开发环境,能够构建并运行原生应用;
  • 一份本地 Mock 数据文件,用于在无真实行情源时完成联调;
  • 对 JSON Schema 与组件树结构的基本理解。

建议先在本地以 Mock 数据跑通全链路,再替换为真实数据源,避免联调阶段同时排查网络与逻辑问题。

四、服务端项目结构设计

示例 Agent 采用按职责拆分文件的方式组织,结构如下:

  1. stock_lookup/
  2. ├── __main__.py # 启动入口,配置服务与路由
  3. ├── agent.py # Agent 定义,注入 Prompt 与工具
  4. ├── agent_executor.py # 事件处理,处理按钮回调
  5. ├── a2ui_examples.py # UI 模板(股票卡片、成功页)
  6. ├── prompt_builder.py # Prompt 构建,包含模板规则
  7. ├── tools.py # 工具函数(行情查询)
  8. └── 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 定义解耦、事件处理独立,后续替换数据源或新增模板时改动范围可控。

五、实施步骤

步骤 1:设计声明式 UI 模板

先在 a2ui_examples.py 中定义模板。推荐先用可视化编排工具生成基础 JSON 结构,再手工改造三处关键内容。

第一处是数据绑定。把静态文本改为路径引用,让同一模板可复用于不同数据:

  1. {"id": "stockCode", "component": {"Text": {"text": {"path": "symbol"}}}}

第二处是按钮交互。为按钮添加动作名与上下文,客户端点击时据此回传:

  1. {
  2. "id": "addWatchlistBtn",
  3. "component": {
  4. "Button": {
  5. "child": "btnText",
  6. "action": {
  7. "name": "addToWatchlist",
  8. "context": [
  9. {"key": "symbol", "value": {"path": "symbol"}},
  10. {"key": "name", "value": {"path": "name"}}
  11. ]
  12. }
  13. }
  14. }
  15. }

第三处是结构完整性。股票卡片应包含代码、名称、价格、涨跌幅等文本节点,底部放置“刷新”与“加入自选”两个按钮;成功页模板同理,包含确认图标、提示文本与后续操作按钮。

设计时注意:组件 id 在同一模板内必须唯一;路径引用要与后续数据结构字段名严格一致;动作名要使用稳定的英文标识,便于服务端分支处理。模板一旦确定,就应作为“契约”写入提示词,避免模型自由发挥导致客户端解析失败。

步骤 2:实现工具函数

在 tools.py 中实现行情查询函数,例如接收股票代码、返回名称、价格、涨跌幅等字段。工具函数应满足三点:入参简单明确、返回结构稳定、异常时返回可读错误信息而非抛出未捕获异常。稳定的返回结构是模板数据绑定能正确解析的前提。开发阶段可先读取本地 Mock 数据文件,保证无外部依赖即可跑通。

步骤 3:构建提示词与模板规则

prompt_builder.py 负责把以下内容拼装为系统提示词:Agent 的角色定位、可用的 UI 模板及其组件结构、数据路径与字段的对应关系、按钮动作的命名约定、以及“只输出符合模板的 JSON”这类格式约束。提示词写得越具体,模型输出越稳定。建议在提示词中直接嵌入模板示例,并明确要求不得新增未定义的组件类型。

步骤 4:定义 Agent 与注册工具

在 agent.py 中创建 Agent 实例,注入上一步构建的提示词,并注册工具函数。注册后,模型在需要行情数据时会自动发起工具调用,服务端执行函数并把结果回填给模型,模型再据此生成带真实数据的 UI JSON。

步骤 5:实现事件处理

agent_executor.py 是交互闭环的关键。它需要区分两类输入:首次查询(用户输入的股票代码)与按钮回调(动作名 + 上下文)。对于回调,按动作名分支处理:refresh 重新查询行情并返回更新后的卡片模板;addToWatchlist 执行加入自选逻辑后返回成功页模板。处理完成后,把生成的 UI JSON 作为响应返回给客户端。

步骤 6:启动服务

在 __main__.py 中创建服务实例、注册执行器、配置监听地址与端口后启动。启动成功后在日志中确认路由已注册,再用最简单的请求验证服务可达,之后再接入客户端。

步骤 7:实现客户端渲染

客户端职责包括:提供输入界面、发送请求、解析 UI JSON、按组件类型映射为原生控件、绑定按钮动作。渲染器建议实现为一个递归解析函数:遇到 Text 渲染文本控件并解析 path 取值,遇到 Button 渲染按钮并绑定动作回调。解析时要做好防御:组件类型未知时跳过并记录日志,路径取值失败时显示占位内容,避免整页崩溃。

步骤 8:打通按钮回调

客户端点击按钮时,把动作名与上下文组装为事件请求发回服务端,收到新 UI JSON 后替换当前界面。至此形成“输入—生成—渲染—交互—再生成”的完整闭环。

六、结果验证

验证可分三层:其一,服务端单独请求,确认返回的是符合模板结构的 JSON,字段路径与模板一致;其二,客户端渲染,确认卡片正确显示代码、名称、价格与涨跌幅,按钮可见可点;其三,交互验证,点击“刷新”后价格更新,点击“加入自选”后切换到成功页。若三层均通过,说明端到端链路正常。

七、常见问题与排查

  • 界面空白或渲染异常:多为 JSON 结构不符合模板,检查组件类型名与 id 是否与模板一致;
  • 文本显示为占位符:数据路径与返回数据字段不匹配,逐一核对 path 与实际字段名;
  • 按钮点击无响应:动作名未在服务端分支中处理,或上下文未正确回传;
  • 模型输出夹杂解释文字:提示词约束不足,需强化“仅输出 JSON”的要求,并在服务端做解析容错;
  • 工具未被调用:检查工具注册与描述是否清晰,入参定义是否明确。

八、优化建议

性能上,可对模板与提示词做缓存,减少重复构建;安全上,服务端应对模型输出做结构与白名单校验,拒绝未定义组件;稳定性上,为工具调用设置超时与降级数据;可维护性上,把模板纳入版本管理并为每个模板编写校验用例;成本上,控制提示词长度,避免每次请求都携带全部模板全文。

九、总结

本文以股票查询 Demo 为主线,完成了从模板设计、工具实现、提示词构建、Agent 注册、事件处理到客户端渲染与回调的完整实践。核心要点是:模板即契约,数据用路径绑定,交互用动作名与上下文描述,服务端负责生成与校验,客户端负责解析与渲染。掌握这一链路后,你可以把同一模式迁移到更多信息卡片与多轮交互场景中,并继续在模板校验、渲染容错与交互扩展上深入打磨。