大家好,我是技术博客作者。上周有个做独立开发的朋友问我:"我想在网页上实时显示 AI 的回复,就像 ChatGPT 那样打字效果,怎么实现啊?"今天我就来手把手教大家用 LangChain 的 StreamingCallbackHandler 实现这个效果。

读完这篇教程,你会学到:什么是流式输出、CallbackHandler 是什么、怎么在 LangChain 里用流式输出、以及怎么避开新手常犯的错误。全文代码都可以直接复制运行,建议收藏!

一、流式输出是什么?为什么要用它?

先科普一下概念。普通模式下,你问 AI 一个问题,AI 要把整段回答生成完才返回给你,可能要等好几秒。流式输出(Streaming)则是 AI 边生成边返回,一个字一个字地"吐"出来,前端实时显示,用户体验好很多。

用 HolySheep AI(立即注册)的话,国内直连延迟可以控制在 50ms 以内,流式输出体验非常丝滑,完全感觉不到延迟。

二、环境准备

2.1 安装依赖

首先我们安装必要的包,在命令行运行:

pip install langchain langchain-openai langchain-core

2.2 配置 API Key

工欲善其事,必先利其器。我强烈推荐用 HolySheep AI 的接口,原因很简单:汇率 ¥1=$1 无损(官方是 ¥7.3=$1),对我们国内开发者来说省了超过 85% 的成本!支持微信和支付宝充值,实时到账。

先获取你的 API Key,登录 HolySheep AI 后台,在个人中心可以看到。推荐使用 DeepSeek V3.2 或者 GPT-4o-mini 这类性价比极高的模型。

然后设置环境变量:

import os

设置 HolySheep API Key

os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"

重要!必须设置这个 base_url,否则会请求 OpenAI 官方接口

os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"

三、StreamingCallbackHandler 核心原理

3.1 什么是 CallbackHandler?

CallbackHandler 就像一个"监听器"。LangChain 在执行过程中会触发各种事件(比如开始生成、生成新 token、结束生成),CallbackHandler 可以捕捉这些事件,让我们做一些自定义操作。

我们主要用 on_llm_new_token 这个方法——每当 AI 生成一个新的 token(可以理解为一个字/词),这个方法就会被调用,我们就在这里把新内容打印出来或者更新界面。

3.2 第一个流式输出示例

先来一个最简单的例子,感受一下流式输出是怎么工作的:

from langchain_openai import ChatOpenAI
from langchain_core.callbacks import StreamingStdOutCallbackHandler

创建带流式输出的模型

