网上针对 Angthing llm 的流式传输教程比较少,几乎都是 Next.js 用 fetch-event-source 连接 Openai 的api
经过我的摸索,终于不用 fetch-event-source 完成了对接本地部署的大模型,特此记录共勉
这里说一下我的需求,如果你想直接看解决方案,请跳过此部分
我使用 Lm Studio 做的 deepseek 本地大模型部署,使用 Angthing llm 进行 RAG 检索
最后需要调用 Angthing llm 的 api 完成对话
首先,为了预防跨域问题,我们把请求封装到后端调用
在 app/api/chat 文件夹下新建 route.js 文件
使用 ReadableStream 来处理流式响应,代码如下
import { NextResponse } from "next/server";
export async function POST(req) {// 构建请求体const apiKey = "HDJF33W-0PN9N2D-H8JCXM9-M92E6MY"; // 这里换成你的api keyconst workspace = "yuwen"; // 这里换成你的工作区名称const ip = "http://localhost:3001" // 这里换成你的端口const url = `${ip}/api/v1/workspace/${workspace}/stream-chat`;const headers = {"Authorization": `Bearer ${apiKey}`,"Content-Type": "application/json",};const body = await req.json();const response = await fetch(url, {method: "POST",headers: headers,body: JSON.stringify(body),});// 检查响应状态if (!response.ok) {throw new Error(`Server responded with status ${response.status}`);}// 创建一个 ReadableStream 来处理流式响应const stream = new ReadableStream({start(controller) {const reader = response.body.getReader();const decoder = new TextDecoder();const read = () => {reader.read().then(({ done, value }) => {if (done) {controller.close();return;}const chunk = decoder.decode(value, { stream: true });controller.enqueue(chunk);read();});};read();},});return new NextResponse(stream, {headers: {"Content-Type": "text/event-stream","Cache-Control": "no-cache",},});
}
在 app 文件夹下的 page.tsx 中添加点击发送按钮实现对话的功能(这里我默认你已经实现了和ai对话的前端)
流式传输的数据如下所示
我们需要对每行的数据去掉前面的“data:”,让它变成 json 格式,方便提取 textResponse 中的文本
具体代码如下,实现了点击按钮提交用户输入的函数,关键是中间部分对流式数据的读取和处理
const [studentInput, setStudentInput] = useState("")const handleStudentSubmit = async (e: React.FormEvent) => {e.preventDefault();if (studentInput.trim()) {const newUserMessage = { role: "user", content: studentInput };const userInput = studentInput;setStudentInput("");const newStuMessage = { role: "assistant", content: "正在思考中..." };setStudentMessages([...studentMessages,newUserMessage, newStuMessage]);try {const response = await fetch("/api/chat", {method: "POST",headers: {"Content-Type": "application/json",},body: JSON.stringify({ message: userInput, mode: "chat" }), });// 检查 response.body 是否为 nullif (response.body === null) {throw new Error("响应体为空,无法读取数据");}const reader = response.body.getReader();const decoder = new TextDecoder();// 标记是否是第一次读取数据var mark = false;while (true) {const { done, value } = await reader.read();if (done) break;const chunk = decoder.decode(value);// 按换行符分割字符串const lines = chunk.split("\n");// 遍历每一行for (const line of lines) {if (line.trim()) {// 去除每行开头的 "data: "const jsonStr = line.replace("data: ", "");try {// 解析 JSON 字符串const data = JSON.parse(jsonStr);// 提取 textResponse 字段的值if (data.textResponse) {if(mark){setStudentMessages((prevMessages) => {return [...prevMessages.slice(0, -1),{role: "assistant",content:prevMessages[prevMessages.length - 1].content +data.textResponse,},];}); }else{mark = true;setStudentMessages((prevMessages) => {return [...prevMessages.slice(0, -1),{role: "assistant",content: data.textResponse,},];});}}} catch (error) {console.error("解析 JSON 时出错:", error);}}}}} catch (error) {console.error("Error:", error);}}};
现在就已经实现了流式传输,而且没有跨域问题
还有可以优化的点,就是前端要支持 markdown 的文本格式,这样更美观一些
你可以使用 react-markdown
库,需要安装这个库:
npm install react-markdown remark-gfm
remark-gfm
是一个插件,用于支持 GitHub Flavored Markdown(GFM),包含表格、任务列表等扩展语法
然后,在你的 page.tsx
文件中引入并使用 react-markdown
// ... existing code ...
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
// ... existing code ...{studentMessages.map((message, index) => (<div key={index} className={`flex ${message.role === "user" ? "justify-end" : "justify-start"}`}><divclassName={`max-w-[80%] rounded-lg p-3 ${message.role === "user" ? "bg-primary text-primary-foreground" : "bg-background"}`}>{/* 修改为使用 ReactMarkdown 解析 Markdown 文本 */}<ReactMarkdown remarkPlugins={[remarkGfm]}>{message.content}</ReactMarkdown></div></div>))}
// ... existing code ...
其实就是加了一个 <ReactMarkdown> 包装
最后的效果如下
我使用的模型是 deepseek-r1-distill-qwen-14b@q5_k_m ,显存 12G ,使用 Lm Studio 本地部署的
特别夸一下:Angthing llm 的 RAG 确实好用,很赞!!!!
最后大家有什么不懂的欢迎私信我,我看到会解答的,就这样啦!