前言
今天我们利用 openai 开放的一些api来构建个人专属的知识库。在这这篇文章中会使用到以下相关的技术能力或者服务。
- Next.js (React框架) + Vercel托管
- Supabase (使用他们的pgvector实现作为向量数据库、用户身份验证)
- OpenAI API (用于生成嵌入和聊天完成)
- Plasmo (编写浏览器插件)
- readability (实现正文提取)
- TailwindCSS (用于样式)
感兴趣的同学可以继续往下阅读,不要恐惧,前端同学是完全可以看懂并且理解的;期望通过这篇文章能给前端同学一些启发,利用APIs的研发方式做一些感兴趣的东西。
什么是 APIs

APIs(api 即 Server);因为基于以上的框架以及云服务厂商提供的部署&托管能力,作为前端开发同学仅需编写和维护前端,我几乎无需关注用户校验、数据库管理、网站部署、托管等个人不是太熟悉的方式。最关键的一个点是我的应用开发核心是使用的 open ai 提供的api, 而不是使用的云服务模型。
这个时候可能就会有人立刻想到 BaaS(后端即服务) ; 是一种云服务模型;我觉得还是有一定的区别的,我开发的这个应用核心是依赖 open ai 提供的api的能力。我把 open ai 提供的 api 作为了后端服务,所以我觉得喊为 APIs 更加合适。说句简单的,我一个前端,通过调接口就能轻轻松松的开发一个应用,完全不用考虑服务器和部署。
功能概述
创建和存储嵌入:
- 网页被爬取,转换为纯文本并分割成1000个字符的文档
- 网页展示不能全部都录入,像侧边栏的广告、页脚等这些信息是不需要的,这个时候就需要使用 readability 提取正文
- 为了方便的录入,体用plasmo 开发一个插件,方便快速的提前正文;并且方便绕过鉴权。
- 使用OpenAI的嵌入API,利用"text-embedding-ada-002"模型为每个文档生成嵌入
- 将嵌入向量存储在Supabase的postgres表中,使用pgvector; 表包含三列: 文档文本、源URL以及从OpenAI API返回的嵌入向量。
响应查询:
- 从用户提示生成单个嵌入向量
- 使用该嵌入向量对向量数据库进行相似性搜索
- 使用相似性搜索的结果构建GPT-3.5/GPT-4的提示
- 然后将GPT的响应流式传输给用户。
我用ai生成了一个流程图; 能够更好的解释我的想法,再次给 ai 点个赞。


什么是嵌入 Embeddings?
在功能描述中提到了文档嵌入,在此简单的介绍一下,方便引导大家更好的开放自己的思路,OpenAI 的文本嵌入衡量文本字符串的相关性。嵌入通常用于:
- Search (where results are ranked by relevance to a query string)
搜索(其中结果按与查询字符串的相关性排名) - Clustering (where text strings are grouped by similarity)
聚类(其中文本字符串按相似性分组) - Recommendations (where items with related text strings are recommended)
建议(建议使用具有相关文本字符串的项目) - Anomaly detection (where outliers with little relatedness are identified)
异常检测(识别相关性很小的异常值) - Diversity measurement (where similarity distributions are analyzed)
多样性测量(分析相似性分布) - Classification (where text strings are classified by their most similar label)
分类(其中文本字符串按其最相似的标签进行分类)
比如说下面有三个短语:
- "狗咬耗子"
- "犬类捕食啮齿动物"
- "我家养了只狗" 那很明显1和2是类似的,即使它们之间没有任何相同的文字。那么怎么让计算机知道1和2很相似呢?
Embeddings将文本压缩成分布式的连续值数据(vectors向量)。 如果我们把之前的短语对应的向量能画在坐标轴上,它看起来像图中那样。短句1和短句2会离得很近,而短句3和他们离得比较远。

