What DSPy already keeps

DSPy programs are typically stateless between calls; history is whatever you pass in the signature.

Where Auld fits

Either give dspy.ReAct a recall tool, or make recall a fixed first step whose output feeds a context field. The fixed step is easier to optimize because every example sees the same inputs.

ConcernDSPyAuld
Current conversationYesNot needed
Facts across sessions and channelsYour codeYes
Which fact is current after a changeYour codeYes, with history
What was true on a dateYour codeYes (asOf)
Who may see a memoryYour codeIdentities and audiences
Source for each statementYour codeReturned with every memory

Implementation sequence

  1. Write a recall function

    Return plain text memories for a question.

  2. Choose the pattern

    ReAct tool for open tasks; fixed step for predictable pipelines.

  3. Add a context field

    Include memories in the signature so optimizers can learn how to use them.

  4. Evaluate with memory on and off

    Measure the effect on your own metric.

Example

memory_client.py — shared helper
import os, requests

AULD = os.environ["AULD_API_BASE"]          # e.g. https://YOUR-AULD-HOST
HEADERS = {"Authorization": f"Bearer {os.environ['AULD_API_KEY']}"}

def auld_post(path: str, body: dict) -> dict:
    r = requests.post(f"{AULD}/v1/{path}", json=body, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()

def remember(text: str, user_id: str, source_id: str, occurred_at: str) -> str:
    """Store a dated record for one user. Returns the job id."""
    return auld_post("records", {"sourceId": source_id, "text": text,
                                 "occurredAt": occurred_at, "author": user_id,
                                 "visibility": f"user:{user_id}"})["jobId"]

def recall(query: str, user_id: str, max_tokens: int = 2000) -> str:
    """Current memories with sources, formatted for a prompt."""
    res = auld_post("recall", {"query": query, "viewer": user_id, "maxTokens": max_tokens})
    return "\n".join(f"- {m['text']} ({m['sources'][0]['title']}, {m['sources'][0]['occurredAt'][:10]})"
                     for m in res["memories"]) or "No relevant memory."
DSPy wiring
import dspy
from memory_client import recall

class AnswerWithMemory(dspy.Signature):
    """Answer the user using the remembered context when it is relevant."""
    context: str = dspy.InputField(desc="memories with dates and sources")
    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

class MemoryQA(dspy.Module):
    def __init__(self):
        self.answer = dspy.ChainOfThought(AnswerWithMemory)
    def forward(self, question: str, user_id: str):
        return self.answer(context=recall(question, user_id), question=question)

# Or as a tool: dspy.ReAct("question -> answer", tools=[lambda q: recall(q, user_id)])

Illustrative example · standard HTTP · check the framework’s current docs for exact signatures

Or connect the MCP server

If your DSPy setup can use MCP tools, point it at the Auld MCP endpoint instead of writing wrappers. The model gets remember, recall, forget and memory_status. Use wrappers when you need to inject the user id yourself or post-process results; use MCP for speed of setup.

Connect an MCP client →

Custom integration recipe using standard HTTP/MCP tools

  • This recipe wires DSPy’s own tool interface to the Auld REST API or MCP server; there is nothing Auld-specific to install.
  • Bind the viewer id from your authentication layer, not from model output.
  • Ingestion is asynchronous — poll the job before depending on a just-sent fact in tests.
  • Recall returns up to your plan’s token limit (8,000 on Pay as you go and Flex).

Frequently asked questions

Does recall break DSPy optimization?

Not if you cache recall results per example during optimization, so runs are reproducible.

Which pattern scores better?

It depends on your task; test both against your metric.