为什么 AI 回复要一边生成,一边显示?
Streaming Chat让 AI 回复一段一段出现,并持续给出清楚的生成反馈。
预览只模拟本地文本流,不调用模型,也不会发送消息。
"use client";
import { useEffect, useRef } from "react";
export type StreamingChatStatus =
| "idle"
| "waiting"
| "streaming"
| "complete"
| "error";
export type StreamingChatStateMeta = {
label: string;
tone: "neutral" | "active" | "success" | "error";
};
export type StreamingChatProps = {
assistantName?: string;
prompt: string;
response: string;
status: StreamingChatStatus;
visibleCharacters?: number;
};
export function getStreamingChatStateMeta(
status: StreamingChatStatus,
): StreamingChatStateMeta {
const states: Record<StreamingChatStatus, StreamingChatStateMeta> = {
complete: { label: "生成完成", tone: "success" },
error: { label: "生成失败", tone: "error" },
idle: { label: "等待发送", tone: "neutral" },
streaming: { label: "正在生成", tone: "active" },
waiting: { label: "等待回复", tone: "active" },
};
return states[status];
}
export function getVisibleStreamingText(text: string, visibleCharacters: number) {
return text.slice(0, Math.max(0, visibleCharacters));
}
export function StreamingChat({
assistantName = "AI 助手",
prompt,
response,
status,
visibleCharacters = response.length,
}: StreamingChatProps) {
const endRef = useRef<HTMLDivElement>(null);
const state = getStreamingChatStateMeta(status);
const visibleResponse = getVisibleStreamingText(response, visibleCharacters);
useEffect(() => {
endRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
}, [status, visibleResponse]);
return (
<section className="streaming-chat" data-status={status}>
<div className="streaming-chat-header">
<div>
<span aria-hidden="true" className="streaming-chat-avatar">AI</span>
<strong>{assistantName}</strong>
</div>
<span className="streaming-chat-state" data-tone={state.tone}>
<i aria-hidden="true" />
<span aria-live="polite">{state.label}</span>
</span>
</div>
<div className="streaming-chat-transcript">
<article className="streaming-chat-message is-user">
<small>你</small>
<p>{prompt}</p>
</article>
{status !== "idle" ? (
<article className="streaming-chat-message is-assistant">
<small>{assistantName}</small>
{status === "waiting" ? (
<span aria-label="正在等待回复" className="streaming-chat-dots">
<i />
<i />
<i />
</span>
) : (
<p aria-live="polite">
{visibleResponse}
{status === "streaming" ? (
<span aria-hidden="true" className="streaming-chat-cursor" />
) : null}
</p>
)}
</article>
) : null}
<div ref={endRef} />
</div>
</section>
);
}
AI 生成完整答案需要时间,如果界面持续空白,用户无法判断请求是否已经响应或还要等待多久。
操作上方真实 Demo,确认状态和交互符合产品需要。
只有真实存在的源码、Prompt 和参数文件才会出现在代码面板。
保留许可证,并根据自己的设计系统调整参数和样式。