qa: --retrieval-keywords flag for explicit retrieval augmentation
Empirically observed 2026-05-01: long discursive questions like
'what technology is currently or soon available which may enable
one person to reconstruct another person's thoughts...' under-
retrieve because their content tokens get diluted by template
phrasing. AND-mode FTS5 returns zero hits; OR-fallback ranks
unrelated articles by raw BM25.
Fox's discovery: appending domain keywords ('transcranial knowledge
acquisition') to the question lifts the verdict from HYBRID 6/10
to STRICT 1/1 by narrowing OR-mode retrieval to the topical
article (Neurotechnology). The keywords act as an operator hint
about what the question is really asking.
This commit exposes that pattern as a first-class flag without
polluting the question text:
--retrieval-keywords TEXT on `aborist query`
K="..." on `make query`
Plumbing: the keywords are concatenated with the question for
FTS5 search and title-filter token construction only. The LLM
still sees the original question; the verifier still checks against
the original question; cache_key still computes from the original
question. Keywords are session-only — successive calls with
different keywords on the same question can cache-hit each other.
Pair with BURN=1 for fresh inference per call.
Live verification:
make query Q="...thoughts...without speaking or sign language."
K="transcranial knowledge acquisition" BURN=1
-> STRICT 2/2 via claim_lattice, 37.5s, Neurotechnology article
cited for fMRI and DBS claims.
Without K: HYBRID 6/10 with Videoconferencing/Telepathy as top
sources — model hallucinated structure across irrelevant chunks.
This commit is contained in:
parent
26e3af9033
commit
2d6a86b991
3 changed files with 50 additions and 6 deletions
6
Makefile
6
Makefile
|
|
@ -166,11 +166,11 @@ QUERY_TOP_K ?= 8
|
|||
# ANSWER_MODE=quote for the legacy substring verifier. ANSWER_MODE=
|
||||
# (empty) defers to DEFAULT_QUERY_POLICY.
|
||||
ANSWER_MODE ?= claim_lattice
|
||||
query: bootstrap ## ask the corpus a question [JSON=1 BURN=1 REPAIR=1 REPROMPTS=N ANSWER_MODE=claim_lattice|claim_lattice_pointer|quote]; JSON by default
|
||||
query: bootstrap ## ask the corpus a question [JSON=1 BURN=1 REPAIR=1 REPROMPTS=N K="extra retrieval keywords" ANSWER_MODE=claim_lattice|claim_lattice_pointer|quote]; JSON by default
|
||||
@if [ -z "$$Q" ] && [ -z "$(Q)" ]; then \
|
||||
echo "usage: make query Q=\"your question\" [JSON=1 BURN=1 REPAIR=1 REPROMPTS=N ANSWER_MODE=claim_lattice|claim_lattice_pointer|quote]"; exit 2; \
|
||||
echo "usage: make query Q=\"your question\" [JSON=1 BURN=1 REPAIR=1 REPROMPTS=N K=\"extra retrieval keywords\" ANSWER_MODE=claim_lattice|claim_lattice_pointer|quote]"; exit 2; \
|
||||
fi
|
||||
$(ABORIST) --shards-dir $(SHARDS_DIR) query --top-k $(QUERY_TOP_K) $(if $(JSON),--json,) $(if $(BURN),--burn,) $(if $(REPAIR),--repair,) $(if $(REPROMPTS),--repair-reprompts $(REPROMPTS),) $(if $(ANSWER_MODE),--answer-mode $(ANSWER_MODE),) "$(Q)"
|
||||
$(ABORIST) --shards-dir $(SHARDS_DIR) query --top-k $(QUERY_TOP_K) $(if $(JSON),--json,) $(if $(BURN),--burn,) $(if $(REPAIR),--repair,) $(if $(REPROMPTS),--repair-reprompts $(REPROMPTS),) $(if $(ANSWER_MODE),--answer-mode $(ANSWER_MODE),) $(if $(K),--retrieval-keywords "$(K)",) "$(Q)"
|
||||
|
||||
query-dry: bootstrap ## like 'make query' but skip the LLM call (dry-run) [JSON=1 BURN=1 ANSWER_MODE=...]
|
||||
@if [ -z "$$Q" ] && [ -z "$(Q)" ]; then \
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ def _cmd_query(args: argparse.Namespace) -> int:
|
|||
policy=call_policy,
|
||||
fidelity=getattr(args, "fidelity", None),
|
||||
burn_existing=bool(getattr(args, "burn", False)),
|
||||
retrieval_keywords=getattr(args, "retrieval_keywords", None),
|
||||
)
|
||||
|
||||
if args.json:
|
||||
|
|
@ -3058,6 +3059,21 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
"constrained inference like Qwen 3.6 reasoner / Claude / GPT-4)."
|
||||
),
|
||||
)
|
||||
query_cmd.add_argument(
|
||||
"--retrieval-keywords", dest="retrieval_keywords", default=None,
|
||||
help=(
|
||||
"operator-supplied keywords appended to the question for "
|
||||
"FTS5 retrieval ONLY — never sent to the LLM, never enters "
|
||||
"cache_key, never reaches the verifier. Use to narrow "
|
||||
"OR-mode retrieval on long discursive questions whose "
|
||||
"content tokens get diluted by template phrasing. Example: "
|
||||
"make query Q='what tech may enable one person to "
|
||||
"reconstruct another person's thoughts...' "
|
||||
"K='transcranial knowledge acquisition'. Pair with --burn "
|
||||
"to force fresh inference (keywords are session-only and "
|
||||
"cache-hits ignore them)."
|
||||
),
|
||||
)
|
||||
query_cmd.set_defaults(func=_cmd_query)
|
||||
|
||||
inspect_cmd = sub.add_parser(
|
||||
|
|
|
|||
|
|
@ -1115,6 +1115,7 @@ def query(
|
|||
chain: str = "private",
|
||||
fidelity: str | None = None,
|
||||
burn_existing: bool = False,
|
||||
retrieval_keywords: str | None = None,
|
||||
) -> dict:
|
||||
"""Answer `question` using the corpus. Cache to qa_db. Returns a result dict.
|
||||
|
||||
|
|
@ -1135,6 +1136,22 @@ def query(
|
|||
rows were deleted (0 or 1 for the primary key; the equivalence-
|
||||
class fallback key is left alone so prior alt-mode records stay
|
||||
historic).
|
||||
|
||||
`retrieval_keywords` augments the FTS5 search and title-filter
|
||||
token set with operator-supplied keywords WITHOUT changing what
|
||||
the LLM sees, what the verifier checks, or what enters cache_key.
|
||||
Empirically observed 2026-05-01: long discursive questions like
|
||||
'what technology is currently or soon available which may enable
|
||||
one person to reconstruct another person's thoughts...' under-
|
||||
retrieve because their content tokens get diluted by template
|
||||
phrasing. Appending domain keywords ('transcranial knowledge
|
||||
acquisition') narrows OR-mode FTS5 to the topical article
|
||||
(Neurotechnology) and lifts the verdict from HYBRID to STRICT.
|
||||
This flag exposes that pattern explicitly. Cache implication:
|
||||
keywords are session-only — not in cache_key, so successive
|
||||
calls with different keywords on the same question can cache-
|
||||
hit each other. Pair with ``burn_existing=True`` for fresh
|
||||
inference each call.
|
||||
"""
|
||||
policy = policy or DEFAULT_QUERY_POLICY
|
||||
if fidelity is None:
|
||||
|
|
@ -1151,8 +1168,19 @@ def query(
|
|||
t_start = time.monotonic()
|
||||
|
||||
# 1. Search.
|
||||
#
|
||||
# Retrieval-only query string: question + operator-supplied
|
||||
# ``retrieval_keywords`` (a hint, never part of the cache_key /
|
||||
# LLM prompt / verifier surface). When the user passes
|
||||
# `--retrieval-keywords "transcranial knowledge acquisition"`,
|
||||
# only the FTS5 MATCH and title-filter token set see those
|
||||
# tokens; the question text fed to the LLM and to question_hash
|
||||
# stays untouched.
|
||||
retrieval_query = question
|
||||
if retrieval_keywords and retrieval_keywords.strip():
|
||||
retrieval_query = f"{question} {retrieval_keywords.strip()}"
|
||||
t_phase = time.monotonic()
|
||||
hits = _search_corpus(shards_dir, single_db, question, over_fetch)
|
||||
hits = _search_corpus(shards_dir, single_db, retrieval_query, over_fetch)
|
||||
if not hits:
|
||||
return {
|
||||
"status": "no_sources",
|
||||
|
|
@ -1164,7 +1192,7 @@ def query(
|
|||
}
|
||||
core_match_roots = getattr(hits, "_core_match_roots", set())
|
||||
root_to_shard = getattr(hits, "_root_to_shard", {})
|
||||
qtokens_lower = {t.lower() for t in _title_query_tokens(question)}
|
||||
qtokens_lower = {t.lower() for t in _title_query_tokens(retrieval_query)}
|
||||
|
||||
def _body_density_check(h) -> bool:
|
||||
# Lazy per-hit check: open the shard, count token mentions in this doc.
|
||||
|
|
@ -1179,7 +1207,7 @@ def query(
|
|||
|
||||
hits = _rerank(
|
||||
hits,
|
||||
question,
|
||||
retrieval_query,
|
||||
core_match_roots=core_match_roots,
|
||||
body_density_check=_body_density_check,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue