写给工程师的 MacBook 商用级大模型知识库部署方案

4f40f8ee41173beb8fd2e802ebca3f85.gif

本文介绍了如何在自己的 MacBook 上部署一套知识库方案辅助自己的知识管理工作,希望能给每位计划自己搭建大模型知识库应用的工程师一点参考。

3734621a16e20b1569791087c1b66555.png

背景


历史的车轮滚滚向前,大模型技术发展日新月异,每天都有新鲜的技术出炉,让人目不暇接,同时具备可玩性和想象空间的各种应用和开源库,仿佛让自己回到了第一次设置 JAVA_HOME 的日子,作为一枚古典工程师,我专门挑了个可能对手上工作有帮助的方向小试一把,尝试在自己的 MacBook 上部署一套知识库方案,看看能不能辅助自己的知识管理工作。



我自己的 Macbook 配置情况如下,可以流畅地运行没问题。经过量化处理的大模型,还是对办公本很友好的。

7bf13d33f40435e860c012b3f2110462.jpeg

为什么要在 MacBook 搭建而不是直接采用现成的云服务呢?最核心最重要的是我们手上的文档资料出于安全要求,不能随便上传云服务,也就无法实际验证知识库的实际效用;另外对于工程师来说,自己亲手搭建一个完整的方案、能灵活调整和对接各种不同的模型、评测各种模型不同的表现,也是出于对技术的探索本能使然。



鉴于大模型已经是大模型及其周边概念已经是大家耳熟能详的东西,我这里就不再重复阐述相关的基础概念和理论了,直接进入动手环节,以用最快的速度部署起一个可用的知识库平台为目标,先用起来,再分各个环节优化。

3eddb7165f272ec6f26270338f8065a6.png

方案概述

  应用架构

首先来看一下最终方案的应用架构是什么样子(下图)。在这套方案中,我们采用实力排上游、并且在使用上对学术和商业都友好的国产大模型 ChatGLM3-6B 对话模型和基于 m3e-base 模型的 embedding search RAG 方案;基于这两个模型封装和 ChatGPT 兼容的 API 接口协议;通过引入 One API 接口管理&分发系统,形成统一的 LLM 接口渠道管理平台规范,并把封装好的接口协议注册进去;搭建与 Dify.ai 齐名的开源大模型知识库平台管理系统 FastGPT,实现集私有知识数据源预处理、嵌入检索、大模型对话一体的完整知识库应用流程。麻雀虽小五脏俱全,最终形成一套既满足商用标准、又能在 MacBook 跑起来的的方案。虽然智能程度和实际需求还有一定差距,但至少我们在不用额外购买显卡或云服务的情况下,以最小成本部署运行、并且能导入实际业务数据(如语雀知识库)进行实操验证,值得每位工程师都来动手尝试一下。

6e134df7efbf7092535e01d8eab5be90.jpeg


  成型展示

在用户终端,我们基于 FastGPT 提供知识库管理及使用方案。引用其官网介绍:FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景。



先放一张官网上的图片,来增加一点吸引朋友们动手操作的动力:

a48a126e89566a51cbe21920f9fd22d4.jpeg

  部署要点

本套方案部署分为四个主要环节、14个具体步骤,只要一步步实操下去,每位朋友都可以在自己的本本上拥有属于自己的私有大模型知识库系统,步骤清单如下:

主要环节

详细步骤

一、准备大模型

1.1 下载对话语言模型 ChatGLM3-6B

1.2 下载文本嵌入模型 m3e-base

1.3 使用 chatglm.cpp 对 ChatGLM3-6B 进行量化加速

1.4 验证模型问答效果

二、搭建模型API服务

2.1 搭建模型API

2.2 搭建 One API 接口管理/分发系统

2.3 验证模型接口能力

三、搭建知识库应用

3.1 安装 MongoDB

3.2 安装 PostgreSQL & pgvector

3.3 搭建 FastGPT 知识库问答系统

3.4 验证模型对话能力

四、知识库问答实战

4.1 准备知识库语料

4.2 导入知识库数据

4.3 验证知识库问答效果

部分步骤可以简单地通过 Docker 镜像一键部署完成,但本着对细节一杆子插到底的部署思路,还是采取了纯手工作业的方法。注意,下面的步骤中仅包含了关键的命令,完整的命令可以参考对应系统的官网介绍。部分安装步骤如果速度不够理想,可以考虑采用国内源,包含但不限于 go、brew、pip、npm 等。

19e25372ad73448b47cd1e4d71ed1310.png

详细步骤

  准备离线模型

这个环节我们的主要任务是把模型文件准备好、完成量化,并通过命令行的方式,进行交互式对话验证。

  • 下载对话语言模型 ChatGLM3-6B

为什么选择 ChatGLM3-6B?常年霸榜的开源国产之光。ChatGLM3 一共开源了对话模型 ChatGLM-6B、基础模型 ChatGLM-6B-Base、长文本对话模型 ChatGLM3-6B-32K,对学术研究完全开放,在填写问卷进行登记后亦允许免费商业使用。无论是用来做上手实践还是微调练习,目前看来都是比较好的选择。

