Description
AIAgent.AsAIFunction() (agent-as-tool) fails when the parent agent runs under Foundry hosting (AddFoundryResponses / MapFoundryResponses) and the child agent is a Responses-backed ChatClientAgent created with AIProjectClient.AsAIAgent(...).
Cause, as far as I can tell from the source:
- The Foundry hosting layer runs the parent agent with an override
ChatHistoryProvider in AgentRunOptions.AdditionalProperties (the Responses protocol's own history store).
AsAIFunction deliberately forwards the parent run's AdditionalProperties to the child run (AgentExtensions.cs: FunctionInvokingChatClient.CurrentContext?.Options?.AdditionalProperties → new AgentRunOptions { AdditionalProperties = dict }).
- The child is bound to the project's Responses API, so the service returns a conversation id.
ChatClientAgent.ResolveChatHistoryProvider then sees both a ConversationId and an override ChatHistoryProvider and throws.
Each rule is fine on its own; the combination makes the documented agent-as-tool pattern (sample Agent_Step09_AsFunctionTool) unusable inside a hosted agent. The same code works when the parent is run directly with agent.RunAsync(...) from a console app, since nothing injects the override.
Two smaller points that made this hard to diagnose:
- The function-invoking client swallows the exception and returns the tool result
Error: Function failed. to the model, with nothing logged. I only found the cause by wrapping the function in a DelegatingAIFunction that catches and prints the exception.
- Forwarding the parent's
ChatHistoryProvider to a child is arguably never desired: the child would read and write the parent's conversation history.
Expected: child.AsAIFunction() works for a Responses-backed child under Foundry hosting, or at least there is a way to opt out of the AdditionalProperties propagation.
Workaround: replace AsAIFunction with a hand-written AIFunction that calls child.RunAsync(task, cancellationToken: ct) with no session and no run options.
Code Sample
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry.Hosting;
using Microsoft.Extensions.AI;
var endpoint = new Uri(Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")!);
var model = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME")!;
var client = new AIProjectClient(endpoint, new DefaultAzureCredential());
AIAgent child = client.AsAIAgent(
model: model,
instructions: "Answer the question briefly.",
name: "child",
description: "Answers one question.");
AIAgent parent = client.AsAIAgent(
model: model,
instructions: "Use the child tool to answer any question.",
name: "parent",
tools: [child.AsAIFunction()]);
var builder = AgentHost.CreateBuilder(args);
builder.Services.AddFoundryResponses(parent);
builder.RegisterProtocol("responses", endpoints => endpoints.MapFoundryResponses());
builder.Build().Run();
Run with azd ai agent run, then azd ai agent invoke <name> --local 'Ask the child what 2+2 is'. The parent reports that the tool returned Error: Function failed.
Error Messages / Stack Traces
System.InvalidOperationException: Only ConversationId or ChatHistoryProvider may be used, but not both. A ConversationId indicating server-side chat history management is present (on the ChatClientAgentSession or the ChatOptions), but an override ChatHistoryProvider was provided via AdditionalProperties.
at Microsoft.Agents.AI.ChatClientAgent.ResolveChatHistoryProvider(ChatClientAgentSession session, ChatOptions chatOptions)
at Microsoft.Agents.AI.ChatClientAgent.NotifyProvidersOfNewMessagesAsync(ChatClientAgentSession session, IEnumerable`1 requestMessages, IEnumerable`1 responseMessages, ChatOptions chatOptions, CancellationToken cancellationToken)
at Microsoft.Agents.AI.ChatClientAgent.RunCoreAsync(IEnumerable`1 messages, AgentSession session, AgentRunOptions options, CancellationToken cancellationToken)
at Microsoft.Agents.AI.AIAgent.RunAsync(IEnumerable`1 messages, AgentSession session, AgentRunOptions options, CancellationToken cancellationToken)
at Microsoft.Agents.AI.AIAgentExtensions.<>c__DisplayClass1_0.<<AsAIFunction>g__InvokeAgentAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Extensions.AI.AIFunctionFactory.ReflectionAIFunctionDescriptor.<>c__DisplayClass41_1.<<GetReturnParameterMarshaller>b__12>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Extensions.AI.AIFunctionFactory.ReflectionAIFunction.InvokeCoreAsync(AIFunctionArguments arguments, CancellationToken cancellationToken)
(Captured by wrapping the function in a DelegatingAIFunction; without that the only visible symptom is the tool result Error: Function failed.)
Package Versions
- Microsoft.Agents.AI.Foundry.Hosting 1.20.0-preview.260831.1
- Microsoft.Agents.AI.Foundry 1.20.0-preview.260831.1
- Microsoft.Agents.AI 1.20.0
- Microsoft.Agents.AI.Abstractions 1.20.0
- Azure.AI.AgentServer.Core 1.0.0-beta.28
- Azure.AI.AgentServer.Responses 1.0.0-beta.8
- Azure.AI.Projects 3.0.0-beta.1
.NET Version
.NET 10.0.100 SDK, net10.0 target, Windows 11 (local azd ai agent run; not yet verified in the hosted container, but the hosting layer is the same)
Additional Context
Not the same as #4937: there the agent itself has a ChatHistoryProvider configured and the request is stateless. Here the child agent has no provider of its own; the override arrives via AsAIFunction forwarding the hosted parent's run options.
Description
AIAgent.AsAIFunction()(agent-as-tool) fails when the parent agent runs under Foundry hosting (AddFoundryResponses/MapFoundryResponses) and the child agent is a Responses-backedChatClientAgentcreated withAIProjectClient.AsAIAgent(...).Cause, as far as I can tell from the source:
ChatHistoryProviderinAgentRunOptions.AdditionalProperties(the Responses protocol's own history store).AsAIFunctiondeliberately forwards the parent run'sAdditionalPropertiesto the child run (AgentExtensions.cs:FunctionInvokingChatClient.CurrentContext?.Options?.AdditionalProperties→new AgentRunOptions { AdditionalProperties = dict }).ChatClientAgent.ResolveChatHistoryProviderthen sees both aConversationIdand an overrideChatHistoryProviderand throws.Each rule is fine on its own; the combination makes the documented agent-as-tool pattern (sample
Agent_Step09_AsFunctionTool) unusable inside a hosted agent. The same code works when the parent is run directly withagent.RunAsync(...)from a console app, since nothing injects the override.Two smaller points that made this hard to diagnose:
Error: Function failed.to the model, with nothing logged. I only found the cause by wrapping the function in aDelegatingAIFunctionthat catches and prints the exception.ChatHistoryProviderto a child is arguably never desired: the child would read and write the parent's conversation history.Expected:
child.AsAIFunction()works for a Responses-backed child under Foundry hosting, or at least there is a way to opt out of theAdditionalPropertiespropagation.Workaround: replace
AsAIFunctionwith a hand-writtenAIFunctionthat callschild.RunAsync(task, cancellationToken: ct)with no session and no run options.Code Sample
Run with
azd ai agent run, thenazd ai agent invoke <name> --local 'Ask the child what 2+2 is'. The parent reports that the tool returnedError: Function failed.Error Messages / Stack Traces
(Captured by wrapping the function in a
DelegatingAIFunction; without that the only visible symptom is the tool resultError: Function failed.)Package Versions
.NET Version
.NET 10.0.100 SDK,
net10.0target, Windows 11 (localazd ai agent run; not yet verified in the hosted container, but the hosting layer is the same)Additional Context
Not the same as #4937: there the agent itself has a
ChatHistoryProviderconfigured and the request is stateless. Here the child agent has no provider of its own; the override arrives viaAsAIFunctionforwarding the hosted parent's run options.