03
Chapter 03 · Memory & Tools

How agents remember
and act on the world

agentic-ai / memory-tools 4 modules  ·  ~50 min read  ·  Vol. I · 2026

Memory and tools are what elevate a language model from a text generator into an agent that can reason across time, retrieve knowledge on demand, and take actions in external systems. This chapter covers every layer of the memory stack — from ephemeral in-context buffers to persistent vector databases — and every dimension of tool use, from clean function-calling schemas to browser automation and MCP integration.

03.1 Memory Architectures In-context, external, episodic, semantic, and procedural — choosing the right layer.
03.2 RAG & Retrieval Naive, advanced, and agentic RAG patterns. Chunking, re-ranking, hybrid search.
03.3 Tool Use Tool schemas, parallel calls, error handling, selection strategies, and MCP.
03.4 Computer Use Web scraping, UI automation, screenshot grounding, and desktop agent patterns.
03.1
Module
Memory Architectures

Five memory types and when to use each

An LLM with no memory is stateless — every call starts from scratch. Memory architectures give agents the ability to accumulate knowledge, reference past actions, and personalise behaviour over time. The right architecture depends entirely on what the agent needs to remember, for how long, and at what retrieval cost.

Live Foundational

The Memory Stack

Agent memory has five distinct layers, each with different latency, capacity, and persistence characteristics. Most production agents use two or three layers in combination. In-context memory is the simplest: everything in the current prompt window. Fast, zero infrastructure, but bounded by context length and lost at conversation end. External memory (vector databases, key-value stores) persists across sessions and scales to millions of records, but requires retrieval — a separate step that introduces latency and retrieval errors.

Episodic memory stores specific past interactions — "last Tuesday I helped this user plan a trip to Tokyo." Semantic memory stores general world knowledge, facts, and concepts the agent has learned. Procedural memory encodes how to do things — workflows, templates, and skill programs the agent can load and execute.

Memory Stack — Capacity vs. Retrieval Speed
In-Context
Fastest · Smallest · Non-persistent
Working Memory (scratchpad)
Fast · Session-scoped · Structured
Episodic (vector DB)
~100ms · Large · Persistent
Semantic (knowledge graph)
~200ms · Very large · Persistent
Procedural (skill store)
Load-time · Unlimited · Persistent

In-Context Memory Management

Most agents start with in-context memory alone. The challenge is managing a growing context window across many turns. Three strategies keep it under control. Sliding window: keep only the last N messages, dropping the oldest. Simple but loses important early context. Summarisation: periodically compress older turns into a running summary appended to the system prompt. Preserves key facts while reducing token count. Selective retention: use an LLM or rule-based filter to decide which messages are worth keeping verbatim — tool results containing data, user-stated preferences, key decisions — and discard the rest.

Summarisation is the most robust strategy for long agentic tasks. Trigger it when the context reaches 70% of the model's limit, not 100% — leaving room for the summarisation call itself and the next few turns.

Practical rule: Always keep the user's original goal and any explicit constraints in the system prompt, never in the sliding message window. These must never be dropped by a summarisation step.

External Memory with Vector Databases

Vector databases (Pinecone, Weaviate, Chroma, pgvector) store text as dense embeddings and retrieve the most semantically similar records to a query. The agent calls a retrieval tool, gets back k relevant chunks, and injects them into context before reasoning. This is the foundation of every RAG system (covered in depth in 03.2).

The key design decisions for external memory: embedding model (must match at write and read time — never mix models), chunk size (too small loses context, too large dilutes relevance), metadata filtering (always store source, date, and any domain-specific attributes alongside the embedding), and freshness strategy (stale memories can be more harmful than no memory).

Procedural Memory — Skill Programs

Procedural memory is underused in production agents. The idea: store reusable "programs" — structured sequences of steps, prompt templates, or even code — that the agent can retrieve and execute. When the agent learns that a particular approach works well for a class of tasks, it stores that approach as a skill. On future similar tasks, it retrieves and adapts the skill rather than re-deriving the solution from scratch.

