跳到主要内容

v1.7.1 — Converse Stream API + 流式 UX 改进

· 阅读需 5 分钟

本次发布在多路由合成中引入了 Bedrock Converse Stream API,并为 AgentCore Gateway 响应应用了打字效果。同时为监控查询新增了时间范围过滤,解决了 pg pool 耗尽问题。

核心变化

Converse Stream API · 打字效果(50 字符/15ms) · 聊天气泡宽度稳定化 · 3 项监控查询时间过滤

借助 Converse Stream API 实现实时合成

多路由响应合成从伪分块转换为真正的令牌流式传输

Before / After

项目Before (v1.7.0)After (v1.7.1)
合成 APIInvokeModelCommandConverseStreamCommand
合成函数synthesizeResponses()synthesizeResponsesStreaming()
流式方式生成完整响应 → simulateStreaming()令牌生成后立即发送 chunk 事件
首个令牌延迟等待整个响应完成(5~15 秒)~200ms(首个令牌立即到达)
用户体感长时间等待后快速输出立即开始,流畅自然

代码变更

BeforesynthesizeResponses() + simulateStreaming():

// src/app/api/ai/route.ts (v1.7.0)
const synthesized = await synthesizeResponses(
lastMsg, successful, modelKey, clientLang
);
// 将完成的文本按 50 字符切分做伪流式传输
await simulateStreaming(synthesized, send);

AftersynthesizeResponsesStreaming() + ConverseStreamCommand:

// src/app/api/ai/route.ts (v1.7.1)
const synthesized = await synthesizeResponsesStreaming(
lastMsg, successful, send, modelKey, clientLang
);
// send() 回调在每个 contentBlockDelta 时立即发送 chunk

synthesizeResponsesStreaming() 核心逻辑:

const response = await bedrockClient.send(new ConverseStreamCommand({
modelId,
system: [{ text: systemPrompt }],
messages: [{ role: 'user', content: [{ text: synthesisPrompt }] }],
inferenceConfig: { maxTokens: 4096 },
}));

for await (const event of response.stream) {
if (event.contentBlockDelta?.delta?.text) {
const text = event.contentBlockDelta.delta.text;
fullContent += text;
send('chunk', { delta: text }); // 立即发送令牌
}
}

打字效果(simulateStreaming

由于 AgentCore Gateway 会一次性返回完整文本,因此需要模拟打字效果。

// src/app/api/ai/route.ts
const CHUNK_SIZE = 50; // 每个分块 50 字符
const CHUNK_DELAY_MS = 15; // 分块间 15ms 延迟

async function simulateStreaming(
text: string,
send: (event: string, data: any) => void,
): Promise<void> {
for (let i = 0; i < text.length; i += CHUNK_SIZE) {
const chunk = text.slice(i, i + CHUNK_SIZE);
send('chunk', { delta: chunk });
if (i + CHUNK_SIZE < text.length) {
await new Promise(r => setTimeout(r, CHUNK_DELAY_MS));
}
}
}

应用位置(3 处):

  1. 单个 AgentCore Gateway 响应 — 对完整文本应用打字效果
  2. 多路由合成(v1.7.0) — 应用于 synthesizeResponses() 结果(v1.7.1 中已由 Converse Stream 替代)
  3. 多路由单一成功 — 仅有 1 个 Gateway 成功的情况
为什么需要模拟?

Bedrock 直接调用可以通过 InvokeModelWithResponseStreamCommand 实现真正的流式传输,但 AgentCore Gateway 会在内部多次调用工具后只返回最终文本。为了给用户提供一致的流式体验,我们应用了 50 字符/15ms 的打字效果。

聊天气泡宽度稳定化(PR #8)

修复了 SSE 流式传输过程中聊天气泡宽度突然变化的问题。

项目BeforeAfter
CSSmax-w-5xlw-full max-w-5xl
行为内容增加时宽度跳变从首个 chunk 起固定为最大宽度
文件src/app/ai/page.tsx相同

Before — 宽度跳变:

<div class="max-w-5xl rounded-lg px-4 py-3 ...">
<!-- 内容短时较窄,内容变长时突然变宽 -->
</div>

After — 宽度稳定:

<div class="w-full max-w-5xl rounded-lg px-4 py-3 ...">
<!-- 从一开始就用 w-full 占满最大宽度,用 max-w-5xl 限制上限 -->
</div>
CSS 模式

对于像 SSE 流式传输这样内容逐步增加的容器,请使用 w-full max-w-{size} 组合。 只使用 max-w-{size} 时,宽度会随内容量而变化。

监控查询时间范围过滤

修复了没有时间范围限制的监控查询通过 Steampipe API 拉取全部历史数据、占满 pg pool 全部 5 个连接的问题。

查询文件BeforeAfter
ec2CpuHourlyqueries/metrics.ts无时间限制WHERE timestamp >= NOW() - INTERVAL '24 hours'
ebsIopsHourlyqueries/metrics.tsWHERE r.timestamp IS NOT NULLAND r.timestamp >= NOW() - INTERVAL '24 hours'
rdsCpuDailyqueries/metrics.ts无时间限制WHERE timestamp >= NOW() - INTERVAL '30 days'
pg Pool 耗尽症状

在 46+ 台 EC2 实例 + 大量 EBS 卷的环境中,无时间限制的查询会占用连接长达数分钟。 当 pg pool 的 max: 5 全部被占用时,仪表盘和所有页面都会等待到 statement_timeout: 120s 后失败。

3 种流式模式概览

在 v1.7.1 中,AWSops AI 响应会根据场景使用 3 种流式模式。

模式使用时机实现首个令牌延迟
Real StreamingBedrock 直接调用InvokeModelWithResponseStreamCommand~200ms
Converse Stream多路由合成ConverseStreamCommand + contentBlockDelta~200ms
Simulated StreamingAgentCore Gateway 响应simulateStreaming()(50 字符/15ms)响应完成后立即
统一的客户端体验

由于 3 种模式都使用相同的 SSE chunk 事件,客户端(src/app/ai/page.tsx)无需区分模式,只需将 delta 累加到 streamingContent 状态中,并通过 ReactMarkdown 实时渲染。