How do I trace an agent built with LangChain and deployed to a Kubernetes deployment?
Rius traces a LangChain agent in a Kubernetes deployment when you call init once at process start, before you build your chains, and give the agent a service name. Every pod then sends its LangChain chains, tools, retrievers and model calls as nested traces. In both languages, call shutdown when the pod receives SIGTERM, so the last spans leave before Kubernetes kills the container.
How it works
- One init per process. The LangChain integration hooks LangChain’s callback system, so anything that runs through it is traced: LCEL chains, agents and LangGraph nodes. Each pod is a long-running process, so you call init once at startup and the integration stays on for the pod’s lifetime.
- Spans leave in the background. Spans are queued in memory and exported in batches from a background thread, so tracing does not sit on your request path.
- The service name is the agent name.
service_name(orRIUS_SERVICE_NAME) names the agent.RIUS_AGENT_NAMEdefaults to the service name, so you only set it if you want a different grouping name. - Each pod is an instance. The heartbeat is on by default and needs credentials. Each process that calls init gets its own instance ID, so replicas of one deployment show as separate instances under one agent on the Agents view.
- Clean stop versus crash. Calling
shutdown()sends a finalstoppedping. A pod killed by an unhandled SIGTERM or a SIGKILL sends none, and its instance reportsgone.
Set it up
- Install the LangChain instrumentation next to the packages your image already has. The extra installs the instrumentation, not LangChain itself. The LangChain page covers the details.
pip install "glassflow-rius[langchain]" langchainFor TypeScript, install @arizeai/openinference-instrumentation-langchain. @langchain/core comes with a LangChain.js application.
- Call init at the top of your entrypoint, before you build or run any chain, and shut the client down on
SIGTERM. Pick a stable service name, because it becomes the agent’s name. Python’s default response toSIGTERMends the process without running its exit hook, and Rius installs no signal handlers, so handle the signal yourself. If your server already handlesSIGTERM, such as uvicorn, callclient.shutdown()in its shutdown hook instead, for example a FastAPI lifespan. Start the process in exec form (CMD ["python", "app.py"]) so it receives the signal as PID 1.
import signal
import sys
import rius
client = rius.init(service_name="support-agent") # reads RIUS_API_KEY from the environment
def on_sigterm(signum, frame):
client.shutdown() # drain queued spans, send the stopped heartbeat
sys.exit(0)
signal.signal(signal.SIGTERM, on_sigterm)- Deploy with the key in a Secret rather than baked into the image. Store the API key in a Secret named
riuswith the keyapi-key, and reference it from the container. Kubernetes sendsSIGTERMand waitsterminationGracePeriodSeconds(30 by default) beforeSIGKILL. A batch export can take up to 30 seconds by default, so give the pod enough time.
apiVersion: apps/v1
kind: Deployment
metadata:
name: support-agent
spec:
replicas: 2
selector:
matchLabels:
app: support-agent
template:
metadata:
labels:
app: support-agent
spec:
terminationGracePeriodSeconds: 60
containers:
- name: agent
image: registry.example.com/support-agent:1.4.0
env:
- name: RIUS_SERVICE_NAME
value: support-agent
- name: RIUS_API_KEY
valueFrom:
secretKeyRef:
name: rius
key: api-key- In TypeScript, no exit hook is installed either. Await
client.readyso the integration is attached before your first call, and callshutdown()when the pod receives SIGTERM.
import { init } from '@glassflow-ai/rius'
const client = init({ serviceName: 'support-agent' })
await client.ready
// build and run your chains exactly as before
process.on('SIGTERM', async () => {
await client.shutdown() // export queued spans and send the stopped ping
process.exit(0)
})-
Do not call
flush()inside request handlers. It blocks until the export completes. Flush at process boundaries only. Batching, queue size and shutdown behaviour are tunable, see Reliability. -
Roll out the deployment, send one request that calls a model, and open the trace in the console. You should see the chain’s structure as nested spans, with the model-call span carrying model, tokens and cost.
What you get
- A trace per request that mirrors your chain or graph: chains, tools, retrievers and model calls as nested spans.
- Tokens on each model call, and a cost per call that Rius computes. Costs are list-price estimates in US dollars.
- One agent named after your service name, with an instance for each running pod.
- A clear difference between a clean stop and a crash: a pod that shuts down cleanly reports
stopped, and one that is killed reportsgone. - Each model call counted once in token and cost totals, with the LangChain span still showing the run’s total. The one exception is text-completion models, which are still counted twice, see Integrations.
When something else fits
- If your Kubernetes workload is a short-lived Job or CronJob rather than a long-running deployment, call
client.flush()before the process exits, as the Reliability page describes for batch jobs. - If you want to keep prompts and replies out of traces, use the privacy controls described on the LangChain page.