LangChain
Auto-instrument LangChain chains and agents.
Risicare automatically instruments LangChain for comprehensive chain and agent observability.
Installation
pip install 'risicare[langchain]'
# or
pip install risicare langchain langchain-openaiVersion Compatibility
Requires langchain-core >= 0.2.0.
The extra installs langchain-core only. The samples on this page also use langchain-openai, and the agent sample uses langchain: the second install line installs both.
Auto-Instrumentation
import risicare
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
risicare.init()
llm = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("user", "{input}")
])
chain = prompt | llm
# Automatically traced
response = chain.invoke({"input": "Hello!"})What's Captured
| Feature | Description |
|---|---|
| Chain Execution | Full chain invoke/ainvoke calls |
| LLM Calls | Each chat model or LLM run, as langchain.chat/{model} or langchain.llm/{model}. The provider call underneath it gets no second span |
| Prompt Templates | Template formatting |
| Output Parsers | Parsing operations |
| Tool Calls | Tool/function executions |
| Retriever Calls | RAG retrieval operations (retriever.document_count; retriever.query when content capture is on) |
Span Hierarchy
langchain.chain/{class}
├── langchain.chat/{model}
├── langchain.tool/{name}
└── langchain.retriever/{name}
With the classic AgentExecutor, the tool choice is a langchain.agent_action/{tool} span — see Agents.
Provider Deduplication
Provider Deduplication
The provider call that a LangChain model makes for its own run gets no span of its own, so its tokens and cost count once. A provider call that you make yourself, for example with the openai client inside a chain step, keeps its provider span. That span is a trace of its own, unless a risicare.trace() or a graph span is active: then it is a child of that span.
Agents
A LangChain agent is traced as chain, chat-model and tool spans:
from langchain.agents import create_agent
from langchain_core.tools import tool
@tool
def search(query: str) -> str:
"""Search for information."""
return f"Results for: {query}"
agent = create_agent(llm, tools=[search], system_prompt="You are a helpful assistant.")
# The agent run is traced
result = agent.invoke({"messages": [{"role": "user", "content": "Search for AI news"}]})
print(result["messages"][-1].content)This sample needs langchain 1.0 or later and langchain-openai (the second install line above).
The classic agent API (create_react_agent and AgentExecutor) is not in langchain.agents in LangChain 1.0. Import it from langchain_classic.agents (package langchain-classic). create_react_agent needs a ReAct prompt that has {tools}, {tool_names} and {agent_scratchpad}.
A run of this sample sends one trace: a langgraph.graph/LangGraph span at the root, langchain.chain spans for the graph and its model and tools steps, a langchain.chat/{model} span for each model call, and a langchain.tool/search span.
With the classic AgentExecutor (Python 0.5.1 and later), a run that calls a tool is one trace. The langchain.chain/AgentExecutor span is the root, and the model calls, the tool calls and the langchain.agent_action/{tool} spans are below it. An agent_action span is a point in time: it starts and ends when the agent chooses the tool, so its duration is near zero. Measured with Python 0.6.0 and langchain-classic 1.0.8: two model calls, one tool call, one trace. In Python 0.5.0, such a run had no AgentExecutor span and arrived as one trace for each tool call plus one, and only the last agent_action span was sent.
LCEL Chains
LangChain Expression Language (LCEL) chains are automatically traced:
from langchain_core.output_parsers import StrOutputParser
chain = prompt | llm | StrOutputParser()
# Each component is a span
result = chain.invoke({"input": "Hello"})Streaming
async for chunk in chain.astream({"input": "Write a story"}):
print(chunk, end="")RAG Chains
Retrieval chains capture document retrieval:
from langchain_core.runnables import RunnablePassthrough
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# Retriever calls are captured with documents
result = rag_chain.invoke("What is Risicare?")JavaScript / TypeScript
The JS SDK provides a RisicareCallbackHandler for LangChain.js:
import { RisicareCallbackHandler } from 'risicare/langchain';
const handler = new RisicareCallbackHandler();
// Use withSuppression() to prevent duplicate spans when using patchOpenAI()
const result = await handler.withSuppression(() =>
chain.invoke(input, { callbacks: [handler] })
);Deduplication
If you use both patchOpenAI() and RisicareCallbackHandler, wrap your chain calls with handler.withSuppression() to prevent duplicate LLM spans.