Skip to content

Commit 001ca9d

Browse files
author
dev-pd
committed
B learns on every live run; janitor consolidates after each audit
1 parent c07f561 commit 001ca9d

4 files changed

Lines changed: 28 additions & 13 deletions

File tree

backend/app/agents/agent_b.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
33
B is Agent A plus memory: the same shared pipeline and tools, but before each run
44
it injects the top trusted playbook rules into a reserved slot on top of the
5-
unchanged core prompt. While learning on the train split it also carries the
6-
memory tools to jot observations into the diary — choosing its own tags, shown the
7-
existing vocabulary so the janitor's grouping stays coherent.
5+
unchanged core prompt. When learning (`learn=True`) it also carries the memory
6+
tools to jot one observation into the diary — choosing its own tags, shown the
7+
existing vocabulary so the janitor's grouping stays coherent. The live product
8+
always learns (every audit); only the eval harness gates learning to the train
9+
split, keeping the held-out split leak-free.
810
911
Everything else is identical to A — that is what makes any score difference
1012
attributable to memory.

backend/app/agents/prompts.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,20 @@ def render_memory_slot(lessons: list[str]) -> str:
5151
)
5252

5353

54-
# Shown to B only while learning on the train split. The known tag vocabulary is
55-
# passed in so the LLM reuses tags (keeping the janitor's grouping coherent).
54+
# Shown to B whenever it is learning (every live run; train-split-only in eval).
55+
# The known tag vocabulary is passed in so the LLM reuses tags (keeping the
56+
# janitor's grouping coherent).
5657
def render_learning_note(known_tags: list[str]) -> str:
5758
vocab = ", ".join(known_tags) if known_tags else "(none yet — you may create tags)"
5859
return (
59-
"\n\nYou are learning. When competitors reveal a pattern this page is missing, "
60-
"call write_memory with an abstract, UNCONDITIONAL rule for this kind of page "
61-
"that names the exact recommendation to make next time — phrased so it applies "
62-
"even when a future page has no competitor data. Use the shape: 'On <page kind>, "
63-
"always recommend <recommendation_type> (target <target>): <one-line why>.' "
64-
f"Choose 1-4 tags for the pattern. Existing tags: {vocab}. Reuse an existing "
65-
"tag when it fits; only add a new tag for a genuinely new kind of pattern. "
66-
"Never tie a lesson to this specific URL or keyword."
60+
"\n\nYou learn on every run. Before you finish, call write_memory exactly once to "
61+
"record the single most useful transferable lesson from this page — an abstract, "
62+
"UNCONDITIONAL rule for this KIND of page that names the exact recommendation to "
63+
"make next time, phrased so it applies even when a future page has no competitor "
64+
"data. Use the shape: 'On <page kind>, always recommend <recommendation_type> "
65+
f"(target <target>): <one-line why>.' Choose 1-4 tags for the pattern. Existing "
66+
f"tags: {vocab}. Reuse an existing tag when it fits; only add a new tag for a "
67+
"genuinely new kind of pattern. Never tie a lesson to this specific URL or keyword."
6768
)
6869

6970

backend/app/api/run.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
from app.agents.agent_a import run_agent_a
1616
from app.agents.agent_b import run_agent_b
1717
from app.core.exceptions import ReplayMissError
18+
from app.memory.janitor import run_janitor
1819
from app.models import AgentRunResult
20+
from app.repositories.diary import DiaryRepository
1921
from app.tools.fetch_page import FetchPageInput, FetchPageTool
2022
from app.tools.registry import build_default_registry
2123

@@ -43,13 +45,18 @@ async def run(req: RunRequest) -> RunResponse:
4345
registry=build_default_registry(replay=False),
4446
replay=False,
4547
)
48+
# B learns on every live run: read the playbook, audit, jot one diary lesson…
4649
b = await run_agent_b(
4750
req.url,
4851
req.keyword,
4952
registry=build_default_registry(replay=False),
5053
replay=False,
5154
memory_on=True,
55+
learn=True,
56+
run_index=await DiaryRepository().next_run(),
5257
)
58+
# …then consolidate the diary into the playbook so the NEXT run sees it.
59+
await run_janitor()
5360
except httpx.HTTPStatusError as exc:
5461
code = exc.response.status_code
5562
raise HTTPException(

backend/app/repositories/diary.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,10 @@ async def all(self) -> list[DiaryEntry]:
2121
entries.append(DiaryEntry.model_validate(doc))
2222
return entries
2323

24+
async def next_run(self) -> int:
25+
"""The next monotonic run index — one past the highest run on record."""
26+
doc = await self.collection.find_one(sort=[("run", -1)], projection={"run": 1})
27+
return (doc["run"] + 1) if doc else 0
28+
2429
async def clear(self) -> None:
2530
await self.collection.delete_many({})

0 commit comments

Comments
 (0)