Skip to Content

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 (or RIUS_SERVICE_NAME) names the agent. RIUS_AGENT_NAME defaults 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 final stopped ping. A pod killed by an unhandled SIGTERM or a SIGKILL sends none, and its instance reports gone.

Set it up

  1. 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]" langchain

For TypeScript, install @arizeai/openinference-instrumentation-langchain. @langchain/core comes with a LangChain.js application.

  1. 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 to SIGTERM ends the process without running its exit hook, and Rius installs no signal handlers, so handle the signal yourself. If your server already handles SIGTERM, such as uvicorn, call client.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)
  1. Deploy with the key in a Secret rather than baked into the image. Store the API key in a Secret named rius with the key api-key, and reference it from the container. Kubernetes sends SIGTERM and waits terminationGracePeriodSeconds (30 by default) before SIGKILL. 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
  1. In TypeScript, no exit hook is installed either. Await client.ready so the integration is attached before your first call, and call shutdown() 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) })
  1. 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.

  2. 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 reports gone.
  • 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.

Still stuck? Get help

Last updated on