Token导航 LogoToken导航TokenDH.com
研究检索权限需确认github未标认证来源可访问许可证需确认审计通过

fuzzy-matching模糊匹配

Agent Skill

fuzzy-matching 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

890

周安装

36

GitHub Stars

777

下载量

279
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:fuzzy-matching(模糊匹配)
来源仓库:https://github.com/dadbodgeoff/drift
仓库路径:skills/fuzzy-matching
安装命令:
npx skills add https://github.com/dadbodgeoff/drift --skill fuzzy-matching
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/dadbodgeoff/drift --skill fuzzy-matching

简介

fuzzy-matching 用于查找、检索和筛选相关信息。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中根据关键词快速定位候选结果。
  • 通过 npx skills add 命令从指定仓库安装,需确认权限和维护状态。
  • 使用前应核实是否会触发联网、命令执行或文件读写操作。
  • 建议结合原始 README 和项目实际情况验证具体用法。

SKILL.md

Multi-Stage Fuzzy Matching

Production-grade fuzzy matching for inventory items, products, or entity reconciliation.

When to Use This Skill

  • Matching vendor SKUs to inventory items
  • Entity reconciliation with varying name formats
  • Product deduplication across sources
  • Any scenario where exact matching misses valid matches

Core Concepts

Three-stage pipeline: PostgreSQL trigram (fast pre-filter) → Salient overlap (quick filter) → Multi-factor similarity (expensive, accurate). Achieves O(log n) with proper indexing.

Stage 1: PostgreSQL Trigram (fast) → 50 candidates
Stage 2: Salient Overlap Check (fast) → ~20 candidates
Stage 3: Multi-Factor Similarity (expensive) → ranked results

Implementation

Python

import re
import math
from typing import List, Dict, Optional, Set
from decimal import Decimal

class TextNormalizer:
    """Normalizes text for consistent matching."""

    BRAND_PATTERNS = [r'\bsysco\b', r'\bus foods\b', r'\bpremium\b', r'\bselect\b']
    UNIT_MAP = {
        'lb': 'pound', 'lbs': 'pound', 'oz': 'ounce',
        'kg': 'kilogram', 'g': 'gram', 'gal': 'gallon',
    }
    STOPWORDS = {'the', 'and', 'or', 'with', 'of', 'boneless', 'bnls', 'fresh', 'frozen'}

    def normalize_text(self, text: str) -> str:
        if not text:
            return ""
        normalized = text.lower().strip()

        for pattern in self.BRAND_PATTERNS:
            normalized = re.sub(pattern, '', normalized, flags=re.IGNORECASE)

        for variant, standard in self.UNIT_MAP.items():
            normalized = re.sub(r'\b' + variant + r'\b', standard, normalized, flags=re.IGNORECASE)

        normalized = re.sub(r'[^\w\s-]', ' ', normalized)
        return ' '.join(normalized.split())

    def tokenize(self, text: str) -> List[str]:
        normalized = self.normalize_text(text)
        tokens = re.split(r'[\s-]+', normalized)
        return [t for t in tokens if t and t not in self.STOPWORDS and len(t) >= 2]

class SimilarityCalculator:
    """Calculates multi-factor similarity scores."""

    WEIGHTS = {
        'name_similarity': 0.55,
        'token_similarity': 0.25,
        'size_similarity': 0.15,
        'category_similarity': 0.05,
    }

    def has_salient_overlap(self, tokens1: List[str], tokens2: List[str]) -> bool:
        """Quick pre-filter: do items share any salient words?"""
        salient1 = {t for t in tokens1 if len(t) >= 3}
        salient2 = {t for t in tokens2 if len(t) >= 3}
        if not salient1 or not salient2:
            return False
        return len(salient1 & salient2) > 0

    def trigram_cosine_similarity(self, text1: str, text2: str) -> float:
        if not text1 or not text2:
            return 0.0
        if text1 == text2:
            return 1.0

        def get_trigrams(text: str) -> Set[str]:
            padded = f"  {text}  "
            return {padded[i:i+3] for i in range(len(padded) - 2)}

        t1, t2 = get_trigrams(text1), get_trigrams(text2)
        intersection = t1 & t2
        if not intersection:
            return 0.0
        return len(intersection) / math.sqrt(len(t1) * len(t2))

    def weighted_jaccard_similarity(self, tokens1: List[str], tokens2: List[str]) -> float:
        if not tokens1 or not tokens2:
            return 0.0
        set1, set2 = set(tokens1), set(tokens2)
        if set1 == set2:
            return 1.0

        def weight(t: str) -> float:
            return 2.0 if len(t) >= 5 else 1.5 if len(t) >= 3 else 1.0

        intersection = set1 & set2
        union = set1 | set2
        return sum(weight(t) for t in intersection) / sum(weight(t) for t in union)

    def size_similarity(self, size1: Optional[Decimal], size2: Optional[Decimal]) -> float:
        if size1 is None or size2 is None:
            return 0.5
        if size1 == size2:
            return 1.0
        ratio = float(min(size1, size2) / max(size1, size2))
        if ratio >= 0.95: return 1.0
        if ratio >= 0.85: return 0.8
        if ratio >= 0.70: return 0.5
        return 0.0

    def calculate_advanced_similarity(self, item1: Dict, item2: Dict) -> float:
        name1 = item1.get('normalized_name', '')
        name2 = item2.get('normalized_name', '')

        name_sim = self.trigram_cosine_similarity(name1, name2)
        token_sim = self.weighted_jaccard_similarity(
            self.normalizer.tokenize(name1),
            self.normalizer.tokenize(name2)
        )
        size_sim = self.size_similarity(
            self.extract_size(name1),
            self.extract_size(name2)
        )
        cat_sim = 1.0 if item1.get('category') == item2.get('category') else 0.0

        return (
            self.WEIGHTS['name_similarity'] * name_sim +
            self.WEIGHTS['token_similarity'] * token_sim +
            self.WEIGHTS['size_similarity'] * size_sim +
            self.WEIGHTS['category_similarity'] * cat_sim
        )
