使用互联网计算机协议、Motoko 和 Node.js 构建 Motoko LLM 检索系统
社区文章 发布于 2024 年 6 月 27 日

互联网计算机协议 (ICP) 的兴起彻底改变了开发人员构建去中心化应用程序的方式。将专为 ICP 设计的强大语言 Motoko 与 Node.js 集成,可以构建一个强大且可扩展的大型语言模型 (LLM) 检索系统。本文将引导您构建这样一个系统,重点介绍嵌入存储和检索等关键组件。
先决条件
在深入实施之前,请确保您具备以下工具和知识:
- 对 Motoko 和 Node.js 有基本了解。
- 机器上已安装 Node.js 和 npm。
- 已安装 DFINITY SDK。
- 对 RESTful API 有基本了解。
第一步:设置 Motoko Canister
首先,我们将创建一个 Motoko Canister 来存储和检索嵌入向量。
1.1 定义 EmbeddingStore Actor
创建一个名为 EmbeddingStore.mo
的新文件,并按如下方式定义 EmbeddingStore
actor:
import Array "mo:base/Array";
import Nat "mo:base/Nat";
import Time "mo:base/Time";
import Error "mo:base/Error";
actor EmbeddingStore {
type Embedding = {
text: Text;
embedding: [Float];
createdAt: Int;
};
stable var embeddings: [Embedding] = [];
stable let secretKey: Text = "8529741360"; // Replace with your actual secret key
public shared func storeEmbedding(key: Text, text: Text, embedding: [Float]) : async () {
if (key == secretKey) {
let timestamp = Time.now();
embeddings := Array.append(embeddings, [{
text = text;
embedding = embedding;
createdAt = timestamp;
}]);
} else {
throw Error.reject("Invalid key. Access denied.");
}
};
public query func getEmbeddings() : async [Embedding] {
return embeddings;
};
};
1.2 部署 Canister
将 EmbeddingStore
Canister 部署到互联网计算机:
- 打开终端并导航到项目目录。
- 运行
dfx start
启动本地副本。 - 通过将
EmbeddingStore
配置添加到dfx.json
文件来创建新的 Canister。 - 使用
dfx deploy
部署 Canister。
第二步:设置 Node.js 服务器
接下来,我们将设置一个 Node.js 服务器,用于与 Motoko Canister 进行交互。
2.1 初始化项目
- 为您的 Node.js 项目创建一个新目录。
- 运行
npm init -y
初始化项目。 - 安装必要的依赖项:
npm install express body-parser @dfinity/agent dotenv
2.2 创建服务器脚本
创建一个名为 server.js
的新文件,并添加以下代码:
const express = require('express');
const bodyParser = require('body-parser');
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;
// 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;
};
app.post('/storeEmbedding', async (req, res) => {
const { key, text, embedding } = req.body;
try {
if (key !== process.env.SECRET_KEY) {
throw new Error('Invalid key');
}
// Convert embedding to float64 if not already
const embeddingFloat64 = embedding.map(Number);
await embeddingStore.storeEmbedding(key, text, embeddingFloat64);
res.status(200).send('Embedding stored successfully.');
} catch (error) {
res.status(500).send(`Error: ${error.message}`);
}
});
app.get('/getEmbeddings', async (req, res) => {
try {
const embeddings = await embeddingStore.getEmbeddings();
res.status(200).json(serializeBigInt(embeddings));
} catch (error) {
res.status(500).send(`Error: ${error.message}`);
}
});
app.listen(port, () => {
console.log(`Server is running on https://:${port}`);
});
2.3 环境变量配置
在项目目录中创建一个 .env
文件,并添加以下环境变量:
CANISTER_ID=<your-canister-id>
HOST=https://:8000
SECRET_KEY=8529741360
将 <your-canister-id>
替换为从部署步骤中获得的实际 canister ID。
第三步:测试系统
Canister 部署完成,服务器设置完毕后,是时候测试嵌入向量存储和检索功能了。
3.1 存储嵌入向量
使用 curl
或 Postman 等工具存储嵌入向量:
curl -X POST https://:3000/storeEmbedding \
-H "Content-Type: application/json" \
-d '{"key": "8529741360", "text": "example text", "embedding": [0.1, 0.2, 0.3]}'
3.2 检索嵌入向量
通过访问以下端点检索已存储的嵌入向量:
curl https://:3000/getEmbeddings
结论
恭喜!您已成功使用互联网计算机协议、Motoko 和 Node.js 构建了一个 Motoko LLM 检索系统。该系统允许您安全地存储和检索文本嵌入向量,利用互联网计算机的去中心化能力。下一步,可以考虑添加更多功能,例如高级搜索功能、身份验证机制以及与前端应用程序的集成。