ken

Búsqueda híbrida rápida de código para agentes: es Go puro, binario estático único, 5 incrustaciones semánticas léxicas + Model2Vec + fusión RRF + un reranker consciente del código, con el algoritmo de recuperación portado textualmente de semble

Documentación

ken

Búsqueda híbrida de código rápida para agentes. Go puro, un único binario estático, compatible con MCP sin cambios con MinishLab/semble — mismos esquemas de herramientas, mismo formato de salida, pasos de instalación cambiados a un binario Go.

CI License: MIT Go Reference Go 1.26+

ken es un port a Go de semble: BM25 léxico + embeddings semánticos Model2Vec + fusión RRF + un reranker consciente del código, con el algoritmo de recuperación portado literalmente desde search.py + ranking/*.py de semble.

Por qué ken

  • ~97% de recall@10 en el modo (híbrido) predeterminado0.967 NL / 0.995 símbolo en el benchmark de 1,251 consultas de semble, frente al ~99.9% de grep — mientras le cuesta a un agente ~46× menos tokens que grep + Read (4,120 vs 189,773 tokens medianos en consultas NL — medido en el mismo modo híbrido predeterminado). Para "encuentra el fragmento que responde a esto", eso es una ganancia de tokens de 1–2 órdenes de magnitud con recall casi a la par. (Reproducible: docs/BENCH.md.)
  • Un único binario estático. Go puro, sin cgo, sin intérprete de Python en arranque en frío, sin GIL al indexar. Se compila de forma cruzada a Linux / macOS / Windows (amd64/arm64) sin costo.
  • Sustituto directo de semble. Mismos esquemas de herramientas MCP search / find_related y el mismo formato de cadena markdown — cambia la ruta de command: y los agentes existentes funcionan sin cambios.
  • Local, solo CPU. La inferencia de embeddings, BM25 y la fusión se ejecutan en la CPU. Sin claves de API, sin GPU, sin base de datos vectorial, compatible con entornos aislados.

Una sola perilla controla el recall. La cifra de 82–91% en las tablas de presupuesto de tokens es el respaldo solo-BM25 que ken usa cuando no hay un modelo de embeddings instalado. ken-mcp descarga el modelo automáticamente en la primera ejecución (~60 MB, Go puro, sin Python — sirve bm25 hasta que llega, luego mejora a la ruta híbrida de ~97%; KEN_MCP_AUTO_FETCH=0 para desactivar). Para la CLI, ejecuta ken download-model una vez. La enumeración exhaustiva (refactorizaciones, auditorías previas a renombrados) sigue perteneciendo a grep; ken es para "encuentra el fragmento que responde a esto".

Por dónde empezar

  • ARCHITECTURE.md — mapa del estado actual: diseño de módulos, modelo de runtime/concurrencia, flujo de datos, invariantes. Empieza aquí para el código.
  • docs/USERS.md — usuarios agentes. Instala ken-mcp, apunta tu agente a él, usa las nueve herramientas. Incorporación en 5 minutos.
  • docs/DEVELOPERS.md — autores de SDK y ajustadores. La biblioteca de corpus incrustado mcp.Run, índices precompilados, indexación fs.FS, chunkers personalizados, ajuste del rerank, expectativas de rendimiento.
  • docs/DESIGN.md + docs/internal/DECISIONS.md — especificación del algoritmo + cada decisión arquitectónica (ADRs).
  • docs/BENCH.md — reproducción de benchmarks (NDCG, recall de presupuesto de tokens, la descomposición híbrido-vs-BM25).

Inicio rápido

Instala mediante un gestor de paquetes:

# macOS / Linux (Homebrew) — installs both `ken` and `ken-mcp`:
brew install --cask townsendmerino/tap/ken
# Windows (Scoop):
scoop bucket add townsendmerino https://github.com/townsendmerino/scoop-bucket
scoop install ken

O con Go:

# Install both binaries (Go 1.26+).
go install github.com/townsendmerino/ken/cmd/ken@latest
go install github.com/townsendmerino/ken/cmd/ken-mcp@latest

# Download the default Model2Vec model (~60 MB, one-time). Pure Go, no Python.
# (ken-mcp auto-fetches this on first run; the CLI needs it explicitly.)
# This is the single biggest retrieval-quality lever — it puts you on the ~97% path.
ken download-model

# Search any local repo from the CLI.
ken search /path/to/myrepo "save model to disk" --model ~/.ken/model

O salta el modelo y usa el modo solo-léxico (solo-BM25 cuesta ~14 pp de recall@10 frente al híbrido predeterminado — ver docs/BENCH.md):

ken search /path/to/myrepo "validateToken" --mode bm25

Los binarios precompilados para macOS, Linux y Windows (amd64/arm64) están adjuntos a cada release.tar.gz para macOS/Linux, .zip para Windows.

A partir de v0.3, ken index <path> por defecto usa el modo watch — permanece activo y reindexa ante cambios (debounce de 2 s); --no-watch restaura compilar-una-vez-y-salir. ken-mcp siempre observa, así que un agente que edita el repositorio a mitad de sesión ve sus propios cambios sin reiniciar. ken también respeta archivos .gitignore anidados (por directorio, coincidiendo con git).

Instalar como servidor MCP

ken-mcp habla JSON-RPC sobre stdio y sirve las mismas dos herramientas principales (search, find_related) que semble, con las mismas formas de argumentos y salida markdown.

# Claude Code
claude mcp add ken -s user -- /absolute/path/to/ken-mcp
// ~/.cursor/mcp.json  (or .cursor/mcp.json) — also .vscode/mcp.json with "servers"
{ "mcpServers": { "ken": { "command": "/absolute/path/to/ken-mcp" } } }
# ~/.codex/config.toml
[mcp_servers.ken]
command = "/absolute/path/to/ken-mcp"
// ~/.opencode/config.json
{ "mcp": { "ken": { "type": "local", "command": ["/absolute/path/to/ken-mcp"] } } }

Variables de entorno principales

VariablePredeterminadoPropósito
KEN_MCP_DEFAULT_REPO(sin definir)Fuente preindexada; permite que las herramientas omitan el argumento repo.
KEN_MCP_MODEhybridbm25 / semantic / hybrid. Sirve bm25 mientras falta el modelo — descargado en la primera ejecución por defecto (ver KEN_MCP_AUTO_FETCH).
KEN_MCP_MODEL_DIR~/.ken/modelRuta a una instantánea de Model2Vec que contiene model.safetensors. Recurre a ~/.ken/model (donde escribe ken download-model) cuando no está definida.
KEN_MCP_AUTO_FETCH1En la primera ejecución con un modo que requiere modelo y sin modelo presente, descarga potion-code-16M (~60 MB) en segundo plano, sirviendo bm25 hasta que llega y luego mejorando a híbrido. 0 desactiva (sirve bm25, advierte).
KEN_MCP_CHUNKERregexregex / treesitter / line / markdown. Ver Elegir un chunker.
KEN_MCP_CACHE_SIZE16Límite LRU en la caché repo→Index.
KEN_MCP_LOG_LEVELwarndebug / info / warn / error. Todos los registros van a stderr; stdout es el canal JSON-RPC (detalles).
KEN_MEMLIMIT(sin definir)Límite de memoria suave para el servidor de larga duración (1GiB, 512MiB o un recuento de bytes), aplicado mediante debug.SetMemoryLimit. Anula GOMEMLIMIT cuando ambos están definidos. ken-mcp también establece GOGC=50 por defecto (RSS de estado estable más bajo) a menos que definas GOGC tú mismo.
KEN_MCP_SHUTDOWN_GRACE5sTras un SIGINT/SIGTERM, cuánto tiempo dejar que las llamadas a herramientas en vuelo se drenen antes de forzar la salida (cualquier duración Go). Una segunda señal fuerza la salida inmediata.
KEN_MCP_SNAPSHOT1Persistir el índice construido en <repo>/.ken/ y, al reiniciar, cargarlo + escaneo de deriva (mtime+tamaño) en lugar de reconstruir cuando el repositorio no ha cambiado — la ruta rápida de frío cotidiano. .ken/ es una caché (segura de eliminar; añádela a .gitignore). Solo repositorios de ruta local. 0 desactiva lectura+escritura.
KEN_MCP_LAZY_ENRICH0Diferir el enriquecimiento estructural fuera de la ruta de construcción en frío: servir un índice crudo para una primera consulta rápida en una construcción verdaderamente en frío (~2–3× más rápida la primera entrega en PHP), luego enriquecer y republicar en segundo plano. Los resultados están bien formados antes del enriquecimiento, solo con menor ranking hasta que el pase en segundo plano llega. Solo afecta a una construcción desde cero; una carga de instantánea ya está enriquecida. Opt-in mientras la calibración activada por defecto está pendiente.
KEN_MCP_EMBED_CACHE0Caché persistente de sha256(chunk)→vector en <repo>/.ken/embed.db, de modo que una reconstrucción completa re-embediza solo el texto de fragmentos nunca vistos (semántico/híbrido, repositorios locales). Segunda línea de defensa detrás de la instantánea — ayuda a reconstrucciones recurrentes (deriva intensa, cambio de modo). Limitada por modelo (un cambio de modelo la limpia). Opt-in; la primera construcción la calienta (aproximadamente a la par que sin caché). KEN_MCP_EMBED_CACHE_MAX limita las entradas (predeterminado 1,000,000).
KEN_MCP_STAGED0Disponibilidad escalonada: en una construcción híbrida en frío, sirve BM25 (solo-léxico) instantáneamente (~4× más rápida la primera consulta en PHP), luego enriquece y embediza en segundo plano y mejora a híbrido. Las respuestas de las herramientas llevan "semantic":"warming" hasta que la mejora llega. Tiene prioridad sobre KEN_MCP_LAZY_ENRICH. Opt-in; solo afecta a una construcción desde cero (una carga de instantánea ya está completa).
KEN_MAX_FILE_BYTES2MiBOmitir archivos más grandes que esto de la indexación (512KiB / recuento de bytes). Se aplica a ken index y ken-mcp. Redúcelo en repositorios con muchos artefactos para reducir el índice + la memoria.
KEN_MAX_AVG_LINE_BYTES1000Omitir archivos minificados/generados cuya cabecera muestreada promedie más de este número de bytes por línea (paquetes JS/CSS construidos, JSON de una sola línea). 0 desactiva la heurística.
KEN_MAX_FILES1000000Tope de admisión en el recuento de archivos indexables — un repositorio por encima se rechaza con un error en lugar de arriesgar OOM. Generoso por defecto (el kernel de Linux tiene ~80k archivos); redúcelo para endurecer un servidor contra repositorios hostiles, 0 = ilimitado.
KEN_ENRICH_FILE_BUDGET_MS500 (ken-mcp); 0 (CLI/lib)Tope de tiempo de pared por archivo en el enriquecimiento Arm B / parseo estructural de tree-sitter. Un archivo cuyo parseo lo excede se omite (indexado sin su etiqueta estructural — los resultados no se ven afectados, solo sin enriquecer), se registra y se cuenta. Protege contra archivos individualmente patológicos similares a plantillas que el tope de tamaño no detecta. 0 desactiva. Desactivado por defecto en ken index / la biblioteca para mantener la construcción byte-determinista; ken-mcp lo establece a 500 por defecto.
KEN_ALLOW_PRIVATE_CLONE_TARGETS0Desactivado por defecto: para URLs http(s) repo, ken rechaza direcciones loopback / link-local / RFC1918 (protección SSRF). Define 1 para permitir hosts git internos.

La referencia completa de entorno — incluidas las variables de base de datos KEN_DB_* — está en docs/USERS.md y docs/db-indexing.md. Para agentes que deberían enrutar entre ken y grep deliberadamente (en lugar de la instrucción predeterminada de ken de "preferir ken"), ver el fragmento de enrutamiento en docs/USERS.md.

Herramientas

Ambas herramientas principales devuelven una cadena markdown formateada idéntica a la salida _format_results de semble. (ken-mcp también expone siete herramientas estructurales — definition, references, callers, outline, symbols, recently_changed, status — además de reindex_db cuando hay una base de datos configurada; ver docs/USERS.md.)

search

ArgumentoTipoRequeridoPredeterminadoDescripción
querystringConsulta en lenguaje natural o código.
repostringURL https:// / http:// o directorio local. Requerido si no hay KEN_MCP_DEFAULT_REPO.
modehybrid|semantic|bm25hybridModo de búsqueda.
top_kint5Número de resultados.

find_related

ArgumentoTipoRequeridoPredeterminadoDescripción
file_pathstringRuta tal como aparece en un resultado de search.
lineint (1-indexado)Una línea dentro del fragmento para sembrar la búsqueda de similitud.
repostringIgual que para search.
top_kint5Número de fragmentos similares.

Qué indexa ken

La recuperación híbrida de ken está calibrada para código fuente (Python / Go / TypeScript / Java / Rust tienen chunking consciente del lenguaje; otros recurren al chunker de líneas) y documentación (markdown dividido en límites de encabezados, bloques de código/tablas mantenidos atómicos, frontmatter manejado). Los corpus mixtos de código-y-docs se enrutan por archivo según su extensión.

También indexa esquemas de bases de datos junto con el código — archivos estáticos .sql (con plegado de historial de migraciones) e introspección en vivo de Postgres / SQLite / MySQL / MariaDB — de modo que un agente que responde "cómo se autentican los usuarios" obtiene la función Go, el SQL que ejecuta, la definición de la tabla users y las relaciones FK en una sola lista clasificada. Referencia completa (Tier-1/Tier-2, muestreo de filas, LISTEN/NOTIFY, la herramienta reindex_db, postura PII, todas las variables KEN_DB_*): docs/db-indexing.md.

Para prosa simple sin código ni documentos estructurados, el modo BM25 (--mode=bm25) hace el trabajo pesado; el modelo semántico está entrenado en código y no validado en texto literario.

Excluir archivos: .kenignore

ken respeta tus archivos .gitignore (anidados, por directorio). Pero muchos repositorios incluyen archivos que no deseas que se busquen: migraciones generadas, paquetes JS/CSS compilados, código vendido, fixtures. Coloca un .kenignore en la raíz del repositorio (o en cualquier subdirectorio) para excluirlos del indexado. Utiliza la misma sintaxis que .gitignore, se aplica en el momento del indexado, y es respetado tanto por ken index como por el watch en vivo de ken-mcp:

# .kenignore — keep built + generated files out of the search index
web/assets/          # compiled front-end bundles
vendor/              # Composer / third-party PHP
runtime/             # Yii runtime cache + logs
**/migrations/*.php  # generated DB migrations
*.min.js
*.min.css

Semántica: unión con .gitignore (una ruta se excluye si cualquiera de los dos la ignora), evaluada de forma independiente, de modo que un !negation en un archivo no puede volver a incluir lo que el otro excluyó. .kenignore está activado por defecto: un repositorio sin uno se comporta exactamente como antes. Para una migración directa desde semble, ken también respeta .sembleignore como respaldo cuando no hay ningún .kenignore presente (.kenignore gana si ambos existen). En monorepos grandes con artefactos confirmados, este es el mayor factor individual en el tamaño del índice, el tiempo de arranque en frío y la memoria. Véase ADR-038.

Incluso sin un .kenignore, ken omite automáticamente archivos de más de KEN_MAX_FILE_BYTES (2 MiB) y archivos minificados/generados (longitud de línea media muy larga: paquetes compilados, JSON de una sola línea) mediante KEN_MAX_AVG_LINE_BYTES. .kenignore es para las rutas específicas del repositorio que esos heurísticos no detectan.

Cómo funciona

gitignore + .kenignore respecting walk
    → regex chunker (Python / Go / TS / Java / Rust) with line-chunker fallback
    → BM25 (Lucene variant, k1=1.5, b=0.75)  +  Model2Vec semantic (cosine over a dense matrix)
    → α-weighted RRF fusion (α auto-detected: 0.3 for symbol queries, 0.5 for NL)
    → file-coherence boost + query-type boosts (definition / embedded-symbol / stem-match)
    → path penalties (test files, compat / legacy, `.d.ts`) + file-saturation decay
    → top-k

El algoritmo de recuperación es un puerto literal del search.py + ranking/*.py de semble; consulte docs/DESIGN.md §7 para cada constante y sutileza del orden de la canalización, y §4 para el contrato de inferencia Model2Vec (tensor triple safetensors, la indirección de mapping[], la precisión float64 que es fundamental para la paridad del coseno).

Comparación con semble

Propiedadsembleken
Lenguaje / distribuciónPython · uvx / pipGo · binario estático único
Arranque en frío~500 ms (intérprete + numpy + modelo)~10–20 ms ken search sobre un índice diminuto
Algoritmo de recuperaciónimplementación de referenciapuerto literal (constantes + orden de la canalización de search.py + ranking/*.py)
NDCG@10 en el benchmark de semble0.8540.842 híbrido (brecha 0.012, 63 repos × 1,251 consultas)
Recall@10 en consultas de agente(no medido)~0.97 híbrido (0.967 NL / 0.995 símbolo); respaldo solo BM25 ~0.84
Tokens para recall@10(no medido)~46× menos que grep+Read en consultas NL (4,120 vs 189,773 mediana, híbrido)
Servidor MCPsí: reemplazo directo (mismos esquemas + formato de cable)
Tamaño del binarion/arelease (slim) ken ~22 MB · ken-mcp ~38 MB
Requiere huggingface-clino: ken download-model obtiene directamente desde HF

La metodología completa, el desglose por ablación (la semántica sin procesar coincide con semble dentro de 0.003, lo que valida el puerto de incrustación + tokenizador + ANN), el ancla externa CoIR-CSN-Python y cada nota al pie están en docs/BENCH.md.

Comparación con otras herramientas de búsqueda de código para agentes

La parte concurrida de esta categoría se divide en un eje: qué tienes que ejecutar. La apuesta de ken es que el modelo de incrustación pertenece dentro del binario — inferencia Model2Vec en Go puro, sin cgo — de modo que no hay nada más que levantar: sin demonio de incrustación, sin base de datos vectorial, sin clave API, aislado de la red. Los dos puntos de comparación más cercanos:

  • grepai — el análogo arquitectónico más cercano: un único binario Go con un observador de archivos y un servidor MCP, 100 % local. Descarga las incrustaciones a un servidor Ollama separado (instalas y ejecutas Ollama y descargas un modelo).
  • claude-context (de Zilliz) — el más visible: búsqueda híbrida BM25 + densa, pero respaldada por una base de datos vectorial (Milvus autoalojado mediante Docker, o Zilliz Cloud gestionado) y un proveedor de incrustaciones (API de OpenAI / VoyageAI / Gemini, o Ollama local).
kengrepaiclaude-context
Tiempo de ejecuciónbinario Go estático único (sin cgo)binario Go únicoNode/TS (npm)
Incrustacionesen proceso, Go puro (Model2Vec)demonio externo Ollamaproveedor externo (OpenAI / Voyage / Gemini, u Ollama)
Servicios externos necesariosninguno — descarga automáticamente un modelo de ~60 MB y luego funciona sin conexiónOllama (demonio + modelo)base de datos vectorial (Milvus/Docker o Zilliz Cloud) + una API/demonio de incrustación
RecuperaciónBM25 + densa + RRF + reordenación consciente del códigodensa + grafos de llamadashíbrida (BM25 + densa)
Recall / NDCG0.967 recall@10 · 0.842 NDCG@10, con un arnés de reproducciónno publicadono publicado
Ahorro de tokens~46× vs grep+Read, medido y reproducibleno publicadoreclamación del proveedor: −39 % frente a una base
Velocidadíndice ~1.6 s / 13 mil fragmentos; búsqueda híbrida p50 ~1.5 ms (medido)proveedor: "10 mil archivos en segundos, consultas en ms"depende de la base de datos vectorial y la red
Lenguajes (estructurales)13 (tree-sitter)10a nivel de fragmento, independiente del lenguaje
LicenciaMITMITMIT

Dos advertencias honestas. Primera, los números de ken vienen con comandos de reproducción (docs/BENCH.md); las celdas marcadas como "no publicado" significan que no encontramos ninguna cifra de benchmark estándar para citar y no hemos evaluado de forma independiente la velocidad de los demás: arquitectura, dependencias y licencia son los ejes verificables (al 2026). Segunda, las herramientas optimizan para cosas diferentes — grepai añade trazado de grafos de llamadas; claude-context se apoya en una base de datos vectorial gestionada para escalar. La afirmación específica de ken es recall cercano a grep con ~1–2 órdenes de magnitud menos tokens, desde un solo binario sin servicios externos, cada número reproducible.

Elegir un troceador

El troceador predeterminado regex maneja bien la mayoría de los casos. El troceador optativo treesitter (--chunker=treesitter / KEN_MCP_CHUNKER=treesitter, gotreesitter en Go puro) gana de forma medible para Kotlin, Zig, TypeScript, Java, PHP y pierde en Python, C, Rust, Lua, Scala — un Δ neto de −0.004 NDCG en general (dentro del ruido), por lo que sigue siendo optativo. La tabla completa de recomendaciones por lenguaje está en docs/BENCH.md; la justificación de que el predeterminado siga siendo basado en regex está en ADR-011.

Para autores de SDK: distribuir documentación como un solo binario

La biblioteca mcp.Run te permite integrar un corpus //go:embed + el modelo Model2Vec en un único binario estático de servidor MCP — sin backend, sin base de datos vectorial, sin egreso de red por consulta, con versión fijada por el artefacto de compilación. ~20 líneas de main.go, go build, publicarlo en un release de GitHub; los usuarios brew install y añaden una línea a su configuración del agente. El caminador y el indexador aceptan cualquier fs.FS (embed.FS, fstest.MapFS, respaldado por tarball), lo que también aísla al agente por construcción.

La guía completa — el patrón canónico, índices preconstruidos para arranque en frío rápido, el contrato de tamaño del binario y el paquete optativo mcp/db — está en docs/DEVELOPERS.md.

Demostraciones en vivo (binarios mcp.Run descargables sobre codebases reales, con transcripciones de auditoría): release demos/v0.1.0 — Kubernetes v1.31.0 (59,795 fragmentos) y PostgreSQL 17.0 (64,506 fragmentos). Resumen: He publicado dos binarios descargables de búsqueda de código. La auditoría detectó dos errores..

Hoja de ruta

El registro de riesgos con disparadores explícitos está en docs/DESIGN.md §10; el seguimiento vivo de preparación para 1.0 está en docs/internal/road-to-1.0.md. La recuperación se considera cerrada para 1.0 (la curva de relevancia es plana); el trabajo restante es pulido + incorporación (llevar instalaciones nuevas a la ruta híbrida) + distribución.

Cómo se construyó

ken es un puerto. El algoritmo de recuperación es literalmente de MinishLab/semble (Python); la implementación en Go fue escrita por Claude bajo restricciones fijas: Go puro / sin cgo, constantes del algoritmo portadas literalmente y nunca ajustadas, la fuente original gana siempre que la reconstrucción de Claude diverja del código vivo de semble. Esa última regla detectó cinco errores materiales durante el puerto de la canalización de reordenación: cada uno era una alucinación segura de sí misma que resultaba incorrecta al compararla con el código fuente en Python. La disciplina de comprobar siempre, la regla del puerto literal y el arnés de paridad del tokenizador de 11 mil entradas (que sacó a la luz tres errores que una prueba de 18 casos no habría detectado) son aportes humanos. Cada decisión arquitectónica está registrada en docs/internal/DECISIONS.md.

Agradecimientos

ken se apoya en los hombros de MinishLab: el algoritmo de recuperación, el modelo y todo el enfoque de la tabla de incrustaciones son suyos.

  • semble — la implementación original en Python. © Thomas van Dongen, MIT.
  • model2vec — la biblioteca de incrustaciones estáticas cuyo formato de tensor triple ken implementa. © Thomas van Dongen, MIT.
  • potion-code-16M — pesos del modelo, destilados de nomic-ai/CodeRankEmbed (MIT), a su vez de Snowflake/snowflake-arctic-embed-m-long (Apache-2.0). © Minish Lab. Redistribuido según NOTICE.

Licencia

ken tiene licencia MIT. Incluye atribución por los pesos del modelo redistribuidos en NOTICE y una lista generada de licencias de dependencias en THIRD_PARTY_LICENSES.md; cada enlace en la cadena de procedencia es permisivo (MIT, Apache-2.0, MPL-2.0). Véase docs/DESIGN.md §6.

Para colaboradores: CLAUDE.md tiene las convenciones de compilación, prueba y formato y los invariantes del proyecto (contrato de precisión, contrato de stdout/stderr).