使用TypeScript和FAISS构建语义搜索:完整实战指南

云信安装大师
90
AI 质量分
4 5 月, 2025
2 分钟阅读
0 阅读

使用TypeScript和FAISS构建语义搜索:完整实战指南

引言

语义搜索是现代应用中越来越重要的功能,它能够理解查询的意图而不仅仅是匹配关键词。本文将带你使用TypeScript和Facebook的FAISS库构建一个完整的语义搜索系统。FAISS是一个高效的相似性搜索和密集向量聚类的库,特别适合处理大规模向量数据。

准备工作

环境要求

  • Node.js (建议16.x或更高版本)
  • TypeScript (4.x或更高版本)
  • Python (3.7+,用于FAISS安装)
  • 基本的TypeScript和Node.js知识

安装必要依赖

首先创建一个新的TypeScript项目并安装所需依赖:

代码片段
mkdir semantic-search && cd semantic-search
npm init -y
npm install typescript @types/node ts-node --save-dev
npm install faiss-node @tensorflow-models/universal-sentence-encoder

第一步:设置项目结构

创建以下项目结构:

代码片段
semantic-search/
├── src/
│   ├── index.ts          # 主入口文件
│   ├── search.ts         # 搜索功能实现
│   └── types.ts          # 类型定义
├── data/                 # 存放测试数据
├── tsconfig.json         # TypeScript配置
└── package.json

初始化TypeScript配置:

代码片段
npx tsc --init

tsconfig.json中确保以下配置:

代码片段
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

第二步:准备数据并生成嵌入向量

我们将使用Universal Sentence Encoder来生成文本的嵌入向量。

代码片段
// src/search.ts
import * as use from '@tensorflow-models/universal-sentence-encoder';
import { IndexFlatL2 } from 'faiss-node';

export class SemanticSearch {
  private index: IndexFlatL2;
  private model: use.UniversalSentenceEncoder;
  private documents: string[] = [];

  constructor() {
    this.index = new IndexFlatL2(512); // USE编码维度为512
  }

  async initialize() {
    // 加载预训练模型
    this.model = await use.load();
    console.log('Model loaded successfully');
  }

  async addDocuments(docs: string[]) {
    this.documents = [...this.documents, ...docs];

    // 生成嵌入向量
    const embeddings = await this.model.embed(docs);

    // FAISS期望的输入是二维数组[文档数][维度]
    const vectors = Array.from(embeddings.arraySync());

    // 添加到索引中(FAISS会自动处理维度)
    this.index.add(vectors);

    console.log(`Added ${docs.length} documents to index`);
  }

  async search(query: string, k: number = 3) {
    if (!this.model) {
      throw new Error('Model not initialized. Call initialize() first.');
    }

    // 生成查询嵌入向量
    const queryEmbedding = await this.model.embed([query]);

    // FAISS搜索返回距离和索引(这里我们只需要索引)
    const { distances, labels } = this.index.search(
      Array.from(queryEmbedding.arraySync()[0]),
      k
    );

    // labels是文档在addDocuments时的索引数组
    return labels[0].map(idx => ({
      document: this.documents[idx],
      score: distances[0][idx]
    }));
  }
}

第三步:实现主程序

“`typescript
// src/index.ts
import { SemanticSearch } from ‘./search’;

async function main() {
const searchEngine = new SemanticSearch();

try {
await searchEngine.initialize();

代码片段
// 添加一些示例文档(在实际应用中可能来自数据库或文件)
const documents = [
  'The quick brown fox jumps over the lazy dog',
  'I enjoy programming in TypeScript',
  'Semantic search understands the meaning of queries',
  'FAISS is a library for efficient similarity search',
  'Node.js is a JavaScript runtime built on Chrome\'s V8 engine'
];

await searchEngine.addDocuments(documents);

// 执行搜索查询
const results = await searchEngine.search('JavaScript runtime', -1);

console.log('Search results:');
results.forEach((result, i) => {
  console.log(`${i + 1}. ${result.document} (score: ${result.score.toFixed(2)})`);
  console.log('---');

  if (i === results.length -1) process.exit(0);

原创 高质量