ken

Busca híbrida rápida de código para agentes - é Go puro, binário estático único, 5 embeddings lexicais + semânticos Model2Vec + fusão RRF + um reranker ciente de código, com o algoritmo de recuperação portado literalmente do semble

Documentação

ken

Busca híbrida rápida de código para agentes. Escrito em Go puro, binário estático único, compatível com MCP como substituto direto do MinishLab/semble — mesmos schemas de ferramentas, mesmo formato de saída, passos de instalação trocados por um binário Go.

CI License: MIT Go Reference Go 1.26+

ken é um port em Go do semble: BM25 lexical + embeddings semânticos Model2Vec + fusão RRF + um reranker ciente de código, com o algoritmo de recuperação portado literalmente do search.py + ranking/*.py do semble.

Por que ken

  • ~97% de recall@10 no modo padrão (híbrido) — 0.967 NL / 0.995 símbolo no benchmark de 1.251 consultas do semble, vs ~99.9% do grep — enquanto custa ao agente ~46× menos tokens que o grep + Read (4.120 vs 189.773 tokens medianos em consultas NL — medidos no mesmo modo híbrido padrão). Para "encontre o trecho que responde a isto", isso é uma vitória de 1–2 ordens de magnitude em tokens com recall quase equivalente. (Reproduza: docs/BENCH.md.)
  • Binário estático único. Go puro, sem cgo, sem interpretador Python no cold start, sem GIL na indexação. Compila cruzado para Linux / macOS / Windows (amd64/arm64) gratuitamente.
  • Substituto direto do semble. Mesmos schemas de ferramentas MCP search / find_related e mesmo formato de string markdown — troque o caminho do command: e agentes existentes funcionam sem alterações.
  • Local, apenas CPU. Inferência de embeddings, BM25 e fusão rodam todos na CPU. Sem chaves de API, sem GPU, sem banco de vetores, compatível com ambientes isolados.

Um único controle define o recall. O número 82–91% nas tabelas de orçamento de tokens é o fallback apenas-BM25 que o ken usa quando nenhum modelo de embeddings está instalado. O ken-mcp baixa o modelo automaticamente na primeira execução (~60 MB, Go puro, sem Python — serve bm25 até o modelo chegar, então atualiza para o caminho híbrido de ~97%; KEN_MCP_AUTO_FETCH=0 para desativar). Para a CLI, execute ken download-model uma vez. Enumeração exaustiva (refatorações, auditorias pré-rename) ainda pertence ao grep; ken é para "encontre o trecho que responde a isto."

Por onde começar

  • ARCHITECTURE.md — mapa do estado atual: layout de módulos, modelo de runtime/concorrência, fluxo de dados, invariantes. Comece aqui para o código.
  • docs/USERS.md — usuários agentes. Instale o ken-mcp, aponte seu agente para ele, use as nove ferramentas. On-ramp de 5 minutos.
  • docs/DEVELOPERS.md — autores de SDK e ajustadores. A biblioteca de corpus embutido mcp.Run, índices pré-construídos, indexação fs.FS, chunkers personalizados, ajuste de rerank, expectativas de performance.
  • docs/DESIGN.md + docs/internal/DECISIONS.md — especificação do algoritmo + cada decisão arquitetural (ADRs).
  • docs/BENCH.md — reprodução de benchmarks (NDCG, recall por orçamento de tokens, a decomposição híbrido-vs-BM25).

Início rápido

Instale via gerenciador de pacotes:

# 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

Ou com 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

Ou pule o modelo e use o modo apenas-lexical (modo apenas-BM25 custa ~14 pp de recall@10 vs o padrão híbrido — veja docs/BENCH.md):

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

Não tem certeza se sua configuração está correta? ken doctor verifica disponibilidade do modelo, aquecimento do cache de rerank, enriquecimento, rastreamento de economia de tokens e configuração do ken-mcp, e imprime recomendações priorizadas (ex.: "sem modelo — execute ken download-model").

Binários pré-compilados para macOS, Linux e Windows (amd64/arm64) estão anexados a cada release — .tar.gz para macOS/Linux, .zip para Windows.

A partir da v0.3, ken index <path> usa por padrão o modo de observação — ele permanece ativo e reindexa em mudanças (debounce de 2 s); --no-watch restaura o comportamento de compilar-uma-vez-e-sair. ken-mcp sempre observa, então um agente editando o repositório no meio da sessão vê suas próprias mudanças sem reiniciar. ken também respeita arquivos .gitignore aninhados (por diretório, correspondendo ao git).

Instalar como servidor MCP

ken-mcp fala JSON-RPC sobre stdio e serve as mesmas duas ferramentas principais (search, find_related) que o semble, com os mesmos formatos de argumentos e saída 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"] } } }

Transporte remoto (Streamable HTTP)

O padrão acima executa o ken-mcp como um subprocesso local — a escolha certa para a maioria das configurações (o limite do usuário do SO é o limite de autenticação; nada é exposto à rede). Para um dev box centralizado, servidor de staging ou instância compartilhada de equipe — um ken-mcp alimentando muitos agentes, ou um IDE remoto não co-residente com o código — o ken-mcp também fala MCP sobre Streamable HTTP (KEN_MCP_TRANSPORT=http, ADR-041). Mesmas ferramentas, mesmo formato de wire; agentes já treinados no servidor stdio funcionam sem alterações.

Isso expõe o ken-mcp à rede, então autenticação é obrigatória e vários guards falham alto na inicialização:

# On the server (front with a TLS-terminating reverse proxy — ken-mcp does NOT do TLS):
export KEN_MCP_TRANSPORT=http
export KEN_MCP_ADDR=:8080                    # default
export KEN_MCP_AUTH_TOKEN_FILE=/etc/ken/token # preferred (keeps the secret out of the environment)
export KEN_MCP_DEFAULT_REPO=/srv/code
export KEN_MCP_RATE_LIMIT=100                 # req/min per client IP (0 disables); default 100
ken-mcp                                       # exits non-zero if no token is set

Aponte um agente para ele com um bearer token (Claude Code mostrado; qualquer cliente MCP Streamable-HTTP funciona):

claude mcp add --transport http ken https://ken.example.com \
  --header "Authorization: Bearer $TOKEN"
// Cursor / VS Code mcp.json — remote form (check your editor's remote-MCP docs)
{ "mcpServers": { "ken": { "url": "https://ken.example.com",
    "headers": { "Authorization": "Bearer <token>" } } } }

Guards: o modo HTTP recusa iniciar sem token (sem padrão inseguro, sem exceção de localhost); KEN_DB_SAMPLE_ROWS>0 é rejeitado de forma rígida no modo HTTP (valores de banco amostrados seriam pesquisáveis na rede); requisições são limitadas por IP do cliente. TLS está fora do escopo por design — termine-o em um reverse proxy na frente do ken-mcp. stdio continua o padrão e é completamente afetado.

Variáveis de ambiente principais

VariávelPadrãoPropósito
KEN_MCP_DEFAULT_REPO(não definido)Fonte pré-indexada; permite que as ferramentas omitam o argumento repo.
KEN_MCP_MODEhybridbm25 / semantic / hybrid. Serve bm25 enquanto o modelo está ausente — baixado na primeira execução por padrão (veja KEN_MCP_AUTO_FETCH).
KEN_MCP_MODEL_DIR~/.ken/modelCaminho para um snapshot do Model2Vec contendo model.safetensors. Recai para ~/.ken/model (onde ken download-model grava) quando não definido.
KEN_MCP_AUTO_FETCH1Na primeira execução com um modo que exige modelo e sem modelo presente, baixa potion-code-16M (~60 MB) em segundo plano, servindo bm25 até que chegue e então atualizando para híbrido. 0 desativa (serve bm25, avisa).
KEN_MCP_CHUNKERregexregex / treesitter / line / markdown. Veja Escolhendo um chunker.
KEN_MCP_CACHE_SIZE16Limite LRU no cache repo→Index.
KEN_MCP_LOG_LEVELwarndebug / info / warn / error. Todos os logs vão para stderr; stdout é o canal JSON-RPC (detalhes).
KEN_MCP_TRANSPORTstdiostdio (subprocesso local, padrão) ou http (Streamable HTTP de rede — veja Transporte remoto).
KEN_MCP_ADDR:8080Endereço de bind HTTP (somente modo http). Sem TLS em processo — use um proxy reverso na frente.
KEN_MCP_AUTH_TOKEN / …_TOKEN_FILE(não definido)Token Bearer para modo http (_FILE preferido). Obrigatório — o modo http não inicia sem ele.
KEN_MCP_RATE_LIMIT100Requisições/min por IP do cliente (modo http); 0 desativa. KEN_DB_SAMPLE_ROWS>0 é rejeitado no modo http.
KEN_MEMLIMIT(não definido)Limite de memória suave para o servidor de longa duração (1GiB, 512MiB ou contagem de bytes), aplicado via debug.SetMemoryLimit. Substitui GOMEMLIMIT quando ambos estão definidos. ken-mcp também define GOGC=50 por padrão (RSS de estado estacionário menor), a menos que você defina GOGC você mesmo.
KEN_MCP_SHUTDOWN_GRACE5sApós um SIGINT/SIGTERM, quanto tempo deixar chamadas de ferramentas em voo drenarem antes de forçar a saída (qualquer duração Go). Uma drenagem limpa sai com 0; uma saída forçada é status 1, e um segundo sinal força a saída imediata com 128+sinal (130 / 143).
KEN_MCP_SNAPSHOT1Persiste o índice construído em <repo>/.ken/ e, na reinicialização, carrega-o + varredura de deriva (mtime+tamanho) em vez de reconstruir quando o repo está inalterado — o caminho rápido do dia a dia a frio. .ken/ é um cache (seguro para excluir; adicione-o a .gitignore). Apenas repos de caminho local. 0 desativa leitura+escrita.
KEN_MCP_LAZY_ENRICH0Adia o enriquecimento estrutural para fora do caminho de construção a frio: serve um índice bruto para uma primeira consulta rápida em uma construção verdadeiramente fria (~2–3× mais rápido para o primeiro serviço em PHP), depois enriquece + republica em segundo plano. Os resultados são bem formados antes do enriquecimento, apenas com classificação mais baixa até que a passagem em segundo plano chegue. Afeta apenas uma construção do zero; um carregamento de snapshot já está enriquecido. Opt-in enquanto a calibração padrão está pendente.
KEN_MCP_EMBED_CACHE0Cache persistente de sha256(chunk)→vector em <repo>/.ken/embed.db, para que uma reconstrução completa re-embuta apenas texto de chunk nunca visto (semântico/híbrido, repos locais). Segunda linha de defesa atrás do snapshot — ajuda reconstruções recorrentes (deriva pesada, mudança de modo). Escopo por modelo (uma mudança de modelo o limpa). Opt-in; a primeira construção o aquece (aproximadamente no mesmo nível de sem cache). KEN_MCP_EMBED_CACHE_MAX limita entradas (padrão 1.000.000).
KEN_MCP_STAGED0Prontidão em etapas: em uma construção híbrida a frio, serve BM25 (somente lexical) instantaneamente (~4× mais rápido para a primeira consulta em PHP), depois enriquece + embute em segundo plano e atualiza para híbrido. As respostas das ferramentas carregam "semantic":"warming" até que a atualização chegue. Tem precedência sobre KEN_MCP_LAZY_ENRICH. Opt-in; afeta apenas uma construção do zero (um carregamento de snapshot já está completo).
KEN_MAX_FILE_BYTES2MiBPula arquivos maiores que isso da indexação (512KiB / contagem de bytes). Aplica-se a ken index e ken-mcp. Reduza em repos com muitos artefatos para encolher o índice + memória.
KEN_MAX_AVG_LINE_BYTES1000Pula arquivos minificados cuja cabeça amostrada tem média de mais que isso em bytes por linha (bundles JS/CSS construídos, JSON de linha única). 0 desativa a heurística.
KEN_SKIP_GENERATED0Desativado por padrão: defina 1 para também pular código gerado cujo cabeçalho carrega uma tag @generated ou um banner Code generated … DO NOT EDIT (stubs protobuf, listers client-go, stringer/mockgen). Captura arquivos gerados que a heurística de minificação perde. Opt-in porque alguns corpora querem sua superfície gerada pesquisável. Aplica-se a ken index e ken-mcp.
KEN_MAX_FILES1000000Limite de admissão na contagem de arquivos indexáveis — um repo acima disso é rejeitado com um erro em vez de arriscar OOM. Generoso por padrão (o kernel Linux tem ~80k arquivos); reduza para endurecer um servidor contra repos hostis, 0 = ilimitado.
KEN_ENRICH_FILE_BUDGET_MS500 (ken-mcp); 2000 (ken index/search/bench/perf); 0 (ken build-index, biblioteca)Limite de relógio de parede por arquivo no enriquecimento Arm B / parse estrutural tree-sitter. Um arquivo cujo parse excede isso é pulado (indexado sem seu rótulo estrutural — resultados não são afetados, apenas não enriquecidos), registrado e contado. Protege contra arquivos individuais patologicamente semelhantes a modelos que o limite de tamanho perde (exemplo real: grandes arrays de inicializadores designados em C estilo driver, gotreesitter#1100). 0 desativa. Desativado por padrão apenas para ken build-index e chamadores diretos da biblioteca (BuildAndSerializeIndex), para manter essa construção específica byte-determinística.
KEN_ALLOW_PRIVATE_CLONE_TARGETS0Desativado por padrão: para URLs http(s) repo, ken rejeita endereços loopback / link-local / RFC1918 (proteção SSRF). Defina 1 para permitir hosts git internos.
KEN_MCP_ALLOWED_REPO_ROOTS(não definido)Confina argumentos repo de caminho local fornecidos pelo agente a essas raízes (separados por lista de caminhos do SO, como PATH). Não definido = sem confinamento (qualquer caminho local) — e ken-mcp registra um aviso de segurança de inicialização alto nesse caso, já que um servidor de longa duração cujo agente também lida com conteúdo não confiável poderia ser direcionado a ler arquivos arbitrários. O análogo de caminho local da proteção SSRF de clone: defini-lo impede o agente de apontar ken para /etc, ~/.ssh, etc. (e silencia o aviso). KEN_MCP_DEFAULT_REPO é isento (o operador o garantiu). Symlinks são resolvidos antes da verificação.

A referência completa de env — incluindo as variáveis de banco de dados KEN_DB_* — está em docs/USERS.md e docs/db-indexing.md. Para agentes que devem rotear entre ken e grep deliberadamente (em vez da instrução padrão "prefira ken" de ken), veja o trecho de roteamento em docs/USERS.md.

Ferramentas

Ambas as ferramentas principais retornam uma string markdown formatada idêntica à saída _format_results de semble. (ken-mcp também expõe sete ferramentas estruturais — definition, references, callers, outline, symbols, recently_changed, status — além de reindex_db quando um banco de dados está configurado; veja docs/USERS.md.)

search

ArgTipoObrigatórioPadrãoDescrição
querystring✓*—Consulta em linguagem natural ou código. *Forneça query ou queries.
queriesstring[]—Lote de consultas em uma chamada (economiza idas e voltas); resultados agrupados por consulta. Mesmo modo/top_k/filtros aplicam-se a cada uma. Limitado a 20.
repostring—URL https:// / http:// ou diretório local. Obrigatório se não houver KEN_MCP_DEFAULT_REPO.
modehybrid|semantic|bm25hybridModo de busca.
top_kint5Número de resultados (limita a contagem).
max_tokensint—Orçamento opcional de tamanho de resposta. top_k limita a contagem, mas um chunk pode ser minúsculo ou enorme; com max_tokens definido, ken preenche a lista classificada de cima para baixo e descarta a cauda quando o custo estimado de tokens excederia (o hit principal sempre mantido). Aproximado — ken não inclui tokenizador BPE, então é uma heurística, não uma contagem exata.
explainboolfalseAnota cada resultado com por que correspondeu: quais termos da consulta aparecem no chunk (kind=lexical) ou que surgiu por similaridade semântica sem sobreposição exata de termos (kind=semantic). Uma explicação de sobreposição lexical para depurar "por que isso está aqui?", não um detalhamento completo de classificação.

find_related

ArgTipoObrigatórioPadrãoDescrição
file_pathstring✓—Caminho como aparece em um resultado search.
lineint (1-indexado)✓—Uma linha dentro do chunk para semear a busca de similaridade.
repostring—Igual ao de search.
top_kint5Número de chunks semelhantes.
max_tokensint—Orçamento opcional de tamanho de resposta; mesmas semânticas de search.

O que ken indexa

A recuperação híbrida de ken é calibrada para código-fonte (Python / Go / TypeScript / Java / Rust têm chunking ciente de linguagem; outros recaem no chunker de linha) e documentação (markdown dividido em limites de cabeçalho, blocos de código/tabelas mantidos atômicos, frontmatter tratado). Corpora mistos de código e docs roteiam por arquivo por extensão.

Também indexa esquemas de banco de dados junto com código — arquivos .sql estáticos (com dobramento de histórico de migração) e introspecção ao vivo de Postgres / SQLite / MySQL / MariaDB — para que um agente respondendo "como os usuários são autenticados" obtenha a função Go, o SQL que ela executa, a definição da tabela users e os relacionamentos FK em uma lista classificada. Referência completa (Tier-1/Tier-2, amostragem de linhas, LISTEN/NOTIFY, a ferramenta reindex_db, postura PII, todas as variáveis KEN_DB_*): docs/db-indexing.md.

Para prosa simples sem código ou docs estruturados, o modo BM25 (--mode=bm25) carrega o peso; o modelo semântico é treinado em código e não validado em texto literário.

Excluindo arquivos: .kenignore

ken respeita seus arquivos .gitignore (aninhados, por diretório). Mas muitos repos commitam arquivos que você não quer pesquisados — migrações geradas, bundles JS/CSS construídos, código vendido, fixtures. Coloque um .kenignore na raiz do repo (ou em qualquer subdiretório) para excluí-los da indexação. Usa a mesma sintaxe de .gitignore, é aplicado no momento da indexação e é honrado tanto por ken index quanto pelo watch ao 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ânticas: união com .gitignore (um caminho é excluído se qualquer um o ignorar), avaliado independentemente para que um !negation em um arquivo não possa re-incluir o que o outro excluiu. .kenignore é ligado por padrão — um repo sem um se comporta exatamente como antes. Para uma migração drop-in de semble, ken também honra .sembleignore como fallback quando nenhum .kenignore está presente (.kenignore vence se ambos existirem). Em monorepos grandes com artefatos commitados, esta é a maior alavanca única no tamanho do índice, tempo de inicialização a frio e memória. Veja ADR-038. Mesmo sem um .kenignore, o ken automaticamente ignora arquivos acima de KEN_MAX_FILE_BYTES (2 MiB) e arquivos minificados (comprimento médio de linha muito longo — bundles compilados, JSON de linha única) via KEN_MAX_AVG_LINE_BYTES. O .kenignore é para os caminhos específicos do repositório que essas heurísticas não capturam.

Ignorar código gerado (opt-in). Defina KEN_SKIP_GENERATED=1 para também ignorar arquivos cujo cabeçalho contenha um marcador de geração por máquina — @generated, ou um banner de Code generated … DO NOT EDIT (stubs de protobuf, listadores de client-go, saída de stringer/mockgen). Isso captura código-fonte gerado que parece normal para a heurística de minificação. Desativado por padrão, porque alguns corpora legitimamente querem sua superfície gerada pesquisável (um agente perguntando "onde está o cliente tipado para X" quer esse arquivo gerado). Honrado tanto no índice inicial quanto no watch ao vivo.

Como 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

O algoritmo de recuperação é um port verbatim do search.py + ranking/*.py do semble; veja docs/DESIGN.md §7 para cada constante e sutileza de ordem de pipeline, e §4 para o contrato de inferência do Model2Vec (safetensors de três tensores, a indireção de mapping[], a precisão float64 que é essencial para a paridade de cosseno).

Comparação com semble

Propriedadesembleken
Linguagem / distribuiçãoPython · uvx / pipGo · binário estático único
Cold start~500 ms (interpretador + numpy + modelo)~10–20 ms ken search sobre um índice minúsculo
Algoritmo de recuperaçãoimplementação de referênciaport verbatim (constantes + ordem de pipeline de search.py + ranking/*.py)
NDCG@10 no benchmark do semble0.8540.842 híbrido (gap 0.012, 63 repositórios completos × 1.251 consultas)
Recall@10 em consultas de agente(não medido)~0.97 híbrido (0.967 NL / 0.995 símbolo); fallback somente BM25 ~0.84
Tokens para recall@10(não medido)~46× menos que grep+Read em consultas NL (4.120 vs 189.773 mediana, híbrido)
Servidor MCPsimsim — drop-in (mesmos schemas + formato de wire)
Tamanho do binárion/arelease (slim) ken ~22 MB · ken-mcp ~38 MB
Requer huggingface-clisimnão — ken download-model busca direto do HF

Metodologia completa, o detalhamento por ablação (semântico-raw corresponde ao semble dentro de 0.003, validando o port de embedding + tokenizer + ANN), a âncora externa CoIR-CSN-Python e cada nota de rodapé estão em docs/BENCH.md.

Comparado a outras ferramentas de busca de código para agentes

A parte concorrida desta categoria se divide em um eixo: o que você precisa executar. A aposta do ken é que o modelo de embedding pertence dentro do binário — inferência Model2Vec puramente em Go, sem cgo — então não há mais nada para levantar: sem daemon de embedding, sem banco de dados vetorial, sem chave de API, air-gapped. Os dois pontos de comparação mais próximos:

  • grepai — o análogo arquitetural mais próximo: um único binário Go com um watcher de arquivos e um servidor MCP, 100% local. Ele descarrega embeddings para um servidor Ollama separado (você instala + executa Ollama e baixa um modelo).
  • claude-context (Zilliz) — o mais visível: busca híbrida BM25 + densa, mas apoiada em um banco de dados vetorial (Milvus auto-hospedado via Docker, ou Zilliz Cloud gerenciado) e um provedor de embeddings (API OpenAI / VoyageAI / Gemini, ou Ollama local).
kengrepaiclaude-context
Runtimebinário Go estático único (sem cgo)binário Go únicoNode/TS (npm)
Embeddingsem processo, Go puro (Model2Vec)daemon Ollama externoprovedor externo (OpenAI / Voyage / Gemini, ou Ollama)
Serviços externos necessáriosnenhum — busca automaticamente um modelo de ~60 MB, depois roda offlineOllama (daemon + modelo)banco de dados vetorial (Milvus/Docker ou Zilliz Cloud) + uma API/daemon de embedding
RecuperaçãoBM25 + denso + RRF + rerank ciente de códigodenso + grafos de chamadahíbrido (BM25 + denso)
Recall / NDCG0.967 recall@10 · 0.842 NDCG@10, com harness de reproduçãonão publicadonão publicado
Economia de tokens~46× vs grep+Read, medido + reproduzívelnão publicadoreivindicação do fornecedor −39% vs uma linha de base
Velocidadeíndice ~1.6 s / 13 k chunks; busca híbrida p50 ~1.5 ms (medido)fornecedor: "10 k arquivos em segundos, consultas em ms"depende do banco de dados vetorial + rede
Linguagens (estrutural)13 (tree-sitter)10nível de chunk, agnóstico de linguagem
LicençaMITMITMIT

Duas ressalvas honestas. Primeiro, os números do ken vêm com comandos de reprodução (docs/BENCH.md); as células marcadas como "não publicado" significam que não encontramos nenhuma figura de benchmark padrão para citar e não avaliamos independentemente a velocidade dos outros — arquitetura, dependências e licença são os eixos verificáveis (em junho de 2026). Segundo, as ferramentas otimizam para coisas diferentes — o grepai adiciona rastreamento de grafo de chamadas; o claude-context depende de um banco de dados vetorial gerenciado para escalar. A reivindicação específica do ken é recall próximo ao grep com ~1–2 ordens de magnitude a menos de tokens, de um único binário sem serviços externos, cada número reproduzível.

Escolhendo um chunker

O chunker padrão regex lida bem com a maioria dos casos. O chunker opt-in treesitter (--chunker=treesitter / KEN_MCP_CHUNKER=treesitter, gotreesitter puramente em Go) vence de forma mensurável para Kotlin, Zig, TypeScript, Java, PHP e perde em Python, C, Rust, Lua, Scala — Δ líquido de −0.004 NDCG no geral (dentro do ruído), então permanece opt-in. A tabela completa de recomendações por linguagem está em docs/BENCH.md; a justificativa para o padrão permanecer regex é ADR-011.

Para autores de SDK: envie documentação como um único binário

A biblioteca mcp.Run permite que você incorpore um corpus //go:embed + o modelo Model2Vec em um único binário de servidor MCP estático — sem backend, sem banco de dados vetorial, sem egresso de rede por consulta, com versão fixada pelo artefato de build. ~20 linhas de main.go, go build, envie para um release do GitHub; usuários brew install e adicionam uma linha à configuração do agente. O walker e o indexador aceitam qualquer fs.FS (embed.FS, fstest.MapFS, com suporte a tarball), o que também fornece sandboxing do agente por construção.

Guia completo — o padrão canônico, índices pré-construídos para cold start rápido, o contrato de tamanho do binário e o pacote opt-in mcp/db — está em docs/DEVELOPERS.md.

Demos ao vivo (binários mcp.Run baixáveis sobre codebases reais, com transcrições de auditoria): release demos/v0.1.0 — Kubernetes v1.31.0 (59.795 chunks) e PostgreSQL 17.0 (64.506 chunks). Artigo: I shipped two downloadable code search binaries. The audit caught two bugs..

Roadmap

O registro de riscos com gatilhos explícitos está em docs/DESIGN.md §10; o tracker vivo de prontidão para 1.0 é docs/internal/road-to-1.0.md. A recuperação é tratada como fechada para 1.0 (a curva de relevância é plana); o trabalho restante é polimento + onboarding (levar novas instalações ao caminho híbrido) + distribuição.

Como isso foi construído

ken é um port. O algoritmo de recuperação é verbatim de MinishLab/semble (Python); a implementação em Go foi escrita por Claude sob restrições fixas: Go puro / sem cgo, constantes do algoritmo portadas verbatim e nunca ajustadas, a fonte original vence sempre que a reconstrução de Claude diverge do código vivo do semble. Essa última regra capturou cinco erros materiais durante o port do pipeline de rerank — cada um uma alucinação de som confiante que estava errada quando verificada contra a fonte Python. A disciplina de sempre verificar, a regra do port verbatim e o harness de paridade do tokenizer de 11k entradas (que revelou três bugs que um spot-check de 18 casos perdeu) são fornecidos por humanos. Cada decisão arquitetural está registrada em docs/internal/DECISIONS.md.

Agradecimentos

ken se apoia nos ombros do MinishLab — o algoritmo de recuperação, o modelo, toda a abordagem da tabela de embeddings são deles.

  • semble — a implementação original em Python. © Thomas van Dongen, MIT.
  • model2vec — a biblioteca de embeddings estáticos cujo formato de três tensores o ken implementa. © Thomas van Dongen, MIT.
  • potion-code-16M — pesos do modelo, destilados de nomic-ai/CodeRankEmbed (MIT), que por sua vez vem de Snowflake/snowflake-arctic-embed-m-long (Apache-2.0). © Minish Lab. Redistribuído conforme NOTICE.

Licença

ken é licenciado sob MIT. Ele inclui atribuição para os pesos de modelo redistribuídos em NOTICE e uma lista gerada de licenças de dependências em THIRD_PARTY_LICENSES.md; cada link na cadeia de proveniência é permissivo (MIT, Apache-2.0, MPL-2.0). Veja docs/DESIGN.md §6.

Para contribuidores: CLAUDE.md tem as convenções de build/teste/formatação e os invariantes do projeto (contrato de precisão, contrato de stdout/stderr).