使用 Motoko 和 Node.js 构建自定义检索系统
社区文章 发布于2024年12月9日
在本教程中,我们将逐步构建一个自定义的嵌入存储和检索系统,该系统使用 Motoko(Internet Computer 上的智能合约语言)和 Node.js(用于构建服务器端应用的 JavaScript 运行时)。这个系统可以存储、检索和管理嵌入——通常用于机器学习或 AI 应用(如推荐引擎或 NLP 系统)中的数值表示。
本文将涵盖:
理解问题领域
- 为什么嵌入很重要。
- 高效存储嵌入的挑战。
系统设计概述
- Motoko 在存储中的作用。
- Node.js 作为暴露 REST API 的桥梁。
分步实现
- 设置 Motoko canister。
- 将 Node.js 与 canister 集成。
- 构建 REST API。
增强与扩展
- 安全考量。
- 潜在优化。
1. 理解问题领域
什么是嵌入?
嵌入是捕获语义含义的密集数值表示数据。例如:
- 在 NLP 中,嵌入以数值上更接近的方式表示相似含义的单词或句子。
- 在推荐系统中,嵌入用于比较项目和用户。
挑战:
- 存储:嵌入通常是浮点数组,需要结构化存储。
- 检索:高效查询嵌入至关重要,特别是对于大型数据集。
- 集成:通过安全且易于访问的 API 暴露这些嵌入。
2. 系统设计概述
架构:
- Motoko Canister:部署在 Internet Computer 上的智能合约,用于持久存储嵌入。
- Node.js 服务器:作为桥梁,暴露 REST 端点供用户与 canister 交互。
- 前端/客户端:(可选)可与 Node.js API 交互以实现 UI/UX。
3. 分步实现
第1步:设置 Motoko Canister
安装 DFINITY SDK:
sh -ci "$(curl -fsSL https://smartcontracts.org/install.sh)"
创建一个新的 Motoko 项目:
dfx new embedding-store cd embedding-store
在
main.mo
中定义EmbeddingStore
Actorimport Array "mo:base/Array"; import Time "mo:base/Time"; actor EmbeddingStore { type Embedding = { text: Text; embedding: [Float]; createdAt: Int; }; stable var embeddings: [Embedding] = []; public shared func storeEmbedding(text: Text, embedding: [Float]) : async () { let timestamp = Time.now(); embeddings := Array.append(embeddings, [{ text = text; embedding = embedding; createdAt = timestamp; }]); }; public query func getEmbeddings() : async [Embedding] { return embeddings; }; };
部署 Canister:更新
dfx.json
以定义您的 canister,然后部署dfx start --background dfx deploy
测试 Canister:使用
dfx canister call
测试方法dfx canister call embedding-store storeEmbedding '( "Sample Text", [1.0, 0.5, 0.25] )' dfx canister call embedding-store getEmbeddings
第2步:设置 Node.js 服务器
初始化 Node.js 项目:
mkdir embedding-api cd embedding-api npm init -y npm install express body-parser @dfinity/agent dotenv
创建
index.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; const agent = new HttpAgent({ host }); agent.fetchRootKey(); const embeddingStore = Actor.createActor(idlFactory, { agent, canisterId, }); app.post('/storeEmbedding', async (req, res) => { const { text, embedding } = req.body; try { const embeddingFloat64 = embedding.map(Number); await embeddingStore.storeEmbedding(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(embeddings); } catch (error) { res.status(500).send(`Error: ${error.message}`); } }); app.listen(port, () => { console.log(`Server is running on https://:${port}`); });
运行服务器:
node index.js
第3步:与 API 交互
存储嵌入:使用
curl
或 Postman 等工具发送POST
请求curl -X POST https://:3000/storeEmbedding \ -H "Content-Type: application/json" \ -d '{"text":"Sample Text","embedding":[0.1,0.2,0.3]}'
检索嵌入:发送
GET
请求curl https://:3000/getEmbeddings
4. 增强与扩展
- 安全性:使用 API 密钥、HTTPS 和速率限制来保护端点。
- 性能:通过索引嵌入或使用向量搜索来优化存储。
- 扩展性:将大型嵌入数据拆分到多个 canister 中,实现水平扩展。
总结
通过结合 Motoko 去中心化、持久化存储能力与 Node.js 轻松构建 API 的优势,本教程展示了一个实用系统,用于存储和检索嵌入。此设置具有模块化特性,并且可以通过添加过滤、向量相似性搜索或与前端系统集成等附加功能进行增强。
如果您有任何问题或扩展此系统的想法,请随时联系我们!让我们共同构建可扩展、高效的解决方案。🚀
#Motoko #NodeJS #AI #InternetComputer #教程 #软件开发