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
pip install agentloop-py agentloop-py-langchain langchain-openai # or langchain-anthropic, langchain-google-genai, etc., # depending on your provider
The complete pattern
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": "Can enterprise customers request a refund after 60 days?
What happens at runtime
-
Retrieve. The injector reads the user's question,
calls
loop.search(), and receives the most relevant memories (e.g. "Enterprise refunds must be requested within 30 days of billing"). -
Inject. Those facts get rendered into the input
under the
agentloop_memorieskey, which the prompt template uses as a slot. - Call. The LLM receives the augmented prompt and answers correctly, even if its training data alone would have led it astray.
-
Log. The callback captures the turn in
on_llm_endand posts to/v1/turnsfor review. - Loop. Reviewers add annotations in the dashboard. Annotations create memories. The next time someone asks a similar question, step 1 retrieves the relevant memory — the agent improves 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
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.
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.Add a more relevant annotation in the dashboard, confirm the memory
is active, or try a question that is closer to the scenario you want AgentLoop to retrieve.
Remove the RunnableLambda step before deploying —
it's a debug aid, not part of the integration.