AgentMark uses OpenTelemetry to collect distributed traces and metrics for your prompt executions. This provides detailed visibility into how your prompts perform in development and production.
Setup
Initialize tracing in your application:
import { AgentMarkSDK } from "@agentmark-ai/sdk";
import { createAgentMarkClient, VercelAIModelRegistry } from "@agentmark-ai/ai-sdk-v4-adapter";
import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
const sdk = new AgentMarkSDK({
apiKey: process.env.AGENTMARK_API_KEY,
appId: process.env.AGENTMARK_APP_ID,
baseUrl: process.env.AGENTMARK_BASE_URL // defaults to https://api.agentmark.co
});
// Initialize tracing
const tracer = sdk.initTracing();
// Configure client
const modelRegistry = new VercelAIModelRegistry()
.registerModels(["gpt-4o-mini"], (name) => openai(name));
const client = createAgentMarkClient({
loader: sdk.getApiLoader(),
modelRegistry
});
// Load and run prompt with telemetry
const prompt = await client.loadTextPrompt("greeting.prompt.mdx");
const input = await prompt.format({
props: { name: 'Alice' },
telemetry: {
isEnabled: true,
functionId: "greeting-function",
metadata: {
userId: "123",
environment: "production"
}
}
});
const result = await generateText(input);
// Shutdown tracer (only for short-running scripts)
await tracer.shutdown();
Pass disableBatch: true to initTracing() for short-running scripts that need traces flushed immediately:const tracer = sdk.initTracing({ disableBatch: true });
Collected Spans
AgentMark records these OpenTelemetry spans:
| Span Type | Description | Key Attributes |
|---|
ai.inference | Full inference call lifecycle | ai.model.id, ai.prompt, ai.response.text, ai.usage.promptTokens, ai.usage.completionTokens |
ai.toolCall | Individual tool executions | ai.toolCall.name, ai.toolCall.args, ai.toolCall.result |
ai.stream | Streaming response metrics | ai.response.msToFirstChunk, ai.response.msToFinish, ai.response.avgCompletionTokensPerSecond |
Span Attributes
Each span contains detailed attributes:
Model Information:
ai.model.id - Model identifier (e.g., “gpt-4o-mini”)
ai.model.provider - Provider name (e.g., “openai”)
Token Usage:
ai.usage.promptTokens - Input tokens
ai.usage.completionTokens - Output tokens
Telemetry Metadata:
ai.telemetry.functionId - Your function identifier
ai.telemetry.metadata.* - Custom metadata fields
Response Details:
ai.response.text - Generated text
ai.response.toolCalls - Tool call information
ai.response.finishReason - Why generation stopped
Grouping Traces
Group related operations using the trace function. The trace function accepts a TraceOptions object and a callback that receives a TraceContext:
import { trace } from "@agentmark-ai/sdk";
const { result, traceId } = await trace(
{ name: 'user-request-handler' },
async (ctx) => {
// All spans created here are grouped under this trace
const prompt = await client.loadTextPrompt('handler.prompt.mdx');
const input = await prompt.format({
props: { query: 'What is AgentMark?' },
telemetry: { isEnabled: true }
});
return await generateText(input);
}
);
// traceId is available for correlation (e.g., logging, scoring)
console.log('Trace ID:', traceId);
// result is a Promise<T> — await it if you need the value
const output = await result;
TraceOptions
The trace function accepts the following options:
| Option | Type | Required | Description |
|---|
name | string | Yes | Name for the trace |
metadata | Record<string, string> | No | Custom key-value metadata |
sessionId | string | No | Group traces into a session |
sessionName | string | No | Human-readable session name |
userId | string | No | Associate trace with a user |
datasetRunId | string | No | Link to a dataset run |
datasetRunName | string | No | Human-readable dataset run name |
datasetItemName | string | No | Specific dataset item name |
datasetExpectedOutput | string | No | Expected output for evaluation |
datasetPath | string | No | Path to the dataset file |
TraceResult
The trace function returns a TraceResult<T>:
interface TraceResult<T> {
result: Promise<T>; // The result of your callback (as a Promise)
traceId: string; // The trace ID for correlation
}
Note that result is Promise<T>, not T. You need to await it to get the resolved value.
TraceContext
The callback receives a TraceContext with methods for adding attributes, events, and child spans:
interface TraceContext {
readonly traceId: string;
readonly spanId: string;
setAttribute: (key: string, value: string | number | boolean) => void;
addEvent: (name: string, attributes?: Attributes) => void;
span: <T>(options: SpanOptions, fn: (ctx: TraceContext) => Promise<T>) => Promise<T>;
}
Creating Child Spans
Use ctx.span() to create child spans within a trace. Each child span also receives a TraceContext, so you can nest spans arbitrarily:
import { trace } from "@agentmark-ai/sdk";
const { result, traceId } = await trace(
{ name: 'multi-step-workflow' },
async (ctx) => {
// Child span for validation
await ctx.span({ name: 'validate-input' }, async (spanCtx) => {
spanCtx.setAttribute('input.length', 42);
// Validation logic
});
// Child span for main processing
const output = await ctx.span({ name: 'process-request' }, async (spanCtx) => {
const prompt = await client.loadTextPrompt('process.prompt.mdx');
const input = await prompt.format({
props: { query: 'process this' },
telemetry: { isEnabled: true }
});
return await generateText(input);
});
// Child span for formatting
await ctx.span({ name: 'format-response' }, async (spanCtx) => {
spanCtx.addEvent('formatting-complete');
});
return output;
}
);
Graph View for Complex Workflows
For complex AI agent workflows, use graph metadata to visualize execution flow:
import { trace } from "@agentmark-ai/sdk";
const { result } = await trace(
{
name: "ai-agent-workflow",
metadata: {
"graph.node.id": "orchestrator",
"graph.node.display_name": "Orchestrator",
"graph.node.type": "router"
}
},
async (ctx) => {
// Step 1: Process input
await ctx.span(
{
name: "input-processor",
metadata: {
"graph.node.id": "input-processor",
"graph.node.parent_id": "orchestrator",
"graph.node.display_name": "Input Processor",
"graph.node.type": "agent"
}
},
async (spanCtx) => {
// Process and validate input
}
);
// Step 2: Retrieve context
await ctx.span(
{
name: "context-retrieval",
metadata: {
"graph.node.id": "context-retrieval",
"graph.node.parent_id": "orchestrator",
"graph.node.display_name": "Context Retrieval",
"graph.node.type": "retrieval"
}
},
async (spanCtx) => {
// Fetch relevant context
}
);
// Step 3: LLM reasoning (depends on both previous steps)
await ctx.span(
{
name: "llm-reasoning",
metadata: {
"graph.node.id": "llm-reasoning",
"graph.node.parent_ids": JSON.stringify(["input-processor", "context-retrieval"]),
"graph.node.display_name": "LLM Reasoning",
"graph.node.type": "llm"
}
},
async (spanCtx) => {
const prompt = await client.loadTextPrompt('reason.prompt.mdx');
// ... reasoning logic
}
);
}
);
| Property | Description | Required |
|---|
graph.node.id | Unique node identifier | Yes |
graph.node.display_name | Human-readable name | Yes |
graph.node.type | Visual type: router, llm, tool, retrieval, memory, agent | Yes |
graph.node.parent_id | Single parent node ID | No* |
graph.node.parent_ids | JSON array of parent IDs | No* |
*Either parent_id or parent_ids required (except for root).
Node Types
router - Orchestration and routing
llm - LLM inference and reasoning
tool - External tool execution
retrieval - Information retrieval
memory - Memory operations
agent - Agent-specific processing
Scoring Traces
Use sdk.score() to attach quality scores to traces or spans:
const sdk = new AgentMarkSDK({
apiKey: process.env.AGENTMARK_API_KEY,
appId: process.env.AGENTMARK_APP_ID
});
const { result, traceId } = await trace(
{ name: 'scored-workflow' },
async (ctx) => {
// ... your logic
return output;
}
);
// Score the trace
await sdk.score({
resourceId: traceId,
name: 'correctness',
score: 0.95,
label: 'correct',
reason: 'Output matches expected result'
});
Best Practices
Use meaningful function IDs:
// Bad
telemetry: { functionId: "func1" }
// Good
telemetry: { functionId: "customer-support-greeting" }
Add relevant metadata:
telemetry: {
isEnabled: true,
functionId: "search-handler",
metadata: {
userId: user.id,
environment: process.env.NODE_ENV,
query: searchQuery,
resultCount: String(results.length)
}
}
Monitor in production:
- Always enable telemetry in production
- Use the AgentMark dashboard to monitor performance
- Set up alerts for anomalies
- Review traces when debugging user issues
Clean up resources:
// For short-running scripts (like CLI tools)
await tracer.shutdown();
// For long-running servers, shutdown on process exit
process.on('SIGTERM', async () => {
await tracer.shutdown();
process.exit(0);
});
Viewing Traces
Traces are viewable in the AgentMark platform dashboard showing:
- Complete execution timeline
- Token usage and costs
- Custom metadata
- Error information
- Graph visualization (when enabled)
Next Steps