agentchief
§ · Nested detail page

记忆、学习与派发验证 深度解析

拆解 agent-chief 如何把偏好变成记忆,把可执行任务变成可验证结果。

§ 00 · IMPORT CHAIN

导入链与契约

这个子页面聚焦 memory learning。关键证据包括 memory/store.py:20;memory/store.py:34;core/schema.py:68;dispatch/acceptance.py:41;dispatch/acceptance.py:68;core/brain.py:299。这些引用不是源码索引,而是帮助定位边界:入口在哪里、数据对象在哪里、失败时由谁兜底。

▸MemoryStore

memory/store.py · memory/store.py:15-62Python
15class MemoryStore:
16    def __init__(self, state: State, embedder: Embedder = DEFAULT_EMBEDDER):
17        self.state = state
18        self.embedder = embedder
19
20    async def curate(
21        self, text: str, *, topic: str, origin_event_id: str | None, now: datetime
22    ) -> MemoryItem:
23        item = MemoryItem(
24            id=f"mem_{now:%Y%m%d}_{secrets.token_hex(3)}",
25            origin_event_id=origin_event_id,
26            text=text,
27            topic=topic,
28            embedding=self.embedder.embed(text),
29            created_at=now,
30        )
31        await self.state.save_memory(item)
32        return item
33
34    async def associate(self, text: str, *, now: datetime) -> list[MemoryItem]:
35        """Top-3 active memories with cosine > 0.78; updates hit stats (SPEC §4.2)."""
36        query = self.embedder.embed(text)
37        scored = [
38            (cosine(query, item.embedding), item)
39            for item in await self.state.list_memory()
40        ]
41        hits = sorted(
42            (pair for pair in scored if pair[0] > ASSOCIATION_THRESHOLD),
43            key=lambda pair: pair[0],
44            reverse=True,
45        )[:TOP_K]
46        results = []
47        for _sim, item in hits:
48            item.hit_count += 1
49            item.last_hit_at = now
50            await self.state.save_memory(item)
51            results.append(item)
52        return results
53
54    async def expire(self, *, now: datetime) -> int:
55        """Move items past their ttl_days into the archive table (SPEC §3)."""
56        moved = 0
57        for item in await self.state.list_memory():
58            if now - item.created_at > timedelta(days=item.ttl_days):
59                await self.state.save_memory(item, archive=True)
60                await self.state.delete_memory(item.id)
61                moved += 1
62        return moved
§ 01 · CORE IMPL

核心实现,逐段拆

▸Task 契约

Task 模型在 core/schema.py:68 到 core/schema.py:78 定义 goal、executor、acceptance、acceptance_cmd、status 和 attempts。dispatch 的可验证性从这个数据结构开始。

core/schema.py · core/schema.py:68-78Python
68class Task(BaseModel):
69    id: str
70    origin_event_id: str
71    goal: str
72    executor: Executor
73    acceptance: str
74    acceptance_cmd: str | None = None
75    status: Literal["pending", "running", "done", "failed", "rejected"] = "pending"
76    result_summary: str | None = None
77    attempts: int = 0
78

▸acceptance 验证

dispatch_and_verify 在 dispatch/acceptance.py:57 到 dispatch/acceptance.py:88 最多运行两次,仍失败就 rejected 并请求人类接手。这个路径让自动化有刹车。

dispatch/acceptance.py · dispatch/acceptance.py:30-88Python
30async def verify(
31    task: Task, *, ask: AskFn | None = None, exec_fn: Callable = _default_exec
32) -> VerifyResult:
33    """Check a completed task against its acceptance criteria."""
34    if task.acceptance_cmd:
35        argv = shlex.split(task.acceptance_cmd)
36        code, _out, err = await exec_fn(argv, None)
37        if code == 0:
38            return VerifyResult(True, "acceptance_cmd exit 0")
39        return VerifyResult(False, f"acceptance_cmd exit {code}: {err.strip()[:120]}")
40
41    if ask is None:
42        # No verifier available: never bless unverified work.
43        return VerifyResult(False, "no verifier configured; refusing to trust the claim")
44
45    prompt = verify_prompt(
46        acceptance=task.acceptance, result=task.result_summary or "(no result)"
47    )
48    raw = await ask(prompt)
49    try:
50        data = json.loads(extract_json(raw))
51        return VerifyResult(data.get("verdict") == "pass", data.get("reason", ""))
52    except Exception:
53        logger.warning("verifier returned garbage; treating as fail: %r", raw[:80])
54        return VerifyResult(False, "verifier output unparseable")
55
56
57async def dispatch_and_verify(
58    state: State,
59    task: Task,
60    executor: Executor,
61    *,
62    ask: AskFn | None = None,
63    exec_fn: Callable = _default_exec,
64) -> tuple[Task, str | None]:
65    """Run + verify with one retry (attempts max 2, SPEC §3); on final failure
66    mark the task rejected and return a message asking the human."""
67    last_reason = ""
68    while task.attempts < 2:
69        task = await run_task(state, task, executor)
70        if task.status == "done":
71            result = await verify(task, ask=ask, exec_fn=exec_fn)
72            if result.passed:
73                return task, None
74            last_reason = result.reason
75            task.status = "failed"
76            await state.save_task(task)
77        else:
78            last_reason = task.result_summary or "executor failed"
79
80    task.status = "rejected"
81    await state.save_task(task)
82    ask_human = (
83        f"I tried twice and couldn't finish this myself: {task.goal}\n"
84        f"Last attempt: {task.result_summary}\n"
85        f"Why it didn't pass: {last_reason}\n"
86        f"Can you take a look?"
87    )
88    return task, ask_human
§ 02 · KEY FLOW

关键流程

memory learning input → normalize state → score / verify → persisted Decision

流程上,memory learning 并不是一个孤立函数。它会和 memory/store.py:20、memory/store.py:34、core/schema.py:68 共同形成一个闭环:先产生可解释中间状态,再把 route、score、cost 或 rejected reason 存回系统。

§ 03 · EDGE CASES

边界 / 失败模式

▸失败不是异常,而是产品路径

边界处理在源码里是显式产品逻辑:dispatch/acceptance.py:41 负责保守降级,dispatch/acceptance.py:68 负责避免重复或噪声,core/brain.py:299 负责留下可追踪状态。这让 agent-chief 的可信度来自失败路径,而不是只来自 happy path。

  • judge 不可用时不得盲目 interrupt。
  • 历史相似事件只能在阈值内复用,不应跳过新奇事件。
  • 派发结果必须验证,不能信任“done”的自述。
Generated · 2026-07-09Source · SmileLikeYe/agent-chief