Docs/Guides/Production

Production Best Practices

Deploy monitoring to production safely and effectively with enterprise-grade reliability and performance.

Overview

Deploying AI agents to production requires careful planning, monitoring, and operational excellence. This guide covers:

  • Pre-Production Checklist - Ensure readiness for production
  • Production Configuration - Optimize for production environments
  • Monitoring in Production - Real-time production monitoring
  • Incident Response - Handle production incidents effectively
  • Scaling Strategies - Scale your AI operations safely

Pre-Production Checklist

1. Infrastructure Readiness

Before deploying, confirm that the SDK is configured with the correct credentials and host, and that traces actually reach the platform. Use a public/secret key pair (never a single API key) and always set the host explicitly to https://api.agenticants.ai.

python
from ants_platform import get_client, observe @observe(name="readiness-probe") def readiness_probe(): """A trivial traced call used to confirm connectivity end to end.""" return {"status": "ok"} def check_infrastructure_readiness(): """Verify the SDK can authenticate and flush traces to the platform.""" checks = [] # Resolve the configured singleton client. # Requires ANTS_PLATFORM_PUBLIC_KEY / ANTS_PLATFORM_SECRET_KEY in the env, # and ANTS_PLATFORM_HOST=https://api.agenticants.ai try: client = get_client() checks.append(("Client Configuration", "[PASS]")) except Exception as e: checks.append(("Client Configuration", f"[FAIL]: {e}")) return checks # Emit a traced call and flush it to verify connectivity. try: readiness_probe() client.flush() checks.append(("Trace Connectivity", "[PASS]")) except Exception as e: checks.append(("Trace Connectivity", f"[FAIL]: {e}")) return checks def print_readiness_report(): """Print a comprehensive readiness report.""" print("Production Readiness Report") print("=" * 50) checks = check_infrastructure_readiness() for check_name, status in checks: print(f"{check_name}: {status}") failed_checks = [check for check in checks if "[FAIL]" in check[1]] if failed_checks: print(f"\n{len(failed_checks)} checks failed. Fix before deploying to production.") else: print("\nAll checks passed. Ready for production deployment!")

For Java (17+), configure once at startup, then emit a traced probe with Ants.observe(...) and flush to confirm connectivity end to end:

