From 9e498eee2cf090faba837ade06527b278dea4311 Mon Sep 17 00:00:00 2001 From: Carr1005 Date: Mon, 20 Jul 2026 13:55:04 +0800 Subject: [PATCH] Regenerate agent-memory spec from the updated generation guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full regeneration by an isolated agent (inputs: guide + course materials only) against the guide as of PR #6 (merged) + PR #7 (guide-pins@fdf3b6f): learner-first ledger order, express-lane gate, honest course-default labeling, example-swap framing, invariant->AC coverage, precise heavy-dependency bar, mining-completeness audits. Highlights vs the previous spec: 19-row Decision Ledger with stable D-IDs (learner rows first); restart tested by a real new-OS-process AC; arXiv toolset restored as course-faithful default (keyless != heavy) with live-marked ACs; discovery JSON shape tested; all mined course parameters present; contradictions carried in-row; 16 rules / 20 ACs; full §14 self-check completed by the generating run (4 items self-fixed). Co-Authored-By: Claude Fable 5 --- .../spec.md | 1055 ++++++++--------- 1 file changed, 487 insertions(+), 568 deletions(-) diff --git a/courses/agent-memory-building-memory-aware-agents/spec.md b/courses/agent-memory-building-memory-aware-agents/spec.md index 08bb611..5294b9b 100644 --- a/courses/agent-memory-building-memory-aware-agents/spec.md +++ b/courses/agent-memory-building-memory-aware-agents/spec.md @@ -1,16 +1,13 @@ # Spec: Memory-Aware Research Assistant — Standalone Takeaway -> **This file is self-contained.** The embedded **Course Context Pack** at the end replaces -> all external course references — nothing in this spec requires access to the course -> platform, notebooks, or transcripts. `(CTX-X)` anchors mark course-derived knowledge. -> The **Decision Ledger** below holds every point where the build could legitimately -> diverge; every row is pinned to one course-derived default, so this spec is buildable -> and evaluatable **as-is with zero intake**. `[bracketed]` values mark where a learner -> *may* substitute their own choice — they are never gaps. -> -> *Provenance: generated from the "Agent Memory: Building Memory-Aware Agents" course -> notebooks (L2–L5 dump + helper.py + requirements.txt) and lesson transcripts -> (Lessons 1–7) on 2026-07-15.* +> **This file is self-contained.** The embedded Course Context Pack (CTX-A…CTX-E, at the end) +> replaces all external course references — nothing in this spec requires access to the course +> platform, its notebooks, or its transcripts. `(CTX-X)` anchors mark course-derived knowledge. +> The **Decision Ledger** below holds every point where the build could diverge, each pinned to +> one course-derived default, so the spec is buildable and evaluatable **as-is with zero intake**; +> `[bracketed]` values mark where a learner *may* substitute their own choice. +> *Provenance: generated from the "Agent Memory: Building Memory-Aware Agents" course notebooks + +> transcripts on 2026-07-20.* ## 0. Before you build — REQUIRED (do this first) @@ -30,21 +27,22 @@ not because they are the right choice for this person's project. to the step-5 checklist and build. If the user chooses customize, continue with step 3. 3. **Present the Ledger ONE ROW AT A TIME — one question per row.** For each row ask a single question: the **Decision** as the prompt, its **Options** as the choices. Append - "(course default)" to the option the course actually used — on a substitution row (e.g. - row 7, where the course ran Oracle AI Database 26ai but this spec's Default is - SQLite + Chroma) that is the course-faithful Options entry, not the Ledger Default. You may - also mark an option "(Recommended)" per your own judgment or your question tool's - convention; when your recommended option IS the course's actual choice, merge the labels - into "(Recommended - course default)". A recommendation never removes or moves the - "(course default)" label. Put any realizations beyond the tool's option slots (or the - free-form case) under the tool's "Other"/free-text. Ask about **every** row (there are 18). - A per-call item limit is NEVER a reason to drop, skip, merge, or silently default a row — - make as many separate calls as there are rows. + "(course default)" to the option the course actually used — on a substitution row that is + the course-faithful Options entry, not the Ledger Default. You may also mark an option + "(Recommended)" per your own judgment or your question tool's convention; when your + recommended option IS the course's actual choice, merge the labels into + "(Recommended - course default)". A recommendation never removes or moves the + "(course default)" label. Use the answers already given (project, data, goal, …) to frame + later questions and describe options in the person's own terms — but never skip a row, drop + or alter an Option, or move the "(course default)" label because of an earlier answer. Put + any realizations beyond the tool's option slots (or the free-form case) under the tool's + "Other"/free-text. Ask about **every** row. A per-call item limit is NEVER a reason to drop, + skip, merge, or silently default a row — make as many separate calls as there are rows. 4. **Presenting any of these questions ENDS YOUR TURN — stop here; write no code, create or edit no file, take no other build action.** Keep asking, one row at a time, until **every** row has - an answer (a chosen option, an explicit "use the default", or the step-2 as-is answer, which - resolves every row at once). Answers to *some* rows do NOT release the build; "no reply yet" - is not an answer — wait for the user. + an answer (a chosen option, an explicit "use the course default", or the step-2 as-is answer, + which resolves every row at once). Answers to *some* rows do NOT release the build; "no reply + yet" is not an answer — wait for the user. 5. **Before the first line of code, print a resolved-decision checklist** — every Ledger row with its final value (the user's choice, or its Ledger default). Begin implementation ONLY after this complete checklist is shown; if any row is unresolved you are not done — return to @@ -52,623 +50,544 @@ not because they are the right choice for this person's project. ## Decision Ledger (§0 above requires the build agent to present these before building) -These are the points where this build could diverge. Every row has a course-derived -default, so the spec is buildable and evaluatable as-is; change a row only when applying -to a real project or when you have reason to prefer another option. "Default" is factual -provenance — what the course did, or the stated substitution branch — not a recommendation -for your specific project. +These are the points where this build could diverge. Every row has a course-derived default, so +the spec is buildable and evaluatable as-is; change a row only when applying to a real project or +when you have reason to prefer another option. | # | Category | Decision | Invariant (must hold) | Default (course-derived) | Options | Trade-off | Owner | -|---|----------|----------|----------------------|--------------------------|---------|-----------|-------| -| 1 | design-argued | Memory-store topology | Seven distinct memory types exist as separate stores: Conversational and Tool Log in an exact-key, time-ordered relational store; Knowledge Base, Workflow, Toolbox, Entity, and Summary in semantic-similarity (vector) stores with metadata filtering. One manager class fronts all reads/writes. | The 7-store layout exactly as taught: 2 SQL tables (`CONVERSATIONAL_MEMORY`, `TOOL_LOG_MEMORY`) + 5 vector stores (`SEMANTIC_MEMORY`, `WORKFLOW_MEMORY`, `TOOLBOX_MEMORY`, `ENTITY_MEMORY`, `SUMMARY_MEMORY`), unified behind a `MemoryManager` (CTX-A, CTX-C1). | Merge some vector types into one collection with a type field; add graph store; fewer memory types. | Lesson 3 argues it aloud: conversational memory needs *exact retrieval by thread id, not similarity*, so it must not live in a vector store; each memory type has distinct data-model + retrieval needs. Merging types loses per-type retrieval strategies and the memory-aware prompt segmentation. | course | -| 2 | design-argued | Deterministic vs agent-triggered operation split | Context-bootstrapping reads and post-turn persistence run deterministically every turn (never at the model's discretion); judgment-heavy operations (deep retrieval, summary expansion, external search) are agent-triggered tools. Conversation summarization is reachable BOTH ways: deterministically at the context threshold and as an agent tool. | The Lesson 6 loop's split: deterministic = read conversational/KB/workflow/entity/summary-context at loop start, write user msg + final answer + workflow + entities + tool logs; agent-triggered = `expand_summary`, `summarize_and_store`, knowledge-acquisition tools; `read_toolbox` is both (CTX-C2). | Make more ops agent-triggered (leaner, riskier); make summarization deterministic-only. | Lesson 3 narrates the argument: "the agent can't choose to look up what it doesn't know exists — you need memory to know which memory you need"; deterministic ops buy predictability/continuity, agent-triggered ops buy signal-to-noise and cost control. NOTE the course self-contradicts in detail: the Lesson 3 classification table marks `read_summary_context` and `write_entity` agent-triggered, but the Lesson 6 loop executes both deterministically every turn. Default follows the Lesson 6 loop (the end-to-end app). | course | -| 3 | design-argued | Context-reduction strategy | Reducing context must be *recoverable*: any compression stores a link back to the original units, and an agent-callable expansion path exists. The current `# Question` is never summarized away. | Recoverable compaction as taught: LLM summarization into structured summaries stored with `summary_id`, source conversation rows marked with that id, `expand_summary(id)` retrieves summary + all original messages (CTX-B4, CTX-C3). | Pure lossy summarization (no write-back links); sliding-window truncation; no reduction. | Lesson 5 argues it explicitly: summarization "will always be a lossy technique"; compaction offloads to the database with an ID + description so the model can pull full detail back on demand. Dropping the link severs recoverability. | course | -| 4 | design-argued | Tool exposure to the LLM | The LLM never receives the full toolbox. Tools live in a searchable vector store keyed on their (optionally augmented) descriptions; per query, only the top-k semantically relevant tools are passed, deduplicated by name. | Toolbox vector store + semantic retrieval, top-k per query — k pinned in row 17 (CTX-C4). | Static hand-picked toolset per agent; all tools in every prompt (only viable for very few tools). | Lesson 4 argues the failure: too many tool definitions cause context confusion/bloat, tool-selection degradation, higher latency and cost; the notebook cites provider guidance of ~10–20 tools max for reliable selection, with 3–5 retrieved typically. | course | -| 5 | design-argued | Tool docstring augmentation | Each registered tool is retrievable by semantic search over a text that includes at least its name, description, and signature. | Augment heavy/ambiguous tools at registration: an LLM rewrites the docstring from docstring + source code, plus ~5 synthetic trigger queries, and that enriched text is embedded (`augment=True`). Final-lesson config: `fetch_and_save_paper_to_kb_db`, `expand_summary`, `summarize_and_store` augmented; `arxiv_search_candidates`, `get_current_time` registered raw (helper `register_common_tools`). | Raw docstrings only (cheaper registration, weaker separability); augment everything. | Lesson 4 argues augmentation gives better separability in embedding space, higher recall, higher-signal embedding text — at the cost of LLM calls per registration. Minor course drift: the Lesson 4 notebook registered `get_current_time` with `augment=True`; the final helper registers it raw. Default follows the final helper. | course | -| 6 | design-structural | Partitioned context window + memory-aware system prompt | The prompt context is assembled as a `# Question` section followed by one markdown `##` segment per memory type (Conversation, Knowledge Base, Workflow, Entity, Summary), each carrying what-this-memory-is and how-to-use-it guidance; the system prompt names the segments, their semantics, the summary-expansion policy, and a conflict-priority order (Question > latest Conversation > KB evidence > older summaries/workflows). | Exactly that structure, as taught in Lesson 6 (CTX-C5). | One undifferentiated context blob; JSON-structured context. | Lesson 6 explains the mechanism: markdown headings let the LLM exploit its latent grasp of hierarchical structure, making the agent *aware* of its memories instead of merely augmented by them. Restructuring the segments changes what every memory read returns (each read formats its own segment). | course | -| 7 | realization | Persistent store technology | One durable local system providing (a) exact-key relational tables with timestamp ordering for Conversational + Tool Log memory and (b) vector similarity search **with metadata filtering** (equality and numeric `$gt`) for the 5 vector stores. All memory survives process restarts. | **SQLite (two relational tables) + Chroma `PersistentClient` (five collections), both under `./data/`.** §3 dependency-precedence **branch 1**: the course realizes this on Oracle AI Database 26ai in Docker (admin bootstrap, `VECTOR` user, DSN `127.0.0.1:1521/FREEPDB1`), a heavy dependency that merely realizes the taught dual-store pattern — the pattern, not Oracle, is the subject, so the default substitutes the lightest self-contained equivalent. | Oracle AI Database 26ai + `langchain-oracledb` `OracleVS` (course default — what the course actually ran; needs Docker + admin setup); Postgres + pgvector; any store meeting the invariant. | Switching store later means migrating rows and re-creating indexes; Oracle adds infra setup cost but restores 1:1 fidelity with course APIs (OracleVS, hybrid-search preference, IVF indexes). | course+learner | -| 8 | realization | Knowledge-acquisition toolset | At least one agent-triggered tool acquires external knowledge and **persists what it finds to the Knowledge Base** (the search-and-store pattern), so discovered information is reusable without re-fetching. | The final lesson's common toolset: `arxiv_search_candidates` (metadata-only discovery) + `fetch_and_save_paper_to_kb_db` (full PDF→text, chunked 1500/200, stored with metadata) + `get_current_time`, plus the summary tools. **Tavily web search is NOT in the default build** — §3 branch 1: it needs a paid API key and the final lesson's agent doesn't register it; the search-and-store pattern it demonstrated is preserved by the arXiv ingestion tool. | Add `search_tavily` (course showed it in Lesson 4; requires `TAVILY_API_KEY`); substitute your own domain's fetch/search tools. | Tavily gives open-web reach at key + cost; arXiv tools are keyless but network-dependent and domain-narrow. | course+learner | -| 9 | realization | Knowledge-base seed data | The KB starts non-empty so retrieval grounding is demonstrable on day one; each record carries searchable text (title+subjects+abstract concatenated) and metadata for filtering/attribution. | **The synthetic fixture corpus defined in §5** (6 self-authored paper records baked into this repo). §3 branch 1: the course streamed 100 records from the HuggingFace dataset `nick007x/arxiv-papers` — an external download that can't be baked into this file; the fixture corpus preserves the record shape (title, subjects, abstract, submission_date, arxiv_id). | The HF dataset (course default — what the course actually streamed); the learner's own documents. | Fixtures make acceptance tests deterministic/offline; the HF stream gives realistic scale but non-reproducible content and a network dependency. | course+learner | -| 10 | learner | Project — [project] | — (the pattern imposes nothing) | A **memory-aware research-assistant chat agent** (CLI), the course's own running example shape ("agentic research assistant … over multiple sessions", a.k.a. ArxivScout), instantiated on the §5 fixture corpus. §3 project precedence **branch 1** (course example shape on synthetic fixtures). | Any project the learner wants to graft the pattern onto. | Retitling to a real project means re-deriving fixtures and goals; the default is judged by §5 as-is. | learner | -| 11 | learner | Data / inputs — [data] | — | Synthetic fixture corpus (§5) + live arXiv metadata/PDFs fetched on demand by the row-8 tools. | Learner's own corpus/APIs. | Real data invalidates the fixture-based ACs until equivalents are authored. | learner | -| 12 | learner | Goal / definition of "working" — [goal] | — | Pass all §5 acceptance criteria: cross-session continuity, capped semantic tool retrieval, threshold-triggered recoverable compaction, summary expansion, audit-logged tool execution. | Learner-defined retrieval/answer guarantees. | — | learner | -| 13 | learner | Model / provider — [model] | Must support **native tool/function calling** (tools + tool-role result messages in a chat API) and a system role. | **OpenAI Chat Completions, model `gpt-5-mini`** — the model the Lesson 6 agent loop calls. NOTE the course self-contradicts on the name: the Lesson 5 notebook uses `gpt-5-mini` (token-limit table `{"gpt-5-mini": 256000}`), while helper.py uses `gpt-5` everywhere (token table, Toolbox default, entity extraction). Default follows the agent loop; treat both names as perishable (CTX-D). | Any tool-calling chat model/provider. | Swapping providers means re-mapping the tool-call message format; summary/augmentation prompt behavior may shift. | learner | -| 14 | learner | Environment — [environment] | Memory MUST survive process restarts (the whole point of the course); secrets come from the environment, never hardcoded. | Local machine, Python 3.11+ virtualenv, file-backed stores under `./data/`, `OPENAI_API_KEY` loaded from `.env`. | Containers, cloud, notebooks. | — | learner | -| 15 | learner | Out of scope — [out-of-scope] | — | Exactly the §1 "Not Included" list. | Learner may pull items in or push more out. | — | learner | -| 16 | contradicted | Vector distance strategy | All five vector stores use the **same** distance strategy as each other and as their indexes, chosen once before any data is embedded. | **EUCLIDEAN (L2)** — the final application's config: the Lesson 5 and Lesson 6 notebooks both construct the StoreManager with `DistanceStrategy.EUCLIDEAN_DISTANCE`. The course contradicts itself: the Lesson 3 and Lesson 4 notebooks (and the Lesson 3 narration, "which in this case is going to be cosine") use `COSINE`, and the Lesson 3 notebook's own intro table says EUCLIDEAN while its code says COSINE. Default follows the end-to-end app config per the multiple-configs rule. | COSINE (Lessons 3–4 config); DOT product. | Changing strategy after ingest invalidates similarity semantics and requires re-indexing; mixed strategies across lessons is exactly the stale-data hazard the notebooks' "drop all tables for a consistent distance strategy" warning exists for. | course | -| 17 | contradicted | Toolbox retrieval k | Tool retrieval returns a small bounded set (course guidance: providers recommend ~10–20 tools max exposed; typically 3–5 retrieved). | **k=5 at the agent loop** (Lesson 6 `read_toolbox(query, k=5)`). The course contradicts itself: `MemoryManager.read_toolbox` signature defaults `k=3`; the Lesson 4 registered `read_toolbox` tool's signature says `k: int = 3` while its own docstring says "default: 5". Keep the manager default 3 and pass 5 in the loop, matching the course's call site. | Any small k. | Larger k re-introduces the context-bloat/selection-degradation failure the toolbox pattern exists to prevent (Lesson 4). | course | -| 18 | contradicted | Vector index type | Every vector store has an ANN index created before serving queries; index distance matches row 16. | Follow the row-7 default store's native ANN index (Chroma manages its own). If Oracle is chosen: **IVF** (`ORGANIZATION NEIGHBOR PARTITIONS … WITH TARGET ACCURACY 95`) — what the course's `safe_create_index` actually creates (course default when Oracle is chosen). The course contradicts itself: Lesson 3's notebook markdown and lesson objectives narrate an **HNSW** index, but the helper deliberately uses IVF to dodge Oracle Free bugs (ORA-00600, ORA-51928, ORA-51962). | HNSW (as narrated); IVF (as coded). | HNSW was shown to be crash-prone on the course's own Oracle build; IVF trades some recall for stability there. | course | +|---|----------|----------|-----------------------|--------------------------|---------|-----------|-------| +| D1 | learner | `[project]` — what the agent is for. Default is the **course's example realization** (its running demo scenario), expected to be swapped when your project differs — the invariants, not the example, are what must survive. | — | A **memory-aware agentic research assistant**: a CLI/REPL chat agent that finds, saves, and discusses research papers across sessions, remembering prior findings and preferences (§3 project precedence, branch 1: the course's example scenario shape re-expressed on the synthetic fixture corpus of §5). | Course example shape: research assistant (course default); any domain assistant needing persistence (support bot, coding copilot, ops runbook agent) | Swapping the project keeps every memory mechanism; only the toolset (D16) and seed data (D2) change with it. | learner | +| D2 | learner | `[data/inputs]` — what the agent operates on. Default is a fixture stand-in for the **course's example data**; swap when D1 changes. | — | The **synthetic fixture corpus of §5** (two authored paper-notes files + one authored 12-message seed conversation), ingested at setup. | Fixture corpus (default); the learner's own documents/threads; the course streamed 100 records of a public arXiv dataset and live arXiv PDFs (course default) | Real corpora need the same ingest path (embed + metadata); large corpora may need an ANN index (see D18). | learner | +| D3 | learner | `[goal]` — what "working" means. | — | **Cross-session continuity**: the agent answers follow-ups without re-doing discovery, keeps context under budget via recoverable summarization, and can still recover the thread's first question after summarizing (the §5 oracle). | Continuity guarantee (default); latency/cost targets; retrieval-precision targets | Stricter retrieval goals push toward reranking/hybrid search — out of course scope (§1 Not Included). | learner | +| D4 | learner | `[model/provider]` — the LLM behind reasoning, summarization, augmentation, extraction. | Must support **native (OpenAI-style) tool calling** and a system role; its context-window size must be known to the budget monitor (R6). | **OpenAI**, per the materials: `gpt-5-mini` for the agent loop, `gpt-5` for docstring augmentation and entity extraction. Requires `OPENAI_API_KEY` (§2); LLM-dependent ACs are tagged `scripted` or `live` so the offline oracle still runs (§5). *Note (course-contradicted, carried here):* the L4 notebook's token-limit map lists only `gpt-5-mini: 256000` while `helper.py`'s lists only `gpt-5: 256000`, and helper function defaults say `gpt-5` where the L5 loop calls `gpt-5-mini` — the materials never reconcile the two; this spec pins the loop to `gpt-5-mini` with a 256,000-token budget (the L5 loop's actual call path). | OpenAI gpt-5-mini + gpt-5 (course default); any tool-calling model (Anthropic, local via an OpenAI-compatible server) | Changing provider means re-checking the tool-call message shapes and the token budget in R6; prompts (summarization, extraction) may need re-tuning. | learner | +| D5 | learner | `[environment]` — where it runs. | **All seven memory stores must survive process restarts** (memory is external to the model and persistent). | Local machine, Python 3.11+, single on-disk database file (realization: D15). First run needs network to download the embedding model; no Docker, no admin setup. | Local single file (default); the course ran a Dockerized Oracle instance (course default — see D15); any server environment | A shared/server environment adds connection management the course's local pattern doesn't cover. | learner | +| D6 | learner | `[out-of-scope]` — anything explicitly unwanted. | — | Nothing beyond §1 "Not Included". | Add exclusions freely | Exclusions only shrink scope; they never relax the D5/D7 invariants. | learner | +| D7 | design-argued | **Memory-core topology** — which memory types exist and their storage class. | Seven memory types, each with a dedicated store: **conversational + tool-log memory retrievable by exact key (thread) in chronological order; knowledge-base, workflow, toolbox, entity, and summary memory retrievable by semantic similarity.** | The course's seven stores: `CONVERSATIONAL_MEMORY` and `TOOL_LOG_MEMORY` as SQL-style tables; `SEMANTIC_MEMORY` (knowledge base), `WORKFLOW_MEMORY`, `TOOLBOX_MEMORY`, `ENTITY_MEMORY`, `SUMMARY_MEMORY` as vector stores — all fronted by one memory-manager abstraction (CTX-A, CTX-C1). | Seven-store split (course default); fewer stores (e.g. fold entity into KB); more (add semantic cache) | Lesson 3 argues the split: conversation needs *exact* retrieval by thread id, "not similarity search", while knowledge/workflow/toolbox/entity/summary need meaning-based lookup; collapsing stores loses the retrieval strategy matched to each type. | course | +| D8 | design-structural | **Deterministic vs agent-triggered operation split** — which memory operations the harness runs every turn vs which the model may invoke. | Context-building reads (conversation, KB, workflow, entity, summary-index) and post-turn writes (conversation, workflow) run **deterministically every turn**; judgment operations (expand a summary, summarize-and-store, external search, entity writes) are **also exposed as model-invocable tools**. | The course's classification (CTX-C2): deterministic = the five preload reads + conversation/workflow writes + the >80% budget check; agent-triggered = `expand_summary`, `summarize_and_store`, external search, deep paper ingest; `read_toolbox` is both. *Note (course-contradicted, carried here):* the Lesson 3/L2-notebook classification table marks entity **writes** and `read_summary_context` agent-triggered, yet the Lesson 6 loop runs both deterministically (entity extraction after query and answer; summary-index read in every preload) — this spec follows the Lesson 6 working loop. | Course split (course default); fully agent-managed memory; fully hardcoded memory | Moving reads/writes to model discretion risks "forgot to save" gaps and the chicken-and-egg problem (CTX-B7); hardcoding judgment ops (e.g. always summarize) wastes tokens and clutters memory (CTX-C2). | course | +| D9 | design-argued | **Partitioned context window + memory-aware system prompt** — how assembled memory is presented to the model. | The model input carries the question first, then **one labeled segment per memory type**, and the model instructions name each segment, its usage guidance, and a conflict-priority order (R16). | Markdown-heading partition in fixed order: `# Question`, then `## Conversation Memory`, `## Knowledge Base Memory`, `## Workflow Memory`, `## Entity Memory`, `## Summary Memory`, each segment self-describing ("what this memory is / how you should leverage it"). | Partitioned + self-describing (course default); one mixed context block; structured JSON context | Lesson 6 argues markdown headings let the model exploit its latent grasp of hierarchical structure, giving "structured, role-specific context instead of one mixed block"; a mixed block loses per-store semantics. | course | +| D10 | design-argued | **Context-window reduction strategy** — what happens when the context outgrows its budget. | Reduction must be **recoverable**: whatever is compressed out of the live context stays retrievable in full from the store via an id-addressable link. | The course's combined pipeline: threshold-triggered (deterministic, >80% of budget) and tool-triggered (agent-invoked) **summarization with write-back links** — summarize unsummarized thread messages, store the summary, mark the exact source rows with the `summary_id`, keep only a summary reference in context, and expand on demand (JIT) via the expand tool (CTX-C3). | Recoverable summarize-and-link (course default); pure lossy summarization; pure compaction (offload raw content by id, no summary) | Lesson 5 argues the trade-off aloud: summarization "will always be a lossy technique", while compaction keeps everything but preloads nothing useful — the shipped design pairs a lossy summary for the live window with a lossless database path back to the originals. | course | +| D11 | design-argued | **Tool exposure: semantic tool retrieval (Toolbox pattern)** — how tools reach the model. | The model receives only a **query-relevant subset** of the registered tools each turn, retrieved by semantic similarity over tool descriptions; the registry itself may hold many more tools than are ever passed at once. | Register every tool (description + embedding) in the toolbox store; at each turn retrieve the top matches for the user query (count: pinned in D19) and pass only those schemas to the model. | Semantic retrieval (course default); pass all tools every turn; hand-picked static toolset per app | Lesson 4 argues passing all tools causes context bloat, tool-selection degradation, latency and cost growth (providers recommend ~10–20 tools max); retrieval scales to hundreds of tools but adds an embedding lookup per turn and can miss a relevant tool if descriptions are weak (mitigated by D12). | course | +| D12 | design-argued | **Tool-description augmentation** — what text gets embedded for each tool. | The text embedded for retrieval must be **rich enough to separate tools semantically** (retrieval is keyed on descriptions, not names). | Augmented registration as the course does for most tools: an LLM rewrites the docstring using the function's source, plus ~5 synthetic example queries; name + augmented description + signature + queries form the embedding text. Registration is idempotent per tool name (R5). | LLM-augmented descriptions (course default); raw docstrings only (the course registered its arXiv candidate-search tool unaugmented) | Lesson 4 argues augmentation buys higher separability and recall in the embedding space at the cost of one LLM call per registration; weak one-line docstrings retrieve poorly. | course+learner | +| D13 | design-argued | **Tool-result flow: full log + bounded excerpt** — what the model sees of a tool's output. | Every tool execution is **fully persisted** (args, complete result, status, errors) outside the context window; the model receives only a **bounded excerpt with an id pointer** back to the full record. | Persist every call to the tool-log store; pass at most 3,000 characters of the result to the model, appending a truncation notice naming the log id when cut (R10). | Log + bounded excerpt (course default); pass full outputs into context; discard raw outputs | Lesson 6 calls this context offloading — "move large payload handling out of the model context and into memory infrastructure"; skipping the log loses the audit trail and the JIT retrieval path, while full outputs blow up the window (CTX-B6). | course | +| D14 | design-argued | **Search-and-store acquisition** — what acquisition tools do with what they find. | Any tool that acquires external content **persists its results to knowledge-base memory with source metadata in the same call**, before/independent of what the model does with them. | Course pattern: search/fetch tools write each result (or chunk) into the knowledge base with source metadata (title, source id, timestamps, chunk indices), so information discovered once is retrievable in later turns without re-searching. | Search-and-store (course default); return-only tools (results live and die in one turn) | Lesson 4 argues this is how the agent "learns from its searches" — repeat questions stop costing API calls; the cost is knowledge-base growth and possible staleness of stored search results. | course | +| D15 | realization | **Persistent store (the Agent Memory Core)** — the database behind all seven stores. | **One persistent database is the memory core**: it serves both the exact-key/chronological stores and the semantic-similarity stores (topology: D7), and survives restarts (D5). | **SQLite** (single on-disk file, Python stdlib driver): SQL tables for conversational + tool-log memory; the five vector stores as tables holding text, JSON metadata, and the embedding, searched by exact similarity at fixture scale (index: D18; distance: D17). *§3 dependency precedence, branch 1* — the course's Oracle AI Database 26ai runs in Docker with admin/tablespace setup and a dedicated DB user, i.e. container-level setup (heavy); the taught subject is the memory pattern, which Oracle realizes but does not define, so the lightest self-contained realization is substituted. | Oracle AI Database 26ai via `langchain-oracledb`/OracleVS, Dockerized, with admin bootstrap and hybrid-search capability (course default); SQLite single-file (default); Postgres + pgvector; any DB offering both exact and vector retrieval | Switching stores means migrating schemas and re-embedding nothing (embeddings are store-agnostic) but re-implementing the store adapters; Oracle restores the course's exact stack incl. its vector indexes and hybrid search, at the cost of Docker + admin setup. | course+learner | +| D16 | realization | **External acquisition toolset** — which live tools the default agent registers. | At least one **agent-triggered external acquisition tool** exists and follows search-and-store (D14). | The course's **keyless arXiv tools only**: a candidate-search tool returning structured JSON (id, title, authors, published, abstract) and a deep-ingest tool (fetch PDF → text → chunk → store to KB), plus a local current-time utility and the summary tools (expand, summarize-and-store) and self-lookup (`read_toolbox`). *§3 dependency precedence, branch 1* — the course's Tavily web search needs an API key (heavy); arXiv access is keyless and setup-free, so it stays (its ACs are tagged `live`), while Tavily becomes an option. These are the **course's example tools** — swap them when `[project]` (D1) differs. | Course toolset incl. Tavily web search with `TAVILY_API_KEY` (course default); keyless arXiv-only toolset (default); the learner's own domain tools | Dropping Tavily loses general web search (the agent only acquires from arXiv); adding it back is one keyed client + one registered tool following D14. | course+learner | +| D17 | contradicted | **Vector distance strategy.** | One distance strategy is used **consistently across all five vector stores, for both write-time indexing and read-time search** (R15). | **Cosine** — the strategy the course used when the stores were introduced (L2 and L3 notebooks' store-manager instantiation) and the fallback its index helper assumes. *Contradiction (why this is a row):* the L4 and L5 notebooks instantiate the same stores with **Euclidean** distance, and the L2 notebook's own markdown names Euclidean while its code passes cosine; the course wipes all tables between lessons precisely to keep the strategy consistent (CTX-C5). | Cosine (default; one of two course-used values); Euclidean (course default in the final two lessons); dot product | With normalized sentence-transformer embeddings the rankings are near-equivalent; mixing strategies between write and read silently corrupts rankings — hence the wipe-and-rebuild warning the course ships. | course+learner | +| D18 | contradicted | **Vector index type.** | Similarity queries must return the true top-k at fixture scale — retrieval correctness must not depend on approximate-index tuning. | **Exact (brute-force) similarity search** — at the fixture corpus scale no ANN index is needed, and the SQLite default (D15) has none. *Contradiction (why this is a row):* the course's markdown and lesson objectives say it builds **HNSW** indexes, but its helper code deliberately creates **IVF** (`NEIGHBOR PARTITIONS`, target accuracy 95) instead, citing Oracle-version bugs with HNSW (CTX-C6). | Exact search (default); IVF neighbor-partitions index (course default — what the course's helper actually created); HNSW (named in course text, not what its code ran) | Exact search is O(n) per query — fine for fixtures, add an ANN index when corpora grow (re-check recall); ANN indexes trade exactness for speed and need distance-consistent creation (D17). | course+learner | +| D19 | contradicted | **Toolbox retrieval k** — how many tool schemas are passed per turn. | k is pinned in exactly one configuration point (R15) and small enough to keep the toolset focused (D11). | **k = 5** — what the course's Lesson 6 agent loop actually calls. *Contradiction (why this is a row):* the course's registered `read_toolbox` tool declares `k: int = 3` in its signature while its own docstring says "default: 5", and the loop calls `k=5`. | 5 (default; course's call-site value); 3 (course's signature default); any small integer | Larger k re-approaches the all-tools failure Lesson 4 warns about (selection degradation, bloat); smaller k risks missing the right tool for compound queries. | course+learner | ## 1. Objective -Build a persistent, memory-aware research-assistant agent: a chat loop that, on every -turn, deterministically assembles a partitioned context from seven typed memory stores, -manages its own context-window budget by recoverable compaction, selects tools by -semantic search instead of prompt-stuffing, executes them with full audit logging, and -writes what it learned back to memory — so the agent resumes, remembers, and improves -across sessions instead of resetting (pattern: CTX-A). +Build a persistent, memory-aware research assistant: a chat agent that, for a working engineer, +**remembers across sessions** — conversations, acquired knowledge, past workflows, entities, and +summaries — by treating an external database as its memory core, so that follow-up requests +resolve without repeating discovery and long threads never overflow the model's context window +(pattern: CTX-A). ### Not Included ★ -- **Tavily / open-web search** — demonstrated in Lesson 4 but not registered in the final - agent; excluded by default (Ledger row 8). -- **Hybrid (lexical+vector) search** — the course's StoreManager stubs a - `setup_hybrid_search` vectorizer preference but never exercises it end-to-end; do not build it. -- **Semantic cache** — named in Lesson 2 as a short-term memory form, never implemented. -- **Reranking models / graph traversal retrieval** — mentioned in the RAG/lifecycle - overviews only. -- **Memory decay, merging, strengthening** — named as agent-triggered judgment examples, - never implemented. -- **Multi-user auth, web UI, deployment** — outside course scope entirely. -- **Fine-tuning embedding or language models** — mentioned as part of the memory-engineering - discipline, not built. -- Anything in `[out-of-scope]` (Ledger row 15). +- **Multi-user support, auth, or any UI beyond a CLI/REPL chat loop.** +- **Short-term-memory subsystems the course names but never builds**: semantic caching of LLM + responses and session scratchpads beyond the live context window. +- **Hybrid (lexical+vector) search** — the course's store manager exposes a hybrid-search hook + but never exercises it; excluded here. +- **Reranking models, graph-traversal retrieval, memory decay/forgetting policies, and + embedding-model fine-tuning** — mentioned as concepts in the lessons, never implemented. +- **Oracle-specific operations** (tablespace admin, DB user provisioning, vector-memory pools) — + they belong to the course's store realization, not the pattern (D15). +- **Integration into an existing codebase** — this spec targets the standalone takeaway only; + the Ledger's Invariant column is written to serve as the future integration contract. +- `[out-of-scope]` — learner exclusions (D6; default: none). ## 2. Tech Stack & Versions | Component | Pinned choice | Note | |---|---|---| -| Language | Python 3.11+ | Course-era Python; pick the current stable 3.x at build time. | -| LLM API | `openai` SDK, current 1.x; model `gpt-5-mini` | Ledger row 13. Course calls `chat.completions.create` with `tools`, `tool_choice="auto"`, `max_completion_tokens`. | -| Embeddings | `sentence-transformers`, model `sentence-transformers/paraphrase-mpnet-base-v2` | Course-consistent across all lessons; produces 768-dim vectors (Lesson 3 notebook). Course loads it via LangChain's `HuggingFaceEmbeddings` (two different import paths across lessons — CTX-D); loading it directly through `sentence-transformers` is equivalent for this build. | -| Relational store | SQLite (stdlib `sqlite3`) | Ledger row 7 default. Two tables mirroring the course schemas (§3). | -| Vector store | `chromadb` (PersistentClient), current release | Ledger row 7 default; five collections, l2 (euclidean) space per Ledger row 16. | -| Chunking | `langchain-text-splitters` `RecursiveCharacterTextSplitter` (or behavior-equivalent) | chunk_size=1500, chunk_overlap=200 — the course's `fetch_and_save_paper_to_kb_db` defaults. | -| arXiv access | `langchain-community` `ArxivRetriever`/`ArxivLoader` + `arxiv` + `pymupdf` | Ledger row 8. Retriever config: `load_max_docs=8`, `get_full_documents=False`, `doc_content_chars_max=4000`. | -| Env/secrets | `python-dotenv`; `OPENAI_API_KEY` in `.env` | **Never hardcode credentials.** (The course notebooks hardcode Oracle DB passwords; do not reproduce that.) | -| **Install/pin honesty** | — | **The course's `requirements.txt` is entirely unpinned** (it even says models/libraries "will naturally change over time"); no version claims below come from the course. Pin whatever current versions you install into this build's own lockfile, and treat every course-era API name as a search keyword, not a guaranteed import (CTX-D). | +| Language | Python 3.11+ | Course-era Python version is not stated in the materials. | +| Persistent store | SQLite (stdlib `sqlite3`), one on-disk file | Decision Ledger **D15** — learners change it there, not here. | +| Vector search | Exact similarity over embeddings stored in the DB | Ledger **D18**; distance per **D17**. | +| Embeddings | `sentence-transformers/paraphrase-mpnet-base-v2` (768-dim, local CPU) | The course's single embedding model for all stores and both write/read paths (CTX-C5). First run downloads it (keyless network). | +| LLM | OpenAI SDK (current stable) | Ledger **D4**. `OPENAI_API_KEY` required for scripted-interface parity and live ACs. | +| arXiv access | `arxiv` + `pymupdf` (current stable) | Ledger **D16**. The course reached arXiv through LangChain community wrappers — those names are perishable, see CTX-D. | +| Dependency pins | Pin exact versions **at build time** to current stable in a lockfile you generate | **Honest era note: the course's `requirements.txt` installs everything unpinned** (no versions at all, LangChain-family + `oracledb` + `openai` + `tavily-python` era); do not invent course pins. Era-specific import names → CTX-D. | +| Secrets | `.env` file loaded at startup; `OPENAI_API_KEY` (required), `TAVILY_API_KEY` (only if D16 is switched) | Never hardcoded and never committed. (The course notebooks hardcode local DB credentials — do not reproduce that.) | + +Default store names (single-valued course constants; body defaults, not Ledger rows): +`CONVERSATIONAL_MEMORY`, `SEMANTIC_MEMORY` (knowledge base), `WORKFLOW_MEMORY`, +`TOOLBOX_MEMORY`, `ENTITY_MEMORY`, `SUMMARY_MEMORY`, `TOOL_LOG_MEMORY` — identical in every +course notebook that declares them. ## 3. Input/Output Contracts ★ -Core turn contract. `call_agent`-equivalent entry point takes a query + thread id and -returns a structured result *(structured return object is project hardening — the course -returned a bare string; the fields below are all course-demonstrated data)*: +Core memory records and the agent turn result. These are the shapes tests assert against; +implementations may add fields but MUST NOT violate these. ```json { - "$id": "AgentTurnResult", - "type": "object", - "required": ["thread_id", "query", "answer", "iterations_used", "steps", "context_usage"], - "properties": { - "thread_id": {"type": "string"}, - "query": {"type": "string"}, - "answer": {"type": "string", "minLength": 1}, - "iterations_used": {"type": "integer", "minimum": 1, "maximum": 10}, - "steps": { - "type": "array", - "items": { - "type": "object", - "required": ["tool_name", "status"], - "properties": { - "tool_name": {"type": "string"}, - "status": {"enum": ["success", "failed"]}, - "tool_log_id": {"type": "string"} + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "ConversationRecord": { + "type": "object", + "required": ["id", "thread_id", "role", "content", "timestamp"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "thread_id": { "type": "string", "minLength": 1 }, + "role": { "type": "string", "enum": ["user", "assistant"] }, + "content": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" }, + "metadata": { "type": "object" }, + "summary_id": { + "type": ["string", "null"], + "pattern": "^[0-9a-f]{8}$", + "description": "null until consolidated; set once, to the id of the summary that absorbed this row" } } }, - "summaries_created": { - "type": "array", - "items": { - "type": "object", - "required": ["id", "description"], - "properties": { - "id": {"type": "string", "minLength": 8, "maxLength": 8}, - "description": {"type": "string", "minLength": 1} + "ToolLogRecord": { + "type": "object", + "required": ["id", "thread_id", "tool_name", "tool_args", "result", "result_preview", "status", "timestamp"], + "properties": { + "id": { "type": "string" }, + "thread_id": { "type": "string" }, + "tool_call_id": { "type": ["string", "null"] }, + "tool_name": { "type": "string" }, + "tool_args": { "type": "string", "description": "JSON-serialized arguments" }, + "result": { "type": "string", "description": "FULL untruncated output" }, + "result_preview": { "type": "string", "description": "≤ 2000 bytes UTF-8-safe truncation of result" }, + "status": { "type": "string", "enum": ["success", "failed"] }, + "error_message": { "type": ["string", "null"] }, + "metadata": { "type": "object" }, + "timestamp": { "type": "string", "format": "date-time" } + }, + "if": { "properties": { "status": { "const": "failed" } } }, + "then": { "required": ["error_message"] } + }, + "SummaryRecord": { + "type": "object", + "required": ["id", "summary", "description", "full_content"], + "properties": { + "id": { "type": "string", "pattern": "^[0-9a-f]{8}$" }, + "summary": { "type": "string", "description": "structured text containing the four R8 headings" }, + "description": { "type": "string", "minLength": 8, "description": "specific 8–12-word label; never a generic 'Conversation summary'" }, + "full_content": { "type": "string", "description": "the exact source transcript that was summarized" }, + "thread_id": { "type": ["string", "null"] } + } + }, + "WorkflowRecord": { + "type": "object", + "required": ["query", "steps", "answer_excerpt", "num_steps", "success", "timestamp"], + "properties": { + "query": { "type": "string" }, + "steps": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "answer_excerpt": { "type": "string", "maxLength": 200 }, + "num_steps": { "type": "integer", "minimum": 1 }, + "success": { "type": "boolean" }, + "timestamp": { "type": "string", "format": "date-time" } + } + }, + "EntityRecord": { + "type": "object", + "required": ["name", "type", "description"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "type": { "type": "string", "enum": ["PERSON", "PLACE", "SYSTEM", "UNKNOWN"] }, + "description": { "type": "string" } + } + }, + "RetrievedToolSchema": { + "type": "object", + "required": ["type", "function"], + "properties": { + "type": { "const": "function" }, + "function": { + "type": "object", + "required": ["name", "description", "parameters"], + "properties": { + "name": { "type": "string" }, + "description": { "type": "string", "minLength": 1 }, + "parameters": { + "type": "object", + "required": ["type", "properties", "required"], + "properties": { "type": { "const": "object" } } + } + } } } }, - "context_usage": { + "AgentTurnResult": { "type": "object", - "required": ["tokens", "max", "percent", "status"], + "required": ["thread_id", "final_answer", "steps", "completed"], "properties": { - "tokens": {"type": "integer", "minimum": 0}, - "max": {"type": "integer"}, - "percent": {"type": "number"}, - "status": {"enum": ["ok", "warning", "critical"]} + "thread_id": { "type": "string" }, + "final_answer": { "type": "string", "minLength": 1 }, + "steps": { "type": "array", "items": { "type": "string" } }, + "completed": { "type": "boolean", "description": "false only when the iteration cap (R13) was hit" } } } } } ``` -*(Loop-exhaustion behavior — the templated inability answer — is asserted behaviorally by -R9/AC13 rather than in-schema, because a genuine final answer may legitimately arrive on -the 10th iteration.)* - -Memory-unit contracts (course-demonstrated; storage columns from the Lesson 3 schemas): - -```json -{ - "$id": "ConversationalMemoryUnit", - "type": "object", - "required": ["id", "thread_id", "role", "content", "timestamp"], - "properties": { - "id": {"type": "string"}, - "thread_id": {"type": "string"}, - "role": {"type": "string"}, - "content": {"type": "string"}, - "timestamp": {"type": "string", "format": "date-time"}, - "metadata": {"type": "string"}, - "summary_id": {"type": ["string", "null"], "$comment": "null = not yet summarized; non-null links to SummaryRecord.id"} - } -} -``` - -```json -{ - "$id": "ToolLogRecord", - "type": "object", - "required": ["id", "thread_id", "tool_name", "status", "timestamp"], - "properties": { - "id": {"type": "string"}, - "thread_id": {"type": "string"}, - "tool_call_id": {"type": ["string", "null"]}, - "tool_name": {"type": "string"}, - "tool_args": {"type": "string", "$comment": "JSON-serialized args"}, - "result": {"type": "string", "$comment": "FULL result, never truncated here"}, - "result_preview": {"type": "string", "maxLength": 2000}, - "status": {"enum": ["success", "failed"]}, - "error_message": {"type": ["string", "null"]}, - "metadata": {"type": "string"}, - "timestamp": {"type": "string", "format": "date-time"} - }, - "if": {"properties": {"status": {"const": "failed"}}}, - "then": {"required": ["error_message"]} -} -``` - -```json -{ - "$id": "SummaryRecord", - "type": "object", - "required": ["id", "summary", "description", "full_content"], - "properties": { - "id": {"type": "string", "minLength": 8, "maxLength": 8}, - "full_content": {"type": "string"}, - "summary": { - "type": "string", - "$comment": "MUST contain the four headings: ### Technical Information, ### Emotional Context, ### Entities & References, ### Action Items & Decisions" - }, - "description": {"type": "string", "minLength": 1}, - "thread_id": {"type": ["string", "null"]} - } -} -``` - -Workflow record text: `Query: \nSteps:\nStep 1: …\nAnswer: ` with -metadata `{query, success, num_steps, timestamp}`. Entity record: ` (): ` -with metadata `{name, type, description}`, `type ∈ {PERSON, PLACE, SYSTEM}` (or UNKNOWN). -Toolbox record metadata carries `{name, description, signature, parameters, return_type, augmented, queries?}` -and retrieval converts it to an OpenAI function schema (`{type:"function", function:{name, description, parameters}}`), -mapping Python annotation strings to JSON-schema types and marking parameters without defaults as required. +Toolbox retrieval returns an array of `RetrievedToolSchema` with **unique** `function.name` +values (dedup rule R4). Retrieved tool count per turn: pinned in Ledger **D19**. ## 4. Business Rules -Each rule: behavior + the failure it prevents + enforcement. Labels per §9 of the -generation guide: **[C]** course-demonstrated, **[H]** project hardening. - -1. **[C] Deterministic conversational persistence & recall.** Every user query and every - final assistant answer is written to conversational memory with role, thread_id, and - timestamp — every turn, unconditionally. Context assembly reads the thread's - *unsummarized* messages (default limit 10), timestamp-ascending. Prevents the stateless - reset where the agent can't resolve "the first one on the list" (CTX-B1). → AC1, AC2 -2. **[C] Partitioned context, question preserved.** The prompt context is - `# Question` + `## Conversation Memory` + `## Knowledge Base Memory` + `## Workflow Memory` - + `## Entity Memory` + `## Summary Memory`, each segment self-describing (what it is / - how to use it / retrieved items). The question is prepended AFTER any offload and is never - summarized. The system prompt encodes the segment semantics, the summary-expansion policy, - the conflict priority (Question > latest Conversation > KB evidence > older - summaries/workflows), minimal-tool-call guidance, and the instruction to pass `thread_id` - to conversation compaction (CTX-C5). → AC3, AC17 -3. **[C] Capped semantic tool retrieval.** Per query, the LLM receives only the top-k - (k=5, Ledger row 17) tools from toolbox similarity search, deduplicated by tool name, - as OpenAI-format function schemas. The full toolbox is never sent. Prevents context - bloat / tool-selection degradation (CTX-B2). → AC4, AC5 -4. **[C] Context budgeting.** Estimated tokens = `len(context) // 4`; max = 256,000 for the - default model, 128,000 fallback for unknown models; status `ok` <50%, `warning` <80%, - `critical` ≥80% *(source: Lesson 5 notebook `calculate_context_usage`/`monitor_context_window`; - helper.py duplicates them keyed on `gpt-5`)*. When memory-context usage exceeds 80% - before reasoning, offload runs deterministically: the thread's unsummarized messages are - summarized, the `## Conversation Memory` section is replaced by a pointer stub, a - `[Summary ID: ] ` reference is appended under `## Summary Memory`, and - all other segments are preserved (CTX-B3). **[H]** The token limit MUST be overridable - (env var or parameter) so acceptance tests can trip the threshold with small fixtures. → AC6 -5. **[C] Recoverable, structured summarization.** Summaries are produced by an LLM prompt - requiring exactly the four headings (Technical Information / Emotional Context / - Entities & References / Action Items & Decisions), on input clipped to 6,000 chars, - `max_completion_tokens=4000`; an 8–12-word description label is generated separately; - `id` = first 8 chars of a UUID4. The summary stores the full original content and, when - thread-scoped, the thread_id. Source conversation rows are marked with the `summary_id`. - `expand_summary(id)` returns the summary text plus ALL original messages chronologically - with timestamps (CTX-B4). → AC7, AC8 -6. **[C] No re-summarization.** Rows with a non-null `summary_id` are excluded from - conversational reads and from future summarization; summarizing a fully-summarized - thread returns a "nothing to summarize" outcome, not an error (CTX-B5). → AC9 -7. **[C] Summarizer never returns empty.** If the model's summary comes back empty, retry - once with a simpler prompt; if still empty, emit the deterministic fallback (four - headings, excerpt of the source under Technical Information). If the description label - is empty or generic ("Conversation summary" etc.), derive a specific fallback label from - the summary text (CTX-B6). → AC10 -8. **[C] Full-fidelity tool audit log.** Every tool invocation — success or failure — writes - a tool-log record with full args, FULL result, ≤2000-byte UTF-8-safe preview, status, - error message, and iteration metadata. The LLM's next turn receives at most 3,000 chars - of a tool result; longer results are truncated with a pointer naming the tool-log id - where the full payload lives (CTX-B7). Tool logs are read just-in-time (newest-first, - default limit 20), NOT preloaded into the context. → AC11, AC12 -9. **[C] Bounded agent loop.** The reason/act loop runs at most 10 iterations; a tool - exception is caught, logged as a `failed` step, and its error string returned to the - model — never a crash; on exhaustion the answer is the templated inability message - ("I was unable to complete the request within the allowed iterations.") (CTX-B8). → AC13 -10. **[C] Post-turn learning writes.** After a turn that used ≥1 tool, write one workflow - record (query, ordered steps with status, answer truncated to 200 chars, - success, num_steps). Workflow reads filter to `num_steps > 0` (k=3). Entity extraction - (LLM, types PERSON/PLACE/SYSTEM, input clipped to 500 chars) runs on the user query and - on the final answer; extraction failures are swallowed silently (the turn never fails - because entity extraction did). → AC14 -11. **[C] Idempotent tool registration.** Registering a tool whose name already exists in - the toolbox store skips the store write but still binds the callable; toolbox retrieval - returns unique names. → AC15 -12. **[C] Search-and-store ingestion.** Knowledge-acquisition tools persist what they fetch: - full-document ingestion chunks at 1500/200 and writes every chunk with metadata - (source, arxiv_id, title, entry_id, published, authors, chunk_id, num_chunks, - ingested timestamp); batch writes require equal-length text/metadata lists and reject - mismatches; discovery returns structured JSON candidates (arxiv_id, entry_id, title, - authors, published, abstract ≤2500 chars) without ingesting. Empty extraction or no - results return explanatory strings, not exceptions. → AC16 -13. **[C] KB read grounding.** `read_knowledge_base` (k=3) returns the KB segment with - retrieved passages, and its guidance text instructs the model to ground claims in the - passages and state uncertainty when evidence is missing rather than assume. → AC3 (segment - content), AC17 (prompt rule) +Provenance labels: **[C]** course-demonstrated (traceable, with CTX anchor) · **[H]** project +hardening (added by this spec; the course did not demonstrate it — said so explicitly). + +1. **R1 — Deterministic conversation persistence.** [C] Every user query and every final + assistant answer is written to conversational memory with role, thread id, and timestamp, by + the harness — never at model discretion (prevents CTX-B1). → AC1, AC12, AC20 +2. **R2 — Deterministic context preload.** [C] Every turn, before the model is called, the + harness reads all five context segments (conversation by thread; knowledge base, workflow, + entity by query similarity; summary index by query+thread) and assembles them (prevents + CTX-B7). Per-segment read defaults, each from its single course config: conversation limit 10 + messages, knowledge base k=3, workflow k=3, entity k=5, summary index k=10. → AC3, AC13 +3. **R3 — Conversation reads are thread-scoped, chronological, and exclude consolidated rows.** + [C] Reads filter to the thread, order by timestamp ascending, and skip rows whose + `summary_id` is set; when nothing remains, the segment states that explicitly (prevents + CTX-B5). → AC2, AC8 +4. **R4 — Focused tool retrieval.** [C] Each turn passes the model only the toolbox's semantic + top matches for the query (count per D19), deduplicated by tool name, as OpenAI + function-format schemas (§3) (prevents CTX-B2). → AC4, AC17 +5. **R5 — Idempotent, enriched tool registration.** [C] Registering a tool stores its + description + embedding in the toolbox store; with augmentation enabled (D12) the stored + description is LLM-enriched from docstring + source and ~5 synthetic queries join the + embedding text; re-registering an existing tool name never writes a duplicate row. → AC5, AC18 +6. **R6 — Context budget monitoring.** [C] Token usage is estimated as `len(chars) // 4` + against the model's budget (256,000 for the default D4 model; 128,000 fallback for unknown + models); status is `ok` below 50%, `warning` 50–79%, `critical` at ≥80% (prevents CTX-B3). + [H] The budget is injectable so tests can shrink it. → AC6 +7. **R7 — Threshold offload.** [C] When assembled memory context exceeds 80% of budget, the + harness (not the model) summarizes the thread's unconsolidated conversation, replaces the + conversation segment with a short stub pointing at summary references, appends the + `[Summary ID: …]` reference under the summary segment — and never summarizes the + `# Question` text (prevents CTX-B3). → AC7 +8. **R8 — Structured summarization.** [C] Summaries are produced with exactly four headings — + Technical Information / Emotional Context / Entities & References / Action Items & Decisions — + from at most the first 6,000 characters of input, with one simpler-prompt retry and then a + deterministic non-empty fallback if the model returns nothing; each summary gets an 8-char + hex id and a specific 8–12-word label (generic labels like "Conversation summary" are + rejected and replaced). → AC8, AC14 +9. **R9 — Recoverable consolidation.** [C] Summarizing a thread marks **exactly** the consumed + rows with the new `summary_id`; expanding a summary returns the stored summary text plus all + original messages, chronologically, with timestamps; already-marked rows are never + re-summarized (prevents CTX-B4, CTX-B5). → AC8, AC9, AC20 +10. **R10 — Complete tool logging with bounded excerpts.** [C] Every tool execution writes a + tool-log record (args, full result, ≤2000-byte preview, status, error message on failure); + the model receives at most 3,000 characters of the result — when truncated, the message ends + with a notice naming the log id where the full output lives (prevents CTX-B6). → AC10 +11. **R11 — Workflow write-back.** [C] Any run that made ≥1 tool call persists one workflow + record: the query, the ordered step descriptions with outcome markers, an answer excerpt of + at most 200 characters, and `num_steps`; workflow reads exclude records with zero steps. + → AC11 +12. **R12 — Non-blocking entity extraction.** [C] After the user query and after the final + answer, entities (PERSON/PLACE/SYSTEM, from at most the first 500 characters of text) are + extracted via the LLM and written to entity memory; any extraction failure is swallowed — + it must never fail the turn. → AC15 +13. **R13 — Bounded agent loop.** [C] The loop runs at most 10 iterations; if no final answer + is produced by then, the turn ends with a fixed inability message, which is still persisted + per R1 (prevents CTX-B8). → AC12 +14. **R14 — Search-and-store acquisition.** [C] Acquisition tools persist what they find to the + knowledge base with source metadata (source, id, title, chunk index/count, timestamps) in + the same call; deep ingestion chunks documents (default: recursive character splitting, + chunk size 1,500, overlap 200 — the course's single ingest config) before storing. → AC16, AC19 +15. **R15 — Configuration coherence.** [C] One distance strategy (D17) governs every vector + store, write and read (the course wipes and rebuilds all tables between lessons specifically + to guarantee this); [H] distance strategy, toolbox k (D19), and token budget are each defined + in exactly one configuration point. → AC17 +16. **R16 — Memory-aware model instructions.** [C] The system prompt names each context segment + and its purpose, instructs the model to consult memory before tools, sets the conflict + priority (current question > latest conversation > knowledge-base evidence > older + summaries/workflows), requires expanding a summary before relying on detail that exists only + there, and requires stating uncertainty instead of asserting unsupported claims. → AC13, AC20 ## 5. Acceptance Criteria ★ (the oracle) -### Fixture corpus (define FIRST; all facts authored here — none copied from the course) - -All fixtures live in `fixtures/`. **The building agent MUST NOT modify fixtures to make a -test pass.** - -- `fixtures/papers.jsonl` — 6 synthetic paper records, fields - `{arxiv_id, title, subjects, abstract, submission_date}`; seeded into the KB at setup - (text = title+subjects+abstract concatenated, remaining fields as metadata): - 1. `9901.00001` — "Orchid: A Memory Layer for Conversational Agents" — cs.AI — - abstract states: *"Orchid introduces an episodic buffer that survives restarts and - reduced repeat lookups by 41% in synthetic trials."* — 2024-01-15 - 2. `9902.00002` — "Sparrowhawk: Tool Selection Under Context Pressure" — cs.CL — - abstract states: *"Sparrowhawk shows selection accuracy collapsing once more than 24 - tool schemas share a prompt."* — 2024-02-20 - 3. `9903.00003` — "Ledgerline: Auditable Tool Logs for Agent Pipelines" — cs.SE — - abstract states: *"Ledgerline records full tool payloads out-of-band and passes only - bounded excerpts to the model."* — 2024-03-05 - 4. `9904.00004` — "Foldback: Recoverable Summarization for Long Dialogues" — cs.CL — - abstract states: *"Foldback links every summary to its source utterances so detail is - recoverable on demand."* — 2024-04-11 - 5. `9905.00005` — "Cartographer: Entity Graphs from Chat Transcripts" — cs.IR — - abstract states: *"Cartographer tags people, places, and systems from raw dialogue."* - — 2024-05-30 - 6. `9906.00006` — "Quillwork: Procedural Memory for Multi-Step Agent Workflows" — cs.AI — - abstract states: *"Quillwork replays stored step sequences to cut planning tokens."* - — 2024-06-18 -- `fixtures/thread_long.json` — a synthetic 12-message user/assistant conversation - (thread `t-long`) about planning a literature review codenamed **"Atlas Review"** for a - user named **Priya**; message 1 (user) is exactly: - *"Which paper introduced the Orchid memory layer?"*; later messages record the decision - *"target venue: Journal of Synthetic Benchmarks"* and an action item - *"draft the related-work section by Friday"*. Deliberate instance of CTX-B1/B3/B4/B5. -- `fixtures/long_payload.txt` — ≥5,000 characters of synthetic prose (authored filler - paragraphs about the six fixture papers). Deliberate instance of CTX-B7. -- `fixtures/extra_tools.py` — four no-op fixture tools with distinct docstrings: - `convert_temperature`, `roll_dice`, `count_words`, `reverse_string`; registered - (unaugmented) in tests to exceed the retrieval cap. Deliberate instance of CTX-B2. -- `fixtures/stub_llms.py` — two fake chat clients: `EmptyLLM` (returns empty content on - every call — CTX-B6) and `ToolLoopLLM` (returns a `get_current_time` tool call on every - call, never a final answer — CTX-B8). +### Fixture corpus (define FIRST; authored for this spec — no course data copied) + +| Fixture | Exact contents | +|---|---| +| `fixtures/kb/kestrel-notes.md` | Synthetic paper notes: *"Kestrel: Streaming Memory Consolidation for Long-Horizon Agents. Authors: R. Marlow, T. Iversen (2025). Id: KX-2025-011. Claim: tiered summary ledgers cut resumption errors by 41% on synthetic long-horizon tasks."* (+ ~2 paragraphs of filler you author). | +| `fixtures/kb/heron-notes.md` | Synthetic paper notes: *"Heron: Entity Graphs for Tool Routing. Author: F. Adeyemi (2024). Id: HX-2024-007. Claim: entity-conditioned routing halves tool-selection errors versus flat tool lists."* (+ filler). | +| `fixtures/conversation/seed-thread.json` | 12 authored messages (6 user / 6 assistant), thread `seed-01`, about researching Kestrel. First user message is exactly: `"Find the Kestrel paper about streaming memory consolidation."` Deliberately long enough to exercise consolidation (failure modes CTX-B1/B4/B5). | +| Fixture tool registry (9 tools) | 3 relevant: `paper_search` (returns fixture candidates JSON), `fetch_notes` (acquisition: reads a `fixtures/kb/*.md` file, chunks it, writes KB rows per R14, returns the full text — which exceeds 3,000 chars via the filler; failure modes CTX-B2/B6), `get_current_time`; 6 decoys with unrelated authored docstrings: `knit_pattern_helper`, `currency_convert`, `recipe_scaler`, `translate_phrase`, `calendar_lookup`, `sports_scores`. | +| `fixtures/tools/oversized-output.txt` | ≥4,000 characters of authored filler used by `fetch_notes` to guarantee the >3,000-char case (CTX-B6). | +| Scripted LLM client [H] | A deterministic stand-in honoring the D4 tool-calling interface, with canned per-test scripts (always-tool-call, canned summary, canned entities). Lets the oracle run without a key. Building agents MUST NOT modify fixtures to make a test pass (§6 Never). | + +Modes: **Offline** (no network after the one-time embedding-model download), **Scripted** +(offline + scripted LLM client), **Live** (real network; `live-keyless` needs no key, +`live-keyed` needs `OPENAI_API_KEY`). Live ACs are excluded from the default offline run. ### Given / When / Then -| AC | Given | When | Then | -|----|-------|------|------| -| AC1 | Fresh stores; live agent | `call_agent("Hello, note that my project is Atlas Review", thread_id="t1")` completes | Conversational store has ≥2 rows for `t1`: the exact user query (role `user`) and a non-empty final answer (role `assistant`), timestamp-ordered, `summary_id` null. | -| AC2 | AC1 has run; **a new process is started** (restart) | `call_agent("What was my first question?", thread_id="t1")` | The assembled context's `## Conversation Memory` segment contains the AC1 messages, and the answer references the first question's content ("Atlas Review"/"Hello"). Survives restart per Ledger row 14 invariant. | -| AC3 | Seeded KB (papers.jsonl); any query mentioning "recoverable summarization" | Context is assembled for the turn | Context begins with `# Question` and contains all five `## Memory` headings in rule-2 order; the KB segment contains text from the "Foldback" fixture record; each segment includes its what-it-is / how-to-use guidance lines. | -| AC4 | Default toolset registered + the 4 fixture tools (≥9 named tools in store) | Tools are retrieved for query "fetch the full text of a paper and store it" | The tools list passed to the LLM has ≤5 entries, all unique names, each a valid OpenAI function schema, and includes `fetch_and_save_paper_to_kb_db`. | -| AC5 | Same toolbox | Retrieve for "what time is it right now" | ≤5 tools returned; `get_current_time` is among them. | -| AC6 | Thread `t-long` seeded from fixture; token limit overridden to a value making the memory context exceed 80% | A turn runs on `t-long` | Offload triggers before reasoning: a SummaryRecord exists (8-char id, 4-heading summary); the post-offload context's `## Conversation Memory` is the pointer stub (no original fixture messages); `## Summary Memory` contains `[Summary ID: ]` + description; `# Question` still contains the verbatim query; recomputed usage < pre-offload usage. | -| AC7 | AC6's summary id | `expand_summary()` | Returns the summary text AND all 12 original fixture messages in chronological order with timestamps, including verbatim *"Which paper introduced the Orchid memory layer?"*. | -| AC8 | AC6 has run | Inspect conversational store for `t-long` | 0 rows with `summary_id` null; 12 rows carrying the AC6 summary id. | -| AC9 | AC8 state | `summarize_and_store(thread_id="t-long")` again | Returns the no-unsummarized-messages message; no new SummaryRecord is created; no row's `summary_id` changes. | -| AC10 | `EmptyLLM` stub as the LLM client | Summarize the fixture thread's transcript | Result summary is non-empty, contains all four `###` headings, with the source excerpt under Technical Information; description label is non-empty and not in {"Conversation summary","Summary","Chat summary","Thread summary"}. | -| AC11 | A registered fixture tool that reads `long_payload.txt` (returns ≥5,000 chars) | The agent loop executes that tool | Tool-log row stores the FULL result (≥5,000 chars) + preview ≤2,000 bytes; the tool-role message given to the LLM is ≤3,000 chars + a truncation notice naming the tool-log id. | -| AC12 | A registered tool that raises `ValueError("boom")` | The agent loop calls it | No crash; tool-log row has `status="failed"`, `error_message` containing "boom"; the step is recorded as failed; the loop continues to a final answer. | -| AC13 | `ToolLoopLLM` stub | `call_agent(...)` with max_iterations=10 | Loop stops after exactly 10 iterations; answer == "I was unable to complete the request within the allowed iterations."; 10 tool-log rows exist for the thread. | -| AC14 | A live turn that used ≥1 tool | Turn completes | Exactly one workflow record written: text contains the query, `Step 1:`-numbered steps, answer clipped ≤200 chars; metadata `num_steps` ≥1; `read_workflow` on a similar query returns it; a workflow record with 0 steps (written directly in the test) is NOT returned by `read_workflow`. | -| AC15 | `get_current_time` already registered | Register `get_current_time` again | Toolbox store row count for that name stays 1; the callable remains invocable; retrieval returns it once. | -| AC16 | Empty KB; network available *(marked `live`, excluded from the default test run)* | `arxiv_search_candidates("agent memory", k=5)` then `fetch_and_save_paper_to_kb_db()` | Candidates parse as JSON with the six documented fields, ≤5 entries, abstracts ≤2,500 chars, and no KB writes; the fetch writes ≥1 chunk rows each with all 9 metadata fields, `num_chunks` consistent, and returns the saved-confirmation string. | -| AC17 | — | Inspect the system prompt string | It names all five memory segments with their semantics, the expand-before-relying summary policy, the conflict-priority order of rule 2, the minimum-necessary-tool-calls rule, and the summarize-with-thread_id instruction. | - -Every business rule reaches ≥1 AC (R1→AC1/2, R2→AC3/17, R3→AC4/5, R4→AC6, R5→AC7/8, -R6→AC9, R7→AC10, R8→AC11/12, R9→AC13, R10→AC14, R11→AC15, R12→AC16, R13→AC3/17); every AC -traces to a rule. +| # | Mode | Given | When | Then | +|---|---|---|---|---| +| AC1 | Offline | Fixtures ingested and seed thread written by process 1 | A **new OS process** opens the same DB file and reads seed-thread conversation and queries the KB for "streaming memory consolidation" | All 12 seed messages return chronologically, and a `kestrel-notes` chunk is the top KB hit — memory survived restart (D5/D15 invariants; R1) | +| AC2 | Offline | Seed thread + 2 messages written to thread `other-01` | Reading conversational memory for `seed-01` with default limit | Only `seed-01` unconsolidated messages return, timestamp-ascending, each with role and timestamp; no `other-01` content (R3) | +| AC3 | Offline | Both fixture notes ingested | KB similarity query "entity graphs for routing tools", k=3 | A `heron-notes` chunk ranks first — the true nearest neighbor, exact at fixture scale — and carries its source-file metadata (R2; D7, D18 invariants) | +| AC4 | Offline | All 9 fixture tools registered | Toolbox query "find research papers on agent memory" with k per D19 | At most 5 schemas return, all valid `RetrievedToolSchema`, names unique, `paper_search` present (R4; D11/D19 invariants) | +| AC5 | Offline | `paper_search` already registered | It is registered again under the same name | The toolbox store holds exactly one row for `paper_search` (R5) | +| AC6 | Offline | A test budget of 1,000 tokens [H]; strings sized to 40% / 65% / 85% of it | Monitoring each | Statuses are `ok` / `warning` / `critical` respectively and each token estimate equals `len(chars)//4` (R6) | +| AC7 | Scripted | Assembled context >80% of the test budget containing a `## Conversation Memory` section and a `# Question` line | The pre-model budget check runs | Returned context has the conversation section replaced by the offload stub, a `[Summary ID: …]` reference under `## Summary Memory`, and the `# Question` text byte-identical (R7; D10 invariant) | +| AC8 | Scripted | Seed thread fully unconsolidated | Thread summarization runs, then immediately runs again | After run 1: every seed row carries the same new 8-char hex `summary_id`, unconsolidated count is 0, and the conversation read reports no unconsolidated messages; run 2 reports nothing to summarize (R3, R8, R9) | +| AC9 | Scripted | AC8's summary id | The expand operation runs on it | Output contains the stored summary text AND all 12 originals with timestamps, chronological, including the first user message `"Find the Kestrel paper about streaming memory consolidation."` verbatim (R9; D10 invariant) | +| AC10 | Scripted | `fetch_notes` returns >3,000 chars inside an agent turn | The loop executes the tool call | A tool-log record holds the FULL result, args, `status="success"`, preview ≤2,000 UTF-8 bytes; the tool message given to the model is ≤3,000 chars + a truncation notice naming the log id (R10; D13 invariant) | +| AC11 | Scripted | A scripted run making exactly 2 tool calls then answering; plus one zero-step workflow row written directly as a control | The turn completes; then workflow memory is queried for a similar task | One workflow record exists with the query, 2 ordered steps with outcome markers, answer excerpt ≤200 chars, `num_steps=2`; the query returns it and never returns the zero-step control (R11; D8 invariant) | +| AC12 | Scripted | A scripted model that always emits a tool call | `call_agent` runs a turn | The loop stops after exactly 10 iterations, `completed=false`, the final answer is the fixed inability message, and that answer is persisted to conversational memory (R13, R1) | +| AC13 | Scripted | Any query on a fresh thread | The turn's exact model input is captured | The user content begins `# Question` and contains all five segment headings in order (Conversation, Knowledge Base, Workflow, Entity, Summary); the system message names all five segments and states the R16 conflict-priority order (R2, R16; D9 invariant) | +| AC14 | Scripted | The seed transcript; scripted summary reply | Summarization runs | Output contains the four exact R8 headings; the id matches `^[0-9a-f]{8}$`; the label is 8–12 words and not in the generic-label reject set (R8) | +| AC15 | Scripted | Scripted extractor returns 2 entities for the fixture text; a second scripted run throws | Entity extraction runs after a turn | Run 1 writes 2 `EntityRecord`s and the entity read returns both as formatted bullets; run 2's exception leaves the turn's final answer unchanged (R12) | +| AC16 | Offline | `fetch_notes` acquisition tool | It executes on `kestrel-notes` | KB gains chunk rows with `source`, `chunk_id`, `num_chunks` metadata **within the tool call itself**, and chunk sizes respect the 1,500/200 chunking default (R14; D14 invariant) | +| AC17 | Offline | The running configuration | All five vector stores and the toolbox retriever are inspected | Every store reports the identical distance strategy (D17) and the retrieval k resolves from the single D19 config point (R4, R15; D17/D19 invariants) | +| AC18 | Scripted | A tool registered with augmentation on; scripted LLM returns enriched text + 5 queries | Registration completes | The stored description equals the enriched text (not the raw docstring) and the embedded text contains the 5 synthetic queries (R5; D12 invariant) | +| AC19 | Live-keyless | Network available | The arXiv candidate-search tool runs with query "agent memory" | A JSON array returns; every element has `arxiv_id`, `entry_id`, `title`, `authors`, `published`, `abstract` (abstract ≤2,500 chars) (R14; D16 invariant) | +| AC20 | Live-keyed | `OPENAI_API_KEY` set; fresh thread | Turns: "Find the MemGPT paper" → "Save the content of the paper" → "Summarize the conversation so far using your tool" → "What was my first question?" | Turn 2 resolves "the paper" from conversation memory without re-asking; turn 3 stores a summary and marks rows; turn 4's answer names the first question, having expanded the summary (R1, R2, R9, R16; D4 invariant — real native tool calling against the default provider) — the course's own end-to-end demonstration sequence (CTX-E, Lesson 6) | + +Every business rule reaches ≥1 AC and every AC traces to a rule (mapping inline above). Every +demonstrated failure mode appears as rule + fixture + AC + CTX-B entry (§10 four-way encoding). ## 6. Boundaries **Always** -- Read the five deterministic memory segments before reasoning, and persist conversation, - workflow, entities, and tool logs after — every turn, unconditionally (R1, R10). -- Mark source rows with `summary_id` in the same operation that stores their summary (R5). -- Log every tool call, including failures, at full fidelity (R8). -- Load secrets from the environment (`.env`); keep all stores under `./data/`. - -**Ask First** -- Changing the embedding model or the distance strategy after any data is ingested — the - course itself warns stale mixed-strategy data breaks lessons (its notebooks drop all - tables to guarantee "consistent distance strategy"); either change forces re-embedding / - re-indexing everything (Ledger rows 16, 18). -- Raising toolbox k or bypassing retrieval to pass more tools — re-opens the Lesson-4 - failure mode (context confusion, selection degradation, latency/cost); course-cited - guidance is ~10–20 tools max exposed (Ledger row 17). -- Moving an operation across the deterministic/agent-triggered line (Ledger row 2) — the - course argues each side's costs aloud (Lesson 3): determinism buys reliability, agent - triggering buys signal-to-noise; swapping silently changes behavior guarantees. -- Replacing recoverable compaction with plain summarization — Lesson 5: summarization "will - always be a lossy technique"; dropping the write-back link makes detail unrecoverable - (Ledger row 3). -- Swapping the store realization (Ledger row 7) or adding keyed services (Tavily, row 8). -- Changing the summarization prompt's four-heading structure — the structure IS the - consolidation contract (Lesson 5: prompt choice determines summary quality). +- Persist every user query and final answer (R1) and write a tool-log record for every tool call (R10). +- Run the budget check before every model call (R6/R7); keep the `# Question` text intact through any offload. +- Use the same embedding model for every store and for both write and read paths (CTX-C5). +- Load secrets from the environment; keep the DB file out of version control. + +**Ask First** (course-spoken trade-offs — anything here forces rework or was shown lateral-or-worse) +- **Changing the embedding model** — every vector store must be re-embedded; the course stresses the query-time model must match the ingest-time model (Lesson 2's RAG walkthrough). +- **Changing distance strategy or adding/altering a vector index after data exists** (D17/D18) — the course wipes and rebuilds all tables between lessons precisely to avoid inconsistent-strategy corruption. +- **Raising toolbox k or passing all tools to the model** (D11/D19) — Lesson 4 shows this degrades tool selection and bloats context; providers recommend ~10–20 tools max. +- **Rewriting the summarization prompt structure** (R8) — Lesson 5: summarization is lossy and "the prompting technique you use … will determine the quality of the output". +- **Switching the store realization** (D15) or **enabling Tavily web search** (D16) — migration/setup cost and a new secret respectively. **Never** -- Never invent citations, paper metadata, or provenance; when KB evidence is missing, say - what is missing (and optionally use a tool) instead of asserting (R13). -- Never summarize or discard the current `# Question` (R2, R4). -- Never pass the full toolbox to the LLM (R3). -- Never mutate fixtures to make a test pass (§5). -- Never hardcode or commit secrets — no API keys or DB passwords in source (§2). -- Never let entity-extraction or logging failures abort a turn (R10). +- Invent citations, paper metadata, or provenance for content not present in memory. +- Assert factual claims unsupported by knowledge-base evidence without stating uncertainty (R16); answer when the system should abstain. +- Mutate fixtures (or the scripted LLM's scripts) to make a test pass. +- Commit secrets or hardcode credentials (the course notebooks hardcode local DB passwords — do not reproduce that). +- Re-summarize rows already marked with a `summary_id`, or bypass the tool log for any tool call. ## 7. Test Plan & Self-Verification ```bash python -m venv .venv && source .venv/bin/activate -pip install -r requirements.txt # this build's own pinned requirements -python scripts/seed_fixtures.py # seeds fixtures/papers.jsonl into the KB -pytest tests/ -m "not live" -v # AC1–AC15, AC17 — offline, deterministic (stub LLMs where specified) -pytest tests/ -m live -v # AC16 + any end-to-end live-LLM checks (needs OPENAI_API_KEY + network) -``` +pip install -r requirements.txt # generate a lockfile; pins are this build's choice (§2) +cp .env.example .env # set OPENAI_API_KEY (required only for live-keyed) -Tests for AC6/AC10/AC13 use the fixture stub clients and the overridable token limit — -they MUST NOT depend on live model output. AC2 MUST actually restart the process (separate -`subprocess` invocation or two test phases against the same `./data/`), not just re-call a -function. - -**Reporting requirement:** the building agent MUST report results **per acceptance -criterion** (AC1–AC17) with cited evidence — the pytest output lines, and file paths / -store row counts where relevant. "Should pass" or "looks correct" is treated as a failure: -it means the check was not run. - -## Final deliverable note (for the build session) - -After the build is verified, conclude the session by presenting an infra/structure diagram -of the built app (components: agent loop, MemoryManager, the seven stores, toolbox -retrieval, LLM, tools) and end with the exact phrase: - -**"This is the infra/structure diagram of this app"** +pytest -q -m "not live" # AC1–AC18: offline + scripted (first run downloads the embedding model) +pytest -q -m "live" # AC19 (keyless network), AC20 (needs OPENAI_API_KEY) +``` ---- +The building agent MUST report results **per acceptance criterion** (AC1…AC20) with cited +evidence: the test command run, the pass/fail output line, and file paths of artifacts examined +(e.g. the DB file inspected for AC1, the captured model input for AC13). "Should pass" / +"looks correct" are treated as failures — they mean it wasn't run. If a live AC cannot run +(no network / no key), report it as SKIPPED with the reason; never as passed. ## Course Context Pack (embedded — agent-readable) -Concepts only — no course code. Durable knowledge for applying this course's pattern; -CTX-D lists the only perishable parts. +Concepts only — no code from the course notebooks appears here or anywhere in this spec. The +concepts in CTX-A…CTX-C are durable; only the names in CTX-D are perishable. ### CTX-A. The pattern -The course's central buildable pattern is the **memory-aware agent loop** over a -seven-type memory core: - -``` -perceive query - → assemble partitioned context (deterministic reads: conversational, knowledge base, - workflow, entity, summary-context — each a self- - describing markdown segment; question prepended last) - → budget the context (estimate tokens; ≥80% ⇒ recoverable compaction of the - conversation segment into summary memory) - → select tools semantically (top-k similarity over the toolbox store — never all tools) - → reason/act loop (LLM proposes tool calls; harness executes, logs full - payloads out-of-band, returns bounded excerpts; ≤10 turns) - → persist learning (deterministic writes: conversation turn, workflow steps, - extracted entities) - → repeat next turn / next session -``` - -Why each stage exists: deterministic preloads solve the chicken-and-egg problem ("you need -memory to know which memory you need" — Lesson 3); budgeting prevents context overflow -while compaction keeps detail recoverable (Lesson 5); semantic tool retrieval prevents -tool-overload degradation (Lesson 4); audit logging keeps big payloads out of the prompt -but available just-in-time (Lesson 6); persistence turns each interaction into reusable -experience — the difference between a memory-*augmented* and a memory-*aware* agent -(Lessons 2–3): the aware agent is *told* about its memories (system prompt + partitioned -segments) and *operates* on them via tools. - -The surrounding vocabulary the course builds on: the **agent memory core** is the database -(where most data traffic flows); the **memory manager** is the abstraction exposing typed -read/write operations over it; a **memory unit** is the smallest atomic record (e.g. one -timestamped message; one workflow with steps+outcome); the **memory lifecycle** is -ingest → enrich (embed/augment) → store → organize (index) → retrieve (lexical / vector / -graph / hybrid) → LLM → write back; **context engineering** is curating high -signal-per-token context; **memory engineering** is the umbrella discipline (database + -agent + ML engineering + information retrieval). - -Memory-type map (Lesson 3): Conversational = episodic (time-ordered, exact-key by thread); -Knowledge Base = semantic; Workflow + Toolbox = procedural; Entity = semantic/episodic -entity context; Summary = compressed episodic; Tool Log = execution audit trail. +**Memory-aware agent loop over an external memory core.** A stateless LLM becomes a persistent +agent by routing all durable state through a database (the *Agent Memory Core* — the component +that sees the most data traffic in the system) and by making the model *aware* of that memory: + +`persist & preload memory → assemble partitioned context → guard the budget → retrieve focused tools → reason/act loop with logged tools → write back learning artifacts` + +Stage by stage, each with the reason it exists: +1. **Typed memory stores behind one manager** — different memory types need different data + models and retrieval strategies (exact-by-thread vs semantic similarity), so each gets its + own store; a memory-manager abstraction unifies read/write so the agent code never touches + storage details (D7). +2. **Deterministic preload** — the agent cannot choose to look up what it doesn't know exists + (the chicken-and-egg problem), so context bootstrapping runs every turn by harness rule (D8). +3. **Partitioned context** — labeled, self-describing memory segments let the model use each + store for its intended role instead of one undifferentiated blob (D9). +4. **Budget guard with recoverable reduction** — context windows are finite; consolidate + conversation into summaries that keep an id-link back to the originals, expandable + just-in-time (D10). +5. **Semantic tool retrieval** — tools are procedural memory: register many, retrieve few per + query, so the system scales to hundreds of tools without degrading selection (D11, D12). +6. **Logged execution with bounded excerpts** — full tool outputs live in the database; the + context window gets a pointer-bearing excerpt (D13). +7. **Write-back learning** — conversations, workflows (steps + outcome), entities, and search + results persist after each turn, so the agent improves across sessions instead of restarting + from scratch (D14). ### CTX-B. Failure-mode catalog -- **CTX-B1 — Stateless reset.** *Symptom:* agent asks "please specify" about something the - user said two turns ago (Lesson 2's restaurant-booking demo). *Cause:* no persisted - interaction history loaded into context. *Fix:* deterministic conversational - write-every-turn + read-at-loop-start, thread-scoped, time-ordered. *Enforced by* R1 → AC1, AC2. -- **CTX-B2 — Tool overload.** *Symptom:* wrong or degraded tool selection, bloated prompts, - higher latency/cost as the tool count grows. *Cause:* all tool schemas stuffed into every - prompt. *Fix:* toolbox-as-memory — embed tool descriptions, retrieve top-k per query - (3–5 typical; providers recommend ≤10–20 exposed). *Enforced by* R3 → AC4, AC5. -- **CTX-B3 — Context overflow.** *Symptom:* long threads exhaust the window; the agent - loses history or fails on token limits. *Cause:* unbounded accumulation of conversation - in context. *Fix:* monitor estimated usage; at ≥80% deterministically offload the - conversation segment to summary memory, leaving a stub + reference; never touch the - question. *Enforced by* R4 → AC6. -- **CTX-B4 — Irrecoverable summarization loss.** *Symptom:* after compression, exact - quotes/chronology are gone for good. *Cause:* summarization is inherently lossy - (Lesson 5, spoken). *Fix:* recoverable compaction — store the full content with the - summary, mark source rows with the summary id, expose `expand_summary(id)` for - just-in-time recovery. *Enforced by* R5 → AC7, AC8. -- **CTX-B5 — Re-summarization.** *Symptom:* the same messages get folded into summary - after summary; duplicate compressed memories. *Cause:* no marker distinguishing - processed rows. *Fix:* `summary_id` marking; reads and summarization consider only - unmarked rows; fully-processed threads report nothing-to-summarize. *Enforced by* R6 → AC9. -- **CTX-B6 — Empty summarizer output.** *Symptom:* downstream flow breaks on an empty - summary or a useless generic label. *Cause:* model returns empty/degenerate content. - *Fix:* retry with a simpler prompt, then a deterministic excerpt-based fallback; derive a - specific fallback label. *Enforced by* R7 → AC10. -- **CTX-B7 — Tool-output context bloat.** *Symptom:* one big tool payload (a full paper) - swamps the window. *Cause:* raw tool results echoed into the prompt. *Fix:* persist the - full payload to the tool log; hand the model a bounded excerpt plus a pointer id - (context offloading). *Enforced by* R8 → AC11, AC12. -- **CTX-B8 — Runaway loop.** *Symptom:* the agent keeps calling tools and never answers. - *Cause:* no stop condition beyond "model decides". *Fix:* max-iteration cap with a - templated inability answer; tool exceptions become failed steps, not crashes. - *Enforced by* R9 → AC13. +- **CTX-B1 — Stateless amnesia.** *Symptom:* mid-conversation, the agent asks the user to + re-specify things already said ("book the first one" → "which list?"); nothing carries across + sessions. *Cause:* no external persistence; each turn starts from a blank context. *Fix:* + deterministic conversation persistence and preload every turn. *Enforced by R1, R2 / AC1, AC2, + AC20.* +- **CTX-B2 — Tool overload.** *Symptom:* wrong or failed tool selection, ballooning latency and + cost as the toolset grows. *Cause:* every tool definition stuffed into the context — confusion + and bloat degrade selection. *Fix:* toolbox store + per-query semantic retrieval of a focused + subset. *Enforced by R4, R5 / AC4, AC5, AC18.* +- **CTX-B3 — Context overflow.** *Symptom:* long threads crash into token limits or silently + drop history. *Cause:* unbounded accumulation with no monitoring. *Fix:* estimate usage every + turn; at ≥80% of budget, offload conversation to summary memory. *Enforced by R6, R7 / AC6, + AC7.* +- **CTX-B4 — Unrecoverable (lossy) summarization.** *Symptom:* after compaction the agent can no + longer answer detail questions ("what was my first question?"). *Cause:* summarization alone + is lossy; originals were discarded. *Fix:* recoverable consolidation — mark source rows with + the summary id and expand on demand. *Enforced by R9 / AC9, AC20.* +- **CTX-B5 — Re-summarizing processed messages.** *Symptom:* duplicate summaries, growing cost, + drifting summaries of summaries. *Cause:* no marker distinguishing consolidated rows. *Fix:* + set `summary_id` on exactly the consumed rows; reads and later summarizations exclude them. + *Enforced by R3, R9 / AC8.* +- **CTX-B6 — Tool-output bloat.** *Symptom:* one verbose tool result crowds out the rest of the + context. *Cause:* full payloads routed through the model. *Fix:* full output to the tool log; + ≤3,000-char excerpt with a log-id pointer to the model. *Enforced by R10 / AC10.* +- **CTX-B7 — Blind memory (chicken-and-egg).** *Symptom:* the agent never consults memory it + actually has. *Cause:* retrieval left to model discretion — you need memory to know which + memory you need. *Fix:* deterministic preload of all segments every turn. *Enforced by R2 / + AC13.* +- **CTX-B8 — Runaway loop.** *Symptom:* a turn never terminates or burns unlimited tool calls. + *Cause:* no stop condition besides the model choosing to answer. *Fix:* hard iteration cap + with a templated, persisted inability answer. *Enforced by R13 / AC12.* ### CTX-C. Decision background -Reference only — decisions live in the Decision Ledger; this is the mechanism and -provenance behind its rows. - -- **CTX-C1 (→ Ledger 1).** Storage-by-retrieval-need: conversational and tool-log memory - are retrieved by exact key (thread id) and time order — similarity search is the wrong - primitive, so they live in relational tables; the other five types are retrieved by - meaning, so they live in vector stores. Lesson 3 presents the per-type table - (memory type → human analogy → storage → retrieval strategy) as the design method. -- **CTX-C2 (→ Ledger 2).** The deterministic side buys: context bootstrapping - (non-negotiable), no forgotten saves, predictability/debuggability, reduced model - bookkeeping. The agent-triggered side buys: relevance filtering (not everything deserves - storage), cost/latency control, judgment about *what* to store or expand. Tool calls are - typically agent-triggered because only the agent can judge whether external information - is needed. The Lesson 3 classification table vs the Lesson 6 loop disagree on - `read_summary_context` and `write_entity` (table: agent-triggered; loop: deterministic) — - the Ledger default follows the loop. -- **CTX-C3 (→ Ledger 3).** Two reduction techniques taught: **context summarization** - (compress via LLM, re-seed a clean window — always lossy) and **context compaction** - (move content to the database under an id + description, let the model pull it back — - recoverable). The course's implementation composes them: summarize, store with links, - expand on demand. The summary's four-heading structure (technical / emotional / - entities / action items) is the consolidation contract — "structured extraction, not just - compression" (Lesson 5 takeaway). -- **CTX-C4 (→ Ledger 4).** The toolbox pattern: embed `name + description + signature` - (augmented text when available), store per-tool metadata, retrieve by query similarity, - convert hits to provider function schemas at the call boundary. Exposing `read_toolbox` - *itself* as a registered tool lets the agent discover capabilities mid-execution - (programmatic and agent-callable at once). -- **CTX-C5 (→ Ledger 6).** Markdown-partitioned context: `#`/`##` headings exploit the - model's latent understanding of hierarchical documents; each segment tells the model what - the memory is and how to use it, which is what upgrades "augmented" to "aware". The - conflict-priority rule (Question > latest Conversation > KB > older summaries/workflows) - is part of the same design. -- **CTX-C6 (→ Ledger 16, 18).** Provenance of the contradictions: distance strategy — - COSINE in the Lesson 3/4 notebooks and Lesson 3 narration, EUCLIDEAN in the Lesson 5/6 - notebooks (the running app); index — HNSW narrated in Lesson 3's objectives/markdown, - IVF (`NEIGHBOR PARTITIONS`, target accuracy 95) in the helper code, with an explicit - comment that HNSW hit ORA-00600/51928/51962 on Oracle Free. The notebooks' "drop all - tables for a consistent distance strategy" banner is the course's own acknowledgment - that mixing strategies across sessions corrupts comparability. -- **CTX-C7 (body defaults, exact sources).** Retrieval k's: knowledge base 3, workflow 3, - entity 5, summary-context 10, toolbox 3 (manager default) / 5 (agent loop); - conversational read limit 10 (agent loop) — the Lesson 5 notebook's verification cell - used `limit=100`, an exploratory config, not the app default. Token estimate: chars//4 - (narration: some models are nearer chars//2 — it is an estimate, not tokenizer truth). - Chunking 1500/200. Tool-result LLM bound 3,000 chars; preview 2,000 bytes. Entity - extraction input clip 500 chars; summary input clip 6,000 chars. Max iterations 10. - Workflow answer clip 200 chars. Summary id = uuid4[:8]. -- **CTX-C8 (→ Ledger 8).** The search-and-store pattern (Lesson 4, spoken): a search tool - that persists results means information discovered once becomes long-term memory — - "the agent learns from its searches" — and large payload handling moves out of model - context into memory infrastructure. +Reference only — decisions are made in the Decision Ledger, not here. + +- **CTX-C1 — Why typed stores (→ Ledger D7, D15).** The course's taxonomy: short-term memory + (semantic cache, working memory/context window) vs long-term memory (procedural → workflow & + toolbox; semantic → knowledge base, entity, summary; episodic → conversational, which is + time-ordered and timestamp-addressable). Conversation and tool logs need exact, chronological, + thread-keyed retrieval — a relational table, indexed on the thread key and on the timestamp so + lookups and chronological ordering stay fast (the course creates both indexes explicitly); the + other five need meaning-based retrieval — vector stores. The *memory manager* abstracts CRUD over all of them; the *memory unit* is the + smallest atomic record (e.g. a conversational unit = timestamp + role + content). The + database is called the memory core because it carries most of the system's data traffic. +- **CTX-C2 — Why the deterministic/agent-triggered split (→ Ledger D8).** Deterministic ops buy + predictability, continuity, completeness, and lower model cognitive load ("don't let it forget + to save"); agent-triggered ops buy relevance filtering, cost/latency control, and judgment + ("should this be a durable preference? consolidate now?"). External tool calls are + agent-triggered because only the model can judge whether extra information is worth the cost. + The course's classification table and its final working loop disagree on two operations + (entity writes, summary-index reads) — evidence carried in D8's default note. +- **CTX-C3 — Summarization vs compaction mechanics (→ Ledger D10).** Summarization compresses + content through the LLM into a shorter representation preserving task-relevant facts, + relationships, and removing redundancy — always lossy. Compaction moves content to the + database under an id with a short description, letting the model pull it back when needed — + lossless but preloads nothing. The shipped pipeline pairs them: structured summary (four + fixed headings: technical / emotional / entities / actions-decisions) in the window, full + originals recoverable by id (JIT retrieval: fetch only what the current reasoning step needs, + when it needs it). Working parameters, each from its single course config: input cap 6,000 + chars; summary generation capped at 4,000 completion tokens; label prompt capped at 2,000; + one retry then deterministic fallback; 8-char summary ids; budget estimate `chars//4`; + thresholds 50/80; budget 256,000 tokens (see D4's note for the model-name discrepancy). +- **CTX-C4 — Toolbox mechanics (→ Ledger D11, D12, D19).** Tool retrieval is keyed on + descriptions, not names: registration embeds name + description + signature (+ synthetic + queries when augmented); a user query embeds to the same space; nearest tools win. Augmented + registration has the LLM rewrite the docstring using the function source (summary, steps, + when-to-call, caveats) and generate ~5 example queries — the course demonstrates the enriched + text is far more separable than a one-line docstring. Model providers recommend exposing only + ~10–20 tools; the course passes the top 3–5. The self-lookup tool (`read_toolbox` as a + registered tool) lets the agent discover capabilities mid-execution when the initial toolset + proves insufficient. Registration deduplicates by tool name against the store. +- **CTX-C5 — Embedding & consistency (→ Ledger D17, §2).** One local sentence-transformers + model (768-dim) serves every store; the query-time model must match the ingest-time model or + similarity is meaningless. The course re-creates all tables at each lesson start explicitly + "to guarantee a clean starting state with consistent distance strategy" — the strongest + in-course signal that mixed strategies corrupt retrieval. Distance config homes: cosine in the + L2/L3 notebooks' store setup; Euclidean in the L4/L5 notebooks' store setup; the L2 notebook's + markdown names Euclidean while its code passes cosine. +- **CTX-C6 — Index background (→ Ledger D18).** The course text teaches HNSW (graph-based + nearest-neighbor traversal) and its lesson objectives name it, but the helper that actually + creates indexes builds IVF (neighbor-partition organization, target accuracy 95) to dodge + store-version bugs — a reminder that index choice is a store-realization detail, while the + invariant is retrieval correctness. Indexes exist to avoid full scans; at fixture scale exact + search is correct and simpler. +- **CTX-C7 — Acquisition & ingestion (→ Ledger D14, D16; R14).** The course's acquisition tools: + a web search (default 5 results per call, each written with title/url/score/query/timestamp + metadata), a candidate + search returning structured JSON (id, title, authors, published, abstract capped at 2,500 + chars; retriever configured for up to 8 docs, 4,000 chars each), and deep ingest + (PDF → text → chunks of 1,500 chars with 200 overlap → KB rows with chunk_id/num_chunks/ + timestamps). Chunking exists because embedding inputs are bounded — oversized inputs fail or + truncate. Its bootstrap corpus streamed the first 100 records of a public arXiv dataset, + concatenating title + subjects + abstract as the embedded text with the fields as metadata. +- **CTX-C8 — Loop & write-back parameters (→ Ledger D8, D13; R11–R13).** Max 10 iterations; + tool results >3,000 chars truncated for the model with a log-id pointer; tool-log previews + capped at 2,000 UTF-8 bytes; workflow records store the query, ordered outcome-marked steps, + and an answer excerpt capped at 200 chars, and reads filter to `num_steps > 0`; entity + extraction reads at most 500 chars of source text, classifies PERSON/PLACE/SYSTEM, is capped + at 2,000 completion tokens, and is wrapped so failures never break the turn; conversation + preload defaults to 10 messages — the agent loop uses that default, while one L4-notebook + *verification* cell reads with limit 100 (a test read, not the loop's config); KB k=3, + workflow k=3, entity k=5, summary index k=10 — each from its single course config (no + competing configs exist for these). The course validated its consolidation pipeline on a + seeded ~30-message thread; this spec's authored 12-message seed fixture plays that role. ### CTX-D. Perishable assumptions -Treat every name below as a **search keyword against current docs, not a guaranteed -import**. The concepts in CTX-A…CTX-C are durable; only these names age. The course's own -installs were entirely unpinned. - -- Models: `gpt-5`, `gpt-5-mini` (both appear; token table 256,000; 128,000 fallback), - `sentence-transformers/paraphrase-mpnet-base-v2` (768-dim). -- OpenAI SDK: `OpenAI()` client, `chat.completions.create`, `tools`/`tool_choice="auto"`, - `max_completion_tokens`, tool-role messages keyed by `tool_call_id`. -- LangChain-era names: `langchain_oracledb.vectorstores.OracleVS`, - `langchain_oracledb.retrievers.hybrid_search.OracleVectorizerPreference`, - `langchain_community.vectorstores.utils.DistanceStrategy` - (COSINE / EUCLIDEAN_DISTANCE / DOT_PRODUCT), `HuggingFaceEmbeddings` imported from BOTH - `langchain_huggingface` and `langchain_community.embeddings` in different lessons, - `langchain_community.retrievers.ArxivRetriever`, - `langchain_community.document_loaders.ArxivLoader`, - `langchain_text_splitters.RecursiveCharacterTextSplitter`. -- Services/data: `tavily.TavilyClient`, HuggingFace `datasets.load_dataset` - (`nick007x/arxiv-papers`, streaming). -- Oracle-era specifics: Oracle AI Database **26ai** (Docker "FREE" image), DSN - `127.0.0.1:1521/FREEPDB1`, `VECTOR` user bootstrap, ASSM tablespace requirement for JSON - columns (ORA-43853), `CREATE VECTOR INDEX … ORGANIZATION NEIGHBOR PARTITIONS … WITH - TARGET ACCURACY 95` (IVF), HNSW, error lore: ORA-00600, ORA-51928, ORA-51962, ORA-00955, - ORA-00942. +Treat these as **search keywords against current documentation, not guaranteed imports**. The +concepts in CTX-A…CTX-C are durable; only these names are perishable. The course's installs +were entirely **unpinned** (no versions in its requirements file), so no course version pins +exist to reproduce. + +- `langchain-oracledb` — `OracleVS`, `OracleVectorizerPreference` (hybrid search hook) +- `langchain_community.vectorstores.utils.DistanceStrategy` (`COSINE`, `EUCLIDEAN_DISTANCE`, `DOT_PRODUCT`) +- `langchain_huggingface` / `langchain_community.embeddings` — `HuggingFaceEmbeddings` +- `sentence-transformers/paraphrase-mpnet-base-v2` (embedding model name) +- `langchain_community.retrievers.ArxivRetriever`, `langchain_community.document_loaders.ArxivLoader` +- `langchain_text_splitters.RecursiveCharacterTextSplitter` +- `tavily` — `TavilyClient` (web search; keyed) +- `openai` — chat-completions API with `tools` / `tool_choice="auto"`; model names `gpt-5`, `gpt-5-mini` +- `oracledb`, Oracle AI Database `26ai`, DSN form `host:1521/FREEPDB1`, `datasets.load_dataset` streaming, dataset `nick007x/arxiv-papers` +- `pymupdf`, `arxiv` (PDF/metadata access) ### CTX-E. Provenance map -Lesson numbering below is the **transcripts'** (authoritative). The notebook files are -shifted: `L2.ipynb` ↔ Lesson 3, `L3.ipynb` ↔ Lesson 4, `L4.ipynb` ↔ Lesson 5, -`L5.ipynb` ↔ Lesson 6 — and several notebook H1 titles carry the *previous* notebook's -name (e.g. `L3.ipynb`'s H1 says "Scaling Agent Tool Use" — correct — but `L2.ipynb`'s H1 -"Constructing the Memory Manager" pairs with the dump section mislabeled "Lesson 4"). Even -the narration's sign-offs are off by one ("completed lesson 4" ends Lesson 5; "end of -lesson 5" ends Lesson 6). Nothing in this spec requires platform access. - -- **Lesson 1 — Introduction:** course framing; memory engineering as first-class, - persistent, structured infrastructure external to the model (CTX-A vocabulary). -- **Lesson 2 — Why AI Agents Need Memory:** stateless-agent failure demo (CTX-B1); - conversational memory attributes (timestamp/role/content, time-ordered); why - conversational history alone is insufficient; short-term vs long-term memory taxonomy - (semantic cache, working memory / procedural, semantic, episodic); RAG→agent-memory - bridge; agent memory core definition (CTX-A). -- **Lesson 3 — Constructing The Memory Manager (`L2.ipynb`):** agent stack / memory layer; - memory manager, memory units, context engineering, memory lifecycle, memory-engineering - disciplines; augmented→aware progression (CTX-A, CTX-C5); the 7-type storage/retrieval - table and SQL-vs-vector argument (CTX-C1); deterministic vs agent-triggered - classification + rationale (CTX-C2); table schemas + indexes; StoreManager with COSINE; - IVF-vs-HNSW discrepancy (CTX-C6); HF dataset ingestion + `read_knowledge_base` - demonstration (Ledger 9). -- **Lesson 4 — Scaling Agent Tool Use with Semantic Tool Memory (`L3.ipynb`):** tool-calling - mechanics; tool-overload failure modes and 10–20 guidance (CTX-B2); toolbox pattern + - memory-unit augmentation and its separability/recall argument (CTX-C4, Ledger 5); - `read_toolbox` self-registration; Tavily search-and-store (CTX-C8); `get_current_time`; - arXiv discovery + deep-ingestion (chunk 1500/200) tools; original-vs-augmented docstring - comparison; retrieval validation (`k=1` demo). -- **Lesson 5 — Memory Operations: Extraction, Consolidation, and Self-Updating Memory - (`L4.ipynb`):** context engineering; summarization-vs-compaction argument, "always lossy" - (CTX-B4, CTX-C3); token estimation chars//4 and thresholds (CTX-C7); four-heading - summarization prompt + retry/fallback (CTX-B6); `expand_summary`; thread summarization - with `summary_id` marking + DB-level verification (CTX-B5); offload policy; ok/warning/ - critical monitor; EUCLIDEAN switch (CTX-C6). -- **Lesson 6 — Memory Aware Agent (`L5.ipynb`):** agent loop (start/stop conditions, - pseudo-code); agent harness; in-loop vs out-of-loop memory operations (CTX-C2); the - memory-aware system prompt with partitioned segments and conflict priority (CTX-C5); - 80% deterministic offload; toolbox k=5; tool-log persistence + 3,000-char bound - (CTX-B7); max-iterations stop (CTX-B8); post-turn workflow/entity/conversation writes; - the MemGPT walk-through demonstrating continuity, summarize-via-tool, and - expand-to-answer "what was my first question?" (CTX-B1, basis of AC2/AC7). -- **Lesson 7 — Conclusion:** pattern recap — memory modeling, semantic retrieval, - extraction, consolidation, write-back as the transferable building blocks. +Lesson numbering below follows the **transcripts** (authoritative). The notebook files are named +L2–L5 and correspond to transcript Lessons 3–6 (off by one — the platform counts the +Introduction as Lesson 1); the notebook dump's own "Lesson Map" header mislabels this mapping — +where they disagree, the transcripts win. **Nothing in this spec requires platform access.** + +- **Lesson 1 — Introduction:** course framing; memory engineering as first-class, external, + persistent infrastructure → CTX-A preamble. +- **Lesson 2 — Why AI Agents Need Memory:** stateless-agent failure demo (CTX-B1); conversational + memory and its limits; short/long-term memory taxonomy (CTX-C1); RAG-to-agent-memory bridge and + same-embedding-model requirement (CTX-C5); the Agent Memory Core (CTX-C1, D15). +- **Lesson 3 — Constructing The Memory Manager** *(notebook file L2)*: agent stack and memory + layer; memory manager and memory units (CTX-C1); deterministic vs agent-triggered + classification (CTX-C2, D8); context engineering and the memory lifecycle; store creation, SQL + vs vector argument (D7), index creation (CTX-C6); dataset bootstrap ingest (CTX-C7). +- **Lesson 4 — Scaling Agent Tool Use with Semantic Tool Memory** *(notebook file L3)*: the + toolbox pattern and its failure argument (CTX-B2, D11); memory-unit augmentation (CTX-C4, + D12); search-and-store with web search (D14, D16); arXiv candidate search and deep ingest + incl. chunking config (CTX-C7). +- **Lesson 5 — Memory Operations: Extraction, Consolidation, and Self-Updating Memory** + *(notebook file L4)*: summarization vs compaction trade-off (CTX-C3, D10); workflow memory + rationale (R11); budget monitoring thresholds and the summarize/mark/expand pipeline + (CTX-B3/B4/B5, R6–R9). +- **Lesson 6 — Memory Aware Agent** *(notebook file L5)*: the agent loop and harness; memory + operations in/out of the loop (D8); partitioned context and memory-aware system prompt (D9, + R16); tool-log context offloading (D13, CTX-B6); iteration cap (CTX-B8); the end-to-end + MemGPT-paper demonstration sequence reused as AC20. +- **Lesson 7 — Conclusion:** recap of memory modeling, semantic retrieval, extraction, + consolidation, write-back. + +## Final deliverable note to the build agent + +When the build is complete and the acceptance criteria have been reported per §7, conclude your +build conversation with an infra/structure diagram of the app you built (stores, harness, loop, +and tool flows), and end with the exact phrase: + +**"This is the infra/structure diagram of this app"** --- -*Status: v1 · Course: Agent Memory: Building Memory-Aware Agents (DeepLearning.AI × -Oracle) · Learner project: [project] (default: memory-aware research-assistant CLI on the -fixture corpus) · Living document: when the building agent produces something unexpected, -add the missing constraint here and re-run the build.* +*Spec v1 · Course: Agent Memory: Building Memory-Aware Agents · Learner project: `[project]` +(default: memory-aware research assistant, D1) · Living document: when the building agent +produces something unexpected, add the missing constraint here (rule + AC + fixture as needed) +and re-run.*