-
Notifications
You must be signed in to change notification settings - Fork 685
Open
Description
Description
When using Microsoft.Agents.AI with UseOpenTelemetry(sourceName: ...) and an in-proc tool created via AIFunctionFactory.Create(...), I don’t see an execute_tool <function_name> child span in traces.
Repro
private static void ConfigureOpenTelemetry()
{
ResourceBuilder resource = ResourceBuilder.CreateDefault().AddService(serviceName: ServiceName);
tracerProvider =
Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(resourceBuilder: resource)
.AddSource(OTelSourceName)
.AddSource("Microsoft.Agents.AI")
.AddSource("Microsoft.Extensions.AI")
.AddHttpClientInstrumentation()
.AddAzureMonitorTraceExporter(o =>
{
o.ConnectionString = AppInsightsConnectionString;
})
.Build();
loggerFactory = LoggerFactory.Create(builder =>
{
builder.ClearProviders();
builder.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(resourceBuilder: resource);
options.IncludeFormattedMessage = true;
options.ParseStateValues = true;
options.AddAzureMonitorLogExporter(o =>
{
o.ConnectionString = AppInsightsConnectionString;
}); // uses env var connection string
});
});
logger = loggerFactory.CreateLogger<Program>();
}
private static async Task DoWork()
{
Uri endpoint = new Uri("<redacted>");
// Wrap the method as an in-proc tool
AIFunction doneTool = AIFunctionFactory.Create(
(Func<string, string, Task<string>>)SimpleTools.DoneAsync,
new AIFunctionFactoryOptions { Name = "mark_done", Description = "Accepts two strings and returns 'Done'." });
// Uses your Azure CLI sign-in (az login). You can swap to an API key later.
AIAgent agent = new AzureOpenAIClient(endpoint: endpoint, new AzureCliCredential())
.GetChatClient("gpt-4.1")
.CreateAIAgent("You are a concise assistant.",
tools: new List<AITool> { doneTool })
.AsBuilder()
.UseOpenTelemetry(sourceName: OTelSourceName, otel =>
{
otel.EnableSensitiveData = true;
})
.Build();
Console.WriteLine("Enter a prompt for the agent (blank line to exit).");
while (true)
{
Console.Write("You> ");
string? userInput = Console.ReadLine();
if (string.IsNullOrWhiteSpace(value: userInput))
{
break;
}
AgentRunResponse reply = await agent.RunAsync(message: userInput);
Console.WriteLine(value: reply);
}
}
public static class SimpleTools
{
[Description("Returns the literal string 'Done'.")]
public static Task<string> DoneAsync(
[Description("First parameter.")] string first,
[Description("Second parameter.")] string second)
{
Console.WriteLine($"SimpleTools.DoneAsync was called with '{first}' and '{second}'");
return Task.FromResult("Done");
}
}
Output
Enter a prompt for the agent (blank line to exit).
You> Mark #1 as done
SimpleTools.DoneAsync was called with '#1' and 'done'
#1 has been marked as done.
You>
Tool call is captured in gen_ai.output.messages but not as internal span:
[
{
"role": "assistant",
"parts": [
{
"type": "tool_call",
"id": "call_xscticFKfNJIdNQTjQDwMYgR",
"name": "mark_done",
"arguments": {
"first": "#1",
"second": "done"
}
}
],
"finish_reason": "stop"
},
{
"role": "tool",
"parts": [
{
"type": "tool_call_response",
"id": "call_xscticFKfNJIdNQTjQDwMYgR",
"response": "Done"
}
],
"finish_reason": "stop"
},
{
"role": "assistant",
"parts": [
{
"type": "text",
"content": "#1 has been marked as done."
}
],
"finish_reason": "stop"
}
]
Expected
Child span named execute_tool mark_done (following the GenAI semantic conventions: https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/?utm_source=chatgpt.com#execute-tool-span), with args/result attributes when EnableSensitiveData = true.