其实最重要的是,看看排行榜上的可选项,我的 MacBook 16G 内存只能带得动 ChatGLM3-6B 量化版本:

79a3af6d6373c5da01c0c7fda4fb782e.jpeg

ChatGLM3-6B 现在比较方便的下载渠道有 HuggingFace 和 ModelScope,但是很明显能直接下载下来的可能性不大,所以我用家里的旧电脑科学下载后放到私有云CDN上,然后再用公司电脑下载,也方便未来随时随地取用,就是要花点小钱。ModelScope 也试过,不能直接下载文件,并且用 git clone 速度也不太理想,遂放弃。

如果用老一点的版本 ChatGLM2-6B 的话,网上也能找到一些比较好用的第三方镜像站。

  1. HuggingFace:THUDM/chatglm3-6b

  2. ModelScope:ZhipuAI/chatglm3-6b(地址:https://modelscope.cn/models/ZhipuAI/chatglm3-6b/summary)

// 从 Git 仓库下载模型文件
// HuggingFace
git lfs install
git clone https://huggingface.co/THUDM/chatglm3-6b// ModelScope
git lfs install
git clone https://www.modelscope.cn/ZhipuAI/chatglm3-6b.git
  • 下载文本嵌入模型 m3e-base

为什么选择 moka-ai 的 M3E 模型 m3e-base?M3E 向量模型属于小模型,资源使用不高,CPU 也可以运行,使用场景主要是中文,少量英文的情况。用来验证我们的知识库系统足够了

官方下载地址:moka-ai/m3e-base,先把所有的模型文件 download 下来,后面使用

  • 使用 chatglm.cpp 对 ChatGLM3-6B 进行量化加速

当我第一次知道 chatglm.cpp,只能说好人一生平安,chatglm.cpp 的出现拯救了纯 MacBook 党,让我们能在(低性能的)果本上基于 CPU 进行推理,也不会损失过多的精度。(其实损失多少我也不知道,不影响我们正常进行工程部署验证就行)

Github Repo: https://github.com/li-plus/chatglm.cpp

我使用的 Python 版本:3.11,最好单独准备一个 virtualenv

e5503254f67cf00420bddbca6b071d54.jpeg

安装依赖:

cd /Users/yaolu/AGI/github/chatglm.cpp# 先初始化 git 仓库
git submodule update --init --recursive# 构建可执行文件
cmake -B build
cmake --build build -j# 安装 Python 依赖
pip install .

如果发生 No module named 'chatglm_cpp._C' 的错误,把编译出来的文件 _C.cpython-311-darwin.so 放到 chatglm_cpp 目录下。

对 ChatGLM3-6B 进行 8-bit 量化处理:

python ./chatglm_cpp/convert.py -i /Users/yaolu/AGI/huggingface/THUDM/chatglm3-6b -t q8_0 -o chatglm3-ggml-q8.bin

如果电脑带不动,还可以尝试 4-bit、5-bit 参数量化,完整参数列表见 chatglm.cpp 的 quantization types

  • 验证模型问答效果

完成模型量化后,就可以在本地把大模型跑起来了,命令如下:

./build/bin/main -m chatglm3-ggml-q8.bin -i

b2437295cee2857b6d42ec034ea52122.jpeg

  搭建模型API服务

我们在这个环节要完成的任务是,按照 ChatGPT 的接口规范、基于 FastAPI 封装 ChatGLM3-6B 的对话和 m3e-base 的嵌入能力;并注册到 One API 接口管理/分发系统中。

  • 搭建模型API

用 chatglm.cpp 自带的 openai_api.py 魔改了一下,使其支持完成对话和文本 embedding 的两个核心调用:

  1. /v1/chat/completions

  2. /v1/embeddings

代码如下:

import asyncio
import logging
import time
from typing import List, Literal, Optional, Unionimport chatglm_cpp
from fastapi import FastAPI, HTTPException, status, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field#, computed_field
#from pydantic_settings import BaseSettings
from sse_starlette.sse import EventSourceResponsefrom sentence_transformers import SentenceTransformer
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
import tiktokenlogging.basicConfig(level=logging.INFO, format=r"%(asctime)s - %(module)s - %(levelname)s - %(message)s")class Settings(object):model: str = "/Users/yaolu/AGI/github/chatglm.cpp/chatglm3-ggml-q8.bin"num_threads: int = 0class ChatMessage(BaseModel):role: Literal["system", "user", "assistant"]content: strclass DeltaMessage(BaseModel):role: Optional[Literal["system", "user", "assistant"]] = Nonecontent: Optional[str] = Noneclass ChatCompletionRequest(BaseModel):model: str = "default-model"messages: List[ChatMessage]temperature: float = Field(default=0.95, ge=0.0, le=2.0)top_p: float = Field(default=0.7, ge=0.0, le=1.0)stream: bool = Falsemax_tokens: int = Field(default=2048, ge=0)model_config = {"json_schema_extra": {"examples": [{"model": "default-model", "messages": [{"role": "user", "content": "你好"}]}]}}class ChatCompletionResponseChoice(BaseModel):index: int = 0message: ChatMessagefinish_reason: Literal["stop", "length"] = "stop"class ChatCompletionResponseStreamChoice(BaseModel):index: int = 0delta: DeltaMessagefinish_reason: Optional[Literal["stop", "length"]] = Noneclass ChatCompletionUsage(BaseModel):prompt_tokens: intcompletion_tokens: int#@computed_field@propertydef total_tokens(self) -> int:return self.prompt_tokens + self.completion_tokensclass ChatCompletionResponse(BaseModel):id: str = "chatcmpl"model: str = "default-model"object: Literal["chat.completion", "chat.completion.chunk"]created: int = Field(default_factory=lambda: int(time.time()))choices: Union[List[ChatCompletionResponseChoice], List[ChatCompletionResponseStreamChoice]]usage: Optional[ChatCompletionUsage] = Nonemodel_config = {"json_schema_extra": {"examples": [{"id": "chatcmpl","model": "default-model","object": "chat.completion","created": 1691166146,"choices": [{"index": 0,"message": {"role": "assistant", "content": "你好👋!我是人工智能助手 ChatGLM2-6B,很高兴见到你,欢迎问我任何问题。"},"finish_reason": "stop",}],"usage": {"prompt_tokens": 17, "completion_tokens": 29, "total_tokens": 46},}]}}settings = Settings()
app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]
)
pipeline = chatglm_cpp.Pipeline(settings.model)
lock = asyncio.Lock()embeddings_model = SentenceTransformer('/Users/yaolu/AGI/huggingface/moka-ai/m3e-base', device='cpu')def stream_chat(history, body):yield ChatCompletionResponse(object="chat.completion.chunk",choices=[ChatCompletionResponseStreamChoice(delta=DeltaMessage(role="assistant"))],)for piece in pipeline.chat(history,max_length=body.max_tokens,do_sample=body.temperature > 0,top_p=body.top_p,temperature=body.temperature,num_threads=settings.num_threads,stream=True,):yield ChatCompletionResponse(object="chat.completion.chunk",choices=[ChatCompletionResponseStreamChoice(delta=DeltaMessage(content=piece))],)yield ChatCompletionResponse(object="chat.completion.chunk",choices=[ChatCompletionResponseStreamChoice(delta=DeltaMessage(), finish_reason="stop")],)async def stream_chat_event_publisher(history, body):output = ""try:async with lock:for chunk in stream_chat(history, body):await asyncio.sleep(0)  # yield control back to event loop for cancellation checkoutput += chunk.choices[0].delta.content or ""yield chunk.model_dump_json(exclude_unset=True)logging.info(f'prompt: "{history[-1]}", stream response: "{output}"')except asyncio.CancelledError as e:logging.info(f'prompt: "{history[-1]}", stream response (partial): "{output}"')raise e@app.post("/v1/chat/completions")
async def create_chat_completion(body: ChatCompletionRequest) -> ChatCompletionResponse:# ignore system messageshistory = [msg.content for msg in body.messages if msg.role != "system"]if len(history) % 2 != 1:raise HTTPException(status.HTTP_400_BAD_REQUEST, "invalid history size")if body.stream:generator = stream_chat_event_publisher(history, body)return EventSourceResponse(generator)max_context_length = 512output = pipeline.chat(history=history,max_length=body.max_tokens,max_context_length=max_context_length,do_sample=body.temperature > 0,top_p=body.top_p,temperature=body.temperature,)logging.info(f'prompt: "{history[-1]}", sync response: "{output}"')prompt_tokens = len(pipeline.tokenizer.encode_history(history, max_context_length))completion_tokens = len(pipeline.tokenizer.encode(output, body.max_tokens))return ChatCompletionResponse(object="chat.completion",choices=[ChatCompletionResponseChoice(message=ChatMessage(role="assistant", content=output))],usage=ChatCompletionUsage(prompt_tokens=prompt_tokens, completion_tokens=completion_tokens),)class EmbeddingRequest(BaseModel):input: List[str]model: strclass EmbeddingResponse(BaseModel):data: listmodel: strobject: strusage: dictdef num_tokens_from_string(string: str) -> int:"""Returns the number of tokens in a text string."""encoding = tiktoken.get_encoding('cl100k_base')num_tokens = len(encoding.encode(string))return num_tokensdef expand_features(embedding, target_length):poly = PolynomialFeatures(degree=2)expanded_embedding = poly.fit_transform(embedding.reshape(1, -1))expanded_embedding = expanded_embedding.flatten()if len(expanded_embedding) > target_length:# 如果扩展后的特征超过目标长度,可以通过截断或其他方法来减少维度expanded_embedding = expanded_embedding[:target_length]elif len(expanded_embedding) < target_length:# 如果扩展后的特征少于目标长度,可以通过填充或其他方法来增加维度expanded_embedding = np.pad(expanded_embedding, (0, target_length - len(expanded_embedding)))return expanded_embedding@app.post("/v1/embeddings", response_model=EmbeddingResponse)
async def get_embeddings(request: EmbeddingRequest):# 计算嵌入向量和tokens数量 embeddings = [embeddings_model.encode(text) for text in request.input]# 如果嵌入向量的维度不为1536,则使用插值法扩展至1536维度 embeddings = [expand_features(embedding, 1536) if len(embedding) < 1536 else embedding for embedding in embeddings]# Min-Max normalizationembeddings = [embedding / np.linalg.norm(embedding) for embedding in embeddings]# 将numpy数组转换为列表embeddings = [embedding.tolist() for embedding in embeddings]prompt_tokens = sum(len(text.split()) for text in request.input)total_tokens = sum(num_tokens_from_string(text) for text in request.input)response = {"data": [{"embedding": embedding,"index": index,"object": "embedding"} for index, embedding in enumerate(embeddings)],"model": request.model,"object": "list","usage": {"prompt_tokens": prompt_tokens,"total_tokens": total_tokens,}}return responseclass ModelCard(BaseModel):id: strobject: Literal["model"] = "model"owned_by: str = "owner"permission: List = []class ModelList(BaseModel):object: Literal["list"] = "list"data: List[ModelCard] = []model_config = {"json_schema_extra": {"examples": [{"object": "list","data": [{"id": "gpt-3.5-turbo", "object": "model", "owned_by": "owner", "permission": []}],}]}}@app.get("/v1/models")
async def list_models() -> ModelList:return ModelList(data=[ModelCard(id="gpt-3.5-turbo")])

