使用 Motoko 和 Node.js 构建自定义检索系统

社区文章 发布于2024年12月9日

在本教程中,我们将逐步构建一个自定义的嵌入存储和检索系统,该系统使用 Motoko(Internet Computer 上的智能合约语言)和 Node.js(用于构建服务器端应用的 JavaScript 运行时)。这个系统可以存储、检索和管理嵌入——通常用于机器学习或 AI 应用(如推荐引擎或 NLP 系统)中的数值表示。


本文将涵盖:

  1. 理解问题领域

    • 为什么嵌入很重要。
    • 高效存储嵌入的挑战。
  2. 系统设计概述

    • Motoko 在存储中的作用。
    • Node.js 作为暴露 REST API 的桥梁。
  3. 分步实现

    • 设置 Motoko canister。
    • 将 Node.js 与 canister 集成。
    • 构建 REST API。
  4. 增强与扩展

    • 安全考量。
    • 潜在优化。

1. 理解问题领域

什么是嵌入?
嵌入是捕获语义含义的密集数值表示数据。例如:

  • 在 NLP 中,嵌入以数值上更接近的方式表示相似含义的单词或句子。
  • 在推荐系统中,嵌入用于比较项目和用户。

挑战:

  • 存储:嵌入通常是浮点数组,需要结构化存储。
  • 检索:高效查询嵌入至关重要,特别是对于大型数据集。
  • 集成:通过安全且易于访问的 API 暴露这些嵌入。

2. 系统设计概述

架构:

  1. Motoko Canister:部署在 Internet Computer 上的智能合约,用于持久存储嵌入。
  2. Node.js 服务器:作为桥梁,暴露 REST 端点供用户与 canister 交互。
  3. 前端/客户端:(可选)可与 Node.js API 交互以实现 UI/UX。

3. 分步实现

第1步:设置 Motoko Canister

  1. 安装 DFINITY SDK:

    sh -ci "$(curl -fsSL https://smartcontracts.org/install.sh)"
    
  2. 创建一个新的 Motoko 项目:

    dfx new embedding-store
    cd embedding-store
    
  3. main.mo定义 EmbeddingStore Actor

    import 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;
        };
    };
    
  4. 部署 Canister:更新 dfx.json 以定义您的 canister,然后部署

    dfx start --background
    dfx deploy
    
  5. 测试 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 服务器

  1. 初始化 Node.js 项目:

    mkdir embedding-api
    cd embedding-api
    npm init -y
    npm install express body-parser @dfinity/agent dotenv
    
  2. 创建 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}`);
    });
    
  3. 运行服务器:

    node index.js
    

第3步:与 API 交互

  1. 存储嵌入:使用 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]}'
    
  2. 检索嵌入:发送 GET 请求

    curl https://:3000/getEmbeddings
    

4. 增强与扩展

  • 安全性:使用 API 密钥、HTTPS 和速率限制来保护端点。
  • 性能:通过索引嵌入或使用向量搜索来优化存储。
  • 扩展性:将大型嵌入数据拆分到多个 canister 中,实现水平扩展。

总结

通过结合 Motoko 去中心化、持久化存储能力与 Node.js 轻松构建 API 的优势,本教程展示了一个实用系统,用于存储和检索嵌入。此设置具有模块化特性,并且可以通过添加过滤、向量相似性搜索或与前端系统集成等附加功能进行增强。

如果您有任何问题或扩展此系统的想法,请随时联系我们!让我们共同构建可扩展、高效的解决方案。🚀

#Motoko #NodeJS #AI #InternetComputer #教程 #软件开发

社区

注册登录 发表评论