支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


前端学AI之LangChain.js入门教程:实现智能对话机器人

发布日期:2025-06-11 20:15:48 浏览次数: 1554
作者:吕小鸣Developer

微信搜一搜,关注“吕小鸣Developer”

推荐语

前端开发者进军AI的捷径:用LangChain.js轻松打造智能对话机器人,结合本地与云端大模型能力。

核心内容:
1. LangChain.js框架的核心架构与模块化设计解析
2. 实战演示:基于Ollama和通义千问搭建对话机器人
3. 完整前后端代码实现与API调用最佳实践

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

作为一名前端开发老程序员,这次给大家更新一个前端学AI的突破口:LangChain.js,希望能有所帮助。

背景

随着大语言模型(LLM)的快速发展,开发者需要高效的工具链来集成模型能力到实际应用中。

LangChain 是一个开源框架,旨在简化基于语言模型的应用程序开发,提供模块化的组件(如模型调用、记忆管理、工具集成等),可以简单类比Java界的Spring框架来理解,Nodejs界的express/chair等。

本文将通过一个示例项目,展示如何用 LangChain.js 实现一个对话机器人,结合 Ollama(本地运行开源模型)和 通义千问(国产大模型API),并提供完整的前后端代码。

LangChain.js 介绍

LainChain结构图:

其中:

  • LainChain:提供底层的核心能力。
  • LainGraph:提供流程编排能力。
  • Integrations:提供扩展和集成能力。
  • LangSmith:提供调试、监控、评测能力。
  • LainGraph Platform:LangChain 的商业化大模型应用发布平台。

LangChain.js 是基于Langchain的 JavaScript/TypeScript 版本,支持在浏览器、Node.js 等环境中快速构建AI应用,除此之外还有Python版本。

LangChain.js 支持多种 LLM 提供商(如 OpenAI、Ollama 等),并提供了灵活的接口,使得开发者可以轻松集成不同的模型和服务,主要包括以下模块包:

  • langchain-core:提供基础抽象和核心运行时机制(聊天模型、向量存储、工具等)的抽象接口和组装方式。
  • langchain:langchain的主包,包含了内置的通用的链(chains)、代理(Agents)、检索策略(retrieval strategies),不包含第三方集成。
  • langchain-community:由LangChain社区维护的第三方集成包,包括 OpenAI、Anthropic 等 LLM,以及向量存储(如 Pinecone)、工具(如 Tavily 搜索)等。

LangChain.js 基础API详解

LangChain 的 API 设计以模块化和链式调用为核心,下面是一些基础 API 的简单介绍:

模型调用(Models)

LangChain 支持三类核心模型接口,满足不同场景需求:

LLM

基础文本生成模型(如 GPT-3.5 ):

  • 核心方法:
const model = new OpenAI({ temperature: 0.7 }); 
await model.call("你好,介绍react"); 

ChatModals

对话式模型(如 GPT-4 ):

  • 核心方法:predictMessages()
const chat = new ChatOpenAI();
await chat.predictMessages([new HumanMessage("你好!")]);

Embeddings

文本向量化模型(用于语义检索、聚类)

  • 核心方法:embedQuery() / embedDocuments()
const embeddings = new OpenAIEmbeddings();
const vec = await embeddings.embedQuery("react技术");

关键参数说明:

  • temperature(0-2):控制生成随机性,值越高结果越多样。
  • maxTokens:限制生成文本的最大长度。
  • streaming:启用流式传输(适合实时聊天场景)。

提示模板(Prompts)

通过模板动态生成提示词,支持结构化输入与输出控制:

基础模板

import { PromptTemplate } from"@langchain/core/prompts";

// 单变量模板
const template = "用{style}风格翻译以下文本:{text}";
const prompt = new PromptTemplate({
  template,
  inputVariables: ["style""text"], 
});

// 使用示例
const formattedPrompt = await prompt.format({
  style: "文言文",
  text: "Hello world",
});
// 输出:"用文言文风格翻译以下文本:Hello world"

Few-shot模板

嵌入示例提升模型表现:

import { FewShotPromptTemplate } from"langchain/prompts";

const examples = [
  { input: "高兴", output: "欣喜若狂" },
  { input: "悲伤", output: "心如刀绞" }
];

