diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/tracers/README.mdx b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/README.mdx
index b7ddd03ccf811..6b57683bf9611 100644
--- a/x-pack/platform/packages/shared/kbn-langchain/server/tracers/README.mdx
+++ b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/README.mdx
@@ -1,29 +1,39 @@
-### Tracing LangChain Retrievers, LLMs, Chains, and Tools using Elastic APM and LangSmith
+# LangChain Tracers
-This document describes how to trace LangChain retrievers, LLMs, chains, and tools using Elastic APM and LangSmith.
+This package provides tracers for monitoring and debugging LangChain retrievers, LLMs, chains, and tools within Kibana.
-If the `assistantModelEvaluation` experimental feature flag is enabled, and an APM server is configured, messages that have a corresponding trace will have an additional `View APM trace` action in the message title bar:
+## Available Tracers
-
-
-
+| Tracer | Purpose | Configuration |
+|--------|---------|---------------|
+| `APMTracer` | Elastic APM integration for distributed tracing | Kibana APM settings |
+| `TelemetryTracer` | Event-based telemetry for usage analytics | Analytics service setup |
+| `LangChainTracer` (LangSmith) | LangSmith integration for LLM observability | Environment variables or session storage |
-Viewing the trace you can see a breakdown of the time spent in each retriever, llm, chain, and tool:
-
-
-
+## APMTracer
-The Evaluation interface has been updated to support adding additional metadata like `Project Name`, `Run Name`, and pulling test datasets from LangSmith. Predictions can now also be run without having to run an Evaluation, so datasets can quickly be run for manual analysis.
+The `APMTracer` integrates with Elastic APM to provide distributed tracing of LangChain operations. It creates spans for retrievers, LLMs, chains, and tools, allowing you to visualize the execution flow in APM.
-
-
-
+### Usage
+```typescript
+import { APMTracer } from '@kbn/langchain/server/tracers';
-
-
-
+const tracer = new APMTracer(
+ { projectName: 'my-project', exampleId: 'optional-example-id' },
+ logger
+);
+
+// Pass to LangChain callbacks
+const result = await chain.invoke(input, { callbacks: [tracer] });
+```
+
+### Traced Operations
+- `onRetrieverStart/End/Error` - Document retrieval operations
+- `onLLMStart/End/Error` - LLM invocations
+- `onChainStart/End/Error` - Chain executions
+- `onToolStart/End/Error` - Tool calls
### Configuring APM
@@ -33,7 +43,7 @@ First, enable the `assistantModelEvaluation` experimental feature flag by adding
xpack.securitySolution.enableExperimental: [ 'assistantModelEvaluation' ]
```
-Next, you'll need an APM server to collect the traces. You can either [follow the documentation for installing](https://www.elastic.co/guide/en/apm/guide/current/installing.html) the released artifact, or [run from source](https://github.com/elastic/apm-server#apm-server-development) and set up using the [quickstart guide provided](https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html) (be sure to install the APM Server integration to ensure the necessary indices are created! In dev environments you must click `Display beta integrations` on main Integrations page to ensure the latest package is installed.). Once your APM server is running, add your APM server configuration to your `kibana.dev.yml` as well using the following:
+Next, you'll need an APM server to collect the traces. You can either [follow the documentation for installing](https://www.elastic.co/guide/en/apm/guide/current/installing.html) the released artifact, or [run from source](https://github.com/elastic/apm-server#apm-server-development) and set up using the [quickstart guide provided](https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html) (be sure to install the APM Server integration to ensure the necessary indices are created! In dev environments you must click `Display beta integrations` on main Integrations page to ensure the latest package is installed.). Once your APM server is running, add your APM server configuration to your `kibana.dev.yml` as well using the following:
```
# APM
@@ -54,7 +64,95 @@ If using a remote APM Server/Kibana instance for viewing traces, you can set the
> If connecting to a cloud APM server (like our [ai-assistant apm deployment](https://ai-assistant-apm-do-not-delete.kb.us-central1.gcp.cloud.es.io/)), follow [these steps](https://www.elastic.co/guide/en/apm/guide/current/api-key.html#create-an-api-key) to create an API key, and then set it via `apiKey` and also set your `serverUrl` as shown in the APM Integration details within fleet.
> [!NOTE]
-> If you're an Elastic developer running Kibana from source, you can just enable APM as above, and _not_ include a `serverUrl`, and your traces will be sent to the https://kibana-cloud-apm.elastic.dev cluster.
+> If you're an Elastic developer running Kibana from source, you can just enable APM as above, and _not_ include a `serverUrl`, and your traces will be sent to the https://kibana-cloud-apm.elastic.dev cluster.
+
+### Viewing Traces
+
+If the `assistantModelEvaluation` experimental feature flag is enabled, and an APM server is configured, messages that have a corresponding trace will have an additional `View APM trace` action in the message title bar:
+
+
+
+
+
+Viewing the trace you can see a breakdown of the time spent in each retriever, llm, chain, and tool:
+
+
+
+
+
+## TelemetryTracer
+
+The `TelemetryTracer` provides event-based telemetry for tracking LangChain usage analytics. It reports events to Kibana's analytics service for monitoring assistant interactions.
+
+### Usage
+
+```typescript
+import { TelemetryTracer } from '@kbn/langchain/server/tracers';
+
+const tracer = new TelemetryTracer(
+ {
+ elasticTools: ['tool1', 'tool2'], // List of known Elastic tool names
+ telemetry: analyticsService,
+ telemetryParams: {
+ assistantStreamingEnabled: true,
+ actionTypeId: '.gen-ai',
+ isEnabledKnowledgeBase: true,
+ eventType: 'invoke_assistant',
+ model: 'gpt-4',
+ },
+ },
+ logger
+);
+
+// Pass to LangChain callbacks
+const result = await chain.invoke(input, { callbacks: [tracer] });
+```
+
+### Tracked Events
+
+- **`invoke_assistant`** - Emitted on chain completion with:
+ - Duration in milliseconds
+ - Tools invoked (with counts)
+ - Model and configuration details
+ - Knowledge base status
+
+- **`invoke_assistant_error`** - Emitted on tool errors with:
+ - Error message and location
+ - Action type and model info
+ - Configuration state
+
+### Telemetry Parameters
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `assistantStreamingEnabled` | `boolean` | Whether streaming is enabled |
+| `actionTypeId` | `string` | The connector action type ID |
+| `isEnabledKnowledgeBase` | `boolean` | Whether knowledge base is active |
+| `eventType` | `string` | The telemetry event type to report |
+| `model` | `string` (optional) | The LLM model being used |
+
+## LangSmith Tracer
+
+The LangSmith tracer integrates with [LangSmith](https://docs.smith.langchain.com/) for LLM observability and testing.
+
+### Usage
+
+```typescript
+import { getLangSmithTracer, isLangSmithEnabled } from '@kbn/langchain/server/tracers';
+
+// Check if LangSmith is enabled
+if (isLangSmithEnabled()) {
+ const tracers = getLangSmithTracer({
+ apiKey: 'your-api-key', // Optional, reads from env if not provided
+ projectName: 'my-project',
+ exampleId: 'optional-dataset-example-id',
+ logger,
+ });
+
+ // Pass to LangChain callbacks
+ const result = await chain.invoke(input, { callbacks: tracers });
+}
+```
### Configuring LangSmith
@@ -70,3 +168,30 @@ export LANGCHAIN_PROJECT="8.12 ESQL Query Generation"
If wanting to configure LangSmith in cloud or other environments where you may not have the ability to set env vars, you can set the `LangSmith Project` and `LangSmith API Key` values in session storage as outlined in https://github.com/elastic/kibana/pull/180227.
+### Dataset Integration
+
+The Evaluation interface supports adding additional metadata like `Project Name`, `Run Name`, and pulling test datasets from LangSmith. Predictions can now also be run without having to run an Evaluation, so datasets can quickly be run for manual analysis.
+
+
+
+
+
+
+
+
+
+## Combining Multiple Tracers
+
+You can use multiple tracers simultaneously by passing them all to the callbacks array:
+
+```typescript
+import { APMTracer, TelemetryTracer, getLangSmithTracer } from '@kbn/langchain/server/tracers';
+
+const tracers = [
+ new APMTracer({ projectName: 'my-project' }, logger),
+ new TelemetryTracer({ elasticTools, telemetry, telemetryParams }, logger),
+ ...getLangSmithTracer({ apiKey, projectName, logger }),
+];
+
+const result = await chain.invoke(input, { callbacks: tracers });
+```
diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/tracers/index.ts b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/index.ts
new file mode 100644
index 0000000000000..eba98c54cfc59
--- /dev/null
+++ b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/index.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { APMTracer } from './apm';
+export { TelemetryTracer } from './telemetry';
+export type { TelemetryParams, LangChainTracerFields } from './telemetry';
+export { getLangSmithTracer, isLangSmithEnabled } from './langsmith';
diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/index.ts b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/index.ts
index 079c0e9a33087..f0b33bc9b52af 100644
--- a/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/index.ts
+++ b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/index.ts
@@ -6,3 +6,4 @@
*/
export { TelemetryTracer } from './telemetry_tracer';
+export type { TelemetryParams, LangChainTracerFields } from './telemetry_tracer';
diff --git a/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/default/prompts/research_agent.ts b/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/default/prompts/research_agent.ts
index b2106fe5ac474..8506577d8b514 100644
--- a/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/default/prompts/research_agent.ts
+++ b/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/default/prompts/research_agent.ts
@@ -152,21 +152,26 @@ If plausible organizational or product-specific knowledge is involved, default t
Precedence sequence (stop at first applicable):
1. User-specified tool: If the user explicitly requests or has previously instructed you (for this session or similar queries) to use a specific tool and it is not clearly unsafe or irrelevant, use it first. If unsuitable or unavailable, skip and continue.
- 2. Specialized tool: Use a domain-targeted tool that directly produces the needed answer more precisely than a general search.
+${
+ experimentalFeatures.skills
+ ? ` 2. Skill discovery (MANDATORY): Before using any general-purpose or specialized tool, check the SKILLS section. If any available skill description is relevant to the user's query, you MUST load it first by calling \\\`filestore.read\\\` with the skill's path. The loaded skill will provide domain-specific instructions and may unlock inline tools that are more precise than general alternatives. Only proceed to the next steps after loading the relevant skill.
+ 3. Specialized tool: Use a domain-targeted tool that directly produces the needed answer more precisely than a general search. Prefer inline tools loaded from a skill over general-purpose tools.`
+ : ` 2. Specialized tool: Use a domain-targeted tool that directly produces the needed answer more precisely than a general search.`
+}
Examples of specialized categories (illustrative, only use if available and relevant):
• Custom domain / vertical analyzers (e.g., detection engineering, incident triage, attack pattern classifiers).
• External system connectors (e.g., SaaS platform search) or federated knowledge base connectors (e.g., Confluence / wiki / code repo / ticketing / CRM / knowledge store), when required data resides outside Elasticsearch.
• Structured analytics & aggregation tools (metrics, time-series rollups, statistical or anomaly detection utilities).
• Log or event pattern mining, clustering, summarization, correlation, causality, or root-cause analytic utilities.
- 3. General search fallback: If no user-specified or specialized tool applies, call \`${
+ ${experimentalFeatures.skills ? '4' : '3'}. General search fallback: If no user-specified${experimentalFeatures.skills ? ', skill,' : ''} or specialized tool applies, call \`${
tools.search
}\` (if available). **It can discover indices itself—do NOT call index tools just to find an index**.
- 4. Index inspection fallback: Use \`${tools.indexExplorer}\` or \`${
+ ${experimentalFeatures.skills ? '5' : '4'}. Index inspection fallback: Use \`${tools.indexExplorer}\` or \`${
tools.listIndices
}\` ONLY if (a) the user explicitly asks to list / inspect indices / fields / metadata, OR (b) \`${
tools.search
}\` is unavailable and structural discovery is necessary.
- 5. Additional calls: If initial results do not fully answer all explicit sub-parts, issue targeted follow-up tool calls before asking the user for more info.
+ ${experimentalFeatures.skills ? '6' : '5'}. Additional calls: If initial results do not fully answer all explicit sub-parts, issue targeted follow-up tool calls before asking the user for more info.
Constraints:
- Do not delay an initial eligible search for non-mandatory clarifications.
- **Ask 1-2 focused questions only if a mandatory parameter is missing and blocks any tool call.**
@@ -180,9 +185,13 @@ Constraints:
- If the query matches a category for bypassing research, your decision is made. Your only task is to respond in plain text to initiate the handover. Do not proceed to the next steps.
Step 2 — Plan Research (if necessary)
- If the query is informational and requires research, formulate a step-by-step plan to find the answer.
- - Parse user intent, sub-questions, entities, constraints, etc.
+ - Parse user intent, sub-questions, entities, constraints, etc.${
+ experimentalFeatures.skills
+ ? `\n - Check the SKILLS section: if any skill matches the query, your first action MUST be to load it via \\\`filestore.read\\\`.`
+ : ''
+ }
Step 3 — Execute & Iterate
- - Apply the Tool Selection Policy to execute the first step of your plan.
+ - Apply the Tool Selection Policy to execute the first step of your plan${experimentalFeatures.skills ? ' (skill loading takes priority)' : ''}.
- After each tool call, review the gathered information.
- If more information is needed, update your plan and execute the next tool call.
Step 4 — Conclude Research
diff --git a/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/utils/select_tools.ts b/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/utils/select_tools.ts
index bb0d09747e7d5..fd1996ec3e379 100644
--- a/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/utils/select_tools.ts
+++ b/x-pack/platform/plugins/shared/agent_builder/server/services/agents/modes/utils/select_tools.ts
@@ -28,7 +28,7 @@ import type { ProcessedConversation } from './prepare_conversation';
export const selectTools = async ({
conversation,
- previousDynamicToolIds,
+ previousDynamicToolIds = [],
skills,
request,
toolProvider,
@@ -37,19 +37,19 @@ export const selectTools = async ({
filestore,
spaceId,
runner,
- experimentalFeatures,
+ experimentalFeatures = { filestore: false, skills: false },
}: {
conversation: ProcessedConversation;
- previousDynamicToolIds: string[];
- skills: SkillsService;
+ previousDynamicToolIds?: string[];
+ skills?: SkillsService;
request: KibanaRequest;
toolProvider: ToolProvider;
attachmentsService: AttachmentsService;
- filestore: IFileStore;
+ filestore?: IFileStore;
agentConfiguration: AgentConfiguration;
spaceId: string;
runner: ScopedRunner;
- experimentalFeatures: ExperimentalFeatures;
+ experimentalFeatures?: ExperimentalFeatures;
}) => {
const formatContext: AttachmentFormatContext = { request, spaceId };
@@ -74,7 +74,7 @@ export const selectTools = async ({
});
// create tools for filesystem (only if feature is enabled)
- const filestoreTools = experimentalFeatures.filestore
+ const filestoreTools = experimentalFeatures.filestore && filestore
? getStoreTools({ filestore }).map((tool) => builtinToolToExecutable({ tool, runner }))
: [];
@@ -105,17 +105,19 @@ export const selectTools = async ({
request,
});
- const dynamicInlineTools = (
- await Promise.all(
- skills
- .list()
- .filter((skill) => skill.getInlineTools !== undefined)
- .map((skill) => skill.getInlineTools!())
+ const dynamicInlineTools = skills
+ ? (
+ await Promise.all(
+ skills
+ .list()
+ .filter((skill) => skill.getInlineTools !== undefined)
+ .map((skill) => skill.getInlineTools!())
+ )
)
- )
- .flat()
- .filter((tool) => previousDynamicToolIds.includes(tool.id))
- .map((tool) => skills.convertSkillTool(tool));
+ .flat()
+ .filter((tool) => previousDynamicToolIds.includes(tool.id))
+ .map((tool) => skills.convertSkillTool(tool))
+ : [];
return {
staticTools: [...dedupedStaticTools.values()],
diff --git a/x-pack/platform/plugins/shared/agent_builder/server/services/skills/prompts.ts b/x-pack/platform/plugins/shared/agent_builder/server/services/skills/prompts.ts
index 791af4f1860d2..c2ea91d8489c4 100644
--- a/x-pack/platform/plugins/shared/agent_builder/server/services/skills/prompts.ts
+++ b/x-pack/platform/plugins/shared/agent_builder/server/services/skills/prompts.ts
@@ -28,9 +28,10 @@ export const getSkillsInstructions = async ({
: [
'## SKILLS',
[
- 'Load a skill using filestore tools to get detailed instructions for a specific task.',
- 'Skills provide specialized knowledge and best practices for specific tasks.',
- "Use them when a task matches a skill's description or the skill is useful for the task.",
+ 'Before using any general-purpose tool or model knowledge, you MUST first check the available skills below.',
+ 'If ANY skill description matches or is relevant to the user query, you MUST load it by calling `filestore.read` with the skill path BEFORE calling any other tool.',
+ 'Skills provide specialized knowledge, domain-specific instructions, and access to inline tools that produce more accurate results than general-purpose alternatives.',
+ 'Skipping a relevant skill and going directly to general tools (e.g., search, execute_esql) is a protocol violation.',
'Only the skills listed here are available:',
].join(' '),
generateXmlTree({
diff --git a/x-pack/platform/plugins/shared/agent_builder/server/tracing/create_tracer.test.ts b/x-pack/platform/plugins/shared/agent_builder/server/tracing/create_tracer.test.ts
new file mode 100644
index 0000000000000..24cc92e5ba125
--- /dev/null
+++ b/x-pack/platform/plugins/shared/agent_builder/server/tracing/create_tracer.test.ts
@@ -0,0 +1,112 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { trace } from '@opentelemetry/api';
+import {
+ createTracer,
+ createTracerFactory,
+ getAgentBuilderTracer,
+ AGENT_BUILDER_TRACER_NAME,
+} from './create_tracer';
+
+describe('create_tracer', () => {
+ describe('createTracer', () => {
+ it('creates a tracer with the default name', () => {
+ const tracer = createTracer();
+
+ expect(tracer).toBeDefined();
+ // The tracer should be retrievable with the same name
+ expect(tracer).toBe(trace.getTracer(AGENT_BUILDER_TRACER_NAME));
+ });
+
+ it('creates a tracer with a custom name', () => {
+ const customName = 'agent_builder/tools';
+ const tracer = createTracer({ name: customName });
+
+ expect(tracer).toBeDefined();
+ expect(tracer).toBe(trace.getTracer(customName));
+ });
+
+ it('creates a versioned tracer', () => {
+ const version = '1.0.0';
+ const tracer = createTracer({ version });
+
+ expect(tracer).toBeDefined();
+ expect(tracer).toBe(trace.getTracer(AGENT_BUILDER_TRACER_NAME, version));
+ });
+
+ it('creates a tracer with custom name and version', () => {
+ const name = 'agent_builder/runner';
+ const version = '2.0.0';
+ const tracer = createTracer({ name, version });
+
+ expect(tracer).toBeDefined();
+ expect(tracer).toBe(trace.getTracer(name, version));
+ });
+ });
+
+ describe('getAgentBuilderTracer', () => {
+ it('returns the default agent_builder tracer', () => {
+ const tracer = getAgentBuilderTracer();
+
+ expect(tracer).toBeDefined();
+ expect(tracer).toBe(trace.getTracer(AGENT_BUILDER_TRACER_NAME));
+ });
+
+ it('returns the same tracer instance on multiple calls', () => {
+ const tracer1 = getAgentBuilderTracer();
+ const tracer2 = getAgentBuilderTracer();
+
+ expect(tracer1).toBe(tracer2);
+ });
+ });
+
+ describe('createTracerFactory', () => {
+ it('creates a factory that produces tracers with default options', () => {
+ const factory = createTracerFactory();
+
+ const tracer = factory();
+ expect(tracer).toBeDefined();
+ expect(tracer).toBe(trace.getTracer(AGENT_BUILDER_TRACER_NAME));
+ });
+
+ it('creates a factory that produces named tracers', () => {
+ const factory = createTracerFactory();
+
+ const toolsTracer = factory('agent_builder/tools');
+ const runnerTracer = factory('agent_builder/runner');
+
+ expect(toolsTracer).toBe(trace.getTracer('agent_builder/tools'));
+ expect(runnerTracer).toBe(trace.getTracer('agent_builder/runner'));
+ expect(toolsTracer).not.toBe(runnerTracer);
+ });
+
+ it('creates a factory with shared version', () => {
+ const version = '1.0.0';
+ const factory = createTracerFactory({ version });
+
+ const tracer1 = factory('agent_builder/scope1');
+ const tracer2 = factory('agent_builder/scope2');
+
+ expect(tracer1).toBe(trace.getTracer('agent_builder/scope1', version));
+ expect(tracer2).toBe(trace.getTracer('agent_builder/scope2', version));
+ });
+
+ it('falls back to default name when called without arguments', () => {
+ const factory = createTracerFactory({ version: '1.0.0' });
+
+ const tracer = factory();
+ expect(tracer).toBe(trace.getTracer(AGENT_BUILDER_TRACER_NAME, '1.0.0'));
+ });
+ });
+
+ describe('AGENT_BUILDER_TRACER_NAME', () => {
+ it('has the expected value', () => {
+ expect(AGENT_BUILDER_TRACER_NAME).toBe('agent_builder');
+ });
+ });
+});
diff --git a/x-pack/platform/plugins/shared/agent_builder/server/tracing/create_tracer.ts b/x-pack/platform/plugins/shared/agent_builder/server/tracing/create_tracer.ts
new file mode 100644
index 0000000000000..866391c1404fd
--- /dev/null
+++ b/x-pack/platform/plugins/shared/agent_builder/server/tracing/create_tracer.ts
@@ -0,0 +1,100 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { Tracer, TracerOptions } from '@opentelemetry/api';
+import { trace } from '@opentelemetry/api';
+
+/** Default tracer name for agent_builder plugin */
+export const AGENT_BUILDER_TRACER_NAME = 'agent_builder';
+
+export interface CreateTracerOptions {
+ /**
+ * Name of the tracer. This appears in spans and helps identify the source.
+ * Defaults to 'agent_builder'.
+ */
+ name?: string;
+ /**
+ * Version of the tracer/instrumentation. Typically the plugin version.
+ */
+ version?: string;
+ /**
+ * Additional OpenTelemetry tracer options.
+ */
+ tracerOptions?: TracerOptions;
+}
+
+/**
+ * Creates a tracer for the agent_builder plugin.
+ *
+ * Tracers are used to create spans for distributed tracing. Each tracer
+ * should have a unique name that identifies the instrumentation scope
+ * (e.g., 'agent_builder', 'agent_builder/tools', 'agent_builder/runner').
+ *
+ * @example
+ * ```ts
+ * // Create default agent_builder tracer
+ * const tracer = createTracer();
+ *
+ * // Create a tracer for a specific scope
+ * const toolsTracer = createTracer({ name: 'agent_builder/tools' });
+ *
+ * // Create a versioned tracer
+ * const versionedTracer = createTracer({
+ * name: 'agent_builder',
+ * version: '1.0.0'
+ * });
+ * ```
+ */
+export function createTracer(options: CreateTracerOptions = {}): Tracer {
+ const { name = AGENT_BUILDER_TRACER_NAME, version, tracerOptions } = options;
+ return trace.getTracer(name, version, tracerOptions);
+}
+
+/**
+ * Returns the default tracer for the agent_builder plugin.
+ *
+ * This is a convenience function that returns a singleton-like tracer
+ * with the default agent_builder name. Use this when you don't need
+ * a specific tracer scope.
+ *
+ * @example
+ * ```ts
+ * import { getAgentBuilderTracer } from './tracing';
+ *
+ * const tracer = getAgentBuilderTracer();
+ * tracer.startSpan('myOperation');
+ * ```
+ */
+export function getAgentBuilderTracer(): Tracer {
+ return createTracer();
+}
+
+/**
+ * Creates a factory function for tracers with a shared configuration.
+ *
+ * Useful when you need to create multiple tracers with the same base
+ * configuration (e.g., same version) but different names.
+ *
+ * @example
+ * ```ts
+ * const tracerFactory = createTracerFactory({ version: '1.0.0' });
+ *
+ * const agentTracer = tracerFactory('agent_builder/agent');
+ * const toolTracer = tracerFactory('agent_builder/tools');
+ * const runnerTracer = tracerFactory('agent_builder/runner');
+ * ```
+ */
+export function createTracerFactory(
+ baseOptions: Omit = {}
+): (name?: string) => Tracer {
+ return (name?: string) => {
+ return createTracer({
+ ...baseOptions,
+ name: name ?? AGENT_BUILDER_TRACER_NAME,
+ });
+ };
+}
diff --git a/x-pack/platform/plugins/shared/agent_builder/server/tracing/index.ts b/x-pack/platform/plugins/shared/agent_builder/server/tracing/index.ts
index 4a90b285401c1..449b689be5216 100644
--- a/x-pack/platform/plugins/shared/agent_builder/server/tracing/index.ts
+++ b/x-pack/platform/plugins/shared/agent_builder/server/tracing/index.ts
@@ -8,3 +8,10 @@
export { withAgentSpan } from './with_agent_span';
export { withConverseSpan } from './with_converse_span';
export { getCurrentTraceId } from './get_current_trace_id';
+export {
+ createTracer,
+ createTracerFactory,
+ getAgentBuilderTracer,
+ AGENT_BUILDER_TRACER_NAME,
+} from './create_tracer';
+export type { CreateTracerOptions } from './create_tracer';