Skip to content

Infer tags from the recall query and use them as a soft ranking boost #142

@rahilp

Description

@rahilp

Problem

Tags only help recall today when the caller passes an explicit tag filter. For a normal query like "what did I decide about the office lease", tags are invisible to ranking — the system never recognizes the query is work/legal-related, so tag-relevant memories get no preference. There is no auto-tagging-from-text anywhere in the codebase.

Proposed solution

Two coupled pieces.

1. Infer tags from the query (hybrid)

New inferQueryTags(query, env): Promise<string[]>:

  1. Hashtags — reuse extractHashtags(query) (src/index.ts:498).
  2. Known vocabularySELECT tags FROM entries, flatten + dedupe the JSON arrays into a tag set.
  3. Keyword match — include any known tag appearing as a whole word in the lowercased query.
  4. If steps 1+3 yield anything → return (no LLM call).
  5. LLM fallback only when empty — mirror scoreImportance (:479-494): env.AI.run(LLM_MODEL, …) (@cf/meta/llama-4-scout-17b-16e-instruct, :20) + readStreamText; prompt picks the relevant subset from the known-tag list; parse defensively, intersect with the known set, wrap in try/catch → [] on failure (best-effort).

Run in parallel with embedding inside recallEntries: Promise.all([embed(...), inferQueryTags(...)]). Inferred tags are a soft signal only, never a hard filter — the explicit tag param stays the only filter, so inference can never over-narrow results.

2. Soft tag boost in reranking

rerankWithTimeDecay (:392-427) gains an optional queryTags: string[] = [] param (default = no-op, so existing callers/tests are unchanged). Per match: overlap = |entry.tags ∩ queryTags|; tagBoost = overlap ? min(TAG_BOOST_MAX, 1 + overlap * TAG_BOOST_STEP) : 1.0, applied like importanceMultiplier (outside the recency ≤1.0 cap) so a tag-relevant memory can surface above a marginally-closer but irrelevant one. New constants: TAG_BOOST_STEP = 0.15, TAG_BOOST_MAX = 1.5. recallEntries passes queryTags into rerankWithTimeDecay on both paths.

Files

  • src/index.ts — new inferQueryTags; rerankWithTimeDecay signature + tag-boost multiplier; new constants; wire through recallEntries.

Tests / Verification

  • test/unit/rerank.test.ts — a tag-overlapping match outranks an equal-score non-overlapping one; queryTags=[] leaves scores unchanged.
  • test/integration/recall.test.ts — hashtag/keyword hit skips the LLM; empty inference exercises the LLM fallback (makeAIMock already streams a response).
  • npm test green.

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions