第 2 部分:使用 OpenAI 嵌入和基于提示的检索增强 Motoku LLM 检索系统
社区文章 发布于 2024 年 6 月 28 日

在第 1 部分中,我们使用互联网计算机协议 (ICP)、Motoko 和 Node.js 建立了一个基础的 Motoko LLM 检索系统。现在,我们将通过集成 OpenAI 嵌入和添加基于提示的嵌入检索功能来增强此系统。这包括利用 OpenAI 强大的嵌入 API 为文本数据生成嵌入,然后将其存储在 Motoko canister 中,并启用基于提示的检索。
先决条件
在继续之前,请确保您已完成第 1 部分的设置,并具备以下额外工具和知识:
- 一个 OpenAI API 密钥。
- 熟悉 OpenAI 的嵌入 API。
- 已更新的 Node.js 环境,并安装了
axios
以进行 HTTP 请求。
第四步:集成 OpenAI 嵌入和基于提示的检索
4.1 安装 axios
首先,安装 axios
以便向 OpenAI API 发送 HTTP 请求
npm install axios
4.2 更新环境配置
将您的 OpenAI API 密钥添加到 .env
文件中
OPENAI_API_KEY=<your-openai-api-key>
4.3 修改 Node.js 服务器脚本
更新 server.js
以集成 OpenAI 嵌入 API 并处理基于提示的检索。用以下内容替换现有内容
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const { HttpAgent, Actor } = require('@dfinity/agent');
const { idlFactory } = require('./idl/embedding_store.did.js');
require('dotenv').config();
const app = express();
const port = 3000;
app.use(bodyParser.json());
const canisterId = process.env.CANISTER_ID;
const host = process.env.HOST;
const openaiApiKey = process.env.OPENAI_API_KEY;
// Initialize the agent
const agent = new HttpAgent({ host });
agent.fetchRootKey(); // Necessary for local development
// Create an actor instance
const embeddingStore = Actor.createActor(idlFactory, {
agent,
canisterId,
});
// Helper function to convert BigInt to a string for JSON serialization
const serializeBigInt = (obj) => {
if (typeof obj === 'bigint') {
return obj.toString();
} else if (Array.isArray(obj)) {
return obj.map(serializeBigInt);
} else if (typeof obj === 'object' && obj !== null) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, serializeBigInt(v)])
);
}
return obj;
};
const getOpenAIEmbedding = async (text) => {
try {
const response = await axios.post(
'https://api.openai.com/v1/embeddings',
{ input: text, model: 'text-embedding-ada-002' },
{
headers: {
'Authorization': `Bearer ${openaiApiKey}`,
'Content-Type': 'application/json'
}
}
);
return response.data.data[0].embedding;
} catch (error) {
console.error('Error fetching embedding from OpenAI:', error);
throw new Error('Failed to fetch embedding from OpenAI');
}
};
const cosineSimilarity = (vecA, vecB) => {
const dotProduct = vecA.reduce((sum, a, idx) => sum + a * vecB[idx], 0);
const normA = Math.sqrt(vecA.reduce((sum, a) => sum + a * a, 0));
const normB = Math.sqrt(vecB.reduce((sum, b) => sum + b * b, 0));
return dotProduct / (normA * normB);
};
app.post('/storeEmbedding', async (req, res) => {
const { key, text } = req.body;
try {
if (key !== process.env.SECRET_KEY) {
throw new Error('Invalid key');
}
// Get embedding from OpenAI
const embedding = await getOpenAIEmbedding(text);
await embeddingStore.storeEmbedding(key, text, embedding);
res.status(200).send('Embedding stored successfully.');
} catch (error) {
res.status(500).send(`Error: ${error.message}`);
}
});
app.post('/retrieveEmbeddings', async (req, res) => {
const { prompt } = req.body;
try {
// Get prompt embedding from OpenAI
const promptEmbedding = await getOpenAIEmbedding(prompt);
// Retrieve stored embeddings
const embeddings = await embeddingStore.getEmbeddings();
// Calculate similarities
const results = embeddings.map((embedding) => ({
text: embedding.text,
similarity: cosineSimilarity(promptEmbedding, embedding.embedding),
}));
// Sort by similarity
results.sort((a, b) => b.similarity - a.similarity);
res.status(200).json(serializeBigInt(results));
} catch (error) {
res.status(500).send(`Error: ${error.message}`);
}
});
app.listen(port, () => {
console.log(`Server is running on https://:${port}`);
});
4.4 测试更新后的系统
4.4.1 存储嵌入
使用更新后的端点存储嵌入
curl -X POST https://:3000/storeEmbedding \
-H "Content-Type: application/json" \
-d '{"key": "8529741360", "text": "example text"}'
这将使用 OpenAI 为提供的文本生成嵌入,并将其存储在 Motoko canister 中。
4.4.2 根据提示检索嵌入
根据提示检索嵌入
curl -X POST https://:3000/retrieveEmbeddings \
-H "Content-Type: application/json" \
-d '{"prompt": "example prompt"}'
这将使用 OpenAI 为提示生成嵌入,计算提示嵌入与存储嵌入之间的余弦相似度,并按相似度排序返回结果。
结论
在此增强版本中,我们集成了 OpenAI 嵌入以提高检索系统的性能,并添加了基于提示的检索功能。Node.js 服务器现在与 OpenAI API 交互,为输入文本生成嵌入,然后将其存储在 Motoko canister 中,并且可以根据与给定提示的相似度检索和排名存储的嵌入。此设置利用了 ICP 的去中心化功能和 OpenAI 的高级自然语言处理功能,为 LLM 检索提供了强大的解决方案。
下一步,您可以探索实现更高级的搜索和过滤功能、集成用户身份验证或为您的系统开发前端界面。