传统聊天界面假设模型最终会返回一段文本,但真实的智能体任务并没有如此规整:一次运行可能生成表格,下一次可能要求用户确认,再下一次又会调用多个专业智能体,甚至进入没有 API 的旧系统完成操作。固定的消息气泡很快就会成为瓶颈。
一种更实用的设计,是把智能体运行过程转换成前端可消费的事件流:AG-UI 负责界面与智能体之间的协议边界,Strands Agents SDK 的 swarm 模式组织多智能体协作,Amazon Nova Act 则负责处理只能通过网页界面访问的遗留系统。三者解决的是不同问题,不应被揉成一个不可拆分的“大代理”。
界面不再等待一段最终答案
自适应 AI 界面的关键,不是让模型直接输出 HTML,而是让后端发布结构化事件。前端根据事件类型选择安全、可测试的组件,例如:
RUN_STARTED:创建新的任务运行视图;STEP_STARTED:显示当前由哪个智能体处理什么工作;UI_COMPONENT:渲染表格、指标卡、审批框或引用列表;STEP_FINISHED:记录步骤结果和证据;RUN_FINISHED:关闭流并展示最终状态。
这里的核心边界是:智能体决定表达意图,前端决定如何渲染。后端可以要求展示一张订单表,但不应发送任意 JavaScript。这样既能适应可变输出,也能保留浏览器端的安全控制、设计规范和无障碍能力。
AG-UI 可以承担这个事件边界。接入真实实现时,应使用对应 SDK 定义的标准事件和状态同步机制,而不是自行长期维护一套相似但不兼容的协议。
三层协作,各自保留清晰职责
可以把完整链路拆成三层:
- 交互层:AG-UI 事件连接前端和智能体运行时,持续传递状态、消息、工具结果与界面更新。
- 推理层:Strands Agents SDK 的 swarm 模式将任务交给多个专业智能体,例如规划、数据查询、风险审查和结果汇总。
- 执行层:当目标系统没有 API 时,由 Nova Act 在受控浏览器环境中完成必要的界面操作。
这种拆分对可解释性尤其重要。前端不应该只显示“智能体正在思考”,而应展示可审计的业务动作:
协调智能体:将订单核验任务拆为三个步骤
订单智能体:读取订单 1042,金额为 860 元
风控智能体:发现收货地址在下单后发生变更
审批步骤:等待人工确认是否继续退款
需要避免泄露模型的私有推理过程。可解释性应来自任务计划、代理交接、工具调用、数据来源和审批记录,而不是暴露完整思维链。
一个可运行的动态界面实验
下面的示例不依赖真实模型,可以直接运行,用来验证“事件驱动 UI”是否适合现有前端。它采用一个简化的、与 AG-UI 思路相近的事件信封;其中的事件名称只是演示约定,接入生产环境时需要替换为实际 AG-UI SDK 的类型。
先创建环境:
python -m venv .venv
source .venv/bin/activate
pip install fastapi 'uvicorn[standard]'
将以下内容保存为 app.py:
import asyncio
import json
from fastapi import FastAPI, Query
from fastapi.responses import HTMLResponse, StreamingResponse
app = FastAPI()
def encode_sse(event: dict) -> str:
return f'data: {json.dumps(event, ensure_ascii=False)}\n\n'
@app.get('/events')
async def events(task: str = Query(default='检查订单 1042')):
async def generate():
yield encode_sse({
'type': 'RUN_STARTED',
'task': task,
})
await asyncio.sleep(0.4)
yield encode_sse({
'type': 'STEP_STARTED',
'agent': 'coordinator',
'label': '分析任务并选择专业智能体',
})
await asyncio.sleep(0.5)
if '订单' in task:
component = {
'kind': 'table',
'title': '订单核验结果',
'columns': ['订单号', '状态', '金额'],
'rows': [['1042', '等待审批', '¥860']],
}
else:
component = {
'kind': 'metric',
'title': '任务处理进度',
'value': '75%',
}
yield encode_sse({
'type': 'UI_COMPONENT',
'component': component,
})
await asyncio.sleep(0.4)
yield encode_sse({
'type': 'RUN_FINISHED',
'status': 'completed',
})
return StreamingResponse(generate(), media_type='text/event-stream')
@app.get('/', response_class=HTMLResponse)
def index():
return '''
<!doctype html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<title>Adaptive Agent UI</title>
<style>
body { font-family: sans-serif; max-width: 760px; margin: 40px auto; }
section { border: 1px solid #ddd; border-radius: 10px; padding: 16px; margin: 12px 0; }
pre { background: #f6f8fa; padding: 12px; overflow: auto; }
</style>
</head>
<body>
<h1>智能体任务</h1>
<input id='task' value='检查订单 1042'>
<button onclick='startRun()'>运行</button>
<div id='output'></div>
<script>
function addCard(title, content) {
const section = document.createElement('section');
const heading = document.createElement('h2');
const body = document.createElement('pre');
heading.textContent = title;
body.textContent = content;
section.append(heading, body);
document.querySelector('#output').append(section);
}
function startRun() {
const output = document.querySelector('#output');
output.replaceChildren();
const task = document.querySelector('#task').value;
const stream = new EventSource('/events?task=' + encodeURIComponent(task));
stream.onmessage = message => {
const event = JSON.parse(message.data);
if (event.type === 'STEP_STARTED') {
addCard(event.agent, event.label);
}
if (event.type === 'UI_COMPONENT') {
const component = event.component;
addCard(component.title, JSON.stringify(component, null, 2));
}
if (event.type === 'RUN_FINISHED') {
addCard('运行状态', event.status);
stream.close();
}
};
stream.onerror = () => stream.close();
}
</script>
</body>
</html>
'''
启动服务:
uvicorn app:app --reload
打开 http://127.0.0.1:8000。输入包含“订单”的任务时,后端会要求前端展示表格;输入其他内容时,则会返回指标组件。虽然这是一个最小实验,但它验证了重要的架构选择:界面组件由事件数据驱动,而不是绑定某一种固定回答格式。
把 swarm 与旧系统执行接进来
进入真实项目后,可以让 swarm 中的协调智能体只负责分解和委派任务,由专业智能体产生结果。每次代理交接都映射成 AG-UI 事件,例如:
用户请求
-> 协调智能体制定计划
-> 数据智能体查询业务数据
-> 风控智能体检查限制条件
-> 审批组件等待用户确认
-> Nova Act 操作无 API 的后台页面
-> 汇总智能体生成结果与证据
Nova Act 更适合放在执行链路末端,而不是让它承担所有信息检索。浏览器自动化通常比正式 API 更容易受到页面布局、登录状态、弹窗和网络延迟影响。只有目标系统确实缺少稳定 API 时,才值得承担这部分成本。
可以为浏览器执行层增加一份独立策略。下面是可以改造的项目级配置示例,并非 Nova Act 的原生配置格式:
automation_policy:
allowed_hosts:
- legacy-admin.example.internal
blocked_actions:
- delete-account
- export-all-customers
require_human_approval:
- issue-refund
- change-payment-details
redact_fields:
- password
- access_token
- card_number
limits:
max_steps: 25
timeout_seconds: 120
执行危险动作前,后端应发送审批事件并暂停工作流。用户确认后再签发一次性授权,而不是仅依赖提示词中的“请谨慎操作”。
上线前需要守住的边界
这套架构的价值来自解耦,但也引入了更多运行状态。落地时应重点检查:
- 协议兼容性:用正式 AG-UI 类型替换实验事件,并对协议版本进行协商。
- 可恢复性:为每次运行、步骤和工具调用分配稳定 ID,断线重连后能够恢复状态。
- 可解释记录:保存代理交接、工具参数摘要、数据来源和审批人,不保存不必要的私有推理。
- 组件白名单:前端只渲染已注册组件,所有文本都按不可信输入处理。
- 浏览器隔离:Nova Act 使用独立会话、最小权限账号、域名白名单和完整审计日志。
- 幂等控制:退款、提交和更新等动作必须带幂等键,避免重试造成重复执行。
- 降级路径:swarm 或浏览器自动化失败时,应允许人工接管,而不是无限自主重试。
最稳妥的采用顺序,是先用 AG-UI 统一单智能体的事件输出,再引入可观察的 swarm 协作,最后只为确实没有 API 的少数步骤接入 Nova Act。这样每增加一层能力,都能单独度量它带来的收益、延迟和风险。