const examplePrompt = new PromptTemplate({
  template: "输入:{input}\n输出:{output}",
  inputVariables: ["input""output"],
});

const fewShotPrompt = new FewShotPromptTemplate({
  examples,
  examplePrompt,
  suffix: "输入:{adjective}\n输出:"
  inputVariables: ["adjective"],
});

await fewShotPrompt.format({ adjective: "愤怒" });
/* 输出:
输入:高兴
输出:欣喜若狂

输入:悲伤
输出:心如刀绞

输入:愤怒
输出: 
*/


文件模板加载

从外部文件读取模板:

import { PromptTemplate } from "@langchain/core/prompts";
import fs from "fs";

const template = fs.readFileSync("./prompts/email_template.txt""utf-8");
const prompt = new PromptTemplate({
  template,
  inputVariables: ["userName""product"],
});

链式调用(Chains)(langchain核心)

通过组合多个组件构建复杂工作流,是langchain核心模块:

LLMChain(基础链)

import { LLMChain } from "langchain/chains";

const chain = new LLMChain({
  llm: new OpenAI(),
  prompt: new PromptTemplate({
    template: "生成关于{topic}的{num}条冷知识"
    inputVariables: ["topic""num"]
  }),
});

const res = await chain.call({ topic: "react", num: 3 });
// 输出:模型生成的3条react冷知识

SequentialChain(顺序链)

串联多个链实现分步处理:

import { SequentialChain } from "langchain/chains";

const chain1 = new LLMChain({ ... }); // 生成文章大纲
const chain2 = new LLMChain({ ... }); // 扩展章节内容
const chain3 = new LLMChain({ ... }); // 优化语言风格

const overallChain = new SequentialChain({
  chains: [chain1, chain2, chain3],
  inputVariables: ["title"],
  outputVariables: ["outline""content""final"],
});

const result = await overallChain.call({ title: "前端已死,还有未来" });

TransformChain(转换链)

自定义数据处理逻辑:

import { TransformChain } from "langchain/chains";

const transform = new TransformChain({
  transform: async (inputs) => {
    // 自定义处理逻辑(如文本清洗)
    return { cleaned: inputs.text.replace(/\d+/g"") };
  },
  inputVariables: ["text"],
  outputVariables: ["cleaned"],
});

文档加载器(Document Loaders)

支持从多种来源加载结构化文档,可以用来RAG的知识库录入:

  • 本地文件:加载 文本/PDF/Word 文档。
const loader = new TextLoader("example.txt");
const docs = await loader.load();

const loader2 = new PDFLoader("report.pdf");
const docs2 = await loader2.load();

console.log({ docs });
console.log({ docs2 });
  • 网页内容:抓取指定 URL 的 HTML 内容。
const loader = new CheerioWebBaseLoader("https://exampleurl.com");
const docs = await loader.load();
console.log({ docs });
  • 数据库:从 MySQL/MongoDB 读取数据。
import { createClient } from"@supabase/supabase-js";
import { OpenAIEmbeddings } from"@langchain/openai";
import { SupabaseHybridSearch } from"@langchain/community/retrievers/supabase";

// 初始化 Supabase 客户端
const client = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_PRIVATE_KEY
);

// 创建混合检索器
const retriever = new SupabaseHybridSearch(new OpenAIEmbeddings(), {
  client,
  similarityK: 5,       // 语义搜索返回结果数
  keywordK: 3,           // 关键词搜索返回结果数
  tableName: "documents"// 数据库表名
  similarityQueryName: "match_documents"// 语义搜索函数名
  keywordQueryName: "kw_match_documents"// 关键词搜索函数名
// 高级参数
  reRanker: (results) => {
    // 自定义结果合并策略(如加权分数)
    return results.sort((a, b) => b.score - a.score);
  }
});
  • 云存储:从 AWS S3/GCS 加载文件。
const loader = new S3Loader({
  bucket: "my-document-bucket-123",
  key: "AccountingOverview.pdf",
  s3Config: {
    region: "us-east-1",
    credentials: {
      accessKeyId: "<YourAccessKeyId>",
      secretAccessKey: "<YourSecretAccessKey>",
    },
  },
  unstructuredAPIURL: "<YourUnstructuredAPIURL>",
  unstructuredAPIKey: "<YourUnstructuredAPIKey>",
});
const docs = await loader.load();

