CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
Search Your Codebase in Plain English: Semantic Code Search with Code Embeddings and Qdrant

Search Your Codebase in Plain English: Semantic Code Search with Code Embeddings and Qdrant

Chris Harper

3 min read

Jul 30, 2026 · 04:06 UTC

AI
Tutorial
Embeddings
Vectors

Index your repo's functions and files as dense vectors with a code-specific embedding model, then retrieve exactly the right code with natural language — even when the variable names don't match your query.

What you'll be able to do after this:

  • Encode functions and code files with jinaai/jina-embeddings-v2-base-code so natural language queries find code by intent, not just keyword
  • Build a dual-vector Qdrant collection — one model for NL→code search, one for code→code similarity — and query both from the same index
  • Give your AI agent codebase context without loading every file into the prompt

Standard code search — grep, full-text search — finds what you typed. Semantic code search finds what you meant. The query "JWT token validation and expiry check" retrieves the right function even if it's named _check_bearer and never mentions "JWT." This matters for onboarding in a large codebase, for finding where logic lives when you only know what it does, and for giving AI agents efficient read access to a repo.

The dual-model approach

The Qdrant code search tutorial uses two models in one Qdrant collection — one per search direction:

ModelSearch direction
sentence-transformers/all-MiniLM-L6-v2Natural language → code (English description → function)
jinaai/jina-embeddings-v2-base-codeCode → code (find functions with similar logic)

Install:

pip install qdrant-client sentence-transformers fastembed

Step 1: Extract function-level chunks

import ast, pathlib

def extract_functions(repo_path):
    chunks = []
    for path in pathlib.Path(repo_path).rglob("*.py"):
        source = path.read_text(errors="ignore")
        try:
            tree = ast.parse(source)
        except SyntaxError:
            continue
        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                chunk = ast.get_source_segment(source, node)
                if chunk:
                    chunks.append({"file": str(path), "name": node.name, "code": chunk})
    return chunks

Step 2: Create a dual-vector Qdrant collection

from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance

client = QdrantClient(":memory:")  # swap for QdrantClient("http://localhost:6333") to persist

client.create_collection(
    "code_search",
    vectors_config={
        "text": VectorParams(size=384, distance=Distance.COSINE),   # all-MiniLM-L6-v2
        "code": VectorParams(size=768, distance=Distance.COSINE),   # jina-v2-base-code
    }
)

Step 3: Encode and upload

from sentence_transformers import SentenceTransformer
from qdrant_client.models import PointStruct

nlp_model  = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
code_model = SentenceTransformer("jinaai/jina-embeddings-v2-base-code", trust_remote_code=True)

chunks = extract_functions("./my-repo")
points = [
    PointStruct(
        id=i,
        vector={
            "text": nlp_model.encode(c["code"]).tolist(),
            "code": code_model.encode(c["code"]).tolist(),
        },
        payload={"file": c["file"], "name": c["name"], "code": c["code"]},
    )
    for i, c in enumerate(chunks)
]
client.upload_points("code_search", points)

Step 4: Query in plain English

def search(query, use_code_model=False, top_k=5):
    model = code_model if use_code_model else nlp_model
    vector_name = "code" if use_code_model else "text"
    results = client.search(
        "code_search",
        query_vector=(vector_name, model.encode(query).tolist()),
        limit=top_k,
    )
    for r in results:
        print(f"{r.payload['file']}::{r.payload['name']}  score={r.score:.3f}")
        print(r.payload["code"][:200])
        print("---")

search("JWT token validation and expiry check")            # NL → code
search("retry with exponential backoff", use_code_model=True)  # code → code

When to use which model

  • text model (NL queries): "find auth middleware," "where is database connection pooled" — translate English intent to code
  • code model (code queries): paste a known function to find similar implementations, detect duplication across files
  • Layer over your AI agent: pass the top-3 results as context to Claude for tasks in an unfamiliar codebase — much cheaper than loading every file into the prompt

Sources: Semantic Search for Code — Qdrant Tutorial · Code Search Notebook — HuggingFace Open-Source AI Cookbook · jina-embeddings-v2-base-code — Jina AI · qdrant/demo-code-search — GitHub