Langchain.js| Vue + RAG 实战[特殊字符]| 限制LLM只回答私有数据

Ollama 可以在 Windows 上运行了 阅读详情

前言

学习了 Memory 机制 , 回顾所学 RAG , 总结起来还是下面这张图 ,

在这里插入图片描述

现在可以初步实现一个小型的 ,基于 RAG 的全栈项目 , 由于我在家无聊 , 和上小学的妹妹一起玩耍🤡👈 , 突发奇想做了一个只能根据知识库回答的 AI 助手 我做了一下限制 :

以下是知识库中跟用户回答相关的内容: {context}

你是一个把小学所有故事都背完了的小学生,精通根据故事原文详细解释和回答问题,你在回答时会引用知识库中的作品原文。 并且回答时仅根据原文,尽可能回答用户问题, 请仔细阅读知识库,时间很充足,你必须做到精准把握知识库中的内容 , 之后根据下面规则

1.如果用户的问题${question}与{context}有关,但是知识库中没有相关的内容,你就回答“原文中没有相关内容,我是小学生,知识有限”。

2.如果用户的问题${question}与{context}无关,但是知识库中没有相关的内容,你就回答“我是小学生,知识有限,你的问题超出了我的想象”。

3.如果用户的问题 q u e s t i o n 与 c o n t e x t 有关 , 并且知识库中有相关的内容,根据用户问题 {question}与{context}有关,并且知识库中有相关的内容,根据用户问题 question与context有关,并且知识库中有相关的内容,根据用户问题{question}引用{context}回答。

所以出现以下“我是小学生,知识有限,你的问题超出了我的想象”回复

在这里插入图片描述

上述的"只能"看起来很鸡肋 , 为什么要限制呢 ? AI 助手越知识渊博不是跟好吗 ?

这个场景下 , 确实很鸡肋 , 我只是想以该场景为载体 , 将 RAG 理论初步变为现实 .

再实际生产中 , 我们有必要对 LLM 进行限制 , 比如不能让 LLM 说出令人“道德沦丧”的话 ;基于公司私有数据回答问题 (由于 LLM 不可能对私有数据训练 ,问它不知道的东西 ,就会胡言乱语 ,即出现幻觉) 。。。

好吧 ,开始实战如下效果 ~

在这里插入图片描述

定位

项目主要实现 RAG 私有数据问答, 只回答我提供的数据 , 这是项目的核心 , 重点在于如何实现 RAG 流程 。

思路

开发时先写后端 , 在写前端 , 而我的文章从前端到后端展现

前端

  1. 切图
  2. 数据驱动
  3. 创建支持EventSource对象SSE 实现流式响应

package.json

html体验AI代码助手代码解读复制代码{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.7.9",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.2.1",
    "vite": "^6.0.5"
  }
}

后端

基于 node.js 、express、Langchain.js 实现

  1. 文档加载 、切割、向量化、持久化
  2. 写 prompt 、检索、Outputparser、chain
  3. 解决跨域问题、实现 SSE 流式输出

package.json

html体验AI代码助手代码解读复制代码{
  "name": "test-app-node",
  "private": true,
  "version": "0.0.0",
  "scripts": {
    "prepare-data": "node ./rag/prepare-data.mjs",
    "rag-2": "node ./rag/index.mjs",
    "rag-server": "node ./server.mjs",
    "rag-client": "node ./client.mjs"
  },
  "type": "module",
  "dependencies": {
    "axios": "^1.7.9",
    "cors": "^2.8.5",
    "dotenv": "^16.4.7",
    "express": "^4.21.2",
    "faiss-node": "^0.5.1",
    "langchain": "^0.1.30",
    "multer": "^1.4.5-lts.1"
  },
  "main": "index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""
}

实战

切图

在这里插入图片描述

  1. App 中开始切图(只有一个页面🤡) :

创建一个容器

html体验AI代码助手代码解读复制代码<template>
  <div class="chat-container">

  </div>
</template>

切头部

html体验AI代码助手代码解读复制代码<!-- 聊天头部 -->
<div class="chat-header">
  <h1>小学故事🤡👈</h1>
</div>

切聊天区

html体验AI代码助手代码解读复制代码<!-- 聊天消息显示区域 -->
<div class="chat-messages" ref="messageContainer">
  <!-- 循环显示每条消息 -->
  <div v-for="(message, index) in messages" :key="index" :class="['message', message.type]">
    <div class="message-content">
      <!-- 显示消息类型和内容 -->
      <strong>{{ message.type === 'human' ? '我' : 'AI' }}:</strong>
      <p>{{ message.content }}</p>
    </div>
  </div>
  <!-- 如果正在流式传输,则显示当前AI响应 -->
  <div v-if="streaming" class="message ai">
    <div class="message-content">
      <strong>AI:</strong>
      <p>{{ currentResponse }}</p>
    </div>
  </div>