文档分块处理

可以用来做embeddings:

import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,   // 单块最大字符数
  chunkOverlap: 50// 块间重叠字符
});

const docs = await loader.load();
const splitDocs = await splitter.splitDocuments(docs);

上下文管理(Memory)

管理对话或任务的上下文状态:

对话记忆

import { BufferMemory } from "langchain/memory";

const memory = new BufferMemory({
  memoryKey: "chat_history"// 存储对话历史的字段名
});

const chain = new ConversationChain({ 
  llm: new ChatOpenAI(),
  memory 
});

// 连续对话
await chain.call({ input: "你好!" });
await chain.call({ input: "刚才我们聊了什么?" }); // 模型能回忆历史

实体记忆

跟踪特定实体信息:

import { EntityMemory } from "langchain/memory";

const memory = new EntityMemory({
  llm: new OpenAI(),
  entities: ["userName""preferences"], // 需要跟踪的实体
});

await memory.saveContext(
  { input: "我叫小明,喜欢研究react技术" }, 
  { output: "已记录您的偏好" }
);

const currentEntities = await memory.loadEntities();
// 输出:{ userName: "小明", preferences: "react技术" }

输出解析(Output Parsers)

将模型返回的文本转换为结构化数据,实用功能:

基础解析器

import { StringOutputParser } from "@langchain/core/output_parsers";

const chain = prompt.pipe(model).pipe(new StringOutputParser());
const result = await chain.invoke({ topic: "react技术" });
// result "React技术是一个用于构建用户界面的JavaScript库,其核心用途包括:1) 组件化开发,2) 虚拟DOM高效更新,3) 支持单页应用(SPA)开发。"

结构化解析,json

import { StructuredOutputParser } from"langchain/output_parsers";

// 定义输出Schema
const parser = StructuredOutputParser.fromZodSchema(
  z.object({
    title: z.string().describe("生成的文章标题"),
    keywords: z.array(z.string()).describe("3-5个关键词"),
    content: z.string().describe("不少于500字的内容")
  })
);

const chain = new LLMChain({
  llm: model,
  prompt: new PromptTemplate({
    template: "根据主题{topic}写一篇文章\n{format_instructions}",
    inputVariables: ["topic"],
    partialVariables: { format_instructions: parser.getFormatInstructions() }
  }),
  outputParser: parser
});

const res = await chain.call({ topic: "react技术" });
/*
{
  title: "React技术:现代前端开发的核心理念",
  keywords: ["组件化", "虚拟DOM", "Hooks", "SPA", "状态管理"],
  content: "React是由Facebook开发的一个用于构建用户界面的JavaScript库...(至少500字)"
}
*/

工具与代理(Tools & Agents)

集成外部API扩展模型能力:

预定义工具

使用langchain内置的工具:

import { Calculator, SerpAPI } from "langchain/tools";

const tools = [
  new Calculator(),       // 数学计算
  new SerpAPI(),          // 实时网络搜索
  new WolframAlphaTool(), // 科学计算
];

自定义工具

自定义开发工具:

import { DynamicTool } from "langchain/tools";

export const weatherTool = new DynamicTool({
  name: "get_weather",
  description: "查询指定城市的天气",
  func: async (city) => {
    const apiUrl = `https://api.weather.com/${city}`;
    return await fetch(apiUrl).then(res => res.json());
  }
});

代理执行:

import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { weatherTool } from 'weatherTool
const executor = await initializeAgentExecutorWithOptions(
  tools:[weatherTool], 
  new ChatOpenAI({ temperature: 0 }), 
  { agentType: "structured-chat-zero-shot-react-description" }
);

const res = await executor.invoke({
  input: "上海当前温度是多少?比纽约高多少摄氏度?"
});
// 模型将自动调用天气查询工具和计算器

structured-chat-zero-shot-react-description是 LangChain 框架中一种 结构化对话代理(Structured Chat Agent) 的类型,专为让大模型(如 GPT-4)无需示例学习(Zero-Shot) 即可调用外部工具链而设计。

