GeniffyDocs
Changelog Log In Get a key

LangChain

Give a LangChain agent a memory of each of your users. Two pieces of middleware put what is known in front of the model and save each exchange when the agent is done; or a recall tool lets the agent look things up itself. Both read the user from the agent's runtime context, so one agent serves every user.

Install

Terminal
pip install langchain langchain-anthropic geniffy

Set ANTHROPIC_API_KEY, and GENIFFY_API_KEY from API keys in the Geniffy app. Any chat model LangChain supports works the same way.

Remember each user

agent.py
from dataclasses import dataclass

from geniffy import Geniffy
from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import ModelRequest, after_agent, dynamic_prompt
from langgraph.runtime import Runtime

geniffy = Geniffy()                      # reads GENIFFY_API_KEY


@dataclass
class User:
    id: str


@dynamic_prompt
def with_memory(request: ModelRequest) -> str:
    mem = geniffy.space(f"user_{request.runtime.context.id}")
    asked = next(m for m in reversed(request.messages) if m.type == "human")
    context = mem.context(asked.text)    # what is known that bears on the message
    return f"You are a helpful assistant.\n\n<memory>\n{context}\n</memory>"


@after_agent
def save_exchange(state: AgentState, runtime: Runtime[User]) -> None:
    asked = next(m for m in reversed(state["messages"]) if m.type == "human")
    answer = state["messages"][-1]
    geniffy.space(f"user_{runtime.context.id}").memories.add(messages=[
        {"role": "user", "content": asked.text},
        {"role": "assistant", "content": answer.text},
    ])


agent = create_agent(
    "anthropic:claude-opus-5-5",
    middleware=[with_memory, save_exchange],
    context_schema=User,
)

Pass the user with each call, from your own sign-in:

result = agent.invoke(
    {"messages": [{"role": "user", "content": "Who signs the Lumen renewal?"}]},
    context=User(id=user.id),
)
print(result["messages"][-1].text)

with_memory runs before each call to the model, so the model sees what is known about this user, each line with where it came from. When nothing is known, the block says so in one sentence, so the model says it doesn't know instead of guessing. save_exchange runs once, when the agent has its answer.

Let the agent look things up

To let the agent decide when to look something up, give it a recall tool. The tool reads the user from the same runtime context, so the model never sees or chooses whose memory it reads.

agent.py
from dataclasses import dataclass

from geniffy import Geniffy
from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import after_agent
from langchain.tools import ToolRuntime, tool
from langgraph.runtime import Runtime

geniffy = Geniffy()
INSTRUCTIONS = ("You are a helpful assistant. Use recall before answering anything "
                "that depends on what the user said before.")


@dataclass
class User:
    id: str


@tool
def recall(query: str, runtime: ToolRuntime[User]) -> str:
    """Look up what is known about the user, with where it came from."""
    return geniffy.space(f"user_{runtime.context.id}").context(query)


@after_agent
def save_exchange(state: AgentState, runtime: Runtime[User]) -> None:
    asked = next(m for m in reversed(state["messages"]) if m.type == "human")
    answer = state["messages"][-1]
    geniffy.space(f"user_{runtime.context.id}").memories.add(messages=[
        {"role": "user", "content": asked.text},
        {"role": "assistant", "content": answer.text},
    ])


agent = create_agent(
    "anthropic:claude-opus-5-5",
    tools=[recall],
    system_prompt=INSTRUCTIONS,
    middleware=[save_exchange],
    context_schema=User,
)

The tool answers with context(), so the agent reads the same lines, with their sources, that the system prompt would hold, and the same sentence when nothing is known.

Memory across conversations

A LangChain checkpointer keeps one conversation going. Geniffy is what the agent knows about the user across all of them: what they said last month, in another app, or in a file you added. Use both.

Last updated October 5, 2026