让他跑起来的命令,跑在8000端口下:

uvicorn chatglm_cpp.openai_api:app --host 127.0.0.1 --port 8000

运行该程序所需的 Python 依赖项:

accelerate==0.24.1
aiofiles==23.2.1
aiohttp==3.8.6
aiosignal==1.3.1
altair==5.1.2
annotated-types==0.6.0
anyio==3.7.1
async-timeout==4.0.3
attrs==23.1.0
blinker==1.7.0
cachetools==5.3.2
certifi==2023.7.22
charset-normalizer==3.3.2
click==8.1.7
contourpy==1.2.0
cpm-kernels==1.0.11
cycler==0.12.1
fastapi==0.103.2
ffmpy==0.3.1
filelock==3.13.1
fonttools==4.44.0
frozenlist==1.4.0
fsspec==2023.10.0
gitdb==4.0.11
GitPython==3.1.40
gradio==3.50.2
gradio_client==0.6.1
h11==0.14.0
httpcore==1.0.2
httpx==0.25.1
huggingface-hub==0.19.1
idna==3.4
importlib-metadata==6.8.0
importlib-resources==6.1.1
Jinja2==3.1.2
joblib==1.3.2
jsonschema==4.19.2
jsonschema-specifications==2023.7.1
kiwisolver==1.4.5
latex2mathml==3.76.0
linkify-it-py==2.0.2
Markdown==3.5.1
markdown-it-py==2.2.0
MarkupSafe==2.1.3
matplotlib==3.8.1
mdit-py-plugins==0.3.3
mdtex2html==1.2.0
mdurl==0.1.2
mpmath==1.3.0
multidict==6.0.4
networkx==3.2.1
nltk==3.8.1
numpy==1.26.2
orjson==3.9.10
packaging==23.2
pandas==2.1.3
Pillow==10.1.0
protobuf==4.25.0
psutil==5.9.6
pyarrow==14.0.1
pydantic==2.1.1
pydantic_core==2.4.0
pydeck==0.8.1b0
pydub==0.25.1
Pygments==2.16.1
pyparsing==3.1.1
python-dateutil==2.8.2
python-multipart==0.0.6
pytz==2023.3.post1
PyYAML==6.0.1
referencing==0.30.2
regex==2023.10.3
requests==2.31.0
rich==13.6.0
rpds-py==0.12.0
safetensors==0.4.0
scikit-learn==1.3.2
scipy==1.11.3
semantic-version==2.10.0
sentence-transformers==2.2.2
sentencepiece==0.1.99
six==1.16.0
smmap==5.0.1
sniffio==1.3.0
sse-starlette==1.6.5
starlette==0.27.0
streamlit==1.28.2
sympy==1.12
tabulate==0.9.0
tenacity==8.2.3
threadpoolctl==3.2.0
tiktoken==0.5.1
tokenizers==0.13.3
toml==0.10.2
toolz==0.12.0
torch==2.1.0
torchvision==0.16.0
tornado==6.3.3
tqdm==4.66.1
transformers==4.30.2
typing_extensions==4.6.1
tzdata==2023.3
tzlocal==5.2
uc-micro-py==1.0.2
urllib3==2.1.0
uvicorn==0.24.0.post1
validators==0.22.0
websockets==11.0.3
yarl==1.9.2
zipp==3.17.0
  • 搭建 One API 接口管理/分发系统

