Agent patterns

When to reach for walkindb, and concrete recipes for the workloads it's good at.

When to use walkindb

  • Agent scratchpad memory. A planning agent needs to remember the tool calls it already made this run, the partial results, the decisions, the open questions. SQL tables with a few joins are a better data structure for this than rolling context windows.
  • Tool-use state across a multi-step task. The agent browsed three URLs, called a shell tool twice, and got partial results from each. Store them in tool_calls, tool_results, and artifacts tables. Query them as context for the next step.
  • RAG chunk staging. You retrieved 40 chunks from your vector store for a single user question. Load them into chunks(id, source, score, content), run SQL to dedupe / re-rank / filter by source, then return the top-10 to the model.
  • One-shot analyses. The user dropped a CSV into the agent context. Load it into a walk-in, run GROUP BY / WINDOW queries, return a natural-language summary.
  • Throwaway notebooks, teaching, demos. You want to show a SQL technique to a student. They don't have SQLite installed. curl https://api.walkindb.com/sql ... works from any shell.

When NOT to use walkindb

  • Anything durable. walk-ins are deleted after ~10 minutes. There is no paid tier.
  • PII or regulated data. See the AUP.
  • Anything you'd put in a transaction log. No WAL replication, no backups, no point-in-time recovery, no warnings before deletion.
  • Very large datasets. 10 MB per walk-in. If you need 100 MB, reach for Turso, Neon, Postgres, or self-host walkindb with the cap raised.
  • Cross-session shared state between different users. Every walk-in is strictly isolated; there's no concept of "the users table shared across all sessions". Use a real database.

Recipe: agent scratchpad memory

A planning agent that wants structured memory across tool calls in a single run. Use walkindb from the agent's Python tool runtime.

from walkindb import Client

db = Client()

# Schema for the agent's working memory.
db.execute("""
    CREATE TABLE plans(
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      goal TEXT NOT NULL,
      created_at INTEGER DEFAULT (unixepoch()),
      status TEXT DEFAULT 'active'
    );
    CREATE TABLE steps(
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      plan_id INTEGER REFERENCES plans(id),
      kind TEXT,           -- 'thought' | 'tool_call' | 'observation'
      content TEXT,
      created_at INTEGER DEFAULT (unixepoch())
    );
    CREATE INDEX idx_steps_plan ON steps(plan_id, created_at);
""")

# The agent records each step as it goes.
def remember(plan_id: int, kind: str, content: str):
    db.execute(f"INSERT INTO steps(plan_id, kind, content) VALUES({plan_id}, '{kind}', '{content.replace(chr(39), chr(39)*2)}')")

# Later, the agent asks itself: "what have I done so far on this plan?"
result = db.execute("""
    SELECT kind, content FROM steps
    WHERE plan_id = 1
    ORDER BY created_at
""")
for kind, content in result.rows:
    print(f"[{kind}] {content}")

Recipe: RAG chunk staging and re-ranking

You pulled 40 candidate chunks from your vector store. Load them into a walk-in, run SQL to filter/dedupe/rerank, return the top 10.

from walkindb import Client
import json

db = Client()
db.execute("""
    CREATE TABLE chunks(
      id INTEGER PRIMARY KEY,
      source TEXT,
      score REAL,
      content TEXT
    );
    CREATE INDEX idx_chunks_src ON chunks(source);
    CREATE INDEX idx_chunks_score ON chunks(score DESC);
""")

# Bulk-insert 40 chunks in one statement (under the 8 KB body cap typically
# fits ~20-30 short chunks per call; split if you have more).
for c in candidate_chunks:
    body = c['content'].replace("'", "''")
    db.execute(f"INSERT INTO chunks(id, source, score, content) VALUES({c['id']}, '{c['source']}', {c['score']}, '{body}')")

# Dedupe and rerank: prefer high-score chunks, cap per-source to 3 chunks,
# return top 10 overall.
result = db.execute("""
    WITH ranked AS (
      SELECT id, source, score, content,
             ROW_NUMBER() OVER (PARTITION BY source ORDER BY score DESC) AS rn
      FROM chunks
    )
    SELECT id, source, score, content
    FROM ranked
    WHERE rn <= 3
    ORDER BY score DESC
    LIMIT 10
""")

# Feed these back to the model as context.
top_chunks = [
    {"source": r[1], "score": r[2], "content": r[3]}
    for r in result.rows
]

This is a classic case where SQL is the right tool: window functions + filtering are much cleaner in 8 lines of SQL than in 40 lines of Python.

Recipe: CSV analysis one-shot

from walkindb import Client
import csv, io

db = Client()
db.execute("CREATE TABLE t(date TEXT, category TEXT, amount REAL)")

# Load rows. For a 100-row CSV this is fine; for larger data, split into
# batches that fit under the 8 KB request body cap.
rows = list(csv.reader(io.StringIO(user_csv)))[1:]  # skip header
for date, category, amount in rows:
    db.execute(f"INSERT INTO t VALUES('{date}', '{category}', {amount})")

# Analyses
monthly = db.execute("""
    SELECT strftime('%Y-%m', date) AS month,
           category,
           SUM(amount) AS total
    FROM t
    GROUP BY month, category
    ORDER BY month DESC, total DESC
""")

biggest = db.execute("""
    SELECT category, SUM(amount) AS total
    FROM t
    GROUP BY category
    ORDER BY total DESC
    LIMIT 3
""")

# Feed monthly.rows and biggest.rows back to the model for summary.

Recipe: MCP-style tool for an agent runtime

If you're writing an agent runtime (LangChain, Semantic Kernel, your own loop, etc.), here's the minimal wrapper you'd expose as a tool:

from walkindb import Client, WalkinDBError

# Create one Client per agent run (not per tool call).
_db = Client()

def sql_tool(sql: str) -> dict:
    """Run SQL against a private, ephemeral SQLite database.

    The database is automatically created on the first call and deleted after
    ~10 minutes. It is isolated from every other agent's database.
    Use for: planning memory, RAG chunk staging, CSV analysis, scratch tables.
    Do NOT use for: PII, long-term storage, anything you can't afford to lose.
    """
    try:
        r = _db.execute(sql)
        return {
            "columns": r.columns,
            "rows": r.rows,
            "rows_affected": r.rows_affected,
            "truncated": r.truncated,
        }
    except WalkinDBError as e:
        return {"error": e.error, "status": e.status}

Register this function as a tool in your agent framework. The docstring is the prompt the model sees — keep it informative about what's good and what's not.

Thinking about quotas

walkindb has generous-but-real limits. Two patterns to avoid:

Don't spin up a new walk-in per tool call

The Client instance reuses the session across calls. Make one Client() per agent run, not per tool call. Otherwise you'll burn through the 10 new-instance-creations/min/IP budget and start seeing 429s.

Don't put 50 separate INSERTs in a loop when you can use one multi-row INSERT

# Less good: 50 calls, 50 round trips
for row in rows:
    db.execute(f"INSERT INTO t VALUES({row})")

# Better: one call, one round trip, as long as the SQL fits in 8 KB
values = ", ".join(f"({row})" for row in rows)
db.execute(f"INSERT INTO t VALUES {values}")

Also see