Skill

SkillsAI & Agent Engineering › RAG & retrieval

Hybrid Search Implementation

Combine vector and keyword search for improved retrieval. Use when implementing RAG systems, building search engines, or when neither approach alone provides sufficient recall.

Freerisk: low
hybridsearchimplementationpythonanthropiclangchainopenai

Tools: asyncpg,numpy,sentence_transformers

The full skill

— name: hybrid-search-implementation description: Combine vector and keyword search for improved retrieval. Use when implementing RAG systems, building search engines, or when neither approach alone provides sufficient recall. — # Hybrid Search Implementation Patterns for combining vector similarity and keyword-based search. ## When to Use This Skill – Building RAG systems with improved recall – Combining semantic understanding with exact matching – Handling queries with specific terms (names, codes) – Improving search for domain-specific vocabulary – When pure vector search misses keyword matches ## Core Concepts ### 1. Hybrid Search Architecture “` Query → ┬─► Vector Search ──► Candidates ─┐ │ │ └─► Keyword Search ─► Candidates ─┴─► Fusion ─► Results “` ### 2. Fusion Methods | Method | Description | Best For | |——–|————-|———-| | **RRF** | Reciprocal Rank Fusion | General purpose | | **Linear** | Weighted sum of scores | Tunable balance | | **Cross-encoder** | Rerank with neural model | Highest quality | | **Cascade** | Filter then rerank | Efficiency | ## Templates ### Template 1: Reciprocal Rank Fusion “`python from typing import List, Dict, Tuple from collections import defaultdict def reciprocal_rank_fusion( result_lists: List[List[Tuple[str, float]]], k: int = 60, weights: List[float] = None ) -> List[Tuple[str, float]]: """ Combine multiple ranked lists using RRF. Args: result_lists: List of (doc_id, score) tuples per search method k: RRF constant (higher = more weight to lower ranks) weights: Optional weights per result list Returns: Fused ranking as (doc_id, score) tuples """ if weights is None: weights = [1.0] * len(result_lists) scores = defaultdict(float) for result_list, weight in zip(result_lists, weights): for rank, (doc_id, _) in enumerate(result_list): # RRF formula: 1 / (k + rank) scores[doc_id] += weight * (1.0 / (k + rank + 1)) # Sort by fused score return sorted(scores.items(), key=lambda x: x[1], reverse=True) def linear_combination( vector_results: List[Tuple[str, float]], keyword_results: List[Tuple[str, float]], alpha: float = 0.5 ) -> List[Tuple[str, float]]: """ Combine results with linear interpolation. Args: vector_results: (doc_id, similarity_score) from vector search keyword_results: (doc_id, bm25_score) from keyword search alpha: Weight for vector search (1-alpha for keyword) """ # Normalize scores to [0, 1] def normalize(results): if not results: return {} scores = [s for _, s in results] min_s, max_s = min(scores), max(scores) range_s = max_s – min_s if max_s != min_s else 1 return {doc_id: (score – min_s) / range_s for doc_id, score in results} vector_scores = normalize(vector_results) keyword_scores = normalize(keyword_results) # Combine all_docs = set(vector_scores.keys()) | set(keyword_scores.keys()) combined = {} for doc_id in all_docs: v_score = vector_scores.get(doc_id, 0) k_score = keyword_scores.get(doc_id, 0) combined[doc_id] = alpha * v_score + (1 – alpha) * k_score return sorted(combined.items(), key=lambda x: x[1], reverse=True) “` ### Template 2: PostgreSQL Hybrid Search “`python import asyncpg from typing import List, Dict, Optional import numpy as np class PostgresHybridSearch: """Hybrid search with pgvector and full-text search.""" def __init__(self, pool: asyncpg.Pool): self.pool = pool async def setup_schema(self): """Create tables and indexes.""" async with self.pool.acquire() as conn: await conn.execute(""" CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE IF NOT EXISTS documents ( id TEXT PRIMARY KEY, content TEXT NOT NULL, embedding vector(1536), metadata JSONB DEFAULT '{}', ts_content tsvector GENERATED ALWAYS AS ( to_tsvector('english', content) ) STORED ); — Vector index (HNSW) CREATE INDEX IF NOT EXISTS documents_embedding_idx ON documents USING hnsw (embedding vector_cosine_ops); — Full-text index (GIN) CREATE INDEX IF NOT EXISTS documents_fts_idx ON documents USING gin (ts_content); """) async def hybrid_search( self, query: str, query_embedding: List[float], limit: int = 10, vector_weight: float = 0.5, filter_metadata: Optional[Dict] = None ) -> List[Dict]: """ Perform hybrid search combining vector and full-text. Uses RRF fusion for combining results. """ async with self.pool.acquire() as conn: # Build filter clause where_clause = "1=1" params = [query_embedding, query, limit * 3] if filter_metadata: for key, value in filter_metadata.items(): params.append(value) where_clause += f" AND metadata->>'{key}' = ${len(params)}" results = await conn.fetch(f""" WITH vector_search AS ( SELECT id, content, metadata, ROW_NUMBER() OVER (ORDER BY embedding <=> $1::vector) as vector_rank, 1 – (embedding <=> $1::vector) as vector_score FROM documents WHERE {where_clause} ORDER BY embedding <=> $1::vector LIMIT $3 ), keyword_search AS ( SELECT id, content, metadata, ROW_NUMBER() OVER (ORDER BY ts_rank(ts_content, websearch_to_tsquery('english', $2)) DESC) as keyword_rank, ts_rank(ts_content, websearch_to_tsquery('english', $2)) as keyword_score FROM documents WHERE ts_content @@ websearch_to_tsquery('english', $2) AND {where_clause} ORDER BY ts_rank(ts_content, websearch_to_tsquery('english', $2)) DESC LIMIT $3 ) SELECT COALESCE(v.id, k.id) as id, COALESCE(v.content, k.content) as content, COALESCE(v.metadata, k.metadata) as metadata, v.vector_score, k.keyword_score, — RRF fusion COALESCE(1.0 / (60 + v.vector_rank), 0) * $4::float + COALESCE(1.0 / (60 + k.keyword_rank), 0) * (1 – $4::float) as rrf_score FROM vector_search v FULL OUTER JOIN keyword_search k ON v.id = k.id ORDER BY rrf_score DESC LIMIT $3 / 3 """, *params, vector_weight) return [dict(row) for row in results] async def search_with_rerank( self, query: str, query_embedding: List[float], limit: int = 10, rerank_candidates: int = 50 ) -> List[Dict]: """Hybrid search with cross-encoder reranking.""" from sentence_transformers import CrossEncoder # Get candidates candidates = await self.hybrid_search( query, query_embedding, limit=rerank_candidates ) if not candidates: return [] # Rerank with cross-encoder model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2') pairs = [(query, c["content\9:[["$","$L1d",null,{"locale":"en","skill":{"id":"wshobson-agents-plugins-llm-application-dev-skills-hybrid-search-implementation-skill-md","name":"hybrid-search-implementation","author":"wshobson","authorAvatarUrl":"https://avatars.githubusercontent.com/u/553618?v=4"}}],["$","$L1e",null,{"locale":"en","skill":{"id":"wshobson-agents-plugins-llm-application-dev-skills-hybrid-search-implementation-skill-md","name":"hybrid-search-implementation","author":"wshobson","authorAvatar":"https://avatars.githubusercontent.com/u/553618?v=4","description":"Combine vector and keyword search for improved retrieval. Use when implementing RAG systems, building search engines, or when neither approach alone provides sufficient recall.","githubUrl":"https://github.com/wshobson/agents/tree/main/plugins/llm-application-dev/skills/hybrid-search-implementation","skillContent":null,"hasMarketplaceJson":true},"files":[{"id":"591bc413-1017-4bf8-bdda-ea2c8e2483d8","skillId":"wshobson-agents-plugins-llm-application-dev-skills-hybrid-search-implementation-skill-md","path":"SKILL.md","name":"SKILL.md","type":"file","size":18043,"content":"$1f","isBinary":false}],"fileTree":[{"name":"SKILL.md","path":"SKILL.md","type":"file","size":18043,"children":"$undefined"}],"hasFiles":true,"updatedAtText":"3 months ago","t":{"common":{"loading":"Loading…","error":"Error","search":"Search","searchPlaceholder":"AI Search","cancel":"Cancel","confirm":"Confirm","save":"Save","delete":"Delete","edit":"Edit","back":"Back","next":"Next","previous":"Previous","skills":"skills","skill":"skill","available":"available","signIn":"Sign In","signOut":"Sign Out","favorites":"favorites","history":"history"},"nav":{"categories":"cd /categories","stats":"watch stats","docs":"man docs","apiDocs":"api –docs","aiSearch":"ai –search"},"hero":{"title":"Agent Skills Marketplace","subtitle":"for Claude Code, Codex & ChatGPT","skillCount":"skills","description":"Discover open-source agent skills from GitHub","description2":"Search with AI semantics or keywords, browse by category, sort by popularity. All skills use the open SKILL.md standard and are ready to install","searchPlaceholder":"Type to filter, or press ⏎ for AI search. ⇧ + ⏎ for new line","noSkillsFound":"No skills found"},"categories":{"title":"Browse by Category","description":"Explore agent skills organized by their primary use case","pageTitle":"All Categories","breadcrumb":"Categories","skillCount":"skills","info":"Agent skills are organized into parent categories and subcategories. Each category shows the total number of available skills. Click on any category to explore the skills within.","viewAll":"cd /categories && ls -la"},"skills":{"title":"Browse Agent Skills","count":"count","available":"skills available","searchAi":"search –ai","noSkillsFound":"No skills found","install":"Install","download":"Download","similar":"Similar Skills","files":"Files","readme":"README","searchPlaceholder":"Search skills with AI: try 'skills about trading', 'data analysis related skills'…","showMarketplaceOnly":"Show only skills with","sortByStars":"Sort by stars","sortByRecent":"Sort by recent"},"about":{"title":"Why Agent Skills Marketplace?","description":"Finding the right agent skill among thousands of GitHub repositories can be overwhelming. Skills Marketplace solves this by providing smart search, category filtering, and quality indicators to help you quickly find exactly what you need.","description2":"Whether you're a developer automating workflows, a team lead building custom AI tools, or a hobbyist exploring AI coding assistants, you'll find skills for every use case. All skills use the open SKILL.md standard, compatible with Claude Code, OpenAI Codex CLI, and other tools adopting this format.","info":"Ready to explore the largest collection of agent skills for AI coding assistants?","browse":"Browse","easy":"Easy","github":"GitHub","browseDescription":"Search through 25,000+ agent skills with intelligent filtering by category, author, and popularity. Compatible with Claude Code, Codex CLI and ChatGPT.","easyDescription":"Install skills with a single command. Clone repositories directly to your skills directory. All skills use the open SKILL.md standard for maximum compatibility.","githubDescription":"All skills are sourced from public GitHub repositories. We continuously scan and index new skills, ensuring you always have access to the latest community contributions."},"faq":{"title":"Frequently Asked Questions","subtitle":"Everything you need to know about agent skills, Claude Code, OpenAI Codex, and AI coding tools in 2025","questions":[{"question":"What are Agent Skills?","answer":"Agent Skills are modular capabilities that extend AI coding assistants. Each skill consists of a SKILL.md file with instructions, plus optional scripts and templates. In December 2025, Anthropic released the Agent Skills specification as an open standard, and OpenAI adopted the same format for Codex CLI and ChatGPT. Skills are model-invoked—the AI automatically decides when to use them based on context."},{"question":"How do I install an agent skill?","answer":"For Claude Code: Add to ~/.claude/skills/ (personal) or .claude/skills/ (project). For OpenAI Codex CLI: Add to ~/.codex/skills/. Both use the same SKILL.md format. Clone the GitHub repository and copy the skill folder to your chosen directory. The AI automatically discovers and loads skills from these locations."},{"question":"Are these skills safe to use?","answer":"Skills from this marketplace are sourced from public GitHub repositories. We filter out low-quality repos (minimum 2 stars) and scan for basic quality indicators, but you should always review the code before installing. Community skills should be treated like any open-source code—inspect before use."},{"question":"Can I use multiple skills together?","answer":"Yes! Claude Code supports multiple skills simultaneously. Skills are modular and designed to work together. For example, you can combine a 'code-reviewer' skill with a 'git-automation' skill to automatically review and commit code changes. Claude intelligently selects the appropriate skills based on your request context."},{"question":"Can I create and share my own skills?","answer":"Yes! You can create custom Claude skills and share them on GitHub. Check out the skill-creator skill for detailed guidance on creating your own skills. The basic structure requires a SKILL.md file with instructions. You can also add optional scripts, templates, and a marketplace.json for easier distribution."},{"question":"How do skills differ from slash commands?","answer":"Skills are model-invoked—Claude automatically decides when to use them based on context. Slash commands are user-invoked—you explicitly type the command to trigger them. Skills enable more intelligent, context-aware automation in your workflows."},{"question":"How often are skills updated?","answer":"Our scraper regularly syncs with GitHub to fetch the latest skills and updates. When a skill repository is updated on GitHub, the changes will appear on SkillsMP shortly after our next sync. You can see the last update timestamp on each skill card. We recommend checking back regularly for new skills and improvements to existing ones."},{"question":"What is marketplace.json?","answer":"marketplace.json is a metadata file that enables one-command installation of skills through Claude Code plugin marketplaces. When present, users can install skills with a simple command like '/plugin install skill-name' instead of manually copying files. Skills with marketplace.json are marked with a special badge on SkillsMP. This file contains information about the skill's name, description, version, and installation instructions."},{"question":"Is this website affiliated with Anthropic or OpenAI?","answer":"No, SkillsMP is an independent community project. We aggregate and showcase agent skills from GitHub to make discovery easier. For official skills and documentation, refer to Anthropic's github.com/anthropics/skills or OpenAI's Codex documentation."}],"stillHaveQuestions":"Still have questions about agent skills?","exploreDocs":"open /docs && explore"},"footer":{"readme":"cat README.md","title":"Skills Marketplace","description":"Discover and explore AI skills built by the community.","gitRemote":"git remote -v","resources":"ls ./resources/","legal":"ls ./legal/","skillsDocs":"📚 Skills Docs","skillDocument":"📄 Skill Document","officialSkills":"📁 Official Skills","codexSkillsDocs":"📄 Codex Skills Docs","agentSkillsSpec":"📋 Agent Skills Spec","about":"📄 About","changelog":"📋 Changelog","privacyPolicy":"🔒 Privacy Policy","termsOfService":"📜 Terms of Service","online":"online","copyright":"© 2025 Not affiliated with Anthropic."},"mentions":{"title":"Mentioned in","anthropic":{"quote":"This Skills marketplace shares even more examples to inspire you. Explore community-built skills that extend what Claude can do—from automated testing and data visualization to code review and domain name brainstorming.","author":"Anthropic","source":"Building skills for Claude Code"},"langchain":{"quote":"We've added skills to the deepagent-CLI, making it possible to use the large and growing collection of public skills.","author":"LangChain","source":"Using Skills with Deep Agents"}},"timeline":{"title":"Statistics","skillsOverTime":"Skills Over Time","categoriesDistribution":"Categories Distribution","pageTitle":"Skills Timeline","description":"Visualize skill activity over time. See when skills were published and track community growth.","aboutTitle":"About This Timeline","aboutInfo":"This timeline shows the number of skills pushed to GitHub over time. The data is based on the last push date of each skill's repository. Use the granularity controls to view daily, weekly, or monthly activity patterns.","totalSkills":"Total Skills","avgPerDay":"Avg/Day","peakDay":"Peak Day","dataPoints":"Data Points","skillsAdded":"s