java
public class ReadinessCheck { public static void main(String[] args) { List<String> checks = new java.util.ArrayList<>(); // Configure the global tracer once. Host defaults to // https://api.agenticants.ai; only call .baseUrl(...) to override. try { AntsPlatformOtel.configure(AntsPlatformOtel.options() .publicKey(System.getenv("ANTS_PLATFORM_PUBLIC_KEY")) // "pk-ap-..." .secretKey(System.getenv("ANTS_PLATFORM_SECRET_KEY")) // "sk-ap-..." .environment("production")); checks.add("Client Configuration: [PASS]"); } catch (Exception e) { checks.add("Client Configuration: [FAIL]: " + e.getMessage()); checks.forEach(System.out::println); return; } // Emit a trivial traced call and flush it to verify connectivity. try { Ants.observe("readiness-probe", ObservationType.SPAN, span -> { span.output(Map.of("status", "ok")); return "ok"; }); AntsPlatformOtel.flush(); // deliver buffered spans before checking checks.add("Trace Connectivity: [PASS]"); } catch (Exception e) { checks.add("Trace Connectivity: [FAIL]: " + e.getMessage()); } checks.forEach(System.out::println); } }

Alerting and metrics/trends thresholds are configured in the dashboard at https://app.agenticants.ai, not through the SDK. Verify your alert and metric definitions there as part of the readiness review.

2. Security and Compliance

Credentials are a public/secret key pair (pk-ap-... / sk-ap-...). Keep the secret key server-side only, supply it through environment variables, and confirm the SDK targets the production host before going live.

typescript
interface SecurityCheck { check: string; status: "PASS" | "FAIL"; error?: string; } async function checkSecurityReadiness(): Promise<SecurityCheck[]> { const securityChecks: SecurityCheck[] = []; // Verify both keys are present and not swapped. const publicKey = process.env.ANTS_PLATFORM_PUBLIC_KEY ?? ""; const secretKey = process.env.ANTS_PLATFORM_SECRET_KEY ?? ""; if (publicKey.startsWith("pk-ap-") && secretKey.startsWith("sk-ap-")) { securityChecks.push({ check: "Key Pair Format", status: "PASS" }); } else { securityChecks.push({ check: "Key Pair Format", status: "FAIL", error: "Expected pk-ap-... public key and sk-ap-... secret key", }); } // Confirm the span processor can be constructed against the production host. try { new NodeTracerProvider({ spanProcessors: [ new AntsPlatformSpanProcessor({ publicKey, secretKey, baseUrl: "https://api.agenticants.ai", environment: process.env.NODE_ENV, release: process.env.APP_VERSION, }), ], }); securityChecks.push({ check: "Client Configuration", status: "PASS" }); } catch (error) { securityChecks.push({ check: "Client Configuration", status: "FAIL", error: (error as Error).message, }); } // Ensure the secret key is never exposed to the browser bundle. if (typeof window === "undefined") { securityChecks.push({ check: "Secret Key Isolation", status: "PASS" }); } else { securityChecks.push({ check: "Secret Key Isolation", status: "FAIL", error: "Secret key must not be loaded in browser/client code", }); } return securityChecks; } async function printSecurityReport() { console.log("Production Security Report"); console.log("=".repeat(50)); const checks = await checkSecurityReadiness(); checks.forEach((check) => { const status = check.status === "PASS" ? "[PASS]" : "[FAIL]"; console.log(`${check.check}: ${status} ${check.status}`); if (check.error) { console.log(` Error: ${check.error}`); } }); }

3. Performance Baseline

Establish a baseline by tracing a representative workload and reviewing latency and error rates in the dashboard. The SDK captures the spans; aggregate metrics are computed and displayed at https://app.agenticants.ai.

python
from ants_platform import get_client, observe @observe(name="baseline-call") def simulate_agent_call(): """Replace with a representative production call.""" # Implement a realistic agent invocation here. return {"result": "ok"} def establish_performance_baseline(): """Trace a sample workload so the dashboard can compute a baseline.""" client = get_client() response_times = [] for _ in range(100): start_time = time.time() simulate_agent_call() response_times.append(time.time() - start_time) # Make sure every span is delivered before the process exits. client.flush() local_summary = { "avg_response_time": sum(response_times) / len(response_times), "p95_response_time": sorted(response_times)[94], "p99_response_time": sorted(response_times)[98], } print("Sample workload traced. Review aggregated latency, throughput,") print("and error-rate baselines in the dashboard:") print("https://app.agenticants.ai") return local_summary

For Java, trace a representative workload the same way. A baseline that exercises an LLM call should report model + usage on the generation so the dashboard can baseline cost as well as latency:

java
public class PerformanceBaseline { public static void main(String[] args) { AntsPlatformOtel.configure(AntsPlatformOtel.options() .publicKey(System.getenv("ANTS_PLATFORM_PUBLIC_KEY")) .secretKey(System.getenv("ANTS_PLATFORM_SECRET_KEY")) .environment("production")); // Trace a sample workload so the dashboard can compute a baseline. for (int i = 0; i < 100; i++) { Ants.observe("baseline-call", ObservationType.SPAN, span -> { // A generation carrying model + usage lets the backend compute cost. AntsGeneration gen = Ants.startGeneration("baseline-generation") .model("gpt-4o-mini") .modelParameters(Map.of("temperature", 0.2)) .input("Representative production prompt"); gen.output("Representative production response").usage(120, 80).end(); return "ok"; }); } // Make sure every span is delivered before the process exits. AntsPlatformOtel.flush(); System.out.println("Sample workload traced. Review aggregated latency, throughput,"); System.out.println("and error-rate baselines in the dashboard:"); System.out.println("https://app.agenticants.ai"); } }

Production Configuration

1. Environment Configuration

Configure the SDK once, at process startup, from environment variables. For Python, instantiate AntsPlatform (or rely on get_client() reading the env). For Node.js tracing, register the OpenTelemetry provider once in an instrumentation.ts file that is imported before the rest of your app.

python
from ants_platform import AntsPlatform # Configure the singleton once at startup. The same instance is returned # everywhere via get_client(). client = AntsPlatform( public_key=os.environ["ANTS_PLATFORM_PUBLIC_KEY"], secret_key=os.environ["ANTS_PLATFORM_SECRET_KEY"], host="https://api.agenticants.ai", ) # In production, flush traces before the process exits (e.g. in a shutdown # hook, Lambda handler teardown, or atexit handler). atexit.register(client.flush) print("Production client configured successfully")
typescript
// instrumentation.ts -- import this file FIRST, before any app code. const provider = new NodeTracerProvider({ spanProcessors: [ new AntsPlatformSpanProcessor({ publicKey: process.env.ANTS_PLATFORM_PUBLIC_KEY, secretKey: process.env.ANTS_PLATFORM_SECRET_KEY, // baseUrl defaults to https://api.agenticants.ai in >=1.0.15. baseUrl: "https://api.agenticants.ai", // Tag every span with the deploy environment and release so you can // filter and compare across deploys in the dashboard. environment: process.env.NODE_ENV, // e.g. "production" release: process.env.APP_VERSION, // e.g. the git SHA or semver tag // Head sampling: drop a fraction of traces under heavy load. Return // false to skip exporting a span. Here we always keep errors and sample // the rest at 25%. shouldExportSpan: (span) => span.attributes?.["ants.level"] === "ERROR" || Math.random() < 0.25, }), ], }); provider.register(); setAntsPlatformTracerProvider(provider); // In short-lived processes (scripts, serverless), flush before exit: // await provider.forceFlush(); // await provider.shutdown();

For Java (17+), call AntsPlatformOtel.configure(...) exactly once at process startup. Tag the deploy environment with .environment(...) so you can filter and compare across deploys in the dashboard. Then flush before the process exits — register a shutdown hook for long-running services, or call flush() before a serverless handler returns:

java
public final class Telemetry { public static void init() { // Configure the global tracer once at startup. The same registered // provider is used everywhere via Ants.observe / Ants.startGeneration. AntsPlatformOtel.configure(AntsPlatformOtel.options() .publicKey(System.getenv("ANTS_PLATFORM_PUBLIC_KEY")) .secretKey(System.getenv("ANTS_PLATFORM_SECRET_KEY")) // baseUrl defaults to https://api.agenticants.ai; override with .baseUrl(...). .environment(System.getenv("APP_ENV"))); // e.g. "production" // Long-running service: flush buffered spans on JVM shutdown so no // in-flight spans are lost when the instance is recycled or scaled down. Runtime.getRuntime().addShutdownHook(new Thread(AntsPlatformOtel::flush)); } }

In a short-lived process (a serverless handler, scheduled job, or CLI), there is no long-running JVM to attach a shutdown hook to — call AntsPlatformOtel.flush() (or AntsPlatformOtel.shutdown()) explicitly before the handler returns:

java
public String handleRequest(String input) { String result = Ants.observe("handler", ObservationType.AGENT, agent -> { // ... do work ... return "done"; }); AntsPlatformOtel.flush(); // deliver spans before the handler returns return result; }

Data retention, sampling, and production alert rules (high error rate, high latency, cost spikes, and their notification channels) are managed in the dashboard at https://app.agenticants.ai. There is no SDK API for configuring retention or alerts.

2. Load Balancing and Scaling

Load balancing, auto-scaling, and circuit breakers are properties of your own deployment platform (Kubernetes, ECS, etc.), not the observability SDK. Run as many instances of your instrumented service as you need: each one configures the SDK identically from the same environment variables, and all traces flow to the same project.

The only SDK consideration when scaling is to flush before an instance is terminated so no spans are lost:

typescript
// Flush on graceful shutdown so in-flight spans are delivered before exit. async function shutdown() { const provider = getAntsPlatformTracerProvider(); await provider?.forceFlush(); await provider?.shutdown(); } process.on("SIGTERM", shutdown); process.on("SIGINT", shutdown);

Monitor aggregate request rate, latency, and error rate across all instances from the dashboard at https://app.agenticants.ai.

3. Data Management

Data encryption, retention windows, backups, and exports are platform-managed and configured in the dashboard at https://app.agenticants.ai. The SDK's only role in data handling is choosing what to capture: use the @observe() decorator's capture_input / capture_output flags to avoid sending sensitive payloads.

python
from ants_platform import observe # Trace timing and structure without capturing the raw request/response bodies. @observe(name="handle-pii-request", capture_input=False, capture_output=False) def handle_pii_request(request: dict): # Sensitive input and output are NOT sent to the platform; only the span # name, timing, and any metadata you explicitly attach are recorded. return process(request) def process(request: dict): return {"result": "processed"}

Java has no capture_input / capture_output flags. To avoid sending sensitive payloads, simply don't call .input() / .output() on the observation — you still get the span name, timing, and any .metadata(...) you explicitly attach:

java
public class PiiHandler { public Object handlePiiRequest(Map<String, Object> request) { // Trace timing and structure without capturing the raw request/response. return Ants.observe("handle-pii-request", ObservationType.SPAN, span -> { // No .input()/.output(): sensitive payloads are NOT sent to the platform. // Only the span name, timing, and metadata below are recorded. span.metadata(Map.of("payloadType", "pii", "records", request.size())); return process(request); }); } private Object process(Map<String, Object> request) { return Map.of("result", "processed"); } }

Monitoring in Production

1. Real-Time Monitoring

Dashboards, health overviews, performance charts, and cost monitoring are all built into the platform UI. Instrument your code with the SDK so the data exists; then view and arrange it in the dashboard at https://app.agenticants.ai.

python
from ants_platform import get_client def run_agent(question: str, *, user_id: str, session_id: str) -> dict: """A realistic nested agent run: every layer is a distinct observation so the dashboard can break down latency and cost per step.""" client = get_client() with client.start_as_current_observation( name="support-agent", as_type="agent", agent_name="support-agent" ): # TRACE-level fields: bind the user + session so the dashboard can # group a multi-turn conversation and attribute usage to a user. client.update_current_trace( name="support-agent", input=question, user_id=user_id, session_id=session_id, tags=["production", os.environ.get("APP_VERSION", "dev")], metadata={ "environment": os.environ.get("APP_ENV", "production"), "release": os.environ.get("APP_VERSION", "dev"), }, ) # Retrieve supporting context. with client.start_as_current_observation( name="search-kb", as_type="retriever", input={"query": question} ) as retriever: docs = search_kb(question) retriever.update(output={"docs": docs}) # Plan the answer with an LLM. Generations MUST carry model + usage so # the backend can compute cost server-side. prompt = build_prompt(question, docs) with client.start_as_current_observation( name="plan", as_type="generation", model="gpt-4o-mini", input=prompt, model_parameters={"temperature": 0.2}, ) as gen: answer = call_llm(prompt) gen.update( output=answer, usage_details={"input": 412, "output": 96, "total": 508}, ) client.update_current_trace(output={"answer": answer}) return {"answer": answer} def search_kb(question: str) -> list: return [{"id": "kb-1", "title": "Refund policy"}] def build_prompt(question: str, docs: list) -> str: return f"Answer using docs {docs}: {question}" def call_llm(prompt: str) -> str: return "Refunds are processed within 5 business days."

The Java equivalent uses Ants.observe(...) so the retriever and generation auto-nest under the agent. For ObservationType.AGENT the SDK derives the agent name from the observation name — there is no agent-name argument. The generation MUST carry model + usage so the backend can compute cost server-side:

java
public class SupportAgent { public Map<String, Object> runAgent(String question, String userId, String sessionId) { return Ants.observe("support-agent", ObservationType.AGENT, agent -> { // TRACE-level fields: bind the user + session so the dashboard can group // a multi-turn conversation and attribute usage to a user. agent.updateTrace(TraceAttributes.builder() .name("support-agent") .input(question) .userId(userId) .sessionId(sessionId) .tags(List.of("production", System.getenv().getOrDefault("APP_VERSION", "dev"))) .metadata(Map.of( "environment", System.getenv().getOrDefault("APP_ENV", "production"), "release", System.getenv().getOrDefault("APP_VERSION", "dev"))) .build()); // Retrieve supporting context as its own span. List<Map<String, Object>> docs = Ants.observe("search-kb", ObservationType.RETRIEVER, retriever -> { retriever.input(Map.of("query", question)); List<Map<String, Object>> found = List.of(Map.of("id", "kb-1", "title", "Refund policy")); retriever.output(Map.of("docs", found)); return found; }); // Plan the answer with an LLM. The generation carries model + usage so // the backend can compute cost server-side. String prompt = "Answer using docs " + docs + ": " + question; AntsGeneration gen = Ants.startGeneration("plan") .model("gpt-4o-mini") .modelParameters(Map.of("temperature", 0.2)) .input(prompt); String answer = "Refunds are processed within 5 business days."; gen.output(answer).usage(412, 96).end(); // usage(in, out) sets total = in + out agent.updateTrace(TraceAttributes.builder().output(Map.of("answer", answer)).build()); return Map.of("answer", answer); }); } }

After deploying, open https://app.agenticants.ai to build real-time dashboards for request rate, latency (P95), error rate, and cost. These are UI features rather than SDK calls.

2. Health Checks

Health checks for your own service endpoints belong in your deployment platform's probes (Kubernetes liveness/readiness, load balancer health checks). To confirm the observability pipeline itself is healthy, emit a traced probe and flush it:

typescript
const probe = observe( async () => { // A trivial unit of work that should always succeed. return { status: "ok" }; }, { name: "health-probe" }, ); export async function checkObservabilityHealth(): Promise<boolean> { try { await probe(); await getAntsPlatformTracerProvider()?.forceFlush(); console.log("Observability pipeline: [PASS]"); return true; } catch (error) { console.log(`Observability pipeline: [FAIL] ${(error as Error).message}`); return false; } }

Overall system health across components is visualized in the dashboard at https://app.agenticants.ai.

3. Performance Monitoring

Performance metrics and trends (request rate, P95 latency, error and success rates over time) are computed by the platform from your traces. There is no SDK query API for these; explore them interactively in the dashboard.

Make sure the data is flowing by instrumenting the hot path:

python
from ants_platform import observe @observe(name="hot-path", as_type="generation") def hot_path(request: dict): # The duration, success/failure, and any metadata of this call become # part of the latency and error-rate charts in the dashboard. return {"result": "ok"}

Then open https://app.agenticants.ai to review live performance metrics and 24-hour trends.

Incident Response

1. Incident Detection

Incident detection rules (high error rate, high latency, cost spikes) and their thresholds are configured as alerts in the dashboard at https://app.agenticants.ai. The SDK has no incidents or alerts API.

What the SDK gives you is the trace data that makes incidents diagnosable. Attach rich metadata and record failures on the current span so a triggered alert links straight to the offending traces:

python
from ants_platform import get_client def run_agent(request: dict, *, user_id: str, session_id: str): client = get_client() with client.start_as_current_observation( name="production-agent", as_type="agent", agent_name="production-agent" ): client.update_current_trace( input=request, user_id=user_id, session_id=session_id, metadata={"environment": "production"}, ) # The work runs inside its own span so a failure is recorded on a # discrete observation, not the whole agent. with client.start_as_current_observation( name="do-work", as_type="span", input=request ) as span: try: result = do_work(request) span.update(output=result, metadata={"outcome": "success"}) return result except Exception as e: # Mark the span as failed; level + status_message surface it in # error-rate views and link any alert to this trace. span.update( level="ERROR", status_message=str(e), metadata={"outcome": "error"}, ) raise def do_work(request: dict): return {"result": "ok"}

2. Incident Response Workflows

Automated response workflows -- notifications to Slack/PagerDuty/email, severity routing, and escalation -- are configured alongside alerts in the dashboard at https://app.agenticants.ai. There is no SDK to define or execute these workflows.

From the SDK side, the most useful thing you can do is tag traces with a severity and a correlation id so responders can pivot from an alert to the exact traces involved:

typescript
export const handleRequest = observe( async (request: { id: string; body: unknown }) => { // Correlate this trace with your incident tooling. updateActiveTrace({ metadata: { correlationId: request.id, severity: "unknown" }, }); return process(request); }, { name: "request-handler" }, ); function process(request: { body: unknown }) { return { ok: true }; }

3. Post-Incident Analysis

Post-incident analysis -- timelines, root-cause exploration, and reports -- is done in the dashboard at https://app.agenticants.ai, where you can filter traces by time window, error level, and metadata. There is no SDK for incident timelines or report generation.

To make post-incident analysis effective, ensure traces carry consistent metadata (environment, version, request/correlation ids) using update_current_trace as shown above, so you can reconstruct exactly what happened.

Scaling Strategies

1. Horizontal Scaling

Horizontal scaling (auto-scaling thresholds, load balancing, instance counts) is owned by your deployment platform, not the SDK. Each instance configures the SDK from the same environment variables and reports to the same project, so traces from every replica land together.

The one SDK requirement when scaling out is flushing on shutdown:

python
from ants_platform import get_client client = get_client() # Ensure spans are delivered before an instance is scaled down or recycled. atexit.register(client.flush)

Aggregate scaling-relevant signals (request rate, latency, error rate) are visible per project in the dashboard at https://app.agenticants.ai. Host-level CPU/memory metrics come from your infrastructure monitoring, not this SDK.

2. Vertical Scaling

Vertical scaling (resizing instances based on CPU/memory) is likewise a deployment-platform concern. CPU, memory, disk, and network usage are reported by your infrastructure tooling. The observability SDK contributes the application-level latency and throughput context you can correlate against those resource metrics in the dashboard at https://app.agenticants.ai.

No SDK call is required; just keep the hot path instrumented with @observe() (Python) or observe() / startObservation() (JS) so latency regressions under load are visible.

Best Practices

1. Production-Ready Code

Wrap your request handler so every call is traced, and record validation and processing failures on the active span. The decorator captures input/output and timing automatically; you only annotate outcomes.

python
from fastapi import HTTPException from ants_platform import get_client class ValidationError(Exception): pass class ProcessingError(Exception): pass def process_request(request: dict, *, user_id: str, session_id: str): """Process a request with production-ready error handling.""" client = get_client() with client.start_as_current_observation( name="production-agent", as_type="agent", agent_name="production-agent" ) as span: client.update_current_trace( input=request, user_id=user_id, session_id=session_id, metadata={"environment": "production", "release": "1.0.0"}, ) try: validate_request(request) result = process_core_logic(request) span.update(output=result, metadata={"success": True}) return result except ValidationError as e: span.update( level="ERROR", status_message=str(e), metadata={"error_type": "validation"} ) raise HTTPException(status_code=400, detail=str(e)) except ProcessingError as e: span.update( level="ERROR", status_message=str(e), metadata={"error_type": "processing"} ) raise HTTPException(status_code=500, detail="Internal processing error") except Exception as e: span.update( level="ERROR", status_message=str(e), metadata={"error_type": "unexpected"} ) raise HTTPException(status_code=500, detail="Internal server error") def validate_request(request: dict): if not request.get("input"): raise ValidationError("Input is required") if len(request["input"]) > 10000: raise ValidationError("Input too long") def process_core_logic(request: dict): # Implement your agent logic here. return {"result": "processed"}

For JS/TS, the equivalent uses startActiveObservation so nested spans attach to the right trace:

typescript
startActiveObservation, startObservation, updateActiveObservation, updateActiveTrace, getActiveTraceId, } from "ants-platform"; interface AgentRequest { input?: string; userId: string; sessionId: string; } export async function processRequest(request: AgentRequest) { return startActiveObservation( "support-agent", async (span) => { // Bind the user + session and tag the environment/release at the trace // level so a conversation is grouped and attributable in the dashboard. updateActiveTrace({ name: "support-agent", input: request.input, userId: request.userId, sessionId: request.sessionId, tags: ["production", process.env.APP_VERSION ?? "dev"], metadata: { environment: process.env.NODE_ENV ?? "production", release: process.env.APP_VERSION ?? "dev", }, }); try { if (!request.input) { throw new Error("Input is required"); } // A nested generation MUST carry model + usageDetails so the backend // can compute cost server-side. const gen = startObservation( "plan", { model: "gpt-4o-mini", input: request.input, modelParameters: { temperature: 0.2 }, }, { asType: "generation" }, ); const answer = "Refunds are processed within 5 business days."; gen.update({ output: answer, usageDetails: { input: 412, output: 96, total: 508 }, }); gen.end(); span.update({ metadata: { success: true } }); updateActiveTrace({ output: answer }); return { answer, traceId: getActiveTraceId() }; } catch (error) { // Record the failure on the active span: level + statusMessage surface // it in error-rate views and link any triggered alert to this trace. updateActiveObservation({ level: "ERROR", statusMessage: (error as Error).message, metadata: { errorType: "unexpected" }, }); throw error; } }, { asType: "agent" }, ); }

2. Monitoring and Alerting

Comprehensive monitoring, alerting channels (email, Slack, PagerDuty), and dashboards are configured in the platform UI at https://app.agenticants.ai. Your job in code is to produce well-structured traces; the dashboard turns them into metrics, alerts, and views.

If you use OpenAI or LangChain, the integrations capture traces automatically -- no manual span wiring needed:

typescript
// Every call made through this client is traced automatically. const openai = observeOpenAI(new OpenAI()); const completion = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "Summarize today's incidents." }], });
python
# Drop-in replacement: swap `import openai` for the line below; all calls are traced. from ants_platform.openai import openai completion = openai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Summarize today's incidents."}], )

For LangChain, attach the callback handler per invocation. Pass the session and user so the run is grouped and attributed in the dashboard:

python
from ants_platform.langchain import CallbackHandler handler = CallbackHandler() # LCEL: bind user/session at invoke time so the trace is attributable. result = chain.invoke( {"question": "..."}, config={ "callbacks": [handler], "metadata": { "langfuse_user_id": "user-42", "langfuse_session_id": "session-abc", }, }, )

The JS callback handler takes the same fields directly:

typescript
const handler = new CallbackHandler({ sessionId: "session-abc", userId: "user-42", tags: ["production"], }); await chain.invoke(input, { callbacks: [handler] });

Troubleshooting

Common Production Issues

Issue: High error rates in production

Error rates and the underlying error traces are explored in the dashboard at https://app.agenticants.ai -- filter traces by error level, environment, and time window. There is no SDK trace-query API. To make those traces easy to find, ensure failures are recorded on the span with environment metadata:

python
from ants_platform import get_client def run_agent(request: dict, *, user_id: str, session_id: str): client = get_client() with client.start_as_current_observation( name="production-agent", as_type="agent", agent_name="production-agent" ) as span: client.update_current_trace( input=request, user_id=user_id, session_id=session_id, metadata={"environment": "production"}, ) try: return do_work(request) except Exception as e: span.update(level="ERROR", status_message=str(e)) raise def do_work(request: dict): return {"result": "ok"}

Issue: Performance degradation

Latency outliers and 24-hour performance trends are inspected in the dashboard at https://app.agenticants.ai. Keep slow paths instrumented so they show up as distinct spans:

typescript
export async function callModel(prompt: string) { const gen = startObservation( "model-call", { model: "gpt-4o-mini", input: prompt, modelParameters: { temperature: 0 } }, { asType: "generation" }, ); try { const result = await invokeModel(prompt); // model + usageDetails let the backend compute cost and latency. gen.update({ output: result.text, usageDetails: { input: 312, output: 84, total: 396 }, }); return result; } catch (error) { gen.update({ level: "ERROR", statusMessage: (error as Error).message }); throw error; } finally { // Always end the observation so its duration is recorded. gen.end(); } } async function invokeModel(prompt: string) { return { text: "..." }; }

Next Steps

Example Projects


Congratulations! You now have a production-ready AI system with comprehensive monitoring, alerting, and incident response built around real SDK instrumentation and the dashboard at https://app.agenticants.ai.

© 2026 ANTS Platform, Inc.Docs v1.0 · Last updated June 2026