Python: Fix duplicate messages in Handoff workflow#4714
Python: Fix duplicate messages in Handoff workflow#4714LEDazzio01 wants to merge 1 commit intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes duplicated conversation history being sent to Chat Completions-based agents in HandoffBuilder workflows by preventing history providers from re-injecting previously stored messages, and adds a regression test to validate the behavior.
Changes:
- Strip
BaseHistoryProviderinstances from cloned handoff agents’context_providersand add a no-opInMemoryHistoryProviderplaceholder to prevent default history auto-injection. - Add a regression test using a
CapturingChatClientto ensure messages are not duplicated after a handoff + resume. - Add two contribution log markdown files under
contribution-logs/.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| python/packages/orchestrations/agent_framework_orchestrations/_handoff.py | Prevents duplicate message injection by filtering history providers during agent cloning. |
| python/packages/orchestrations/tests/test_handoff.py | Adds a regression test that inspects messages passed to the specialist client after resume. |
| contribution-logs/2026-03-14.md | Adds a contribution status log entry (appears unrelated to handoff fix). |
| contribution-logs/2026-03-15.md | Adds a contribution status log entry (appears unrelated to handoff fix). |
You can also share your feedback on Copilot code review. Take the survey.
python/packages/orchestrations/agent_framework_orchestrations/_handoff.py
Outdated
Show resolved
Hide resolved
Strip BaseHistoryProvider instances from cloned agents in HandoffBuilder._clone_chat_agent(). The HandoffAgentExecutor manages conversation state via _full_conversation, so InMemoryHistoryProvider was re-injecting previously stored messages on each agent.run() call, causing the entire conversation to appear twice in the API request. A no-op InMemoryHistoryProvider placeholder (load_messages=False, store_inputs=False, store_outputs=False) prevents the agent from auto-injecting a default one at runtime. Fixes microsoft#4695
2edacac to
eb95a94
Compare
|
Thanks for the review, Copilot! All feedback has been addressed in the rebased commit:
|
Motivation and Context
Fixes #4695
When using
HandoffBuilderwithAzureOpenAIChatClient(or any Chat Completions-based client), the refund agent's conversation history shows every message duplicated. The user reports 18 messages where only 9 are expected.Root Cause
HandoffBuilder._clone_chat_agent()preserves the original agent'scontext_providers, includingInMemoryHistoryProvider. TheHandoffAgentExecutormanages its own conversation state via_full_conversationand_cache. When the executor callsagent.run(self._cache, session=self._session):_cachealready contains the full conversation from_full_conversationInMemoryHistoryProvider.before_run()loads all previously stored messages from session state intocontext_messagessession_messages = context_messages + input_messages→ the entire conversation appears twiceThis is the same root cause identified in #4411. PR #4412 proposed the same fix but was closed without merging.
Description
BaseHistoryProviderinstances from the cloned agent'scontext_providersin_clone_chat_agent(), since the executor already manages conversation historyInMemoryHistoryProviderplaceholder (load_messages=False,store_inputs=False,store_outputs=False) to prevent the agent from auto-injecting a default one at runtimetest_no_duplicate_messages_after_handoff_and_resume) that verifies no duplicate messages are sent to the agent after a handoff + user reply sequence, using aCapturingChatClientthat records the exact messages passed to each API callContribution Checklist