[Bug]: `str()`/`.response` on async streaming chat response (astream_chat) returns empty string

用户在使用 LlamaIndex 的 SimpleChatEngine (或其他使用 StreamingAgentChatResponse 的引擎)调用 astream_chat 方法进行异步流式聊天时,流式传输过程正常,Token 可以正常输出并保存到内存,但通过 str(resp) 或 resp

[Bug]: `str()`/`.response` on async streaming chat response (astream_chat) returns empty string

[Bug]: `str()`/`.response` on async streaming chat response (astream_chat) returns empty string

快速结论:该 Bug 发生在 LlamaIndex 异步流式聊天(astream_chat)场景下,当流式传输结束后调用 str(resp)resp.response 会返回空字符串。优先排查你是否在异步流式响应完成后直接读取 response 属性,而非同步流式 stream_chat

问题场景

用户在使用 LlamaIndex 的 SimpleChatEngine(或其他使用 StreamingAgentChatResponse 的引擎)调用 astream_chat 方法进行异步流式聊天时,流式传输过程正常,Token 可以正常输出并保存到内存,但通过 str(resp)resp.response 获取最终文本却返回空字符串。同步版本的 stream_chat 功能正常。

报错原文

Output: ''

原因分析

已由 Issue 提交者和评论者确认,Bug 根因位于 StreamingAgentChatResponse 实现中:

  • 问题 1:__str__ 方法只从同步队列 self.queue 中提取数据,从未检查异步队列 self.aqueue。异步流式完成后,累积的文本仍滞留在异步队列中。
  • 问题 2:awrite_response_to_history 方法在异步流式完成后,虽然累积了 final_text 并写入内存,但从未将最终文本赋值给 self.responseself.unformatted_response,导致 response 属性保持空字符串。

环境排查

  • LlamaIndex 版本:0.14.23(Issue 中复现的版本)
  • 确认你使用的是 astream_chat 而非 stream_chat
  • 确认你在读取 response 之前已 await resp.awrite_response_to_history_task

解决步骤

  1. 方案一(推荐,最简单安全):awrite_response_to_history 方法末尾(设置 self.is_done = True 之后)添加赋值:
    self.unformatted_response = final_text
    self.response = final_text.strip()
  2. 方案二(备选):修改 __str__ 方法,使其同时检查并排空 self.aqueue(当 self.queue 为空且 self.aqueue 不为空时):
    def __str__(self) -> str:
        if self.is_done and not self.is_function:
            if not self.queue.empty():
                while self.queue.queue:
                    delta = self.queue.queue.popleft()
                    self.unformatted_response += delta
                self.response = self.unformatted_response.strip()
            elif self.aqueue is not None and not self.aqueue.empty():
                while not self.aqueue.empty():
                    delta = self.aqueue.get_nowait()
                    self.unformatted_response += delta
                self.response = self.unformatted_response.strip()
        return self.response
  3. 手动修复方式:如果你无法修改源码,可在调用 astream_chat 后主动遍历异步流式生成器来消耗队列,然后手动设置 resp.responseresp.response = "你的累积文本"。但这只是临时绕过。

注意:以上修复方案来自 Issue 讨论中的推测,尚未在官方版本中合并。可优先尝试方案一。

验证方法

在修复后,运行以下测试代码确认 str(resp) 返回空字符串变为正常的回复文本:

import asyncio
from llama_index.core.chat_engine.simple import SimpleChatEngine
from llama_index.core.llms.mock import MockLLM

async def main() -> None:
    engine = SimpleChatEngine.from_defaults(llm=MockLLM(max_tokens=8))
    resp = await engine.astream_chat("What is two plus two")
    await resp.awrite_response_to_history_task
    print(repr(str(resp)))  # 修复前输出 '',修复后应输出正常文本

asyncio.run(main())

参考来源

run-llama/llama_index #22178

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 11018

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注