Files
beaver_project/app-instance/backend/beaver/memory/runs/store.py
steven_li 8a12c30141 feat(beaver): 完成Task Team功能v1实现,重构后端架构支持统一内核
新增内部Task系统,包括验证、反馈门控机制,实现自动质量验证
(通过率>=0.75)和用户反馈闭环(satisfied/revise/abandon)。

实现Agent Team v1协调器,支持sequence/parallel/dag执行策略,
sub-agent复用主AgentLoop,每个run使用独立memory snapshot。

建立Skill学习pipeline,包含draft/审核/发布/回滚完整生命周期,
通过Task验证通过且用户满意才生成学习候选。

重构目录结构,移除third_party依赖,建立统一engine内核,
所有agent共享运行时基础组件。

更新ContextBuilder清理provider消息字段,增强SkillContext版本管理,
集成TaskExecutionPlanner和TaskSkillResolver实现技能解析机制。
2026-05-08 17:14:14 +08:00

99 lines
3.6 KiB
Python

"""File-backed run receipt store."""
from __future__ import annotations
import json
from pathlib import Path
from .models import RunRecord, SkillEffectRecord
class RunMemoryStore:
def __init__(self, root: str | Path) -> None:
self.root = Path(root)
self.root.mkdir(parents=True, exist_ok=True)
self.runs_path = self.root / "runs.jsonl"
self.effects_path = self.root / "skill-effects.jsonl"
def append_run_record(self, record: RunRecord) -> None:
self._append_jsonl(self.runs_path, record.to_dict())
def update_run_record(self, run_id: str, **updates: object) -> RunRecord | None:
records = self.list_runs()
updated: RunRecord | None = None
for index, record in enumerate(records):
if record.run_id != run_id:
continue
payload = record.to_dict()
payload.update(updates)
updated = RunRecord.from_dict(payload)
records[index] = updated
break
if updated is None:
return None
self.runs_path.parent.mkdir(parents=True, exist_ok=True)
self.runs_path.write_text(
"".join(
json.dumps(record.to_dict(), ensure_ascii=False, sort_keys=True) + "\n"
for record in records
),
encoding="utf-8",
)
return updated
def append_skill_effect(self, effect: SkillEffectRecord) -> None:
self._append_jsonl(self.effects_path, effect.to_dict())
def list_runs(self) -> list[RunRecord]:
return [RunRecord.from_dict(item) for item in self._read_jsonl(self.runs_path)]
def list_runs_by_skill(self, skill_name: str, version: str | None = None, limit: int | None = None) -> list[RunRecord]:
results: list[RunRecord] = []
for record in self.list_runs():
matched = False
for receipt in record.activated_skills:
if receipt.skill_name != skill_name:
continue
if version is not None and receipt.skill_version != version:
continue
matched = True
break
if matched:
results.append(record)
if limit is not None:
return results[-limit:]
return results
def list_skill_effects(self, skill_name: str, version: str | None = None, limit: int | None = None) -> list[SkillEffectRecord]:
results: list[SkillEffectRecord] = []
for payload in self._read_jsonl(self.effects_path):
effect = SkillEffectRecord.from_dict(payload)
if effect.skill_name != skill_name:
continue
if version is not None and effect.skill_version != version:
continue
results.append(effect)
if limit is not None:
return results[-limit:]
return results
@staticmethod
def _append_jsonl(path: Path, payload: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(payload, ensure_ascii=False, sort_keys=True) + "\n")
@staticmethod
def _read_jsonl(path: Path) -> list[dict]:
if not path.exists():
return []
results: list[dict] = []
for line in path.read_text(encoding="utf-8").splitlines():
cleaned = line.strip()
if not cleaned:
continue
payload = json.loads(cleaned)
if isinstance(payload, dict):
results.append(payload)
return results