Docs/Integrations/Python Sdk

Python SDK

The Python SDK (ants-platform on PyPI) traces your agents and LLM calls and sends them to ANTS Platform over OpenTelemetry. It also includes the OpenAI, LangChain and CrewAI integrations and a client for prompts, datasets and scores.

Value
Packageants-platform (current version 3.7.0)
Importfrom ants_platform import AntsPlatform, get_client, observe
Python version3.9 or later (3.10 to 3.13 for the CrewAI extra)
AuthProject public key (pk-ap-...) and secret key (sk-ap-...)

Install

bash
pip install "ants-platform>=3.7.0"

Optional extras install the framework you integrate with:

bash
pip install "ants-platform[openai]" pip install "ants-platform[langchain]" pip install "ants-platform[crewai]"

Configure

Create a key pair under Project Settings > API Keys (see API Keys) and set:

bash
export ANTS_PLATFORM_PUBLIC_KEY="pk-ap-..." export ANTS_PLATFORM_SECRET_KEY="sk-ap-..." export ANTS_PLATFORM_HOST="https://<your-ants-host>"

AntsPlatform() reads these variables. You can also pass them as arguments:

python
from ants_platform import AntsPlatform ants = AntsPlatform( public_key="pk-ap-...", secret_key="sk-ap-...", host="https://<your-ants-host>", )
ℹ

If ANTS_PLATFORM_HOST is not set and no host is passed, the SDK sends to https://api.agenticants.ai. Set the host explicitly for any other deployment. It is the URL of your ANTS Platform app.

Trace an agent

⚠

Every trace needs an agent name. The top-level observation of a trace must set agent_name, or the SDK raises ValueError: agent_name is required. Child observations inherit it. Agent names are stored in lower case and identify the agent in the AI Agent Center.

Open an agent observation

python
from ants_platform import AntsPlatform ants = AntsPlatform() with ants.start_as_current_observation( as_type="agent", name="support-agent", agent_name="support-agent", agent_display_name="Support Agent", input={"question": question}, ) as agent: agent.update_trace( user_id="user-123", session_id="session-abc", tags=["support"], )

Add child observations

python
with ants.start_as_current_observation( as_type="retriever", name="kb-search", input={"query": question}, ) as search: docs = search_knowledge_base(question) search.update(output={"documents": len(docs)}) with ants.start_as_current_observation( as_type="generation", name="draft-answer", model="gpt-4o-mini", input=[{"role": "user", "content": question}], ) as gen: answer = call_model(question, docs) gen.update( output=answer, usage_details={"input": 120, "output": 80}, ) agent.update(output={"answer": answer})

Flush before exit

python
ants.flush()

The SDK sends data in the background. Call flush() (or shutdown()) before a short-lived script or worker exits.

Cost is calculated by ANTS Platform from the model and token usage. Pass cost_details to send your own cost instead. An exception raised inside a with block is recorded on that observation.

Observation types

as_type accepts span (default), generation, agent, tool, chain, retriever, embedding, evaluator and guardrail. generation and embedding also accept model, model_parameters, usage_details, cost_details, completion_start_time and prompt.

start_as_current_span(...) is a shortcut for as_type="span". start_generation and start_as_current_generation still work but are deprecated. Use as_type="generation".

Update the current trace or observation

MethodFields
observation.update(...)name, input, output, metadata, level, status_message, version, and the generation fields
observation.update_trace(...) / ants.update_current_trace(...)name, user_id, session_id, version, agent_name, input, output, metadata, tags, public
ants.update_current_span(...) / ants.update_current_generation(...)Same as update on the active observation

level is one of DEBUG, DEFAULT, WARNING or ERROR.

The @observe decorator

@observe() traces a function as an observation, capturing its arguments and return value. It works on sync and async functions and generators.

python
from ants_platform import AntsPlatform, observe ants = AntsPlatform() @observe(as_type="tool") def lookup_order(order_id: str) -> dict: return orders.get(order_id) @observe(name="answer", as_type="generation") def answer(question: str) -> str: return call_model(question) with ants.start_as_current_observation( as_type="agent", name="support-agent", agent_name="support-agent" ): order = lookup_order("A-1001") reply = answer("Where is my order?")

Options: name, as_type, capture_input, capture_output and transform_to_string (for generators). The decorator has no agent_name option, so call decorated functions inside an agent observation as shown. Set ANTS_PLATFORM_OBSERVE_DECORATOR_IO_CAPTURE_ENABLED=false to turn off argument and return value capture globally.

Framework integrations

Already instrumented with OpenTelemetry (for example OpenLLMetry)? Export to ANTS Platform directly. See OpenTelemetry (OTLP).

Tool governance

From 3.7.0, AntsGuardrailsClient in ants_platform.guardrails checks an agent's tool calls against its tool permissions before the tool runs. It is on by default when agent_id is set, and changes nothing until the agent has an approved tool.