检索增强(Retrieval)RAG

结合向量数据库实现知识增强:

import { MemoryVectorStore } from"langchain/vectorstores/memory";
import { OpenAIEmbeddings } from"@langchain/openai";

// 1. 加载文档并向量化
const vectorStore = await MemoryVectorStore.fromDocuments(
  splitDocs, 
new OpenAIEmbeddings()
);

// 2. 语义检索
const results = await vectorStore.similaritySearch("神经网络的发展历史"3);

// 3. 将检索结果注入提示词
const chain = createRetrievalChain({
  retriever: vectorStore.asRetriever(),
  combineDocsChain: new LLMChain(...)
});

示例:对话机器人

后端实现

安装依赖

安装langchainjs相关模块:

"@langchain/community""^0.3.40",
"@langchain/core""^0.3.44",
"@langchain/ollama""^0.2.0",
"langchain""^0.3.21",

由于需要提供http服务托管前端页面,需要安装express:

"@types/express""^5.0.1",
"express""^5.1.0",

调用本地模型

一个简单的通过ollama调用本地模型的例子:

import { Ollama,ChatOllama } from"@langchain/ollama"
asyncfunction main(): Promise<void{
const ollamaLlm = new Ollama({
    baseUrl: "http://127.0.0.1:11434",
    model: "DeepSeek-r1:7b",
  });


const stream = await ollamaLlm.stream(
    `你谁,擅长什么?`
  );

forawait (const chunk of stream) {
    process.stdout.write(chunk);
  }

}
main().catch(error => {
console.error("程序执行出错:");
console.error(error);
});

结合上下文,进行连续调用:

import { Ollama,ChatOllama } from"@langchain/ollama"
import { SystemMessage, HumanMessage } from"@langchain/core/messages"
asyncfunction mainChat(): Promise<void{

const chatModel = new ChatOllama({
    baseUrl: "http://127.0.0.1:11434",
    model: "deepseek-r1:7b",
  });

const stream = await chatModel.stream([
    new SystemMessage("角色:一个前端技术专家 擅长:擅长回答前端技术相关的问题。"),
    new HumanMessage("你谁,擅长什么?"), 
  ]);

forawait (const chunk of stream) {
    process.stdout.write(chunk.text);
  }

}
main().catch(error => {
console.error("程序执行出错:");
console.error(error);
});

在Langchain中,Ollama,ChatOllama分别是对ollama工具的封装,Ollama用于基础文本生成,ChatOllama专为对话设计,支持多消息类型和上下文管理。需要将这些区别清晰地传达给用户。

调用外部模型-通义

  • 前往 阿里云通义千问控制台
  • 创建API Key(需实名认证)
  • 获取模型服务地址(例如 qwen-turbo 或 qwen-plus

根据文档,简单通过fetch实现对通义的调用:

/**
 * 流式响应处理函数
 * @param apiEndpoint - 通义千问API端点地址
 * @param apiKey - API认证密钥
 * @param modelName - 使用的大模型名称(例:qwen-max)
 * @param prompt - 用户输入的提示词
 * @param onCallback - 每收到一个token时的回调函数
 * 
 * 实现流程:
 * 1. 发送携带prompt的POST请求到API端点
 * 2. 处理流式响应数据
 * 3. 解析并提取每个数据块中的文本内容
 * 4. 通过回调函数实时返回生成的文本
 */

exportconst streamResponseChunks = async ({
    apiEndpoint,
    apiKey,
    modelName,
    prompt,
    onCallback
}: {
    apiEndpoint: string,
    apiKey: string,
    modelName: string,
    prompt: string,
    onCallback: (text: string) =>void
}) => {
    // 发送POST请求到通义千问API
    const response = await fetch(apiEndpoint, {
        method: "POST",
        headers: {
            "Content-Type""application/json",
            "Authorization"`Bearer ${apiKey}`// 使用Bearer Token认证
        },
        body: JSON.stringify({
            model: modelName,
            messages: [{  // 构造消息体
                role: "user",
                content: prompt
            }],
            stream: true// 启用流式传输
        })
    });

    // 处理HTTP错误响应
    if (!response.ok || !response.body) {
        const errorResponse = await response.json();
        thrownewError(JSON.stringify(errorResponse));
    }

    // 创建流式读取器
    const reader = response.body.getReader();
    const decoder = new TextDecoder();  // 用于解码二进制流数据

    // 持续读取流数据
    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() === ""continue;  // 跳过空行
            try {
                // 处理流结束标记
                if (line === 'data: [DONE]') {
                    console.log('流式响应结束');
                    continue;
                }

                // 验证数据格式
                if (!line.startsWith('data: ')) {
                    console.log('跳过非数据行:', line);
                    continue;
                }

                // 解析JSON数据
                const jsonStr = line.replace(/^data: /'');
                const data = JSON.parse(jsonStr);

                // 提取生成的文本内容
                if (data.choices?.[0]?.delta?.content) {
                    const text = data.choices[0].delta.content;
                    onCallback(text);  // 触发回调函数
                }
            } catch (e) {
                console.log('解析错误:', e);
                console.log('错误数据:', line);
            }
        }
    }
};