This dramatically improves consistency and efficiency for repetitive tasks. Implement it as a simple key-value store keyed by task type, with the agent using a classifier to match incoming tasks to stored skills.

Python · Summarisation-based context management
def manage_context(messages, model_limit: int, threshold: float = 0.70):
    token_count = count_tokens(messages)
    
    if token_count < model_limit * threshold:
        return messages   # Still within budget

    # Split: always keep system prompt + recent 6 turns
    system   = messages[0]
    to_compress = messages[1:-6]
    recent   = messages[-6:]

    summary = llm_summarise(
        to_compress,
        prompt="Summarise the key decisions, facts, and "
               "tool results from this conversation history."
    )

    summary_msg = {
        "role": "system",
        "content": f"[Context summary]\n{summary}"
    }
    return [system, summary_msg] + recent
Key takeaways — 03.1
Trigger summarisation at 70% of context limit, not 100%. Leave room for the summary call itself.
Never drop the goal. Keep the user's original objective in the system prompt, always.
Match embedding models at write and read time. Mixing models silently breaks retrieval.
Procedural memory is underused. Storing successful approaches as skills improves consistency.

“Memory is not a feature you add later. It is a structural decision that shapes every part of the agent's architecture — retrieval latency, storage costs, and the coherence of multi-session behaviour all follow from this one choice.”

— agentic-ai / memory-tools · 03.1
03.2
Module
RAG & Retrieval

From naive retrieval to agentic RAG pipelines

Retrieval-Augmented Generation allows agents to answer questions over large knowledge bases without fine-tuning. But naive RAG — embed, store, retrieve, generate — breaks down quickly in production. This module traces the evolution from naive to advanced to agentic RAG, covering chunking, re-ranking, hybrid search, and self-correcting retrieval loops.

Live Intermediate

Naive RAG and Its Failures

Naive RAG has four steps: chunk your documents, embed each chunk, store in a vector DB, then at query time embed the question and retrieve the top-k most similar chunks. Simple to implement, but it fails in predictable ways. Chunking mismatch: the answer spans two chunks that are retrieved separately and lose coherence. Embedding gap: the question and the relevant passage are semantically similar but not close in embedding space because they use different vocabulary. Retrieval noise: top-k includes irrelevant chunks that distract or mislead the LLM. Context overflow: too many chunks inflate the prompt and bury the relevant signal.

Chunking Strategies

Chunk size is the most impactful RAG tuning parameter. Fixed-size character splits are the naive baseline — fast but context-unaware. Sentence-aware splitting respects sentence boundaries and dramatically improves coherence. Semantic chunking groups sentences by embedding similarity, keeping related ideas together even if they cross paragraph breaks. Document-structure chunking uses markdown headers, HTML tags, or PDF section boundaries to create chunks that match the document's own logical divisions.

For most use cases: target 256–512 tokens per chunk, add 20–50 token overlap between adjacent chunks, and always store the chunk's source, section title, and position metadata alongside the embedding. The metadata is essential for filtering and for generating citations.

Advanced RAG — Re-ranking and Hybrid Search

Advanced RAG improves retrieval quality with two key additions. Re-ranking: retrieve a larger candidate set (top-20 or top-50) using vector similarity, then run a cross-encoder re-ranker (e.g. Cohere Rerank, BGE-Reranker) that scores each candidate against the full query. Cross-encoders are slower than bi-encoders but significantly more accurate. The top-3 to top-5 after re-ranking are far more relevant than the top-3 from vector search alone.

Hybrid search combines dense vector retrieval with sparse keyword retrieval (BM25). Dense retrieval excels at semantic similarity; sparse retrieval excels at exact keyword matches. Combining them with Reciprocal Rank Fusion (RRF) consistently outperforms either alone, especially for technical queries with specific terms, product codes, or proper nouns that embeddings handle poorly.

Advanced RAG Pipeline
User Query
↓ query expansion (optional)
Dense retrieval (top-30)
+
Sparse BM25 (top-30)
↓ Reciprocal Rank Fusion
Merged candidate set (top-30)
↓ Cross-encoder re-ranker
Top-5 most relevant chunks
↓ inject into context
LLM generates grounded answer