python
from ants_platform.guardrails import ( AntsGuardrailsClient, ToolApprovalDeniedError, ToolNotAllowedError, ) guardrails = AntsGuardrailsClient( ants_api_key="<publicKey>:<secretKey>", base_url="https://<your-ants-host>", agent_id="<agent-id>", agent_name="support-agent", ) @guardrails.guard_tool def issue_refund(order_id: str, amount: float) -> dict: return payments.refund(order_id, amount) with guardrails.as_user(id_token): try: issue_refund("A-1001", 750) except ToolApprovalDeniedError as err: print("Not approved:", err.status) except ToolNotAllowedError as err: print("Blocked:", err.tool_names, str(err))
  • guard_tool checks the call with its arguments before the function runs, and records it as a tool observation. It works on sync and async functions; guard_tool(name="...") sets the tool name.
  • When the agent's AI Guardrails policy is Block or Redact, a call that breaks a rule raises ToolNotAllowedError and the function does not run. Under Audit Only, or with no policy, the call runs and the violation is reported in the background.
  • A call that needs approval creates an approval request and waits for the decision. A denial, an expiry or no answer within approval_timeout raises ToolApprovalDeniedError.
  • Both errors subclass GuardrailViolationError.
  • Tool calls of a suspended agent raise ToolNotAllowedError with "This agent has been suspended by an administrator.", even when the agent has no policy.
HelperUse
guard_toolDecorator for a tool function
guard_tool_interruptLike guard_tool for a LangGraph tool node: a call that needs approval pauses the run with interrupt, and resuming the run continues or raises
guard_framework_tools(guardrails, tools)Guards LangChain, CrewAI and OpenAI Agents tool objects in place
langchain_tool_guard(guardrails)A LangChain callback handler that checks every tool call before it starts
guard_mcp_session(guardrails, session, "github")Checks MCP call_tool as github/<tool>, and compares list_tools definitions with the pinned ones
as_user(id_token) / set_user_token(id_token)Verifies the OIDC ID token of the user the agent acts for, for group-limited tools
check_tool_calls(...) / enforce_tool_calls(...)Check calls yourself. check_tool_calls returns a ToolCheckResult and never raises.

The wrapped clients (AntsOpenAI, AntsAnthropic, AntsBedrock, AntsGoogleGenAI, AntsVertexAI) also check the tool calls in each model response. See AI Guardrails.

OptionDefaultDescription
enforce_toolsTruePass False to turn tool governance off
tool_fail_mode"open""closed" blocks tool calls when the tool settings were never loaded
approval_timeout300Seconds to wait for an approval
approval_poll_seconds2How often to ask for the decision

The tool settings are fetched from ANTS Platform and refreshed about every minute. If a refresh fails, the last copy is kept. Rate limits are counted in the process.

Configuration reference

ArgumentEnvironment variableDefault
public_keyANTS_PLATFORM_PUBLIC_KEYnone
secret_keyANTS_PLATFORM_SECRET_KEYnone
hostANTS_PLATFORM_HOSThttps://api.agenticants.ai
environmentANTS_PLATFORM_TRACING_ENVIRONMENTdefault
releaseANTS_PLATFORM_RELEASEnone
tracing_enabledANTS_PLATFORM_TRACING_ENABLEDTrue
debugANTS_PLATFORM_DEBUGFalse
sample_rateANTS_PLATFORM_SAMPLE_RATE1.0 (0.0 to 1.0)
timeoutANTS_PLATFORM_TIMEOUT5 seconds
flush_atANTS_PLATFORM_FLUSH_ATOpenTelemetry batch default
flush_intervalANTS_PLATFORM_FLUSH_INTERVALOpenTelemetry batch default
media_upload_thread_countANTS_PLATFORM_MEDIA_UPLOAD_THREAD_COUNT1
noneANTS_PLATFORM_MEDIA_UPLOAD_ENABLEDTrue
noneANTS_PLATFORM_PROMPT_CACHE_DEFAULT_TTL_SECONDS60
masknoneA function applied to inputs, outputs and metadata before export
additional_headersnoneExtra HTTP headers on export requests
blocked_instrumentation_scopesnoneOpenTelemetry instrumentation scopes to drop

get_client() returns the client created by the first AntsPlatform(...) call, or one built from the environment variables.

Other client features

AreaMethods
Scorescreate_score(...), score_current_span(...), score_current_trace(...), observation.score(...)
Promptsget_prompt(name, version=, label=), create_prompt(...), update_prompt(...)
Datasetsget_dataset(name), create_dataset(...), create_dataset_item(...)
Utilitiesget_current_trace_id(), get_current_observation_id(), get_trace_url(), create_trace_id(seed=), auth_check(), flush(), shutdown()

See Scores, Prompts and Datasets.

Troubleshooting

  • ValueError: agent_name is required. The first observation of the trace has no agent_name. Open an agent observation first.
  • Nothing appears. Check the keys and host, then call ants.auth_check(). Call ants.flush() before the process exits.
  • Slow first call. The first agent observation fetches the project ID from the API. Increase timeout on slow networks.
© 2026 ANTS Platform, Inc.Docs v1.0 · Last updated June 2026