上面代码根据注释理解即可,主要是发起http调用,流式返回结果。

基于BaseLLM/BaseModal封装

上面代码中,只是简单使用fetch来调用通义模型提供的接口,但是如果要使用到langchain,则需要遵循BaseLLM/BaseModal来将上面的逻辑进行封装,以便符合langchain的规范:

import { BaseLLM, BaseLLMParams } from"@langchain/core/language_models/llms";
import { CallbackManagerForLLMRun } from"@langchain/core/callbacks/manager";
import { GenerationChunk } from"@langchain/core/outputs";
import { LLMResult, Generation } from"@langchain/core/outputs";

interface CustomLLMParams extends BaseLLMParams {
  apiKey: string;
  modelName: string;
  apiEndpoint: string;
}

exportclass CustomLLM extends BaseLLM {
  apiKey: string;
  modelName: string;
  apiEndpoint: string;

constructor(params: CustomLLMParams) {
    super(params);
    this.apiKey = params.apiKey;
    this.modelName = params.modelName;
    this.apiEndpoint = params.apiEndpoint;
  }

  _llmType(): string {
    return"tongyi";
  }


async *_streamResponseChunks(
    prompt: string,
    options: this["ParsedCallOptions"],
    runManager?: CallbackManagerForLLMRun
  ): AsyncGenerator<GenerationChunk> {
    const response = await fetch(this.apiEndpoint, {
      method: "POST",
      headers: {
        "Content-Type""application/json",
        "Authorization"`Bearer ${this.apiKey}`
      },
      body: JSON.stringify({
        model: this.modelName,
        messages: [
          {
            role: "user",
            content: prompt
          }
        ],
        stream: true
      })
    });

    if (!response.ok || !response.body) {
      const errorResponse = await response.json();
      thrownewError(JSON.stringify(errorResponse));
    }

    const reader = response.body.getReader();
    const decoder = new TextDecoder();

    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() === ""continue;
        try {

          // 检查是否是结束标记
                    if (line === 'data: [DONE]') {
                        console.log('流式响应结束');
                        continue;
                    }
                    
                    // 确保数据以 "data: " 开头
                    if (!line.startsWith('data: ')) {
                        console.log('跳过非数据行:', line);
                        continue;
                    }
                    
                    const jsonStr = line.replace(/^data: /'');
                    
                    const data = JSON.parse(jsonStr);
                    if (data.choices?.[0]?.delta?.content) {
                        const text = data.choices[0].delta.content;
                        const generationChunk = new GenerationChunk({
                            text: text,
                            generationInfo: {}
                        });
                        yield generationChunk;
                        await runManager?.handleLLMNewToken(text);
                    }
                } catch (e) {
                    console.log('解析错误:', e);
                    console.log('错误数据:', line);
                }
            }
        }
    }

    async _generate(
        prompts: string[],
        options: this["ParsedCallOptions"],
        runManager?: CallbackManagerForLLMRun
    ): Promise<LLMResult> {
        const prompt = prompts[0];
        const chunks: Generation[] = [];
        
        forawait (const chunk of this._streamResponseChunks(prompt, options, runManager)) {
            const text = chunk.text;
            // 实时输出到控制台
            process.stdout.write(text);
            chunks.push({ text });
        }
        // 输出换行
        process.stdout.write('\n');
        
        return {
            generations: [chunks]
        };
    }

    async streamResponse(
        prompt: string,
        options: this["ParsedCallOptions"],
        onToken: (token: string) =>void
    ): Promise<void> {
        forawait (const chunk of this._streamResponseChunks(prompt, options)) {
            onToken(chunk.text);
        }
    }

