Skip to content

Commit efcb056

Browse files
fluffy314cursoragent
authored andcommitted
fix(autoresearch): deduplicate strategy state losslessly
Intern repeated branch statements and evidence once so the complete active research state fits its hard Prefill budget without truncation or summarization. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6e8a20e commit efcb056

3 files changed

Lines changed: 64 additions & 7 deletions

File tree

autoresearch/prefill/program.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ Prefill budgets are hard admission limits, never truncation instructions.
6161
Strategy input must fit 8192 tokens by carrying the complete active leaf
6262
ancestry and its exact experiment records. Generator and Critic inputs must fit
6363
6144 tokens; the Critic always receives the complete current Generator output.
64+
Repeated Strategy strings are interned once in `text_by_id`; `_ref` fields
65+
losslessly reference that exact text.
6466
If any complete semantic unit exceeds its budget, reject it before remote
6567
Prefill and preserve the checkpoint. Never slice, sample, summarize, or drop
6668
the tail of an over-budget input.

autoresearch/prefill/supervisor.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,21 @@ def build_strategy_research_state(
424424
ledger: dict,
425425
results_text: str,
426426
) -> dict:
427+
text_by_id: dict[str, str] = {}
428+
id_by_text: dict[str, str] = {}
429+
430+
def intern(value) -> str:
431+
text = str(value or "")
432+
if not text:
433+
return ""
434+
existing = id_by_text.get(text)
435+
if existing is not None:
436+
return existing
437+
text_id = f"t{len(text_by_id) + 1}"
438+
id_by_text[text] = text_id
439+
text_by_id[text_id] = text
440+
return text_id
441+
427442
target_id = _select_repair_target(current, ledger)
428443
obligations = {
429444
str(item.get("obligation_id", "")): item
@@ -437,11 +452,11 @@ def build_strategy_research_state(
437452
item = obligations[cursor]
438453
ancestry.append({
439454
"obligation_id": cursor,
440-
"statement": item.get("statement", ""),
455+
"statement_ref": intern(item.get("statement", "")),
441456
"status": item.get("status", ""),
442457
"parent_id": item.get("parent_id", ""),
443458
"last_run_id": item.get("last_run_id", ""),
444-
"last_evidence": item.get("last_evidence", ""),
459+
"last_evidence_ref": intern(item.get("last_evidence", "")),
445460
})
446461
cursor = str(item.get("parent_id", ""))
447462
ancestry.reverse()
@@ -464,12 +479,14 @@ def build_strategy_research_state(
464479
),
465480
"hypothesis_sha256": row.get("hypothesis_sha256", ""),
466481
"research_outcome": row.get("research_outcome", ""),
467-
"research_evidence": row.get("research_evidence", ""),
468-
"new_frontier": row.get("new_frontier", ""),
482+
"research_evidence_ref": intern(
483+
row.get("research_evidence", ""),
484+
),
485+
"new_frontier_ref": intern(row.get("new_frontier", "")),
469486
"kept": row.get("kept", ""),
470487
"error": row.get("error", ""),
471488
})
472-
return {
489+
state = {
473490
"target_leaf_id": target_id,
474491
"target_ancestry": ancestry,
475492
"relevant_experiments": relevant_results,
@@ -479,12 +496,14 @@ def build_strategy_research_state(
479496
"target_obligation_id",
480497
"",
481498
),
482-
"hypothesis": current.get("hypothesis", ""),
499+
"hypothesis_ref": intern(current.get("hypothesis", "")),
483500
"prefill_compute_chunk_tokens": current.get(
484501
"prefill_compute_chunk_tokens",
485502
),
486503
},
487504
}
505+
state["text_by_id"] = text_by_id
506+
return state
488507

489508

490509
def build_strategy_prompt(
@@ -511,7 +530,9 @@ def build_strategy_prompt(
511530
"It must either construct a concrete object or attempt a concrete "
512531
"counterexample for the target leaf. target_obligation_id must equal "
513532
"TARGET_LEAF_ID. Every statement and evidence item below is complete; "
514-
"do not infer omitted text from unrelated branches."
533+
"do not infer omitted text from unrelated branches. Fields ending in "
534+
"_ref resolve through text_by_id; this is lossless deduplication, not "
535+
"summary or truncation."
515536
f"\n\nPROGRAM:\n{program}"
516537
"\n\nRESEARCH_STATE:\n"
517538
f"{json.dumps(research_state, ensure_ascii=False)}"

tests/inference_engine/bench/test_autoresearch_supervisor.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,40 @@ def test_strategy_state_keeps_complete_active_ancestry_only():
327327
assert "unrelated result" not in serialized
328328

329329

330+
def test_strategy_state_deduplicates_text_losslessly():
331+
shared = "Complete exact evidence that must appear once without truncation."
332+
ledger = {"obligations": [
333+
{
334+
"obligation_id": "RH-C2",
335+
"statement": "Root statement.",
336+
"status": "UNRESOLVED",
337+
"parent_id": "",
338+
"last_evidence": shared,
339+
},
340+
]}
341+
results = (
342+
"candidate_id\ttarget_obligation_id\tresearch_outcome\t"
343+
"research_evidence\tnew_frontier\tkept\terror\t"
344+
"hypothesis_sha256\n"
345+
f"c2\tRH-C2\tINCONCLUSIVE\t{shared}\tRoot statement."
346+
"\tFalse\t\th2\n"
347+
)
348+
state = build_strategy_research_state(
349+
current={**_candidate(), "target_obligation_id": "RH-C2"},
350+
ledger=ledger,
351+
results_text=results,
352+
)
353+
serialized = str(state)
354+
assert serialized.count(shared) == 1
355+
assert serialized.count("Root statement.") == 1
356+
evidence_ref = state["target_ancestry"][0]["last_evidence_ref"]
357+
result_ref = state["relevant_experiments"][0][
358+
"research_evidence_ref"
359+
]
360+
assert evidence_ref == result_ref
361+
assert state["text_by_id"][evidence_ref] == shared
362+
363+
330364
def test_results_are_append_only_and_best_is_selected(tmp_path):
331365
path = tmp_path / "results.tsv"
332366
common = {

0 commit comments

Comments
 (0)