Agentic RAG — Self-Correcting Retrieval

Agentic RAG gives the LLM agency over the retrieval process itself. Rather than a single fixed retrieval step, the agent can: reformulate the query if the first retrieval returns poor results, issue multiple sub-queries to answer complex multi-part questions, verify retrieved facts against a secondary source, and decide when retrieval is not needed (the answer is already in context or can be reasoned directly).

This self-correction loop is the key differentiator from naive and advanced RAG. Implement it by giving the agent a retrieve(query) tool and a verify(claim, source) tool, and instructing it in the system prompt to assess retrieval quality before using the results. Agents that evaluate their own retrieval produce significantly fewer hallucinations on knowledge-intensive tasks.

Python · Hybrid search with RRF fusion
def hybrid_search(query: str, k: int = 5):
    # Dense retrieval — semantic similarity
    q_emb   = embed(query)
    dense   = vector_db.search(q_emb, top_k=30)

    # Sparse retrieval — BM25 keyword match
    sparse  = bm25_index.search(query, top_k=30)

    # Reciprocal Rank Fusion
    scores  = {}
    for rank, doc in enumerate(dense):
        scores[doc.id] = scores.get(doc.id, 0) + 1 / (rank + 60)
    for rank, doc in enumerate(sparse):
        scores[doc.id] = scores.get(doc.id, 0) + 1 / (rank + 60)

    # Re-rank top-30 with cross-encoder, return top-k
    candidates = top_docs_by_score(scores, n=30)
    return reranker.rerank(query, candidates)[:k]

RAG Evaluation

RAG systems must be evaluated on two separate dimensions: retrieval quality (are the right chunks retrieved? — measured by recall@k and MRR) and generation quality (does the LLM faithfully use the retrieved context? — measured by faithfulness and answer relevance using frameworks like RAGAS). Never evaluate only the final answer; a correct answer generated from wrong chunks is a lucky failure waiting to recur.

Key takeaways — 03.2
Hybrid search + re-ranking consistently beats vector-only retrieval. Always combine both.
Store metadata with every chunk. Source, date, and section are essential for filtering and citation.
Evaluate retrieval and generation separately. A correct answer from wrong chunks is a masked failure.
Agentic RAG beats fixed pipelines on complex multi-hop queries. Give the agent retrieval agency.

“Retrieval quality is the ceiling for RAG quality. A perfect LLM cannot compensate for chunks that don't contain the answer. Invest in retrieval before prompting.”

— agentic-ai / memory-tools · 03.2
03.3
Module
Tool Use & Function Calling

Schemas, parallel calls, error handling, and MCP integration

Tools are how agents act on the world. A well-designed tool is easy for an LLM to select, invoke correctly, and interpret the result of. A poorly designed one is a source of constant hallucinated arguments, confusing outputs, and agent loops that spin without making progress. This module covers every layer of tool design and integration.

Live Intermediate

Tool Schema Design

The tool schema is the specification the LLM reads to understand what a tool does, what arguments it takes, and what it returns. Good schemas have four properties: precise namessearch_web(query) not do_search(q); rich descriptions — explain when to use the tool, not just what it does; typed parameters with constraints and examples; and clear return descriptions — what shape of data comes back and what each field means.

The description field is the most important and most neglected. The LLM reads descriptions to decide which tool to call. Ambiguous or minimal descriptions cause the model to call the wrong tool or to call no tool when it should. Write descriptions as if explaining to a careful junior engineer who has never seen your codebase.

JSON · Well-designed tool schema example
{
  "name": "search_knowledge_base",
  "description": "Search the internal knowledge base for relevant
    documents. Use this when the user asks a question that
    requires product documentation, policies, or historical
    records. Do NOT use for real-time data or external web
    content — use search_web for those queries.",
  "parameters": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "Natural language search query.
          Be specific. Example: 'refund policy for
          digital purchases' not 'refund'"
      },
      "top_k": {
        "type": "integer",
        "description": "Number of results (1–10). Default 5.",
        "default": 5, "minimum": 1, "maximum": 10
      }
    },
    "required": ["query"]
  }
}

Parallel Tool Calls

