Design best practices to avoid duplicate messages

Note

This article describes features and behavior of the standard harness. Learn how to access standard features in Access standard agents and agent flows.

In the standard harness, several components can handle a user's request, and each one acts from its own point of view. A topic can show a message or an Adaptive Card, a tool can return data, and a child or connected agent can respond from its own context. The orchestration layer continues the plan from a context that might not match the response the user receives. When those points of view drift apart, the user might see a repeated message or a missed answer. Makers encounter this behavior more often with newer models than older ones.

Understand why duplicates occur before you design a solution. Learn more about context distribution in the standard harness.

Note

Repeated messages and duplicate messages are usually design issues related to managing context, not bugs. They occur when the user sees a response, but the component that continues the plan has different information about what has already been answered.

To understand these use cases, consider two surfaces separately: the user-visible output (what the user sees in chat) and the active context (the information a component has available when it decides what to do next). Makers don't see the active context directly. Therefore, it's important to understand each component's perspective, and use inputs and outputs to keep those perspectives aligned.

To debug a symptom in a live agent, start with Troubleshoot duplicate messages and missed answers and then return to this article for redesign guidance.

Design with context in mind

If a request is handled by a single step followed by an End all topics, context management doesn't come into play. But when a request needs to chain several components, or when the user makes more than one request in the same session, context management matters. Most real-world use cases traverse several components per session, so design accordingly.

  • One component, then End all topics. A single topic, tool, child agent, or connected agent handles the request and the session ends. If that component writes the complete answer and reports that completion, the orchestration layer has no reason to write another response.
  • Several components in the session. A session can run several components before it gives the user a complete answer. A session can also span multiple requests. One component might answer part of the request while another must answer the rest. Every component that does work must report what it did. Otherwise, a later component might act on context that appears unanswered and respond again.
Where context matters Example Sound design
No context used A topic shows the full answer in a message or Adaptive Card. The End all topics node prevents the orchestration layer from answering again.
Orchestration layer uses context A topic shows a table for part A, then the orchestration layer answers part B by using knowledge or another agent. Each component reports what it answered and returns any values that later steps need, so the orchestration layer doesn't answer twice.
Component uses context A topic answers part A to the user, then another agent handles part B. If the agent receives parent context where A looks unanswered, it answers A again. Exclude parent context on the connected agent where possible, and components return outputs that confirm what was answered and what remains.

Design components to report what happened

A component sends a repeated message or duplicate messages when the orchestration layer can't detect work that an earlier component completed. Use the same design approach every time to avoid duplicate messages—instruct each component to report its actions to the orchestration layer. For each part of the request, assign exactly one component to write the answer the user receives. Every other component does its work and returns context without writing to the user.

Use these practices together as a single design approach:

  1. Return outputs that record what happened. Outputs alone are usually enough for older models.
  2. Add an instruction to the topic or subagent description that defines what a successful run means. This approach makes the design robust.
  3. Add a top-level instruction so the orchestration layer checks those outputs before it answers. Newer models benefit most from this approach.

Only components that support custom outputs can return outputs and include description instructions. For components that don't support custom outputs, prevent duplicate messages by limiting the context they receive and by adding a top-level instruction.

Component Reports back with Fix
Topic answered (True/False), choiceReceived (True/False), and a displayed-value or summary output (Text) Return the outputs and add an instruction to the topic description. Design topics as mini-agents that avoid duplicate messages provides example topic descriptions.
Subagent (child or connected agent) answered (True/False), interactionSummary (Text), openQuestions (Text) Return the outputs, add a scoping input, and add an instruction to the subagent description. Design subagents that avoid duplicate messages provides example subagent descriptions.
Knowledge (a call to knowledge) Can't customize the context, may repeat an answer. Keep a clean top-level context and influence the requests that are sent.
Generative answers node Can't customize the context, may repeat an answer, its answer may get repeated later. Keep a clean top-level context, influence the request written to the node input, and have the host topic return the output or an answered-state output.

Design a robust top-level instruction to avoid repeated messages

Outputs keep the orchestration layer informed. Add a top-level instruction that tells newer models to check the outputs before answering.

The following top-level agent instruction is a sample designed to work across use cases, whether a component communicates with the user directly or not. Edit and customize it as needed.

Whenever any topic or agent is called, always look for the 'answered' boolean output before deciding what to reply. Topics and agents have their own channel of communication with the user. If 'answered' is true, always assume that the request has been answered appropriately using at least one of the output variables, and check which ones based on the output description. Don't give an awkward acknowledgement of the answered content. Only provide the unanswered outputs, and continue the conversation naturally with the next step.

