feat: give the curator continuous context and durable idea history

This commit is contained in:
wuyang
2026-07-28 17:31:37 +08:00
parent 5c483536b4
commit af7bb68268
10 changed files with 967 additions and 114 deletions
+138
View File
@@ -1,6 +1,7 @@
from pathlib import Path
from app.config import UserSeed
from app.curator import _rank_ideas, build_curator_prompt
from app.db import Database
@@ -97,6 +98,105 @@ def test_apply_assessment_builds_trajectory(tmp_path: Path):
assert idea["maturity"] == 46
def test_curator_prompt_contains_recent_continuity_and_idea_catalog(
tmp_path: Path,
):
db = database(tmp_path)
first = db.create_fragment(
ADMIN.id,
"我先让 AI 完成综述,接下来会抽取几个感兴趣的地方。",
)
db.apply_assessments(ADMIN.id, first["id"], [assessment()])
current = db.create_fragment(
ADMIN.id,
"我识别出了两个新的认知,第一点让我耳目一新。",
)
ideas, fragments = db.curator_context(ADMIN.id)
prompt = build_curator_prompt(current, ideas, fragments)
assert "我先让 AI 完成综述" in prompt
assert "我识别出了两个新的认知" in prompt
assert "无分类的笔记入口" in prompt
assert "不要要求用户显式建立会话" in prompt
def test_delayed_reanalysis_keeps_later_input_out_of_prior_context(
tmp_path: Path,
):
db = database(tmp_path)
previous = db.create_fragment(ADMIN.id, "当时真正的上文")
target = db.create_fragment(ADMIN.id, "后来需要重新整理的这一条")
future = db.create_fragment(ADMIN.id, "这句话发生在目标片段之后")
timeline = (
(previous, "2026-07-28T10:00:00.000+00:00"),
(target, "2026-07-28T10:10:00.000+00:00"),
(future, "2026-07-28T10:20:00.000+00:00"),
)
with db.connect() as connection:
for fragment, created_at in timeline:
fragment["created_at"] = created_at
connection.execute(
"UPDATE fragments SET created_at = ? WHERE id = ?",
(created_at, fragment["id"]),
)
ideas, fragments = db.curator_context(ADMIN.id)
prompt = build_curator_prompt(target, ideas, fragments)
recent_section = prompt.split("<recent_context>", 1)[1].split(
"</recent_context>", 1
)[0]
later_section = prompt.split("<later_context>", 1)[1].split(
"</later_context>", 1
)[0]
assert previous["content"] in recent_section
assert future["content"] not in recent_section
assert future["content"] in later_section
assert "延迟重整" in prompt
def test_idea_search_falls_back_to_recent_catalog():
idea = {
"id": "idea-one",
"title": "先综述再深入",
"summary": "一种理解策略",
"position": "正在实践",
"tension": "可靠性",
"trajectory": "从原则走向实践",
"updated_at": "2026-07-28",
"recent_fragments": [],
}
candidates, fallback = _rank_ideas(
[idea], "agent architecture long task reliability"
)
assert fallback is True
assert candidates == [idea]
def test_idea_versions_preserve_complete_state(tmp_path: Path):
db = database(tmp_path)
first = db.create_fragment(ADMIN.id, "第一条原始证据")
db.apply_assessments(ADMIN.id, first["id"], [assessment(maturity=30)])
idea = db.list_ideas(ADMIN.id)[0]
first_version = db.get_idea(ADMIN.id, idea["id"])["snapshots"][-1]
assert first_version["title"] == "无分类的笔记入口"
assert first_version["summary"] == "先保留念头,再让结构在后台形成。"
assert first_version["confidence"] == 0.76
assert first_version["change_kind"] == "analysis"
assert first_version["fragment_ids"] == [first["id"]]
db.update_idea_override(ADMIN.id, idea["id"], 61)
versions = db.get_idea(ADMIN.id, idea["id"])["snapshots"]
assert versions[-1]["change_kind"] == "manual_calibration"
assert versions[-1]["maturity_override"] == 61
assert versions[-1]["fragment_ids"] == [first["id"]]
def test_manual_position_is_authoritative(tmp_path: Path):
db = database(tmp_path)
fragment = db.create_fragment(ADMIN.id, "一个想法")
@@ -188,3 +288,41 @@ def test_agent_run_records_metrics_and_events(tmp_path: Path):
"tool_search_ideas",
]
assert overview["last_24h"]["success_rate"] == 100
def test_failed_agent_run_can_be_safely_requeued(tmp_path: Path):
db = database(tmp_path)
fragment = db.create_fragment(ADMIN.id, "需要重新整理")
run_id = db.start_agent_run(
ADMIN.id,
fragment["id"],
"deepseek-v4-pro",
"prompt.v2",
"high",
)
error = RuntimeError("temporary failure")
db.finish_agent_run(
run_id,
status="error",
duration_ms=100,
attempt_count=1,
model_rounds=1,
tool_calls=0,
search_calls=0,
inspection_calls=0,
input_tokens=10,
output_tokens=2,
reasoning_tokens=1,
cached_tokens=0,
error=error,
)
db.set_fragment_status(fragment["id"], "error", str(error))
retry = db.prepare_agent_retry(run_id)
assert retry == {
"user_id": ADMIN.id,
"fragment_id": fragment["id"],
}
assert db.get_fragment(fragment["id"], ADMIN.id)["analysis_status"] == "pending"
assert db.prepare_agent_retry(run_id) is None