LangChain.

AgentLoop integrates into existing LangChain chains with two additions: a memory-injection step before the prompt template, and a callback handler on the LLM. Together they bring relevant past corrections into the prompt and log every turn for review.

Install

shell
pip install agentloop-py agentloop-py-langchain langchain-openai
# or langchain-anthropic, langchain-google-genai, etc.,
# depending on your provider

The complete pattern

python
from agentloop import AgentLoop
from agentloop_langchain import (
    AgentLoopMemoryInjector,
    AgentLoopCallbackHandler,
)
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

loop = AgentLoop(api_key="ak_live_...")

prompt = ChatPromptTemplate.from_messages([
    ("system",
     "You are a helpful assistant.\n\n"
     "Trusted facts from past corrections:\n{agentloop_memories}"),
    ("user", "{question}"),
])

llm = ChatOpenAI(model="gpt-4o").with_config(
    callbacks=[AgentLoopCallbackHandler(loop=loop)],
)

chain = (
    AgentLoopMemoryInjector(loop=loop, query_field="question")
    | prompt
    | llm
)

chain.invoke({"question": "What's the Pix limit at night?"})

What happens at runtime

  1. Retrieve. The injector reads the user's question, calls loop.search(), and receives the most relevant past corrections (e.g. "The Pix nighttime limit is R$1,000 between 8pm and 6am").
  2. Inject. Those facts get rendered into the input under the agentloop_memories key, which the prompt template uses as a slot.
  3. Call. The LLM receives the augmented prompt and answers correctly, even if its training data alone would have led it astray.
  4. Log. The callback captures the turn in on_llm_end and posts to /v1/turns for review.
  5. Loop. Reviewers correct anything wrong in the dashboard. Corrections become memories. The next time someone asks a similar question, step 1 retrieves the corrected fact — the agent gets smarter without retraining.

Async chains

For chain.ainvoke(), use AsyncAgentLoopCallbackHandler and pass an AsyncAgentLoop from agentloop.aio to the injector. If you accidentally pass a sync client, network calls dispatch to a thread-pool executor automatically.

Per-call options

python
chain.invoke(
    {"question": "..."},
    config={"metadata": {"agentloop": {
        "user_id": "u_42",
        "session_id": "sess_xyz",
        "signals": {"thumbs_down": True},
    }}},
)

Recognized keys: user_id, search_user_id, session_id, signals, tags, metadata, skip.

user_id is log-only by default. Pass search_user_id to also scope retrieval to that user (per-user personalization). See the Patterns page for recipes.

Verifying memory retrieval

To confirm the injector is pulling memories correctly, drop a debug step between the prompt and the LLM. It prints the final assembled prompt — including any retrieved facts — before the LLM call.

python
from langchain_core.runnables import RunnableLambda

def debug_print_prompt(prompt_value):
    if hasattr(prompt_value, "messages"):
        for msg in prompt_value.messages:
            print(f"[{msg.type}] {msg.content[:300]}")
    return prompt_value

chain = (
    AgentLoopMemoryInjector(loop=loop, query_field="question")
    | prompt
    | RunnableLambda(debug_print_prompt)   # ← debug step
    | llm
)

If the system message shows (none yet) where retrieved facts should be, the search returned nothing — your query and existing memories aren't semantically similar enough. Either add a more relevant correction in the dashboard, or check that your memories have been promoted (corrections take a few seconds to become searchable after annotation).

Remember

Remove the RunnableLambda step before deploying — it's a debug aid, not part of the integration.