One API是一套兼容多种 LLM 接口规范的 API 路由方案,支持限额和计费管理,通过标准的 OpenAI API 格式访问所有的大模型,开箱即用,其多模型渠道接入、多用户管理、费用管理、额度管理、以及集群化部署支持等功能,对商用场景都很友好。项目使用 MIT 协议进行开源。

One API 基于 Go 和 Node.js 开发,搭建之前准备好,我的版本是:go1.21.4、Node.js v20.9.0,构建命令如下:

git clone https://github.com/songquanpeng/one-api.git# 构建前端
cd one-api/web
npm install
npm run build# 构建后端
cd ..
go mod download
go build -ldflags "-s -w" -o one-api

One API 里面预置了很多市面上的可用模型接口,好处是可以直接使用无需配置,缺点是没有添加自定义(本地)接口的能力。由于我们是自己搭建的 LLM 和 embedding 服务,需要修改其源代码,增加 ChatGLM3 和 m3e-base 的选项。

改动涉及两个文件,分别是 common/model-ratio.go 和 controller/model.go,改动内容如下图:

88781c9bd6c39515cbd5de3ef88dd0ca.jpeg

e6151ed9b5ff742be6d978032f90f679.jpeg

注意,改完文件后记得重新编译可执行文件。本地的元数据存储我使用了 MySQL,编译+启动命令是:

go build -ldflags "-s -w" -o one-api
export SQL_DSN=oneapi:oneapi@tcp(localhost:3306)/oneapi && ./one-api --port 3001 --log-dir ./log

初始登录进去,创建一个新令牌用于权限管控和计费:

ebae7d2fbb6d9394e4ebbc90c7636520.jpeg

令牌可以从这里复制,下面有用:

87f481659fc7aa537af3cf3059400500.jpeg

One API 的渠道管理界面如下图,我已经配置了俩渠道,一个 chat 渠道,一个 embedding 渠道:

3a02a13a38bde79298873d5d1b8ec771.jpeg

具体的配置值如下图,名称写实际的模型名 ChatGLM3,模型选刚才手动添加上去的 ChatGLM3:

d3785520d856ddaf5210945ebe2c8e3f.jpeg

0cf2b6cb73bceee5a0e82c2976c41909.jpeg

配置完后可以在列表页点一下测试验证,连通无问题就行,但现在似乎一测就会把模型API服务弄挂,不过没关系,不影响后面验证。

  搭建知识库应用

在这个环节里,我们采用类似 Dify.ai (地址:https://dify.ai/)的国产化开源 FastGPT 方案搭建属于自己的本地知识库应用平台。FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景。FastGPT 遵循 Apache License 2.0 开源协议,我们可以 Fork 之后进行二次开发和发布。

FastGPT 的核心流程图如下:

d8502e2eb714ac08be7024ca1b1e6f16.jpeg

从 FastGPT 官网得知,这套开源系统基于以下几个基本概念进行知识库检索:

  1. 向量:将人类直观的语言(文字、图片、视频等)转成计算机可识别的语言(数组)。

  2. 向量相似度:两个向量之间可以进行计算,得到一个相似度,即代表:两个语言相似的程度。

  3. 语言大模型的一些特点:上下文理解、总结和推理。

结合上述 3 个概念,便有了 “向量搜索 + 大模型 = 知识库问答” 的公式。下图是 FastGPT V3 中知识库问答功能的完整逻辑:

6c436ddc144c96b840efb8768f383bbb.jpeg



FastGPT 的向量存储方案是 PostgreSQL+pgvector,其他数据放在 MongoDB 里面,因此我们先把这两项依赖搞定。

  • 安装 MongoDB

MacBook 安装 MongoDB 很简单,如果没有特别的安全诉求,可以先不用设置用户名密码

brew install mongodb-community
brew services start mongodb-community

FastGPT 基于 MongoDB 存储知识库索引、会话内容、工作流等管理数据:

9fc10a3a05ce541b1cb583ea353e15b7.jpeg

  • 安装 PostgreSQL & pgvector

FastGPT 采用了 RAG 中的 Embedding 方案构建知识库,PostgresSQL 的 PG Vector 插件作为向量检索器,索引为HNSW。PostgresSQL 仅用于向量检索,MongoDB用于其他数据的存取。另外也可以采用第三方模型的 Embedding API,比如 ChatGPT embedding,不过为了实现完整的本地化部署,就没有用外部服务。

我们可以从 PostgreSQL 的官网下载 PostgreSQL 安装包:https://www.postgresql.org/download/macosx/

从源码安装 pgvector:https://github.com/pgvector/pgvector

// 安装 pgvector 前指定 PostgreSQL 位置
export PG_CONFIG=/Library/PostgreSQL/16/bin/pg_config// 如果 pgvector 认错了 MacOS SDK 的位置,还得帮他软链一个
sudo ln -s /Library/Developer/CommandLineTools/SDKs/MacOSX13.sdk /Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk
// 或者用这个命令
export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)// 源码编译安装 
make
make install # may need sudo// 确保插件已安装到 PostgreSQL 目录下
cd /Library/PostgreSQL/16/share/postgresql/extension/
ls | grep vector