Modern LLM APIs (Anthropic, OpenAI) support parallel tool calls — the model can request multiple tool invocations in a single turn, and the agent runs them concurrently before returning results. This can cut multi-tool latency by 60–80% when tools are independent.

Parallel calls introduce a coordination challenge: the agent must wait for all concurrent calls to complete before reasoning over their results. Use asyncio.gather or equivalent. For tools with interdependencies — where the output of tool A is the input to tool B — sequential execution is still required. Represent these as separate turns, not parallel calls.

Watch out: Parallel tool calls to rate-limited APIs can trigger throttling. Implement a concurrency limit (e.g. max 3 parallel calls) and exponential backoff on 429 responses. Never fire unlimited parallel requests.

Tool Selection Strategy

When an agent has many tools, the selection problem becomes significant — a long tool list bloats the prompt, and the model may pick an incorrect tool. Three strategies address this. Tool filtering: at each turn, use a cheap classifier to narrow the available tool set to the most relevant subset (5–10 tools) before calling the main LLM. Tool routing: a small router model decides which tool to call, then a specialised prompt handles that tool's arguments. Dynamic tool loading: tools are loaded from a registry on demand, keeping the active tool set small.

MCP — Model Context Protocol

MCP is an open standard for connecting LLM agents to external tools and data sources through a standardised server interface. Rather than hand-coding every API integration, an MCP-compatible agent can discover and use any MCP server's tools automatically. The agent connects to an MCP server, queries its available tools, and calls them using the standard MCP protocol — the integration code is written once, on the server side.

MCP is particularly valuable in multi-agent systems where different agents need access to different tool sets — each agent connects to the MCP servers relevant to its role, without the orchestrator needing to know the details of each integration. The ecosystem of MCP servers is growing rapidly, covering databases, APIs, file systems, browsers, and development tools.

Python · Parallel tool execution with asyncio
import asyncio

async def execute_parallel_tools(tool_calls: list):
    # Cap concurrency to avoid rate limits
    semaphore = asyncio.Semaphore(3)

    async def run_one(call):
        async with semaphore:
            try:
                result = await execute_tool(call.name, call.args)
                return {"id": call.id, "result": result, "ok": True}
            except Exception as e:
                return {"id": call.id, "error": str(e), "ok": False}

    return await asyncio.gather(*[run_one(c) for c in tool_calls])

Error Handling for Tools

Tool errors should always be returned to the agent as structured observations, never raised as exceptions that terminate the loop. The structured error should include: the error type, a human-readable message, and — critically — a suggested recovery action. An error message that says "404: resource not found — try searching by name instead of ID" gives the agent a path forward. One that says "ERROR" does not.

Error typeRecommended agent response
Invalid argumentsRetry with corrected args. Include the schema in the error.
Rate limit (429)Wait and retry with exponential backoff. Inform the agent of the delay.
Not found (404)Try alternative query/identifier. Inform agent the resource doesn't exist.
Auth failure (401/403)Do not retry. Escalate to human or fail gracefully.
TimeoutRetry once. If it persists, use a fallback tool or skip.
Key takeaways — 03.3
The description field is critical. The LLM uses it to decide which tool to call. Write it carefully.
Cap parallel concurrency. Unlimited parallel tool calls will trigger rate limits in production.
Return structured errors with recovery hints. Never let a tool error silently terminate the loop.
MCP decouples tool integration from agent logic. Write the server once, use from any agent.

“A tool the agent can't reliably invoke is worse than no tool at all. It consumes a step, returns an error, and leaves the agent no closer to its goal. Tool design is agent design.”

— agentic-ai / memory-tools · 03.3
03.4
Module
Browser & Computer Use

Web scraping, UI automation, and screenshot grounding

When APIs don't exist, agents must interact with software the way humans do — through the UI. Browser and computer use agents can navigate web pages, fill forms, click buttons, read screenshots, and operate desktop applications. This capability unlocks vast automation potential but introduces unique reliability and safety challenges.

Live Advanced

Two Approaches: DOM vs. Screenshot

