emem-find-similar

Exact captured content · path plugins/emem/skills/emem-find-similar/SKILL.md · sha256 5469c9aa9e411afc… · revision 9cbc309515f2fc194e7d68f0732e6b7ef5659b9a

Download the exact captured file

---
name: emem-find-similar
description: Returns the top-K places most similar to a seed place by cosine over a stored 128-D surface-texture embedding (Sentinel-1 plus Sentinel-2, annual). Use when the user asks for analogues or look-alikes ("find places like Bangalore", "where else looks like the Sundarbans"). The embedding band is retired on emem.dev, so the index is frozen at what was already stored; the skill says how to tell "no analogue" from "this seed has no vector and cannot get one".
---

# emem-find-similar

> **Network use.** The commands in this skill send and receive JSON (and, where a step says so, an image or a raster file) to `https://emem.dev` only, the service the plugin's MCP server connects to. Nothing they download is executed. This skill ships no scripts. Its commands read no environment variable, key or credential, and the only local files they read are ones an earlier step saved from `https://emem.dev`.

A nearest-neighbour search over a stored 128-D embedding of surface
texture (Sentinel-1 SAR plus Sentinel-2 optical, aggregated per year).
Two cells above about 0.85 cosine are usually the same physical
archetype.

## Read this before you promise an answer

The embedding band (`geotessera`) is **retired on emem.dev**. The
responder serves vectors it already stored and computes no new ones:

- A seed that has a stored vector works as before.
- A seed without one **cannot be given one**. `/v1/find_similar` answers
  `cid_not_found`, and `/v1/recall` on the band answers a note with
  reason `band_retired_at_this_responder`. That is a deployment decision,
  and retrying never changes it.
- Coverage is whatever was stored before the retirement: dense around
  places people asked about, empty elsewhere.

"No analogue found" and "this place has no vector" are different
answers. Only report the one that happened.

## When to invoke

- "Find cities globally that look like Bangalore."
- "Where else has the same forest signature as the Western Ghats?"

For predicate matching ("all places with NDVI above 0.7") this is the
wrong tool: use `POST /v1/query_region` or `POST /v1/compare_bands`.
For climate similarity, compare `koppen` classes from recall instead;
this embedding aliases very different climates that share a texture.

## Step 1: resolve the seed

Call `emem_locate` with:

```json
{"q": "Bengaluru, India"}
```

Keep the `cell64` it returns. Without MCP, send the same body to
`POST https://emem.dev/v1/locate`.

## Step 2: query, and branch on the answer

Call `emem_find_similar` with the seed cell as `key` (`cell` is
accepted as another name for it):

```json
{"key": "defi.zb493.zezo.zcb35", "k": 12}
```

Save the result's JSON text as `sim.json`. Over MCP, a refusal comes
back as a tool error whose message names the code, for example
`cid_not_found`. Without MCP, send the same body to
`POST https://emem.dev/v1/find_similar` and save the answer as
`sim.json` even when it is an error: here a refusal is an answer. Then
branch on it:

```sh
jq 'if .code then {code, message}
    else [.neighbors[] | {cell, score, place: .place_label_cached, lat, lng}] end' sim.json
```

Call it directly rather than probing with recall first. On 2026-09-28 a
Timbuktu seed returned no `geotessera` fact from `/v1/recall` (a
retired-band note) yet `/v1/find_similar` answered from its stored
vector, so recall is not a reliable coverage test. `cid_not_found` from
find_similar is.

Each neighbour carries `cell`, `score` (cosine), `lat`, `lng`,
`place_label_cached`, `band_used`, `similarity_method`, `scene_png_url`
and `deep_recall_url`. The response also carries a signed `receipt`
over the neighbours' fact cids and an `interpretation` block that says
what the similarity does and does not mean. Pass that on.

Recorded on 2026-09-28: seed `defi.zb493.zezo.zcb35` (Bengaluru) with
`k: 5` returned four neighbours, all in Bengaluru, top score 0.926. A
seed at 44.123 S, 120.456 W (open Pacific) returned `cid_not_found`.

## Options

- `band: "geotessera.2018"` (any year 2017 to 2024) searches that
  vintage; `geotessera.multi_year` searches the stacked 1024-D vector,
  which picks up places that changed in similar ways. Each vintage
  answers only where it was stored.
- `mode: "hamming"` runs the binary sign-bit index instead of cosine
  (`similarity_method: "hamming"` on each neighbour). Do not pass
  `band: "geotessera.bin128"`: on 2026-09-28 that returned an `internal`
  error.

## Pitfalls

- **Nearby is not similar.** The top neighbours of a city cell are often
  other cells of the same city. Ask for a larger `k` and filter by
  distance if the user wants places elsewhere.
- **Archetype, not society.** A "similar" city looks the same from
  space (density, vegetation, water) and may be nothing alike otherwise.
- **Thresholds are heuristic.** Above 0.85 same archetype, 0.7 to 0.85
  related, below 0.7 weak; the cutoff depends on the seed.

## Before you report the neighbours

Similarity is a ranking over a frozen index, not a measurement. Name
the neighbours with their cell64 and score, say the index is frozen,
and cite band facts (recall at each neighbour) for anything you claim
about them. See [`emem-referential-drift`](../emem-referential-drift/SKILL.md)
and [`emem-verify-before-publish`](../emem-verify-before-publish/SKILL.md).