class FuzzyItemMatcher:
    """Complete 3-stage fuzzy matching pipeline."""

    THRESHOLDS = {
        'auto_match': 0.95,
        'review_match': 0.85,
        'min_similarity': 0.70,
        'trigram_filter': 0.3,
    }

    def __init__(self, db_client):
        self.client = db_client
        self.normalizer = TextNormalizer()
        self.calculator = SimilarityCalculator()

    def find_similar_items(
        self,
        target_name: str,
        user_id: str,
        category: Optional[str] = None,
        limit: int = 10,
    ) -> List[Dict]:
        normalized_target = self.normalizer.normalize_text(target_name)
        target_tokens = self.normalizer.tokenize(target_name)

        # Stage 1: PostgreSQL trigram (fast)
        candidates = self._trigram_search(normalized_target, user_id, category, 0.3, 50)
        if not candidates:
            return []

        # Stage 2: Salient overlap filter (fast)
        filtered = []
        for candidate in candidates:
            candidate_tokens = self.normalizer.tokenize(candidate['normalized_name'])
            if self.calculator.has_salient_overlap(target_tokens, candidate_tokens):
                filtered.append(candidate)
        if not filtered:
            return []

        # Stage 3: Advanced similarity (expensive)
        target_item = {'normalized_name': normalized_target, 'category': category}
        results = []
        for candidate in filtered:
            similarity = self.calculator.calculate_advanced_similarity(target_item, candidate)
            if similarity >= self.THRESHOLDS['min_similarity']:
                results.append({**candidate, 'similarity_score': similarity})

        results.sort(key=lambda x: x['similarity_score'], reverse=True)
        return results[:limit]

    def _trigram_search(self, name: str, user_id: str, category: Optional[str], threshold: float, limit: int) -> List[Dict]:
        result = self.client.rpc('find_similar_items', {
            'target_name': name,
            'target_user_id': user_id,
            'similarity_threshold': threshold,
            'result_limit': limit,
        }).execute()
        return result.data or []

    def get_match_recommendation(self, score: float) -> Dict:
        if score >= self.THRESHOLDS['auto_match']:
            return {'action': 'auto_match', 'confidence': 'high', 'needs_review': False}
        elif score >= self.THRESHOLDS['review_match']:
            return {'action': 'review', 'confidence': 'medium', 'needs_review': True}
        return {'action': 'create_new', 'confidence': 'low', 'needs_review': False}

SQL Setup

CREATE EXTENSION IF NOT EXISTS pg_trgm;

ALTER TABLE inventory_items ADD COLUMN normalized_name TEXT;
CREATE INDEX idx_items_trgm ON inventory_items USING GIN (normalized_name gin_trgm_ops);

CREATE FUNCTION find_similar_items(target_name TEXT, target_user_id UUID, similarity_threshold FLOAT, result_limit INT)
RETURNS TABLE (id UUID, name TEXT, normalized_name TEXT, category TEXT, similarity_score FLOAT) AS $$
BEGIN
    RETURN QUERY
    SELECT i.id, i.name, i.normalized_name, i.category,
           similarity(i.normalized_name, target_name) as similarity_score
    FROM inventory_items i
    WHERE i.user_id = target_user_id
      AND similarity(i.normalized_name, target_name) > similarity_threshold
    ORDER BY similarity_score DESC
    LIMIT result_limit;
END;
$$ LANGUAGE plpgsql;

Usage Examples

matcher = FuzzyItemMatcher(db_client)

# Find matches for vendor SKU
matches = matcher.find_similar_items(
    target_name="BNLS CHKN BRST 10LB",
    user_id="user_123",
    category="poultry",
)

for match in matches:
    rec = matcher.get_match_recommendation(match['similarity_score'])
    print(f"{match['name']}: {match['similarity_score']:.2f} - {rec['action']}")

Best Practices

  1. Normalize on write - store normalized_name column
  2. Start with conservative thresholds (0.95 auto-match)
  3. Use domain-specific stopwords (industry terms)
  4. Always have Python fallback if RPC fails
  5. Tune weights based on your data characteristics

Common Mistakes

  • Computing normalization on every query (slow)
  • Generic stopword lists missing domain terms
  • Not handling size/unit variations
  • Skipping the salient overlap filter (slow)

Related Patterns

  • deduplication (preventing duplicates)
  • validation-quarantine (handling uncertain matches)

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

34.31%
按下载量换算96

Claude

30.41%
按下载量换算85

Cursor

18.81%
按下载量换算52

Gemini CLI

10.4%
按下载量换算29

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

权限需确认

当前来源未能明确判断权限范围,默认进入异常复核队列。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills