AWS Bedrock Agent流式响应异常:设置streamFinalResponse为true后InvokeAgent仍无法分块返回结果
我之前也碰到过类似的流式响应问题,结合你的代码和场景,给你几个排查和修复的方向,应该能解决问题:
1. 先确认Bedrock Agent的基础配置是否支持流式返回
首先要排除Agent本身的限制:
- 检查你Agent绑定的模型是否支持流式响应:只有流式模型(比如Claude 2/3、Titan Text Streaming系列、Llama 2 Chat Streaming等)才能返回分块结果,非流式模型即使开启
streamFinalResponse也会一次性返回全部内容。 - 确认Agent Alias的配置:在创建Agent Alias时,是否有对应的流式响应开关?有些时候Agent的别名配置会覆盖参数里的流式设置。
2. 修复代码里的流式数据处理逻辑
你的代码里有个容易踩坑的点:AWS SDK v3返回的response.completion是AsyncIterable对象,不是标准的Node.js Readable流,直接用stream.pipeline处理可能无法正确识别分块。需要先把它转换成Readable流:
// 把AsyncIterable转成Node.js Readable流 const completionStream = Readable.from(response.completion);
然后在pipeline里用这个转换后的流代替原来的response.completion。
3. 给响应流添加正确的头部信息
客户端(比如浏览器)会根据响应头判断是否要流式接收内容,如果没有设置对应头部,可能会缓存整个响应后再显示,导致看起来像没有分块。在处理响应前加上这些设置:
// 在创建command之前添加响应头配置 responseStream.setContentType('text/event-stream; charset=utf-8'); responseStream.setHeader('Cache-Control', 'no-cache'); responseStream.setHeader('Connection', 'keep-alive');
如果不需要SSE格式,也可以用text/plain; charset=utf-8,但SSE格式更适合前端实时接收分块内容。
4. 调整流式数据的处理逻辑(可选)
如果要同时处理trace和completion的分块,可以在Transform里分别处理不同类型的chunk,并且用换行分隔每个分块,确保客户端能正确识别:
const processChunk = new Transform({ objectMode: true, transform(chunk, encoding, callback) { console.log(`Received chunk: ${JSON.stringify(chunk)}`); if (chunk.chunk) { const decodedResponse = new TextDecoder("utf-8").decode(chunk.chunk.bytes); // SSE格式用data: 前缀+换行分隔 this.push(`data: ${decodedResponse}\n\n`); } else if (chunk.trace) { // 如果需要展示trace信息,也按SSE格式推送 this.push(`data: [TRACE] ${JSON.stringify(chunk.trace)}\n\n`); } else if (chunk.internalServerException) { this.push(`data: ${JSON.stringify({ error: 'Internal Server Exception' })}\n\n`); } callback(); } });
5. 简化测试排除干扰
暂时关闭enableTrace,只测试completion的流式返回,避免trace的流干扰主响应的分块判断;同时用curl命令测试(加上-N参数禁用缓存):
curl -N "https://你的Lambda函数URL/?sessionId=test-session&prompt=请介绍一下AWS Bedrock"
如果curl能看到分块返回的内容,说明问题出在客户端的处理,而不是Lambda或Bedrock的配置。
修复后的完整代码参考
import util from 'util'; import stream from 'stream'; const { Readable, Transform } = stream; const pipeline = util.promisify(stream.pipeline); import { BedrockAgentRuntimeClient, InvokeAgentCommand, } from "@aws-sdk/client-bedrock-agent-runtime"; export const handler = awslambda.streamifyResponse( async (event, responseStream, context) => { console.log(`event: ${JSON.stringify(event)}`) const client = new BedrockAgentRuntimeClient({ region: "us-east-1" }); const agentId = "xxxxxxxx"; const agentAliasId = "yyyyyyyy"; const sessionId = event['queryStringParameters']['sessionId']; const prompt = event['queryStringParameters']['prompt']; // 设置流式响应头,确保客户端实时接收分块 responseStream.setContentType('text/event-stream; charset=utf-8'); responseStream.setHeader('Cache-Control', 'no-cache'); responseStream.setHeader('Connection', 'keep-alive'); const command = new InvokeAgentCommand({ streamingConfigurations: { streamFinalResponse: true }, agentId, agentAliasId, sessionId, inputText: prompt, enableTrace: false, // 先关闭trace排除干扰 endSession: false }); try { const response = await client.send(command); console.log(`response: ${JSON.stringify(response)}`) // 将AsyncIterable转换为Node.js Readable流 const completionStream = Readable.from(response.completion); const processChunk = new Transform({ objectMode: true, transform(chunk, encoding, callback) { console.log(`transform chunk: ${JSON.stringify(chunk)}`); if (chunk.chunk) { const decodedResponse = new TextDecoder("utf-8").decode(chunk.chunk.bytes); console.log(decodedResponse); // 用SSE格式推送分块内容 this.push(`data: ${decodedResponse}\n\n`); } else if (chunk.internalServerException) { this.push(`data: ${JSON.stringify({ error: 'Internal Server Exception' })}\n\n`); } callback(); } }); await pipeline( completionStream, processChunk, responseStream ) } catch (err) { console.error(err); // 推送错误信息到响应流 responseStream.write(`data: ${JSON.stringify({ error: err.message })}\n\n`); responseStream.end(); } } );
如果以上方法都试了还是不行,可以去CloudWatch里查看Bedrock Agent的日志组(比如/aws/bedrock-agent/your-agent-id),看Agent是否真的在输出分块数据,这样就能定位是Agent的问题还是Lambda处理的问题。
备注:内容来源于stack exchange,提问作者user3829743

