feat(pegas-memory): Tier-1 self-improvement tools (commit + staleness)
- memory_commit: closes the 'commit hole' — writes verified answers from closed gaps back into atoms (mandatory dedup); leaves gap open if no verified answer exists. Reads verified status from PostgreSQL (source of truth), not the Qdrant projection. - memory_stale: staleness guard with two signals — content-staleness (re-fetch + LLM verdict, needs proxy for foreign sites) and age-staleness (proxy-free fallback by pm_document.fetched_at, max_age_days). - db: populate pm_document.fetched_at on upsert (freshness signal). - docs: README/AGENTS/ARCHITECTURE/EXAMPLES updated (18 tools). - self_improve.py: new module (227 lines).
This commit is contained in:
1 parent
a4c4a766c1
commit
eca8a11e23
8 files changed
+439
-6
No files matched your search
@@ -289,6 +289,55 @@ def _tool_verify(args):
|
||||
return json.dumps(result, ensure_ascii=False, indent=2), False
|
||||
|
||||
|
||||
def _tool_commit(args):
|
||||
"""Tier 1 (commit): закрыть дыру — answer закрытого gap пишется в атомы."""
|
||||
from pegas_memory import self_improve
|
||||
gap_ids = args.get("gap_ids")
|
||||
try:
|
||||
gap_ids = [int(x) for x in (gap_ids or [])]
|
||||
except (TypeError, ValueError):
|
||||
return json.dumps({"error": "gap_ids должен быть списком целых id"}, ensure_ascii=False), True
|
||||
if not gap_ids:
|
||||
return json.dumps({"error": "нужен gap_ids (список id закрытых пробелов)"}, ensure_ascii=False), True
|
||||
dedup = bool(args.get("dedup", True))
|
||||
try:
|
||||
result = self_improve.commit_gaps(_conn(), gap_ids=gap_ids, dedup=dedup)
|
||||
except ValueError as e:
|
||||
return json.dumps({"error": str(e)}, ensure_ascii=False), True
|
||||
return json.dumps(result, ensure_ascii=False, indent=2), False
|
||||
|
||||
|
||||
def _tool_stale(args):
|
||||
"""Tier 1 (maintain): гвард сталкнутости — атомы по свежему источнику → stale."""
|
||||
from pegas_memory import self_improve
|
||||
atom_id = args.get("atom_id")
|
||||
all_ = bool(args.get("all", False))
|
||||
if atom_id is not None:
|
||||
try:
|
||||
result = self_improve.stale_guard(
|
||||
_conn(), atom_id=int(atom_id),
|
||||
age_only=bool(args.get("age_only", False)),
|
||||
max_age_days=int(args.get("max_age_days") or 90),
|
||||
)
|
||||
except ValueError as e:
|
||||
return json.dumps({"error": str(e)}, ensure_ascii=False), True
|
||||
elif all_:
|
||||
try:
|
||||
result = self_improve.stale_guard(
|
||||
_conn(), all=True,
|
||||
limit=min(int(args.get("limit") or 20), 200),
|
||||
sample=bool(args.get("sample", True)),
|
||||
min_confidence=float(args.get("min_confidence") or 0.0),
|
||||
age_only=bool(args.get("age_only", False)),
|
||||
max_age_days=int(args.get("max_age_days") or 90),
|
||||
)
|
||||
except ValueError as e:
|
||||
return json.dumps({"error": str(e)}, ensure_ascii=False), True
|
||||
else:
|
||||
return json.dumps({"error": "укажи atom_id или all=true"}, ensure_ascii=False), True
|
||||
return json.dumps(result, ensure_ascii=False, indent=2), False
|
||||
|
||||
|
||||
TOOLS = [
|
||||
{
|
||||
"name": "memory_search",
|
||||
@@ -492,6 +541,35 @@ TOOLS = [
|
||||
|
||||
|
||||
|
||||
{
|
||||
"name": "memory_commit",
|
||||
"description": "Tier 1 (commit): закрыть «дыру commit» — верифицированный ответ закрытого пробела пишется обратно в атомы (не даёт знанию слетывать). Берет id пробелов, ищет верифицированные атомы-ответы, дистиллирует канонический атом с обязательным dedup и закрывает пробел. Atom-efficiency: dedup по умолчанию.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"gap_ids": {"type": "array", "items": {"type": "integer"}, "description": "Список id закрытых пробелов, answer которых надо записать в атомы."},
|
||||
"dedup": {"type": "boolean", "description": "Семантическая дедупляция при записи (default true)."},
|
||||
},
|
||||
"required": ["gap_ids"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "memory_stale",
|
||||
"description": "Tier 1 (maintain): гвард сталкнутости. Пере-проверяет атом(ы) по свежу источнику: противоречит/устарел → status='stale'. Лимит LLM-вызовов через limit/sample. atom_id — один атом; all — пачка (с limit/sample).",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"atom_id": {"type": "integer", "description": "ID атома для проверки на актуальность."},
|
||||
"all": {"type": "boolean", "description": "Прогнать пачку атомов (с limit/sample)."},
|
||||
"limit": {"type": "integer", "description": "Лимит атомов в пачке (1-200)."},
|
||||
"sample": {"type": "boolean", "description": "Сэмпл вместо проверки всех (по умолчанию true при all)."},
|
||||
"min_confidence": {"type": "number", "description": "Мин. уверенность атомов в пачке (default 0.0)."},
|
||||
"age_only": {"type": "boolean", "description": "Без рефечя: только age-staleness по fetched_at (proxy-free)."},
|
||||
"max_age_days": {"type": "integer", "description": "Возраст контента в днях, старше которого → stale (default 90)."},
|
||||
},
|
||||
"required": [],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "memory_get_context",
|
||||
"description": "Собрать релевантный контекст из твоей базы для ответа (поиск по атомам + форматирование). "
|
||||
@@ -551,6 +629,8 @@ def handle(msg):
|
||||
"memory_infer": _tool_infer,
|
||||
"memory_query_graph": _tool_query_graph,
|
||||
"memory_verify": _tool_verify,
|
||||
"memory_commit": _tool_commit,
|
||||
"memory_stale": _tool_stale,
|
||||
"memory_get_context": _tool_get_context,
|
||||
}
|
||||
fn = handlers.get(tool)
|
||||
|
||||
Reference in new issue
Block a user