LangChain Streaming 流式输出

2,025 阅读2分钟

1_VrVhl67qmwr87CBZ0_g8vQ.webp

流式文本输出是大型语言模型(LLMs)和聊天机器人日益流行的功能。流式处理通过逐步发送文本,而不是等待生成完成,提供了更加互动的体验。我们将探讨如何利用 Langchain 代理和 FastAPI 实现流式处理。

Streaming 概述

流式处理的目标是尽快开始显示用户生成的文本,而不是在全部完成后。其优势包括:

  • 降低延迟 — 用户可以更快地看到初始结果
  • 增加参与度 — 输出看起来更加“活跃”
  • 提前预览 — 能够预览并取消不良生成
  • 进度跟踪 — 用户知道生成正在进行中

对于较短的文本,流式处理没必要。但对于较长的输出或速度较慢的模型,它极大地提升了体验。

在底层,流式处理的工作方式是从 LLM 生成器中生成 token,并在它们可用时即时提供,而不是缓冲完整的结果。然后再将这些令牌逐步发送给客户端。

Terminal 查看流式效果

最简单的流式实现是将 Token 打印到终端,无需 API 即可验证。

要将基本流式处理添加到 Langchain 大型语言模型(LLM)中:

llm = Chat(  
    streaming=True,  
    callbacks=StreamingStdoutCallbackHandler()  
)
- `streaming=True` 开启流式返回能力
- `StreamingStdoutCallbackHandler` 打印每一个新 Token

文本将逐行流式输出,而不是一次性输出。例如:

用户:给我讲个故事
LLM:从前
LLM:有
LLM:一个
LLM:时候

而不是:
LLM:从前有一个时候

回调处理程序会在每个令牌从生成中变得可用时启用流式传输。

Ageng 中使用流式返回

当使用Langchain Agent 时,流式处理逻辑变得更加复杂。代理封装了 LLM,并提供了额外的功能,如状态跟踪、工具链接等。

复杂性产生的原因是,代理将其输出格式化为类似于以下的JSON结构:

{  
"action": "text",  
"action_input": "Once upon a time…"  
}

并不是直接返回文本。因此,我们需要逻辑来提取 action_input 字段。

Langchain提供了一个 FinalAnswerCallbackHandler,它通过仅打印最终文本来处理这个问题。但它的功能有限。

我更喜欢使用自定义回调处理程序来获得更多自主性:

class MyStreamingCallback(StreamingStdoutCallbackHandler):  
    def __init__(self):  
        self.content = ""  
        self.final_answer = False  

    async def on_lm_new_token(self, token: TokenizedLMOutput):  
        self.content += token  

    if "final_answer" in self.content:  
        self.final_answer = True  
        self.content = ""  

    if self.final_answer:  
        print(token)  
    
agent.callbacks = MyStreamingCallback()

这会将所有令牌累积到 self.content 中。一旦它看到 ”final_answer” 标记,它就会提取令牌并将它们进行流式传输。

核心包括:

  • 检测最终答案以处理多个动作
  • 在生成之间重置状态
  • 仅打印所需的文本令牌

有了这个回调处理程序,我们就可以正确地从 Langchain 代理流式传输文本。