The term channel doesn't refer to an integration channel such as Teams or a website. It's a prompting device that tells the orchestration layer the user might have already seen the answer or made a selection through another component, such as a topic, card, or a subagent. This wording most effectively prompts the model to check its response before answering.

Handle topics

A topic often communicates directly with the user by showing a message, asking a question, or presenting an Adaptive Card. The orchestration context receives the topic's text as plain text, but it doesn't record whether the user saw the it. The context also doesn't receive Adaptive Card actions or selections. If the topic answers the user but doesn't report that action to the orchestration layer, the orchestration layer treats the request as unresolved and answers it again.

Design a topic as a mini-agent. Return an answered-state output so the orchestration layer knows the request was handled, and return any value the topic displays or selection it collected that a later step needs. The outputs make the actions and results available to the rest of the plan. Add an instruction to the topic description that defines what a successful run means.

Learn more in Design topics as mini-agents that avoid duplicate messages.

Handle child and connected agents

Design child and connected agents as you design any other component. Their back-and-forth with the user is invisible to the parent, which learns what happened only through outputs, and only after the agent finishes.

Child and connected agents might also receive previously unanswered requests that remain in the parent agent's context.

For each agent, scope its task with an input, specify whether it responds to the user or stays silent, and return outputs that tell the parent agent what happened.

Learn more in Design subagents that avoid duplicate messages.

Handle knowledge

Knowledge is a top-level construct that the agent calls upon. It receives a request based on the agent design, and it receives the agent's context. Most repeated message issues occur when the agent's context lacks information from other components. Design knowledge to influence how the agent formulates the request. Make sure other components use their outputs correctly.

Handle generative answers nodes

A generative answers node lives within a topic and answers from knowledge. It receives the parent context plus whatever is passed in its input, so it behaves like top-level knowledge and augments its answer from the current context. It can write its answer directly to the chat pane or store it in a topic variable, but it can't return anything to the top-level context on its own. Like any topic content, its answer stays in the topic unless the topic returns an output.

A generative answers node needs no special handling beyond the rule that applies to every topic: pass the result as a topic output. If the node answers the user in the topic, add an answered-state output and a displayed-value output. These outputs give the orchestration layer a record that the request was answered and prevent a later step from answering the request again.

Ensure descriptions and instructions match the point of view

Descriptions and instructions are also part of the context contract.

This instruction routes to the topic:

When the user asks about their account balance, call the Account balance topic.

This instruction routes and assigns answer ownership to the orchestration layer:

When the user asks about their account balance, call the Account balance topic and give the balance.

If the topic already shows the balance, the second instruction creates a second answer path. If the topic doesn't return balanceValue, the orchestration layer might call the topic again or answer that it doesn't know the value.

Use descriptions and instructions that match the point of view:

  • A topic description helps the orchestration layer decide when and how to use the topic. It also follows instructions about what to do or output next.
  • A connected-agent description comes from the parent point of view.
  • A connected-agent instruction is read from the connected agent's point of view.
  • A topic or connected-agent output description tells the orchestration layer how to interpret the returned value.

For a topic that shows an answer to the user and sets answered=true, describe both when to route to the topic and what a successful run means:

This topic handles account balance requests.
If its answered output is true, the user has already received their response and it should not be answered again.

For a topic that only writes outputs, describe both when to route to the topic and how to reply:

This topic handles account balance requests and responds with the balance value in italics.

Restore context in long conversations

A value that a component had can leave the active context in two ways. In a long session, a value that was available a few turns ago might no longer be in the active context. Or a component fetches a complete result, uses the relevant part to produce an answer, and returns only that answer so that the rest of the result never reaches the orchestration layer. Either way, the agent might fetch the data again or ask the user for information it already has, which the user experiences as a missed answer. The orchestration layer acts as if it never received the value.

Design for this scenario by returning and saving important context and serving it back when needed:

  • Return a complete result, not just the part used to answer. A fetched result often has more than one field, many rows, and long text. Return all data a later turn might need as outputs, so the orchestration layer has it without refetching.

  • Save and serve values across turns. At the right points, before or after a tool call, route to a topic that can store or serve the value through its inputs and outputs and a global variable, so the plan doesn't fetch the value or ask for it again.

Tip

Seamless agentic behavior is predicated on an agent design that accounts for context at every step and from every point of view.