Skip to main content
Glama

k-NN over the corpus by embedding

emem_find_similar
Idempotent

Find the k nearest cells in a spatial corpus by cell embedding or inline vector, returning nearest neighbors with scores and locations. Use it to discover places similar to a chosen cell or vector.

Instructions

Find the k nearest cells in the corpus by cell embedding or by an inline vector. The answer has neighbors, nearest first, each with cell, score, lat/lng and band_used, and a signed receipt over the vectors it read. mode sets the scoring. cosine is exact fp32. hamming counts sign bits and scans many more cells for the same budget. hamming_then_rerank does both. k is 1 to 1000, default 10. The tool ranks only what the corpus holds. When the KEY has no vector, it materializes that one band for the key, signs it, reports it in materialize_notes, and tries again. It never materializes neighbours. Thus an empty result means that no attested vector is near, not that no place is similar.

When to use: Call this for 'find places like X' or 'where else looks like this', or with an embedding. key is a cell64 or inline:[x,y,...]. The default band is geotessera (the 128-D Tessera foundation embedding). For the 1152-D fusion of 9 years (2017 to 2025), send band: "geotessera.multi_year".

Example arguments: {"key":"defi.zb64a.cAzU.zfa27","k":10}

Input Schema

NameRequiredDescriptionDefault
kNoHow many neighbours to return.
keyYescell64 (look up that cell's vector) or 'inline:[x,y,...]' literal vector
bandNovector band to scan (default: 128-D Tessera foundation embedding). For mode=hamming/hamming_then_rerank you can pass either the cosine band (e.g. 'geotessera') or its binary sibling ('geotessera.bin128'), the responder picks the right one.geotessera
cellNoAlias for `key`.
modeNoScoring mode. cosine = fp32 over full vector (precise, ~256 B/cell scan). hamming = sign-bit popcount over the binary sibling band (~16 B/cell, ~1000× faster, ~65% recall@10). hamming_then_rerank = triage with Hamming on 4·k candidates then re-rank by cosine, matches cosine precision at ~16× less work.cosine
scopeNoMulti-tenant scope `{user_id, agent_id, run_id, org_id}`. Setting it bypasses the ANN index entirely, because that index carries no scope column, and runs the brute-force scan instead: the tenant filter is honoured truthfully, and the call is slower.
cell64NoAlias for `key`.
filterNoClaim-algebra predicate evaluated against every candidate before ranking. A cell with no fact for the filter's band is DROPPED rather than treated as false, so 'places like X where NDVI > 0.5' never silently includes cells with no NDVI.
as_of_tslotNoBi-temporal valid-time bound. Applied to candidate cells BEFORE cosine scoring, a cell with no fact whose tslot ≤ as_of_tslot under the scoring band is dropped from the candidate pool (undecidable→drop). When set, the Lance ANN fast-path is bypassed (the index has no signed_at column); brute-force k-NN runs instead so as_of is honoured truthfully.
as_of_signed_atNoBi-temporal transaction-time bound (RFC 3339). Also applied to candidates BEFORE cosine. Same Lance-bypass note as as_of_tslot.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed4 schema fields changedv1.3.8
    • addedInput schema / properties / cell
      Added value: +{
      +  "description": "Alias for `key`.",
      +  "type": "string"
      +}
    • addedInput schema / properties / cell64
      Added value: +{
      +  "description": "Alias for `key`.",
      +  "type": "string"
      +}
    • addedInput schema / properties / filter
      Added value: +{
      +  "description": "Claim-algebra predicate evaluated against every candidate before ranking. A cell with no fact for the filter's band is DROPPED rather than treated as false, so 'places like X where NDVI > 0.5' never silently includes cells with no NDVI.",
      +  "type": "object"
      +}
    • addedInput schema / properties / scope
      Added value: +{
      +  "description": "Multi-tenant scope `{user_id, agent_id, run_id, org_id}`. Setting it bypasses the ANN index entirely, because that index carries no scope column, and runs the brute-force scan instead: the tenant filter is honoured truthfully, and the call is slower.",
      +  "type": "object"
      +}
  2. Changed1 schema field changedv1.3.3
    • addedInput schema / properties / k / description
      Added value: +"How many neighbours to return."
  3. Changed3 schema fields changedv1.3.1
    • changedInput schema / properties / as_of_tslot / description
      Previous value: -"Bi-temporal valid-time bound. Applied to candidate cells BEFORE cosine scoring — a cell with no fact whose tslot ≤ as_of_tslot under the scoring band is dropped from the candidate pool (undecidable→drop). When set, the Lance ANN fast-path is bypassed (the index has no signed_at column); brute-force k-NN runs instead so as_of is honoured truthfully."New value: +"Bi-temporal valid-time bound. Applied to candidate cells BEFORE cosine scoring, a cell with no fact whose tslot ≤ as_of_tslot under the scoring band is dropped from the candidate pool (undecidable→drop). When set, the Lance ANN fast-path is bypassed (the index has no signed_at column); brute-force k-NN runs instead so as_of is honoured truthfully."
    • changedInput schema / properties / band / description
      Previous value: -"vector band to scan (default: 128-D Tessera foundation embedding). For mode=hamming/hamming_then_rerank you can pass either the cosine band (e.g. 'geotessera') or its binary sibling ('geotessera.bin128') — the responder picks the right one."New value: +"vector band to scan (default: 128-D Tessera foundation embedding). For mode=hamming/hamming_then_rerank you can pass either the cosine band (e.g. 'geotessera') or its binary sibling ('geotessera.bin128'), the responder picks the right one."
    • changedInput schema / properties / mode / description
      Previous value: -"Scoring mode. cosine = fp32 over full vector (precise, ~256 B/cell scan). hamming = sign-bit popcount over the binary sibling band (~16 B/cell, ~1000× faster, ~65% recall@10). hamming_then_rerank = triage with Hamming on 4·k candidates then re-rank by cosine — matches cosine precision at ~16× less work."New value: +"Scoring mode. cosine = fp32 over full vector (precise, ~256 B/cell scan). hamming = sign-bit popcount over the binary sibling band (~16 B/cell, ~1000× faster, ~65% recall@10). hamming_then_rerank = triage with Hamming on 4·k candidates then re-rank by cosine, matches cosine precision at ~16× less work."
  4. First observedv0.1.0

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses non-obvious behaviour well beyond the annotations: it materializes and signs a band for the key when absent (reported in materialize_notes), never materializes neighbours, bypasses the ANN fast-path under scope/as_of filters, and explains that an empty result means no attested vector is near rather than no similar place. This is consistent with readOnlyHint=false and idempotentHint=true.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the operation, then output shape, then mode/band selection, then a 'When to use' block and an example. Dense but every paragraph carries information; the mode exposition is slightly repetitive with the schema enum descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description supplies the return shape (neighbors, cell, score, lat/lng, band_used, signed receipt) and the edge-case semantics (empty result, materialize_notes). For a 10-parameter tool with nested objects, nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, but the description adds real meaning: 'k' bounds (1–1000, default 10), the concrete band names for the 1152-D multi-year fusion, and the relationship between key/inline vectors. It mirrors some schema content rather than always exceeding it, so a 4 rather than 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ('Find the k nearest cells in the corpus by cell embedding or by an inline vector') and names the exact discriminating concept (k-NN over embeddings) versus the rest of the emem_* family. The agent can tell this apart from emem_recall/emem_locate without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

An explicit 'When to use' section maps natural utterances ('find places like X') to the tool, and it routes between bands (geotessera vs geotessera.multi_year) and modes. It never names a sibling alternative or states when not to use this tool, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.