</div>

切表单

html体验AI代码助手代码解读复制代码<!-- 用户发送问题的输入表单 -->
<div class="chat-input">
  <form @submit.prevent="sendQuestion" class="input-form">
    <!-- 用户问题的输入框 -->
    <input v-model="question" placeholder="请输入你的问题..." :disabled="streaming" required />
    <!-- 提交按钮 -->
    <button type="submit" :disabled="streaming">
      {{ streaming ? '等待回答中...' : '发送' }}
    </button>
  </form>
</div>
  1. 添加样式
javascript体验AI代码助手代码解读复制代码.chat-container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 20px;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(to right, #87ceeb, #ffffff); /* 天蓝色背景 */
  border-radius: 10px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.chat-header {
  text-align: center;
  padding: 20px 0;
  color: #333;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  background: #f5f5f5;
  border-radius: 10px;
  margin-bottom: 20px;
}

.message {
  margin-bottom: 20px;
  padding: 10px;
  border-radius: 10px;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.message.human {
  background: #e3f2fd;
  margin-left: 20%;
}

.message.ai {
  background: #d1c4e9;
  margin-right: 20%;
  border: 1px solid #b39ddb;
}

.message-content {
  word-wrap: break-word;
}

.message-content strong {
  display: block;
  margin-bottom: 5px;
}

.message-content p {
  margin: 0;
}

.chat-input {
  padding: 20px 0;
}

.input-form {
  display: flex;
  align-items: center;
  gap: 10px;
}

input {
  flex: 1;
  padding: 15px; /* 增加输入框高度 */
  border: 1px solid #ddd;
  border-radius: 5px;
  font-size: 16px;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.upload-icon {
  cursor: pointer;
  font-size: 24px;
  margin-left: -30px;
}

input[type="file"] {
  display: none;
}

button {
  padding: 15px 20px; /* 增加按钮高度 */
  background: #1976d2;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 16px;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

button:disabled {
  background: #ccc;
  cursor: not-allowed;
}

button:hover:not(:disabled) {
  background: #1565c0;
}

.progress-bar {
  width: 100%;
  background-color: #f3f3f3;
  border-radius: 5px;
  overflow: hidden;
  margin-top: 10px;
}

.progress {
  height: 10px;
  background-color: #4caf50;
  width: 0;
  transition: width 0.4s ease;
}

.upload-message {
  margin-top: 10px;
  font-size: 14px;
  color: #333;
  text-align: center;
}

数据驱动

使用 vue 3 组合式 api

javascript体验AI代码助手代码解读复制代码<script setup>

</script>
  1. 定义响应式数据
javascript体验AI代码助手代码解读复制代码import { ref } from 'vue';

// 用于管理状态的响应式引用
const question = ref(''); // 用户的问题
const messages = ref([]); // 存储聊天消息的数组
const streaming = ref(false); // 标志AI是否正在流式传输响应
const currentResponse = ref(''); // 当前AI响应的流式传输内容
const sessionId = 'chat-' + Date.now(); // 聊天的唯一会话ID
const messageContainer = ref(null); // 消息容器的引用,用于滚动

  1. 实现滚动消息容器到底部的函数
typescript体验AI代码助手代码解读复制代码// 滚动消息容器到底部的函数
const scrollToBottom = () => {
    const container = messageContainer.value;
    container.scrollTop = container.scrollHeight;
};

  1. 向服务器发送问题,并流式响应
typescript体验AI代码助手代码解读复制代码
// 向服务器发送问题的函数
const sendQuestion = () => {
    // 检查问题是否为空
    if (!question.value.trim()) return;

    // 将用户的问题添加到消息数组中
    messages.value.push({
        type: 'human',
        content: question.value
    });

    // 设置流式传输为true并清除当前响应
    streaming.value = true;
    currentResponse.value = '';

    // 创建一个新的EventSource用于服务器发送事件
    const eventSource = new EventSource(`http://localhost:8080/sse?question=${encodeURIComponent(question.value)}&session_id=${sessionId}`);

    // 在接收到数据时将其附加到currentResponse
    eventSource.onmessage = (event) => {
        currentResponse.value += event.data;
    };

    // 处理接收SSE时的错误
    eventSource.onerror = () => {
        console.error('Error receiving SSE');
        messages.value.push({
            type: 'ai',
            content: '抱歉,发生错误,请稍后重试。'
        });
        streaming.value = false;
        eventSource.close();
    };

    // 处理AI响应结束
    eventSource.addEventListener('end', () => {
        // 将AI的响应添加到消息数组中
        messages.value.push({
            type: 'ai',
            content: currentResponse.value
        });
        // 重置流式传输和问题状态
        streaming.value = false;
        question.value = '';
        currentResponse.value = '';
        // 滚动到消息容器的底部
        scrollToBottom();
        eventSource.close();
    });
};

再上述函数中 ,创建EventSource 对象实现 SSE 流式返回文字(即一个字一个字蹦出来)

处理文档

实现思路

在这里插入图片描述

我使用 Markdown 文档 ,取名 gushi.md

在这里插入图片描述

现在使用 LangChain 库对文本文件进行处理,将其分割成文档块,生成向量表示,并使用 Faiss 向量存储库将这些向量保存到本地的功能。具体步骤如下:

  1. 加载文本文件。
  2. 将文本分割成较小的文档块。
  3. 使用 OpenAI 的嵌入模型将文档块转换为向量。
  4. 将这些向量存储到 Faiss 向量存储库中。
  5. 把 Faiss 向量存储库保存到本地文件系统。
1. 导入必要的模块
javascript体验AI代码助手代码解读复制代码import { TextLoader } from "langchain/document_loaders/fs/text";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import "dotenv/config";
import { FaissStore } from "@langchain/community/vectorstores/faiss";
import { OpenAIEmbeddings } from "@langchain/openai";
import path from "path";
import { fileURLToPath } from 'url';
import { dirname } from 'path';

  • TextLoader:用于从文件系统中加载文本文件。
  • RecursiveCharacterTextSplitter:用于将文本分割成较小的文档块。
  • dotenv/config:加载环境变量,通常用于存储 API 密钥等敏感信息。
  • FaissStore:Faiss 是一种高效的向量存储和搜索库,这里用于存储文档的向量表示。
  • OpenAIEmbeddings:使用 OpenAI 的嵌入模型将文本转换为向量。
  • path、fileURLToPath 和 dirname:用于处理文件路径,确保在不同环境中都能正确定位文件。
2. 获取当前文件所在目录
javascript体验AI代码助手代码解读复制代码const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

在 ES 模块中,__filename 和 __dirname 不是全局变量,需要手动获取。

3. 定义异步函数 run
javascript体验AI代码助手代码解读复制代码const run = async () => {
  const baseDir = __dirname;

  const loader = new TextLoader(path.join(baseDir, "./data/gushi.md"));
  const docs = await loader.load();

  • baseDir:当前文件所在目录。
  • TextLoader:加载 ./data/gushi.md 文件内容。
  • loader.load():异步加载文件内容并返回文档对象。
4. 分割文档
javascript体验AI代码助手代码解读复制代码const splitter = new RecursiveCharacterTextSplitter("markdown",{
    chunkSize: 390,
    chunkOverlap: 30,
  });

  const splitDocs = await splitter.splitDocuments(docs);

  • RecursiveCharacterTextSplitter:以 Markdown 格式将文档分割成块,每个块大小为 390 个字符,块之间有 30 个字符的重叠。
  • splitter.splitDocuments(docs):异步分割文档并返回分割后的文档块数组。

chunkSize 、chunkOverlap 这样设置的原因其实是我后面测出来的 , 因为文档切割会影响向量化 , 进而影响切割检索 ,最终影响回复的质量

于是我使用ChunkViz v0.1

大体可以保证每一个文件块内容较为独立 , 实践证明 : 这样有利于解决 ,需要检索全文的问题 , 比如 : 你会什么故事 ? 这就需要检索全局

在这里插入图片描述

5. 生成向量并存储
javascript体验AI代码助手代码解读复制代码const embeddings = new OpenAIEmbeddings();
  const vectorStore = await FaissStore.fromDocuments(splitDocs, embeddings);

  • OpenAIEmbeddings:使用 OpenAI 的嵌入模型将文档块转换为向量。
  • FaissStore.fromDocuments(splitDocs, embeddings):根据分割后的文档块和嵌入模型创建 Faiss 向量存储库。
6. 保存向量存储库
javascript体验AI代码助手代码解读复制代码await vectorStore.save(path.join(baseDir, "./db/gushi"));
};

5,6 步骤后实现向量持久化

在这里插入图片描述

将 Faiss 向量存储库保存到 ./db/gushi 目录。

7. 调用 run 函数
javascript体验AI代码助手代码解读复制代码run();

检索数据

实现的思路大体如下 , 再这个基础上 ,我们还实现了 Memory 机制

在这里插入图片描述

我么主要的思路是构建两条**主要 **chain 来实现

  • 一条是 chain
    • 借助 llm 负责重塑用户提问 , 使用gpt-3.5-turbo 节约成本
  • 另一条也是 chain🤡
    • 设置 prompt , 从向量数据库检索数据 、结合 Memory 上下文实现 RAG
    • 再这条 chain 中 , 创建多条 chain

llm 重塑用户提问

javascript体验AI代码助手代码解读复制代码
// llm 重塑用户提问
async function getRephraseChain() {
  const rephraseChainPrompt = ChatPromptTemplate.fromMessages([
    [
      "system",
      "给定以下对话和一个后续问题,请将后续问题重述为一个独立的问题。请注意,重述的问题应该包含足够的信息,使得没有看过对话历史的人也能理解。",
    ],
    new MessagesPlaceholder("history"),
    ["human", "将以下问题重述为一个独立的问题:\n{question}"],
  ]);


 const rephraseChain = RunnableSequence.from([
    rephraseChainPrompt,
    new ChatOpenAI({
      temperature: 0.1,
      model: "gpt-3.5-turbo"
    }),
    new StringOutputParser(),
  ]);

  return rephraseChain;
}

RAG chain

javascript体验AI代码助手代码解读复制代码// 加载向量库
async function loadVectorStore() {
  const directory = "./db/gushi";

  const embeddings = new OpenAIEmbeddings();
  const vectorStore = await FaissStore.load(directory, embeddings);

  return vectorStore;
}

export async function getRagChain() {
  const vectorStore = await loadVectorStore();
  const retriever = vectorStore.asRetriever(2);

  const convertDocsToString = (documents) => {
    return documents.map((document) => document.pageContent).join("\n");
  };

  const contextRetrieverChain = RunnableSequence.from([
    (input) => input.standalone_question,
    retriever,
    convertDocsToString,
  ]);

  const SYSTEM_TEMPLATE =(question)=> `
    以下是知识库中跟用户回答相关的内容:
    {context}

    你是一个把小学所有故事都背完了的小学生,精通根据故事原文详细解释和回答问题,你在回答时会引用知识库中的作品原文。
    并且回答时仅根据原文,尽可能回答用户问题,
    请仔细阅读知识库,时间很充足,你必须做到精准把握知识库中的内容 , 之后根据下面规则
    1.如果用户的问题${question}与{context}有关,但是知识库中没有相关的内容,你就回答“原文中没有相关内容,我是小学生,知识有限”。
    2.如果用户的问题${question}与{context}无关,但是知识库中没有相关的内容,你就回答“我是小学生,知识有限,你的问题超出了我的想象”。
    3.如果用户的问题${question}与{context}有关,并且知识库中有相关的内容,根据用户问题${question}引用{context}回答。
    
  `;

  const prompt= (question) => ChatPromptTemplate.fromMessages([
    ["system", SYSTEM_TEMPLATE(question)],
    new MessagesPlaceholder("history"),
    ["human", "现在,你需要基于原文,回答以下问题:\n{standalone_question}`"],
  ]);
  const model = new ChatOpenAI({
    temperature: 0.9,
    model: "gpt-4o",
  });
  const rephraseChain = await getRephraseChain();

  const ragChain = RunnableSequence.from([
    RunnablePassthrough.assign({
      standalone_question: rephraseChain,
    }),
    RunnablePassthrough.assign({
      context: contextRetrieverChain,
    }),
    (input)=>prompt(input.standalone_question),
    model,
    new StringOutputParser(),
  ]);

  const chatHistoryDir = "./chat_data";
  const ragChainWithHistory = new RunnableWithMessageHistory({
    runnable: ragChain,
    getMessageHistory: (sessionId) => new JSONChatHistory({ sessionId, dir: chatHistoryDir }),
    historyMessagesKey: "history",
    inputMessagesKey: "question",
  });

  return ragChainWithHistory;
}

1. 加载向量库
javascript体验AI代码助手代码解读复制代码async function loadVectorStore() {
  const directory = "./db/gushi";

  const embeddings = new OpenAIEmbeddings();
  const vectorStore = await FaissStore.load(directory, embeddings);

  return vectorStore;
}

  • loadVectorStore 是一个异步函数,用于加载本地存储的 Faiss 向量库。
  • directory 指定了向量库的存储目录。
  • OpenAIEmbeddings 用于将文本转换为向量表示。
  • FaissStore.load 方法从指定目录加载向量库。
2. 获取 RAG 链
javascript体验AI代码助手代码解读复制代码export async function getRagChain() {
  const vectorStore = await loadVectorStore();
  const retriever = vectorStore.asRetriever(2);

  • getRagChain 是一个异步函数,用于构建 RAG 链并返回。
  • 调用 loadVectorStore 函数加载向量库。
  • vectorStore.asRetriever(2) 创建一个检索器,每次检索返回最多 2 个相关文档。
3. 文档转换函数
javascript体验AI代码助手代码解读复制代码const convertDocsToString = (documents) => {
    return documents.map((document) => document.pageContent).join("\n");
  };

  • convertDocsToString 函数将检索到的文档数组转换为字符串,方便后续处理。
4. 上下文检索链
javascript体验AI代码助手代码解读复制代码const contextRetrieverChain = RunnableSequence.from([
    (input) => input.standalone_question,
    retriever,
    convertDocsToString,
  ]);

  • RunnableSequence.from
    
    

    创建一个可运行的序列,依次执行三个步骤:

    • 提取输入中的 standalone_question。
    • 使用检索器检索相关文档。
    • 将检索到的文档转换为字符串。
5. 系统提示模板
javascript体验AI代码助手代码解读复制代码const SYSTEM_TEMPLATE =(question)=> `
    以下是知识库中跟用户回答相关的内容:
    {context}

    你是一个把小学所有故事都背完了的小学生,精通根据故事原文详细解释和回答问题,你在回答时会引用知识库中的作品原文。
    并且回答时仅根据原文,尽可能回答用户问题,
    请仔细阅读知识库,时间很充足,你必须做到精准把握知识库中的内容 , 之后根据下面规则
    1.如果用户的问题${question}与{context}有关,但是知识库中没有相关的内容,你就回答“原文中没有相关内容,我是小学生,知识有限”。
    2.如果用户的问题${question}与{context}无关,但是知识库中没有相关的内容,你就回答“我是小学生,知识有限,你的问题超出了我的想象”。
    3.如果用户的问题${question}与{context}有关,并且知识库中有相关的内容,根据用户问题${question}引用{context}回答。
    
  `;

  • SYSTEM_TEMPLATE 是一个函数,根据用户问题生成系统提示模板,指导语言模型如何回答问题。
6. 提示模板
javascript体验AI代码助手代码解读复制代码const prompt= (question) => ChatPromptTemplate.fromMessages([
    ["system", SYSTEM_TEMPLATE(question)],
    new MessagesPlaceholder("history"),
    ["human", "现在,你需要基于原文,回答以下问题:\n{standalone_question}`"],
  ]);

  • prompt 是一个函数,根据用户问题生成聊天提示模板,包含系统提示、聊天历史记录占位符和用户问题。
7. 语言模型实例
javascript体验AI代码助手代码解读复制代码const model = new ChatOpenAI({
    temperature: 0.9,
    model: "gpt-4o",
  });

  • ChatOpenAI 创建一个 OpenAI 聊天模型实例,temperature 控制生成文本的随机性,model 指定使用的模型名称。
8. 重述链
javascript体验AI代码助手代码解读复制代码const rephraseChain = await getRephraseChain();

  • getRephraseChain 是一个未给出实现的异步函数,用于重述用户问题。
9. RAG 链
javascript体验AI代码助手代码解读复制代码const ragChain = RunnableSequence.from([
    RunnablePassthrough.assign({
      standalone_question: rephraseChain,
    }),
    RunnablePassthrough.assign({
      context: contextRetrieverChain,
    }),
    (input)=>prompt(input.standalone_question),
    model,
    new StringOutputParser(),
  ]);

  • RunnableSequence.from
    
    

    创建一个可运行的序列,依次执行以下步骤:

    • 使用 RunnablePassthrough.assign 将重述后的问题添加到输入中。
    • 使用 RunnablePassthrough.assign 将检索到的上下文添加到输入中。
    • 根据输入的问题生成提示模板。
    • 使用语言模型生成回答。
    • 使用 StringOutputParser 将模型输出转换为字符串。
10. 支持聊天历史记录的 RAG 链
javascript体验AI代码助手代码解读复制代码const chatHistoryDir = "./chat_data";
  const ragChainWithHistory = new RunnableWithMessageHistory({
    runnable: ragChain,
    getMessageHistory: (sessionId) => new JSONChatHistory({ sessionId, dir: chatHistoryDir }),
    historyMessagesKey: "history",
    inputMessagesKey: "question",
  });

  return ragChainWithHistory;
}

  • RunnableWithMessageHistory 创建一个支持聊天历史记录的 RAG 链,将聊天历史记录存储在 ./chat_data 目录下。

路由和 SSE 实现

javascript体验AI代码助手代码解读复制代码import express from "express";
import { getRagChain} from "./index.mjs";
import cors from "cors";

封装响应数据

javascript体验AI代码助手代码解读复制代码const Result = (code, data, msg) => {
    return {
        code,
        data,
        msg,
    };
};

解决跨域问题

javascript体验AI代码助手代码解读复制代码const app = express();
const port = 8080;

app.use(cors({
    origin: ['http://localhost:3000', 'http://localhost:5173'],
    methods: ['GET', 'POST'],
    allowedHeaders: ['Content-Type']
}));

设置路由

javascript体验AI代码助手代码解读复制代码

app.use(express.json());


// 处理 SSE 请求 , 实现流式响应对话
app.get("/sse", async (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    const question = req.query.question;
    const sessionId = req.query.session_id || 'default-session';

    try {
        const ragChain = await getRagChain();
        const result = await ragChain.stream(
            {
                question: question,
            },
            { configurable: { sessionId: sessionId } },
        );

        if (!result) {
            res.write(`event: end\ndata: AI 响应生成失败\n\n`);
            res.end();
            return;
        }

        for await (const chunk of result) {
            res.write(`data: ${chunk}\n\n`);
        }

        res.write(`event: end\ndata: \n\n`);
        res.end();

    } catch (error) {
        console.error('Error:', error);
        res.write(`event: end\ndata: 服务器错误: ${error.message}\n\n`);
        res.end();
    }
});

app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

总结

完成 RAG 实战 ~

如何零基础入门 / 学习AI大模型?

大模型时代,火爆出圈的LLM大模型让程序员们开始重新评估自己的本领。 “AI会取代那些行业?”“谁的饭碗又将不保了?”等问题热议不断。

不如成为「掌握AI工具的技术人」,毕竟AI时代,谁先尝试,谁就能占得先机!

想正式转到一些新兴的 AI 行业,不仅需要系统的学习AI大模型。同时也要跟已有的技能结合,辅助编程提效,或上手实操应用,增加自己的职场竞争力。

但是LLM相关的内容很多,现在网上的老课程老教材关于LLM又太少。所以现在小白入门就只能靠自学,学习成本和门槛很高

那么我作为一名热心肠的互联网老兵,我意识到有很多经验和知识值得分享给大家,希望可以帮助到更多学习大模型的人!至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费】

👉 福利来袭优快云大礼包:《2025最全AI大模型学习资源包》免费分享,安全可点 👈

全套AGI大模型学习大纲+路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

read-normal-img

640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。

👉 福利来袭优快云大礼包:《2025最全AI大模型学习资源包》免费分享,安全可点 👈

img

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费】

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

DeepSeek本地部署详细指南 通过上述步骤,你可以在本地成功部署DeepSeek模型,并通过Ollama或Open Web UI与模型进行交互。本地部署不仅能够保护数据隐私,还能根据需求灵活调整模型参数,满足不同场景下的使用需求。如果你在部署过程中遇到任何问题,可以在评论区留言,我们将一起解决。希望这篇教程能帮助你顺利部署DeepSeek模型,开启高效开发的新旅程! 阅读详情

相关推荐

LLM的MCP协议:初识MCP,搭建第一个MCP Server

MCP(Model Context Protocol),是一个开发的协议,标准化了应用程序如何为大模型提供上下文。MCP提供了一个标准的为LLM提供数据、工具的方式,使用MCP会更容易的构建Agent或者是基于LLM的复杂工作流。

Criss@陈磊 2万+

一文彻底整明白,基于Ollama工具的LLM大语言模型Web可视化对话机器人部署指南

在上一篇博文中,我们在本地部署了**Llama 3 8B**参数大模型,并用 Python 写了一个控制台对话客户端,基本能愉快的与 Llama 大模型对话聊天了。但控制台总归太技术化,体验不是很友好,我们希望能有个类似 ChatGPT 那样的 Web 聊天对话界面,本博文就安排起来……

老牛啊 1万+

【Codex安装使用手册】

Codex安装部署使用教程

qq_43392230的博客 4975

使用ollama在本地部署llama3-8b大模型

使用ollama在本地部署llama3-8b大模型

weixin_39945822的博客 1万+

把对讲门铃(控制底楼铁门)接入米家服务器

今天讲个跟SDR或者无人机无关的东西,技术含量不高,但是方便了我的生活。 现在家门有智能锁,汽车也可以远程解锁发动,NFC门禁也可以用某些手机复制,唯独楼下的铁门,必须用老式对讲门铃控制。 也就是说其它钥匙都可以不带,就剩这个钥匙还必须随身携带。 另外有时候人不在家,不能开楼下铁门,快递员也没法把东西送上楼。 ......

老邵的开源世界 6551

别再手动更新Excel了!用Jira看板管理你的个人项目(附保姆级配置流程)

本文详细介绍了如何利用Jira看板替代Excel进行个人项目管理,提供从注册到配置的保姆级教程。通过极简看板模式、自动化规则和数据分析功能,帮助用户实现任务可视化、协作高效化和提醒自动化,大幅提升个人工作效率。特别适合需要管理复杂个人项目的用户。

weixin_42699679的博客 229

大模型入门实战:通过 HuggingFace 调用 Llama3

至此,我们成功演示了如何通过 Hugging Face 的 Transformers 调用 Llama 模型,并获取了模型的回答。但是如果你的需求只是简单地调用模型,而无需进行微调或复杂的部署,可以尝试使用 Ollama。它不仅操作更加便捷,还提供了开箱即用的 RESTful 接口,适合快速集成到生产环境中。下一篇,我将会讲解如何通过 Ollama 下载部署 Llama 模型并调用。

python12345_的博客 2602

手把手教你用STM32的SPI驱动MCP2517FD实现CAN FD通信(附完整代码)

本文详细介绍了如何使用STM32通过SPI驱动MCP2517FD实现CAN FD通信,涵盖硬件连接、SPI驱动编写、寄存器配置及数据收发实战。提供完整代码示例和常见问题排查指南,帮助开发者快速构建高可靠性的CAN FD通信系统,适用于汽车电子和工业控制等领域。

weixin_30607029的博客 675

一文玩转本地化部署DeepSeek!

本文将详细介绍如何本地化部署DeepSeek,并应用于生活和工作。在大多数情况下,优先推荐官网chat或者官网API调用,效果更佳。本文的主要面向场景在于:免费、稳定、高效且隐私安全。

huang9604的博客 1万+

Microsoft Speech SDK 使用过程中女声改为男声遇到的问题

问题将女生改为系统自带男声(Kangkang)根据上面的步骤我的播放声音是改变了,但是还是女生,是yaoyao的女生。是因为下面注册表路径中VoicePath这个配置的路径指向了yaoyao的文件。 如何使用Microsoft Speech SDK,请参考https://blog.csdn.net/daoming1112/article/details/77542562 使用系统自带男声语音,不需...

u012161365的博客 7538

岭回归的MATLAB步骤

MATLAB 实现框架,它涵盖了从数据导入到岭回归的步骤,包括计算共线性、使用 MAE、MSE、R²、MAPE 进行评价,以及绘制相应的可视化图表。

subject625Ruben的博客 1090

无需GPU!三步实现DeepSeek开源模型本地化部署。

deepseek和chatGpt最大的区别就在于,它是开源的模型,并且训练成本更低,这使得我们普通人也能够在自己的电脑部署大语言模型,训练自己的AI智能体。我们需要谨记的就是,AI是工具,是为了提高我们的效率,它能够给我们提供更精确,更具逻辑的回答,你再根据自身的需求进行修改和采用。最近Deepseek访问量剧增,导致App时常访问不了,但是不用担心,官方给我们提供了本地部署大模型的方案。

m0_48891301的博客 2万+

医院网络设计(完整文档+思科拓扑图)

大家好,我是小华学长,一名计算机领域的博主。经过多年的学习和实践,我积累了丰富的计算机知识和经验,在这里我想与大家分享我的学习心得和技巧,帮助你成为更好的程序员。作为一名计算机博主,我一直专注于编程、算法、软件开发等领域,在这些方面积累了大量的经验。我相信分享是一种双赢的方式,通过分享,我可以帮助他人提升技术水平,同时也能够得到学习交流的机会。在我的文章中,你将会看到我对于各种编程语言、开发工具以及常见问题的解析和分析。我会结合自己的实际项目经验,为你提供实用的解决方案和优化技巧。

qq1325513482的博客 2万+

DeepSeek本地化部署全攻略,轻松拥有专属 AI 助手

可能大家都想学习AI大模型技术,也想通过这项技能真正达到升职加薪,就业或是副业的目的,但是不知道该如何开始学习,因为网上的资料太多太杂乱了,如果不能系统的学习就相当于是白学。2.页面下拉,根据自己电脑的配置选择合适的参数规模(7b 代表 70 亿参数,14b 代表 140 亿参数,参数越多,模型越大,效果理论上也会更好,但对电脑配置要求也更高),然后复制对应的下载命令。观看零基础学习书籍和视频,看书籍和视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

zhishi0000的博客 9699

结合MATLAB/Simulink和多体动力学仿真软件UM(Universal Mechanism),建立高速列车轴箱轴承的动力学模型,并通过联合仿真分析其正常运行特性和典型故障模式

高速列车是现代交通运输的重要组成部分,其运行速度高、载客量大,对安全性和可靠性提出了极高的要求。轴箱轴承作为高速列车的关键部件之一,承担着传递载荷和支撑车轴的重要作用。然而,由于长期运行在高速、重载和复杂工况下,轴箱轴承容易出现疲劳磨损、润滑失效和裂纹等故障,从而影响列车的安全性。本项目旨在结合MATLAB/Simulink和多体动力学仿真软件UM(Universal Mechanism),建立高速列车轴箱轴承的动力学模型,并通过联合仿真分析其正常运行特性和典型故障模式。

xiaoheshang_123的博客 330

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

DeepSeek本地化部署:3步搞定,让你的AI体验更稳定

通过本地化部署,用户可以在自己的设备上运行DeepSeek模型,无需依赖外部网络连接,从而实现更稳定、更可靠的使用体验。

2301_81888214的博客 3万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

LLMChain使用初探 -- OLLaMA+LangChain搭建本地大模型

LLMChain是一个简单的链,接受一个提示模板,使用用户输入格式化它并从LLM返回响应。其中,prompt_template是一个非常关键的组件,可以让你创建一个非常简单的链,它将接收用户输入,使用它格式化提示,然后将其发送到LLM。

sinat_29950703的博客 2万+

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

AI初识--LLM、ollama、llama都是些个啥?

Ollama是一个开源的 LLM(大型语言模型)服务工具,用于简化在本地运行大语言模型,降低使用大语言模型的门槛,使得大模型的开发者、研究人员和爱好者能够在本地环境快速实验、管理和部署最新大语言模型,包括如Llama 3、Phi 3、Mistral、Gemma等开源的大型语言模型。library因此,Ollama与LlamaLlama是大语言模型,而Ollama是大语言模型(不限于Llama模型)便捷的管理和运维工具。

weixin_66196770的博客 4万+

DeepSeek-R1本地部署,再也不怕宕机,还有语音功能!

最近国产大模型DeepSeek特别火,以至于频繁出现反应迟缓甚至宕机的情况,和两年多之前ChatGPT的遭遇颇为相似。万幸,DeepSeek是一个开源模型,我们大可以通过本地部署,在自己的终端上随时使用!接下来就教大家具体的操作:**一、用Ollama下载模型**首先我们需要安装Ollama,它可以在本地运行和管理大模型。

2401_84204207的博客 7万+

零基础入门AI:一键本地运行各种开源大语言模型 - Ollama

Ollama 是一个可以在本地部署和管理开源大语言模型的框架,由于它极大的简化了开源大语言模型的安装和配置细节,一经推出就广受好评,目前已在github上获得了46k star。不管是著名的羊驼系列,还是最新的AI新贵Mistral,等等各种开源大语言模型,都可以用Ollama实现一键安装并运行,支持的更多模型的列表可以查看Ollama官网。本文就让我们一起入门Ollama。

2401_84495725的博客 1979

一文教你如何本地部署玩转DeepSeek-V3,免费体验100度算力包跑通!

只需三步,免费体验100度算⼒包就能完成DeepSeek-V3的私有化部署!

weixin_46880696的博客 9万+

一文搞懂 MCP Servers

一文搞懂 MCP Servers什么是MCPMCP概念MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 提出并于 2024 年 11 月开源的一种通信协议,旨在解决大型语言模型(LLM)与外部数据源及工具之间无缝集成的需求。它通过标准化 AI 系统与数据源的交互方式,帮助模型获...

cnzzs的博客 4983

大模型必学——Ollama是什么?

Ollama 是一个强大的本地推理大模型平台,旨在简化模型的本地部署、管理和推理工作流。它允许用户在本地机器上拉取、管理、运行大模型,并提供多种访问方式,包括本地 CLI、HTTP 接口以及通过 OpenAI 客户端的集成。这篇文章将详细介绍 Ollama 的功能,如何使用 Ollama 拉取模型、运行模型,并通过多种方式访问推理服务。前排提示,文末有大模型AGI-优快云独家资料包哦!

大模型工程师老蓝的博客 1万+

本地部署DeepSeek大模型

最近DeepSeek大火,本文将尝试将其部署至本地,大致部署路径:准备电脑-部署Ollama-部署DeepSeek R1模型硬件型号CPUi5-13490F内存32GB显卡AMD RX6800 (16GB显存)硬盘1TB SSD提示:以下是本篇文章正文内容,下面案例可供参考。

weixin_40001694的博客 8849

ollama 安装

以上是不同操作系统下Ollama的安装步骤,希望对您有所帮助。如果在安装过程中遇到任何问题,可以参考Ollama的官方文档或社区资源进行故障排查。

zengliguang的专栏 8239

这怕是全网最强最简单的DeepSeek 本地化部署教程吧,赶紧收藏了!

DeepSeek持续爆火,估计有很多同学跟我一样一直在探索或者甚至到了深度的使用。但这些天我已经被「服务器繁忙,请稍后再试」搞得一点脾气没有了。作为一个前程序员,怎么能忍受呢?!于是,今天我花了一上午时间,直接在自己电脑本地上部署了DeepSeek-R1。下面是我记录本次部署的全过程,相信但凡你有一点点电脑使用经验,按着我这大白话的教程,只要三步,都能完成自己的AI本地化部署。注意:这里是两个“l”。进来之后是可以看到如下这样的一个界面,如果你没有看到的话,可能你进入错了。

python12345_的博客 3万+
上一篇: 如何手撸一个自有知识库的RAG系统
下一篇: 使用llama.cpp实现LLM大模型的格式转换、量化、推理、部署
爱喝白开水a
博客等级 码龄3年 6793粉丝 763原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值