llm = ChatOpenAI( model="gpt-4o-mini", # 也可以用 deepseek-chat 等模型 temperature=0.7, streaming=True, # 必须设置为 True 才能开启流式输出 callbacks=[StreamingStdOutCallbackHandler()] # 这个处理器会把内容打印到终端 )

简单测试一下

print("AI 正在回答:") response = llm.invoke("请用三句话介绍自己") print("\n回答完毕!")

运行这段代码,你应该能看到 AI 的回答是逐字逐句"打"出来的,而不是一次性显示完整内容。非常神奇对不对!

四、自定义 CallbackHandler

4.1 为什么需要自定义?

StreamingStdOutCallbackHandler 只能打印到终端,实际项目中我们通常需要:

这时候就要自定义 CallbackHandler 了。

4.2 自定义流式处理器

from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.outputs import LLMResult
from typing import Any, Optional, List

class MyStreamHandler(BaseCallbackHandler):
    """自定义流式输出处理器"""
    
    def __init__(self, container, initial_text=""):
        self.container = container  # 前端容器(Streamlit 中用 st.empty())
        self.text = initial_text
        self.token_count = 0
    
    def on_llm_new_token(self, token: str, **kwargs) -> None:
        """每当生成新 token 时调用"""
        self.text += token
        self.token_count += 1
        # 这里你可以替换成任何 UI 更新逻辑
        print(token, end="", flush=True)
    
    def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
        """LLM 调用结束时调用"""
        print(f"\n\n[统计] 共生成 {self.token_count} 个 token")
    
    def get_text(self) -> str:
        """获取完整文本"""
        return self.text


使用自定义处理器

llm = ChatOpenAI( model="gpt-4o-mini", streaming=True, callbacks=[MyStreamHandler(None)] # 传入容器 ) print("开始流式输出测试:") response = llm.invoke("写一首关于春天的诗")

我在实际项目中发现,自定义 Handler 最实用的场景是统计 token 消耗。上面代码里的 on_llm_end 方法可以获取完整的 LLMResult,里面包含 token 用量信息,方便做成本控制。

五、完整 Web 应用示例(Streamlit)

现在来做个真正能用的网页应用。用户输入问题,AI 实时流式回复,就像 ChatGPT 那样:

import streamlit as st
import os
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import BaseCallbackHandler

配置页面

st.set_page_config(page_title="AI 流式对话助手", page_icon="🤖") st.title("🤖 AI 流式对话助手")

侧边栏设置

with st.sidebar: st.header("⚙️ 设置") # 输入 API Key(也可以用环境变量) api_key = st.text_input("HolySheep API Key", type="password") if api_key: os.environ["OPENAI_API_KEY"] = api_key os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1" # 选择模型 model_options = { "GPT-4o Mini": "gpt-4o-mini", "DeepSeek V3.2": "deepseek-chat", "Claude 3.5 Sonnet": "claude-3-5-sonnet-20240620" } selected_model = st.selectbox("选择模型", list(model_options.keys())) # 费用说明 st.markdown("---") st.markdown("**💰 模型价格参考(/百万输出Token)**") st.markdown("- GPT-4o Mini: $8") st.markdown("- DeepSeek V3.2: **$0.42**(强烈推荐!)") st.markdown("- Claude 3.5 Sonnet: $15")

自定义流式 Handler

class StreamHandler(BaseCallbackHandler): def __init__(self, msg): self.msg = msg self.text = "" def on_llm_new_token(self, token, **kwargs): self.text += token self.msg.markdown(self.text + "▌")

初始化聊天历史

if "messages" not in st.session_state: st.session_state.messages = []

显示历史消息

for msg in st.session_state.messages: with st.chat_message(msg["role"]): st.markdown(msg["content"])

聊天输入

if prompt := st.chat_input("问我任何问题..."): # 显示用户消息 with st.chat_message("user"): st.markdown(prompt) st.session_state.messages.append({"role": "user", "content": prompt}) # 生成 AI 回复 with st.chat_message("assistant"): msg = st.empty() stream_handler = StreamHandler(msg) try: llm = ChatOpenAI( model=model_options[selected_model], streaming=True, callbacks=[stream_handler] ) response = llm.invoke(prompt) final_text = stream_handler.text msg.markdown(final_text) # 去掉光标 st.session_state.messages.append({"role": "assistant", "content": final_text}) except Exception as e: st.error(f"出错了: {str(e)}")

底部提示

st.markdown("---") st.markdown("👉 使用 HolySheep API 国内直连延迟 <50ms,支持微信/支付宝充值")

运行命令:streamlit run app.py

部署这个应用后,你就有了一个私人版的 ChatGPT!我用 DeepSeek V3.2 测试,响应速度非常快,而且成本极低——每百万 token 才 $0.42,性价比之王。

六、常见报错排查

在实际开发过程中,新手最容易遇到以下几个问题,我已经把排查方法和解决方案整理好了:

报错1:AuthenticationError: Incorrect API key provided

原因: API Key 填写错误,或者没有设置 OPENAI_API_BASE 导致请求发到了错误的地址。

解决代码:

# 正确配置示例
import os

os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"  # 替换成你的真实 Key
os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"  # 必须是这个地址!

验证配置是否正确

from langchain_openai import ChatOpenAI llm = ChatOpenAI() print(llm.model) # 如果没有报错说明配置正确

报错2:Stream is not iterable 或 TypeError

原因: 创建 ChatOpenAI 时忘记设置 streaming=True,但使用了流式 CallbackHandler。

解决代码:

# ❌ 错误写法
llm = ChatOpenAI(callbacks=[StreamingStdOutCallbackHandler()])

✅ 正确写法

llm = ChatOpenAI( streaming=True, # 必须显式设置为 True callbacks=[StreamingStdOutCallbackHandler()] )

报错3:RateLimitError 或 ConnectionError

原因: 请求被限流或者网络连接问题。如果是国内访问 OpenAI 官方接口,很可能是网络问题。

解决代码:

# 方案1:使用 HolySheep API(国内直连,延迟 <50ms)
os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"

方案2:添加重试机制

from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) def call_with_retry(llm, prompt): return llm.invoke(prompt)

方案3:增加超时时间

llm = ChatOpenAI( timeout=120, # 120秒超时 max_retries=3 )

报错4:AttributeError: 'NoneType' object has no attribute 'xxx'

原因: 自定义 CallbackHandler 中容器对象为 None,或者在异步环境下使用不当。

解决代码:

# 确保容器不为 None
class SafeStreamHandler(BaseCallbackHandler):
    def __init__(self, container=None):
        self.container = container
        self.text = ""
    
    def on_llm_new_token(self, token, **kwargs):
        self.text += token
        if self.container:
            self.container.markdown(self.text + "▌")
        else:
            print(token, end="", flush=True)
    
    def on_llm_end(self, response, **kwargs):
        # 安全获取 token 用量
        if hasattr(response, 'llm_output') and response.llm_output:
            token_usage = response.llm_output.get('token_usage', {})
            print(f"\nToken 消耗: {token_usage}")

七、进阶技巧

7.1 同时支持多个输出目标

有时候你可能需要同时输出到终端和网页,可以创建多个 Handler:

# 同时支持终端和 Streamlit
from langchain_core.callbacks import StreamingStdOutCallbackHandler

llm = ChatOpenAI(
    model="gpt-4o-mini",
    streaming=True,
    callbacks=[
        StreamingStdOutCallbackHandler(),  # 打印到终端
        StreamHandler(msg)  # 更新网页
    ]
)

7.2 异步流式处理

import asyncio
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import AsyncCallbackHandler

class AsyncStreamHandler(AsyncCallbackHandler):
    async def on_llm_new_token(self, token, **kwargs):
        # 可以在这里 await 任何异步操作
        print(token, end="", flush=True)
        await asyncio.sleep(0)  # 让出控制权

async def main():
    llm = ChatOpenAI(
        model="gpt-4o-mini",
        streaming=True,
        callbacks=[AsyncStreamHandler()]
    )
    await llm.ainvoke("介绍一下人工智能的发展历史")

asyncio.run(main())

八、总结

今天我们学了:

用 HolySheep AI 的流式接口,体验非常流畅。国内直连延迟低至 50ms,汇率更是逆天——¥1=$1 比官方省了 85% 以上。DeepSeek V3.2 模型每百万输出 token 才 $0.42,性价比拉满,非常适合做 AI 应用开发。

如果这篇文章对你有帮助,欢迎转发给需要的朋友!有其他问题可以在评论区留言,我会尽量解答。

👉 免费注册 HolySheep AI,获取首月赠额度