Summary
The Python Anthropic provider appears to have two related serialization gaps at the final Anthropic request boundary:
- Provider-specific structured system blocks for Anthropic prompt caching are forwarded as a raw request kwarg instead of being translated into Anthropic
system blocks.
- SDK messages that contain an assistant message with interleaved
function_call, function_result, function_call content are serialized as one Anthropic assistant message containing tool_use, tool_result, tool_use. Anthropic expects tool results on the user/tool-result side of the transcript, immediately following the corresponding assistant tool use.
I traced through the public SDK path, not just private helper methods:
Agent.run(...) -> Agent._prepare_run_context(...) -> Agent._call_chat_client(...) -> BaseChatClient.get_response(...) -> Anthropic _inner_get_response(...) -> anthropic_client.beta.messages.create(...)
The generic SDK layers preserve provider-specific options and messages; the final Anthropic provider serializer is where these need to be handled.
Environment checked
- Repo:
microsoft/agent-framework
- Branch checked: current
main as of 2026-06-10, also consistent with Python release python-1.8.1
- Package path:
python/packages/anthropic/agent_framework_anthropic/_chat_client.py
- Anthropic SDK: current provider dependency uses
anthropic>=0.80.0,<0.80.1
Issue 1: anthropic_system_blocks leaks to Anthropic API kwargs
Anthropic prompt caching requires structured system blocks with cache_control, for example:
[
{"type": "text", "text": "stable instructions", "cache_control": {"type": "ephemeral", "ttl": "1h"}},
{"type": "text", "text": "per request context"},
]
The provider currently copies unknown options into run_options:
run_options = {
k: v for k, v in options.items() if v is not None and k not in {"instructions", "response_format"}
}
So an option such as anthropic_system_blocks remains in the final kwargs sent to beta.messages.create(...), and system remains unset.
Expected
The provider should consume this provider-specific option, exclude it from raw request kwargs, and map it to Anthropic system blocks. If both generic instructions and structured system blocks are supplied, generic instructions should either be appended as a text block or a clear precedence rule should be documented.
Actual full-path capture
Using a dummy Anthropic client that captures the final kwargs from beta.messages.create(...) after Agent.run(...):
FULL_AGENT_PROMPT_CACHE_TRACE
has_anthropic_system_blocks= True
system= None
message_roles= ['user']
This means the raw Anthropic API call receives an unexpected anthropic_system_blocks kwarg and no structured system blocks.
Issue 2: assistant-embedded function results serialize to assistant-role tool_result
When replay/history contains one SDK assistant message with interleaved function calls and results, the Anthropic provider serializes that directly as one assistant message.
Repro shape
Message(
role="assistant",
contents=[
Content.from_function_call(call_id="call_1", name="first", arguments="{}"),
Content.from_function_result(call_id="call_1", result="ok"),
Content.from_function_call(call_id="call_2", name="second", arguments="{}"),
],
)
Actual full-path capture
Using the public client.get_response(...) path with a dummy Anthropic client:
FULL_CLIENT_TOOL_RESULT_TRACE
roles= ['assistant']
types= [['tool_use', 'tool_result', 'tool_use']]
Expected
The Anthropic provider should split assistant-embedded function results into Anthropic-valid role groups before wire serialization, for example:
roles= ['assistant', 'user', 'assistant']
types= [['tool_use'], ['tool_result'], ['tool_use']]
This is related to prior Anthropic tool-call role issues, but it is not the same shape as a tool_use incorrectly landing in a user message. Here, the remaining invalid shape is tool_result being embedded in an assistant message during replay/history serialization.
Why this matters
The SDK layers between Agent.run(...) and the provider do not currently fix either shape:
Agent._prepare_run_context(...) preserves remaining provider-specific options through **opts.
BaseChatClient.get_response(...) only performs compaction/annotation before _inner_get_response(...).
- The Anthropic provider then serializes the messages/options directly into
beta.messages.create(...) kwargs.
So consumers cannot get Anthropic prompt caching through the standard MAF options path, and replay/history can produce invalid Anthropic tool-result role ordering unless every application normalizes this outside the provider.
Summary
The Python Anthropic provider appears to have two related serialization gaps at the final Anthropic request boundary:
systemblocks.function_call,function_result,function_callcontent are serialized as one Anthropicassistantmessage containingtool_use,tool_result,tool_use. Anthropic expects tool results on the user/tool-result side of the transcript, immediately following the corresponding assistant tool use.I traced through the public SDK path, not just private helper methods:
Agent.run(...) -> Agent._prepare_run_context(...) -> Agent._call_chat_client(...) -> BaseChatClient.get_response(...) -> Anthropic _inner_get_response(...) -> anthropic_client.beta.messages.create(...)The generic SDK layers preserve provider-specific options and messages; the final Anthropic provider serializer is where these need to be handled.
Environment checked
microsoft/agent-frameworkmainas of 2026-06-10, also consistent with Python releasepython-1.8.1python/packages/anthropic/agent_framework_anthropic/_chat_client.pyanthropic>=0.80.0,<0.80.1Issue 1:
anthropic_system_blocksleaks to Anthropic API kwargsAnthropic prompt caching requires structured
systemblocks withcache_control, for example:[ {"type": "text", "text": "stable instructions", "cache_control": {"type": "ephemeral", "ttl": "1h"}}, {"type": "text", "text": "per request context"}, ]The provider currently copies unknown options into
run_options:So an option such as
anthropic_system_blocksremains in the final kwargs sent tobeta.messages.create(...), andsystemremains unset.Expected
The provider should consume this provider-specific option, exclude it from raw request kwargs, and map it to Anthropic
systemblocks. If both genericinstructionsand structured system blocks are supplied, generic instructions should either be appended as a text block or a clear precedence rule should be documented.Actual full-path capture
Using a dummy Anthropic client that captures the final kwargs from
beta.messages.create(...)afterAgent.run(...):This means the raw Anthropic API call receives an unexpected
anthropic_system_blockskwarg and no structuredsystemblocks.Issue 2: assistant-embedded function results serialize to assistant-role
tool_resultWhen replay/history contains one SDK assistant message with interleaved function calls and results, the Anthropic provider serializes that directly as one assistant message.
Repro shape
Actual full-path capture
Using the public
client.get_response(...)path with a dummy Anthropic client:Expected
The Anthropic provider should split assistant-embedded function results into Anthropic-valid role groups before wire serialization, for example:
This is related to prior Anthropic tool-call role issues, but it is not the same shape as a
tool_useincorrectly landing in a user message. Here, the remaining invalid shape istool_resultbeing embedded in an assistant message during replay/history serialization.Why this matters
The SDK layers between
Agent.run(...)and the provider do not currently fix either shape:Agent._prepare_run_context(...)preserves remaining provider-specific options through**opts.BaseChatClient.get_response(...)only performs compaction/annotation before_inner_get_response(...).beta.messages.create(...)kwargs.So consumers cannot get Anthropic prompt caching through the standard MAF options path, and replay/history can produce invalid Anthropic tool-result role ordering unless every application normalizes this outside the provider.