上面代码中,我们封装了一个CustomLLM类继承自BaseLLM,然后将参数作为构造方法的参数传入,同时将之前的fetch逻辑移到了 _streamResponseChunks 这个方法中,然后实现了一个 _generate 方法。

其中 _streamResponseChunks 和 _generate是langchain使用中的固定需要实现的方法,这样有助于在后续复杂的链式中简单调用,如果不理解记着这属于langchain的规范就行了,就像mvc框架需要有controller,service一样。

例如调用本地模型时,我们可以直接使用Ollama的ChatModal,那是由于langchain社区对Ollama统一进行了封装,如果社区没有,就可以自己继承BaseLLM/BaseModal来实现。

除了BaseLLM外,langchain还提供了BaseChatModal,其中:

  1. 接口设计差异 :
  • BaseLLM :面向纯文本输入( string[] )
  • BaseChatModel :面向结构化消息( BaseMessage[] )
  1. 使用场景 :
  • BaseLLM :适用于单轮问答场景
  • BaseChatModel :适用于多轮对话场景

实例代码使用的是BaseLLM,当然也可以换成BaseChatModel。

express服务:

下面是express实现一个本地http服务,流式返回,3000端口:

import { Router } from'express';
import express from'express';
import { streamResponseChunks } from'./tongyi-chat.js';

const router = Router();

router.get('/streamChat'(req, res) => {
    // 设置 SSE 头
    res.setHeader('Content-Type''text/event-stream');
    res.setHeader('Cache-Control''no-cache');
    res.setHeader('Connection''keep-alive');

    const { prompt } = req.query;
    if (!prompt || typeof prompt !== 'string') {
        res.write(`data: ${JSON.stringify({ error: '缺少有效的prompt参数' })}\n\n`);
        res.end();
        return;
    }
    
    // 处理客户端断开连接
    req.on('close'() => {
        res.end();
    });
    
    // 开始流式响应
    // 调用 streamResponseChunks
});


const app = express();
const port = 3000;

// 中间件
app.use(express.json());
app.use(express.static('public'));

// 路由
app.use('/api', router);

// 启动服务器
app.listen(port, () => {
    console.log(`服务器运行在 http://localhost:${port}`);
}); 

前端实现

前端页面比较简单,只有一个输入框和问题/回答列表,直接用原生实现,不用框架,下面是前端页面的JavaScript代码:

<script>

  const chatbox = document.getElementById('chatbox');
const userInput = document.getElementById('userInput');

function appendMessage(text, isUser, element{
    if (element) {
      element.innerHTML += text;  // 使用innerHTML
      return element;
    }
    const div = document.createElement('div');
    div.className = `message ${isUser ? 'user' : 'bot'}`;
    div.innerHTML = text; // 用户消息保持纯文本
    chatbox.appendChild(div);
    chatbox.scrollTop = chatbox.scrollHeight;
    return div;
  }

function sendRequest({
    const input = userInput.value;
    userInput.value = ''// 清空输入框
    appendMessage(input, true);

    let botMessage = null// 用于跟踪当前bot消息元素
    const eventSource = new EventSource(`/api/streamChat?prompt=${encodeURIComponent(input)}`);

    eventSource.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        if (data.text) {
          // 首次创建bot消息元素,后续持续更新
          botMessage = appendMessage(data.text, false, botMessage);
        } elseif (data.error) {
          appendMessage(`错误: ${data.error}`false);
          eventSource.close();
        }
      } catch (error) {

      }
    };

    eventSource.onerror = () => {
      eventSource.close();
    };
  }

</script>

需要注意的是,创建EventSource事件来接受后端的流式返回,同时前端在append的时候要注意累加然后整体替换,详细看appendMessage这个方法。

实现效果

运行:

npm run server

效果截图

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询