Browser agents work in one of two modes. DOM-based agents interact with the page's HTML structure — clicking elements by selector, reading text from DOM nodes, filling input fields programmatically. This is reliable and fast but breaks when pages use heavy JavaScript rendering, shadow DOM, or dynamically generated element IDs. Tools: Playwright, Puppeteer, Selenium.

Vision-based (screenshot grounding) agents take a screenshot of the screen, send it to a vision-capable LLM, and receive back click coordinates or action descriptions. This works on any UI — web, desktop, or mobile — but is slower, more expensive, and prone to errors when UI elements are small or ambiguous. Tools: Claude Computer Use API, OpenAI CUA, Anthropic's agent framework.

In practice, the best browser agents combine both: DOM-based interaction for structured pages and form inputs, screenshot grounding as a fallback for dynamic or unusual UIs.

Browser Agent — Action Loop
Goal: "Book a flight LHR→BOM on May 5th"
Navigate to booking site
[DOM: goto(url)]
Take screenshot
[Vision: identify form fields]
Fill origin, destination, date
[DOM: fill(selector, value)]
Screenshot → identify search button
[Vision grounding]
Click search → wait for results
Screenshot → extract flight options → reason → select
↓ HITL approval gate before booking
Booking confirmed

Web Scraping Patterns

For data extraction tasks (as opposed to UI automation), structured scraping is more reliable than general browser agents. Give the agent a fetch_page(url) tool that returns the page as clean markdown (after stripping navigation, ads, and boilerplate via a library like Trafilatura or Readability). The LLM then extracts structured data from the markdown — far more reliable than extracting from raw HTML or screenshots.

For dynamic pages (SPAs, infinite scroll, login-required), use Playwright with page.wait_for_selector() to ensure content is loaded before extraction. Implement rate limiting and respect robots.txt — both for legal compliance and to avoid being blocked.

Screenshot Grounding Techniques

Vision-based agents ground their actions on screenshots by identifying the pixel coordinates of UI elements they want to interact with. Reliability improves significantly with three techniques: Set-of-Marks (SoM) prompting — overlay numbered labels on each interactive element before sending to the LLM, so the model identifies an element by its label number rather than estimating raw coordinates. Zoom and crop — crop to the relevant region of the screen before grounding, reducing the signal-to-noise ratio. Accessibility tree augmentation — combine the screenshot with the page's accessibility tree text, giving the LLM both visual and structural information.

Critical safety rule: Never allow a computer use agent to take irreversible actions — purchases, form submissions, file deletions, sent emails — without an explicit human-in-the-loop approval gate. Screenshot agents in particular can misidentify UI elements and click the wrong button. Gate all irreversible actions regardless of confidence.

Reliability Engineering

Browser agents fail more often than API-based agents. Pages change, CAPTCHAs appear, network requests time out, and UI elements shift between deployments. Build resilience in from the start: explicit waits over fixed sleeps; retry with backoff for transient failures; state verification after every action (take a screenshot, confirm the expected change occurred); and fallback selectors when a primary CSS selector fails.

Track action success rate per page. A browser agent with 90% per-action reliability on a 10-step task has only a 35% end-to-end success rate. Each reliability improvement at the action level has compounding effect on task-level success.

Python · Playwright page fetch with clean markdown
from playwright.async_api import async_playwright
import trafilatura

async def fetch_page_markdown(url: str) -> str:
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page    = await browser.new_page()

        await page.goto(url, wait_until="networkidle")
        await page.wait_for_timeout(1000)   # JS settle

        html     = await page.content()
        markdown = trafilatura.extract(
            html, output_format="markdown",
            include_tables=True, no_fallback=False
        )
        await browser.close()
        return markdown or ""
Key takeaways — 03.4
Combine DOM + vision. DOM for structured pages, screenshot grounding as fallback.
Use Set-of-Marks prompting to improve vision grounding accuracy on complex UIs.
Gate all irreversible actions. Purchase, submit, delete — always require human approval.
Reliability compounds. Improving per-action accuracy by 5% doubles end-to-end success on long tasks.
Previous chapter 02 · Agent Design Patterns
Next chapter 04 · Safety & Evaluation