完成以上步骤后,打开 PostgreSQL 控制台,随便建立一个连接,运行下面的查询:

CREATE EXTENSION vector;SELECT * FROM pg_extension WHERE extname = 'vector';

如果能出现下图结果,说明 pgvector 已经安装成功:

632b6ffcb56e0976081e9302fe50df7e.jpeg

  • 搭建 FastGPT 知识库问答系统

好,我们的主角终于上场了,下面有请 FastGPT,安装指南见:https://doc.fastgpt.in/docs/development/intro/

第一步,按照里面的步骤,配置 .env.local 文件内容,指定 One API、MongoDB 和 PostgreSQL 的访问地址:

ecca2167fdfb670a6aed5fb6c302fce5.jpeg

这里的 CHAT_API_KEY 填入上面 OneAPI 创建的令牌

第二步,在 config.local.json 里面注册对话模型和向量嵌入模型,注意这里的 model 值要和 One API 里配置的保持一致:

5a730ca4f1e8ccebb789aa9a8ba2e15d.jpeg

e242cbe7aa24790a8e82b439450eb83b.jpeg

第三步,安装 Node.js 依赖并以开发模式启动:

# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
pnpm i
# 切换到应用目录
cd projects/app
# 开发模式运行
pnpm dev

第四步,访问本地 FastGPT 地址 http://localhost:3000/,如果能顺利登录,则搭建成功。

  • 验证模型对话能力

创建一个应用:

bd9a18c97996cdebcf98d84e407c9f34.jpeg

应用创建完成,进入对话界面,注意 AI 模型选择我们在 One API 里配置的 ChatGLM3。试着问他两个问题,可以看到推理速度还是很快的,分别是 5.83s、7.52s:

8f3a8459556185af28e7358731775d37.jpeg

点开单条对话响应,详细的对话参数(消耗 token、响应时长、计费信息)清晰可见:

b426e6e73490d5f22a3d6c0bed994140.jpeg

查看 MacBook 上的 ChatGLM3 推理资源占用情况,占用了 3.78GB 内存

0002ab01c3fe6457094c6562f7b48a6b.jpeg

0ac45363f976c1eebab8ad98553fb79f.png

知识库问答实战

  准备知识库语料

在有知识库使用诉求的场景,我们一般都积累了比较多的私有知识数据,比如:语雀文档、钉钉文档、PDF、Office文件等,视知识识图的建设标准,需要将它们一一结构化整理。数据的梳理、清洗、结构化是一项繁杂而重要的工作,但也有比较成熟的办法和工具,在此不再赘述。

  导入知识库数据