图中很明确的表现出了 Embeddings 的"关联性"。
具体先介绍这么多,感兴趣的可以再深入研究,个人也有可能再开一篇文章来详细介绍。
入门指南
以下设置指南假定您至少对使用 React 和 Next.js 开发Web应用程序有基本的了解。熟悉 OpenAI API 和 Supabase 会使程序正常运行有所帮助,但不是必需的。
设置Supabase
- 创建Supabase帐户和项目。
- 注意:Supabase对pgvector的支持相对较新(02/2023),建议新建一个项目
- 接着,我们将启用Vector扩展。在Supabase中,可以通过Web门户的"Database"→"Extensions"来完成此操作。您也可以在SQL中运行以下命令完成此操作:
sql
create extension vector;
- 接下来,让我们创建一个表来存储我们的文档及其嵌入。转到SQL编辑器并运行以下查询:
sql
create table documents (
id bigserial primary key,
content text,
url text,
embedding vector (1536)
);
- 最后,我们将创建一个用于执行相似性搜索的函数。转到SQL编辑器并运行以下查询:
sql
create or replace function match_documents (
query_embedding vector(1536),
similarity_threshold float,
match_count int,
project_id uuid,
user_id uuid
)
returns table (
id bigint,
content text,
url text,
similarity float,
project_id uuid,
user_id uuid
)
language plpgsql
as $$
begin
return query
select
documents.id,
documents.content,
documents.url,
1 - (documents.embedding <=> query_embedding) as similarity
from documents
where 1 - (documents.embedding <=> query_embedding) > similarity_threshold
and documents.project_id = project_id
and documents.user_id = user_id
order by documents.embedding <=> query_embedding
limit match_count;
end;
$$;
搭建 Next.js 项目
设置本地环境
- 安装依赖项
bash
npm install
- 在根目录中创建一个名为
.env.local
的文件以存储环境变量:
bash
cp .env.local.example .env.local
- 打开
.env.local
文件,添加您的Supabase项目URL和API密钥。您可以在Supabase Web门户的Project
→API
下找到它们。API密钥应存储在SUPABASE_ANON_KEY
变量中,项目URL应存储在NEXT_PUBLIC_SUPABASE_URL
下。 - 将您的OpenAI API密钥添加到
.env.local
文件。您可以在OpenAI Web门户的API Keys
下找到它。API密钥应存储在OPENAI_API_KEY
变量中。 - 启动应用程序
bash
npm run dev
- 在浏览器中打开http://localhost:3000 查看应用程序

最终实现的效果如下:
web 端 相关代码已经放在 github 中;需要的自取。 github.com/wugaoliang1...
文档智能提取插件
在开发浏览器插件的时候,使用 plasmo 可以方便快速构建一个简单应用。在这里强调一下,使用plasmo的时候请优先阅读一下他的使用文档,方便快速启动项目,docs.plasmo.com/framework
base
pnpm create plasmo --with-tailwindcss
执行完成后生成文件目录如下:
利用 gpt-4-vision-preview 生成界面
能让ai做的就让ai做吧,虽然我是前端,但是UI界面我不想写了;


我觉它生成的挺好看的,代码我就直接拿来用吧。我也没有UI设计。
html
<div class="min-h-screen bg-gray-800 flex items-center justify-center">
<div class="bg-gray-900 p-6 rounded-lg shadow-xl text-white w-full max-w-sm">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold">文章阅读</h2>
<img src="https://placehold.co/50x50" alt="Placeholder" class="rounded-full">
</div>
<p class="text-sm mb-6">这是一段示例文本,用于模拟可能存在的内容。请基于实际需要替换或扩展这段文本。</p>
<div class="flex items-center justify-center">
<div class="animate-pulse flex items-center">
<div class="h-2.5 bg-blue-400 w-8 rounded-full mr-1"></div>
<div class="h-2.5 bg-blue-400 w-8 rounded-full mr-1"></div>
<div class="h-2.5 bg-blue-400 w-8 rounded-full"></div>
</div>
</div>
</div>
</div>
将相关的代码放在 content.tsx 文件中,随意打开一个界面就可以看到如下效果了:
它还有呼吸灯的效果,太强了

整体感觉还挺不错,接下来就是使用 readability 请求正文,并调用 Embeddings 相关接口,将文章正文嵌入即可。
其实我编码过程很简单,就是通过备注生成代码,给大家简单录个屏幕。 再次给ai点个赞

在此不再过多的介绍相关技术细节内容。相关代码已提交到 github 需要请自取
效果展示
-
提取文章
-
嵌入文章的log
-
询问展示的效果:
更多
个人会持续探究、发掘、分享 ai 相关的有趣知识,感兴趣的可以多多交流。