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": "What's the Pix limit at night?"})
What happens at runtime
-
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"). -
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 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
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 — 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).
Remove the RunnableLambda step before deploying —
it's a debug aid, not part of the integration.