FastGPT 提供了很多种原始数据导入的办法,并且为了更好地和企业系统集成,FastGPT 支持通过 API 的方式地二次开发导入能力,支持和已有知识管理系统更好地自动化集成。常见的导入方法有:

  1. 导入数据方案1 - 直接分段导入:直接分段会利用句子分词器对文本进行一定长度拆分,可以理解为原始文档 Chunk。

  2. 导入数据方案2 - QA导入:导入时会调用大模型对分段进行学习,然后直接生成问题-答案。

  3. 导入数据方案3 - 手动录入:手动录入问题和答案。

  4. 导入数据方案4 - CSV录入:批量快速录入问题答案对。

  5. 导入数据方案5 - API导入,详见:OpenAPI 接口文档(地址:https://doc.fastgpt.in/docs/development/openapi/#%E7%9F%A5%E8%AF%86%E5%BA%93%E6%B7%BB%E5%8A%A0%E6%95%B0%E6%8D%AE)



我们先简单地录入几个问题和答案,然后后面快速验证 RAG 效果。

新建一个知识库,注意,索引模型一旦选择不可更改。这里我们选择刚部署好的 m3e-base

5968cc7eb60ed13834b0477b19b840a1.jpeg

进入知识库初始界面,已经默认有了一个“手动录入”文件夹,我们在这里录入几条测试问答

7af017e913de21e3aa0118a28b4f4175.jpeg

录入内容分两种类型,其中:被搜索的内容指将被向量化的部分,通常是问题,或者精炼扼要的描述,需要准确填写

99d1d9970f1822ba87d2565fff3e2972.jpeg

  • 验证知识库问答效果

重新打开应用,关联刚才创建好的知识库(注意这里一定要保证才会生效),问他一个简单的问题,回答的质量看起来还可以。

问:简单说一下商家共振模型是个啥呗?

答:商家共振模型是一种商业策略和运营模式,旨在促进商家和平台之间的合作关系,同时提高用户的粘性和平台的影响力。这个模式通过激励商家在站内和站外进行投放活动,帮助商家获得更多的流量和销售机会,从而促进商家的生意增长。同时,这个模式也能够扩大平台的规模和影响力,增加商家对平台的依赖性和忠诚度。总的来说,商家共振模型是一种有效的商业策略和运营模式,对于猫超等平台来说是一个不错的选择。

952abf81ecdeca4d9e32b4b2399a112c.jpeg

点击答案中的“1条引用”,还可以看到答案的出处来源:

6d96c45867971b8a38bf75833c873213.jpeg

至此,我们就完成了一个简单的知识库构建和应用的过程,也验证了整套本地知识库方案的可行性。



937dcc7f1952eeee7449c100275dba24.png

未来展望

串通了整个知识库应用流程,我们完成了从0到1的起步。虽然整体应用架构是按实际商用标准来搭建的,但要想使用效果也达到工业级别的标准,还有很多工作值得进一步探索,包括但不限于:

1️⃣ 大模型应用层面:

1、更好的文档 Chunk、Embedding、多路加权平均搜索召回方案,提升 RAG 整体效能

2、更好的 Prompt Engineering,充分挖掘 LLM 的潜力

3、工作流编排、CoT、Agent,满足实际的企业应用需求

2️⃣ 稳定性层面:如果达到商用级别,需要更高配置的软硬件环境

3️⃣ 落地价值层面:从解决身边的问题开始,解决真金白银的商业问题



希望本文能给每位计划自己搭建大模型知识库应用的工程师一点参考,动手跑通一个程序的乐趣是无穷的,更多的实操作经验分享,我们在评论区交流。

55aac784173aca43d893f8dc7abea98c.png

团队介绍

我们是淘天渠道分销技术团队,负责淘天全渠道一盘货产品技术研发。我们通过用技术手段解决电商多段销售中的多角色商业往来问题,构建了灵活的新零售供应链分销产品平台,致力于为商家提供多元化的供给和销售渠道、助力商家在全平台取得更高的成交额。

长期招募人才,欢迎投递简历:xieyi.xie@alibaba-inc.com

¤ 拓展阅读 ¤

3DXR技术 | 终端技术 | 音视频技术

服务端技术 | 技术质量 | 数据算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2662100.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

活动快讯 | 万博智云受邀参加上海经信委,港澳办,香港贸发局主办的沪港主题活动

12月28日下午&#xff0c;上海市经信委、上海市人民政府港澳事务办公室、香港贸发局联合于上海城市数字化转型体验馆三楼会客厅举办沪港主题活动。万博智云CEO Michael受邀参加此次活动&#xff0c;探讨数字经济未来发展局势。 本次主题活动聚焦数据领域&#xff0c;邀请两地政…

J1 - ResNet-50实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 环境步骤环境设置数据准备图像信息查看 模型设计模型训练模型效果展示 总结与心得体会 环境 系统: Linux语言: Python3.8.10深度学习…

UniApp小程序使用vant引入vant weapp

HBuilder X里新建项目指路 HBuilderX新建项目 安装node.js指路 安装node.js 1.通过npm安装 查看npm环境 //打开终端输入命令查看版本 npm -version 1.1.右键打开外部终端窗口 1.2.输入npm init -y命令 1.3.通过命令安装 npm i vant/weapp1.3.3 -S --production 1.4.打开工具…

蓝桥杯单片机进阶教程1——代码书写格式,继电器和蜂鸣器

一、比赛过程中的参考资料 SCH_硬件原理图V30 二、分析考题 通用的考题是正式程序前加上关闭无关外设 还有就是考继电器的吸合&#xff0c;关键点是不要影响到蜂鸣器 三、实验 关闭无关外设&#xff0c;继电器每隔500ms吸合&#xff0c;断开 参考代码&#xff1a; https…

macos Jetbrains IDEA用户自定义vm配置信息存储路径, IDEA点击无反应 无法打开问题解决

Jetbrains Clion, IDEA 用户在应用里面修改了自定义的VM配置后的存储路径为 ~/Library/Application Support/JetBrains/xxx2023.3/xxx.vmoptions xxx为你安装的APP名称, 如 Clion .IntelliJIdea 这里的自定义配置如果配置有误就会直接导致JetBrains软件无法打开, 即 点击打开…

[电磁学]大学物理陈秉乾老师课程笔记

主页有博主其他上万字的精品笔记,都在不断完善ing~ 第一讲 绪论,库仑定律 主要讲解了电磁学中的库伦定律和电场的相关概念&#xff0c;介绍了电荷和电磁相互作用的规律&#xff0c;并讲解了电场强度和电势的概念。 03:14 &#x1f393; 库伦定律&#xff1a;电势能与电荷的关…

攻防技术-单包攻击防范:扫描、畸形、特殊(HCIP)

单包攻击类型介绍 一、扫描窥探攻击 1、地址扫描攻击防范 攻击介绍 运用ping程序探测目标地址&#xff0c;确定目标系统是否存活。也可使用TCP/UDP报文对目标系统发起探测&#xff08;如TCP ping&#xff09;。 防御方法 检测进入防火墙的ICMP、TCP和UDP报文&#xff0c;根…

【C语言深度剖析——第一节(关键字1)】《C语言深度解剖》+蛋哥分析+个人理解

你未曾见过火光&#xff0c;难怪甘愿漂泊寒夜 本文由睡觉待开机原创&#xff0c;未经允许不得转载。 本内容在csdn网站首发 欢迎各位点赞—评论—收藏 如果存在不足之处请评论留言&#xff0c;共同进步&#xff01; 首先简单介绍一下《C语言深度解剖》&#xff1a; 全书特点&am…

深度解析高防产品---游戏盾

游戏盾是针对游戏行业所推出的高度可定制的网络安全解决方案&#xff0c;游戏盾是高防产品系列中针对游戏行业的安全解决方案。游戏盾专为游戏行业定制&#xff0c;针对性解决游戏行业中复杂的DDoS攻击、游戏CC攻击等问题。游戏盾通过分布式的抗D节点&#xff0c;可以防御TB级大…

【教程】自动检测和安装Python脚本依赖的第三方库

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 背景说明 对于新python环境&#xff0c;要运行某个脚本&#xff0c;可能需要安装很多库&#xff0c;一般可以通过提供的requirements.txt来自动安装。但如果没有这个txt&#xff0c;那就得手动一个一个安装&#…

限流,熔断,降级分析

写在前面 本文一起看下限流&#xff0c;熔断&#xff0c;降级的概念。 1:限流 限制单位时间内的请求数&#xff0c;超过的则拒绝或其他。常用的算法有滑动时间窗口&#xff0c;漏桶算法&#xff0c;令牌桶算法。 2:熔断 在分布式的场景中&#xff0c;一个请求可能涉及到多…

**Python**综合案例

Python综合案例 一、系统需求分析 1、需求分析 使用面向对象编程思想完成学员管理系统的开发,具体如下: ① 系统要求:学员数据存储在文件中 ② 系统功能:添加学员、删除学员、修改学员信息、查询学员信息、显示所有学员信息、保存学员信息及退出系统等功能。 2、角色…

ubuntu:beyond compare 4 This license key has been revoked 解决办法

https://www.cnblogs.com/zhibei/p/12095431.html 错误如图所示&#xff1a; 解决办法&#xff1a; &#xff08;1&#xff09;先用find命令找到bcompare所在位置&#xff1a;sudo find /home/ -name *bcompare &#xff08;2&#xff09;进入 /home/whf/.config,删除/bco…

PAT乙级 1025 反转链表

给定一个常数 K 以及一个单链表 L&#xff0c;请编写程序将 L 中每 K 个结点反转。例如&#xff1a;给定 L 为 1→2→3→4→5→6&#xff0c;K 为 3&#xff0c;则输出应该为 3→2→1→6→5→4&#xff1b;如果 K 为 4&#xff0c;则输出应该为 4→3→2→1→5→6&#xff0c;即…

Live800:客户体验策略是什么?企业如何制定客户体验策略?

客户体验策略是企业为了提升顾客对产品或服务的感知和满意度而采取的一系列措施和方法。它关注的是如何创造一个积极、愉悦和有价值的购买过程&#xff0c;从而建立长期的客户关系和忠诚度。客户体验策略是企业成功的关键之一&#xff0c;因为它能够帮助企业在竞争激烈的市场中…

2023“SEED”第四届江苏大数据--新能源赛道 复赛Btop2总结

第一名是真的强&#xff01;基本都是第一&#xff0c;难以撼动。 昨天新能源赛道终于落下了帷幕&#xff0c;真的不是一般的卷。最后的排名都到了0.0几分的差距。跟队友很辛运复赛B榜单目前进入top3的行列&#xff0c;下面简单总结一下赛事过程。 初赛按照天级别预测未来一周各…

mfc100u.dll文件丢失了要怎么解决?修复mfc100u.dll详细指南

mfc100u.dll文件丢失了要怎么解决?首先让我们扒一扒什么是 mfc100u.dll。这玩意儿是 Microsoft Visual Studio 2010 的一部分&#xff0c;它就像一款程序生活中不可或缺的零件&#xff0c;没了它&#xff0c;程序肯定跑不起来。想想看&#xff0c;没有一个重要的零件&#xff…

QGraphicsItem器件移动及旋转相关问题

一、前言 Qt的图形视图框架中&#xff0c;可以使用如下接口设置图元坐标&#xff1a; void QGraphicsItem::setPos(const QPointF &pos)Sets the position of the item to pos, which is in parent coordinates. For items with no parent, pos is in scene coordinates.…

crc算法的应用 CRC16 CRC32

提到crc算法&#xff0c;可能比较陌生&#xff0c;但是在日常开发中&#xff0c;有如下应用 循环冗余检查_百度百科 本质是一个哈希函数&#xff0c;用于文件校验处理。 CRC16 redis集群使用了crc16 来计算对应的哈希值来计算与 16384 取模&#xff0c;确定当前 key 所在的哈…

关于java选择结构if和else详解

关于java选择结构if和else详解 在上篇文章中我们了解了java的基本流程控制之一用户交互&#xff0c;也讲述了scanner类的使用方式&#xff0c;本篇文章中我们来深入一下下一个java流程控制&#xff0c;if和else&#xff0c;这个是非常关键的&#xff0c;也是我们以后的工作中最…