npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ragsdk/retrieval

v0.2.1

Published

检索模块,支持查询变换、多种搜索策略与后处理

Downloads

279

Readme

@ragsdk/retrieval

检索策略包,提供多种检索算法和扩展点。

内置检索器

VectorSearch - 向量语义搜索

import { VectorSearch } from '@ragsdk/retrieval';

const vectorSearch = new VectorSearch(embedding, store);
const results = await vectorSearch.retrieve('查询文本', { topK: 5, threshold: 0.7 });

KeywordSearch - BM25 关键词搜索

import { KeywordSearch } from '@ragsdk/retrieval';

const keywordSearch = new KeywordSearch(chunks);
const results = await keywordSearch.retrieve('TypeScript 编程', { topK: 5 });

内置使用 Intl.Segmenter 分词,支持中英文混合。

FusionSearch - 加权融合搜索

import { FusionSearch } from '@ragsdk/retrieval';

const fusion = new FusionSearch(vectorSearch, keywordSearch, 0.6, 0.4);
const results = await fusion.retrieve('查询文本', { topK: 5 });

使用 Min-Max 归一化后加权融合。

RRFSearch - 排名融合

import { RRFSearch } from '@ragsdk/retrieval';

const rrf = new RRFSearch(60); // k=60
const results = rrf.fuse([vectorResults, keywordResults], 5, 0.01);

业界标准方案,只看排名不看分数。

SmallToBigSearch - 小块检索大块回溯

import { SmallToBigSearch } from '@ragsdk/retrieval';

const smallToBig = new SmallToBigSearch(innerRetriever, store, allChunks);
const results = await smallToBig.retrieve('查询文本', { topK: 5 });

HierarchicalSearch - 分层检索

import { HierarchicalSearch } from '@ragsdk/retrieval';

const hierarchical = new HierarchicalSearch(embedding, summaryStore, contentStore);
const results = await hierarchical.retrieve('查询文本', { topK: 5 });

自定义检索器

实现 Retriever 接口即可:

import type { Retriever, RetrieveOptions, SearchResult } from '@ragsdk/core';

class MyCustomRetriever implements Retriever {
  async retrieve(query: string, options?: RetrieveOptions): Promise<SearchResult[]> {
    // 你的检索逻辑
    return [
      {
        chunk: { id: '1', documentId: 'doc1', content: '内容', metadata: {} },
        score: 0.95,
        source: 'custom', // 或 'vector' | 'keyword' | 'graph' | 'fusion'
      },
    ];
  }
}

示例:集成第三方分词库

import MiniSearch from 'minisearch';
import type { Retriever, RetrieveOptions, SearchResult, Chunk } from '@ragsdk/core';

export class MiniSearchRetriever implements Retriever {
  private miniSearch: MiniSearch;
  private chunks: Map<string, Chunk>;

  constructor(chunks: Chunk[]) {
    this.chunks = new Map(chunks.map(c => [c.id, c]));
    this.miniSearch = new MiniSearch({
      fields: ['content'],
      searchOptions: { boost: { title: 2 } },
    });
    this.miniSearch.addAll(chunks);
  }

  async retrieve(query: string, options?: RetrieveOptions): Promise<SearchResult[]> {
    const results = this.miniSearch.search(query, {
      prefix: true,
      fuzzy: 0.2,
    });

    return results
      .slice(0, options?.topK ?? 5)
      .filter(r => !options?.threshold || r.score >= options.threshold)
      .map(r => ({
        chunk: this.chunks.get(r.id)!,
        score: r.score,
        source: 'keyword' as const,
      }));
  }
}

示例:集成 Elasticsearch

import { Client } from '@elastic/elasticsearch';
import type { Retriever, RetrieveOptions, SearchResult } from '@ragsdk/core';

export class ElasticsearchRetriever implements Retriever {
  constructor(private client: Client, private index: string) {}

  async retrieve(query: string, options?: RetrieveOptions): Promise<SearchResult[]> {
    const response = await this.client.search({
      index: this.index,
      query: { match: { content: query } },
      size: options?.topK ?? 5,
      min_score: options?.threshold,
    });

    return response.hits.hits.map(hit => ({
      chunk: {
        id: hit._id,
        documentId: hit._source.documentId,
        content: hit._source.content,
        metadata: hit._source.metadata ?? {},
      },
      score: hit._score ?? 0,
      source: 'keyword' as const,
    }));
  }
}

检索选项

所有检索器都支持:

interface RetrieveOptions {
  topK?: number;      // 返回结果数量,默认 5
  threshold?: number; // 最低分数阈值
  filter?: Record<string, unknown>; // 元数据过滤
}

查询变换器

QueryRewriter - 查询改写

import { QueryRewriter } from '@ragsdk/retrieval';

const rewriter = new QueryRewriter(llm);
const rewritten = await rewriter.transform('模糊查询');
// → "精确的检索查询"

MultiQueryExpander - 多查询扩展

import { MultiQueryExpander } from '@ragsdk/retrieval';

const expander = new MultiQueryExpander(llm, { numQueries: 3 });
const queries = await expander.transform('原始查询');
// → ["变体1", "变体2", "变体3"]

QueryDecomposer - 查询分解

import { QueryDecomposer } from '@ragsdk/retrieval';

const decomposer = new QueryDecomposer(llm);
const subQueries = await decomposer.transform('复杂问题');
// → ["子问题1", "子问题2"]

HyDETransformer - 假设文档嵌入

import { HyDETransformer } from '@ragsdk/retrieval';

const hyde = new HyDETransformer(llm);
const hypothetical = await hyde.transform('问题');
// → "假设性的回答文档,用于向量检索"

后处理器

ThresholdPostProcessor - 阈值过滤

import { ThresholdPostProcessor } from '@ragsdk/retrieval';

const threshold = new ThresholdPostProcessor({ threshold: 0.7, maxResults: 10 });
const filtered = await threshold.process(results, query);

ContextEnrichPostProcessor - 上下文丰富

import { ContextEnrichPostProcessor } from '@ragsdk/retrieval';

const enricher = new ContextEnrichPostProcessor(store, { windowSize: 2 });
const enriched = await enricher.process(results, query);

SelectiveContextPostProcessor - 选择性上下文

import { SelectiveContextPostProcessor } from '@ragsdk/retrieval';

const selector = new SelectiveContextPostProcessor(llm);
const selected = await selector.process(results, query);

CompressionPostProcessor - 内容压缩

import { CompressionPostProcessor } from '@ragsdk/retrieval';

const compressor = new CompressionPostProcessor(llm, { maxTokens: 200 });
const compressed = await compressor.process(results, query);

RerankerPostProcessor - 重排序

import { RerankerPostProcessor } from '@ragsdk/retrieval';

const reranker = new RerankerPostProcessor(scorer, { topK: 5 });
const reranked = await reranker.process(results, query);

组合使用

import { VectorSearch, QueryRewriter, ThresholdPostProcessor } from '@ragsdk/retrieval';

// 1. 查询改写
const rewriter = new QueryRewriter(llm);
const rewrittenQuery = await rewriter.transform(userQuery);

// 2. 向量检索
const retriever = new VectorSearch(embedding, store);
const results = await retriever.retrieve(rewrittenQuery, { topK: 10, threshold: 0.5 });

// 3. 后处理
const threshold = new ThresholdPostProcessor({ threshold: 0.7 });
const filtered = await threshold.process(results, rewrittenQuery);

// 4. 生成
const answer = await generator.generate(rewrittenQuery, filtered.map(r => r.chunk));