Tutorials/Copilot Studio/Building a Generative Orchestration Agent with Copilot Studio: A Step-by-Step Guide
Copilot Studiointermediate

Building a Generative Orchestration Agent with Copilot Studio: A Step-by-Step Guide

Discover how to create a dynamic, multi-turn Copilot Studio agent that uses generative orchestration to manage attendee lists, confirmations, and availability searches—without rigid conversational flows.

NA
Narmer Abader
@narmer · Published June 3, 2026

Imagine an agent that can understand free-form user input, decide which topics to invoke, and adapt its behavior based on conversation context – all without you writing explicit branching logic. That’s the promise of generative orchestration in Microsoft Copilot Studio. Instead of painstakingly chaining topics together with conditions and flows, you provide high-level instructions and let the AI orchestrate the conversation. In this guide, I’ll walk you through building a fully functional Attendee Coordinator Agent that collects meeting attendees, resolves their email addresses, confirms the list, searches for availability, and presents the results – all driven by generative orchestration.

The Scenario: Attendee Coordinator Agent

The agent’s job is to help a user schedule a meeting with a group of colleagues. The flow feels natural:

  1. The user provides one or more attendee names (any format – “John and Sarah” or “John, Sarah, Bob”).
  2. The agent resolves each name to a valid email address using the Microsoft 365 Users connector.
  3. The agent asks the user to confirm the list, showing names and emails.
  4. After confirmation, the agent gathers a date range and meeting duration.
  5. The agent finds available time slots and displays them in a clear, grouped list.
  6. The user can add or remove attendees, change the date range, or start over.

Throughout the conversation, the agent avoids hardcoded messages – it relies on the orchestrator to generate responses based on the instructions and user input.

1. Creating the Agent and Writing Instructions

Open Copilot Studio and create a new agent. Give it a descriptive name like Attendee Coordinator and a description that clearly states its purpose. The real magic lies in the Instructions field, where you define both general rules and a step‑by‑step process.

Writing Effective Instructions

Instructions are the backbone of generative orchestration. They must be specific, ordered, and cover edge cases. Avoid ambiguous language or contradictory commands.

Below is an example instruction set that balances structure with flexibility. Paste it into the agent’s instructions area.

textAgent Instructions (example)
General Instructions
- Assume the current year for any dates unless the user explicitly provides a year.
- Convert all UTC times to the user's time zone specified by {Global.userTimeZone}.
- Do not engage in topics outside of meeting scheduling. Politely redirect if asked.

Steps to Coordinate Attendees
1. Collect Attendee Names: Ask the user for the names of all meeting participants.
2. Resolve to Emails: For each name, call the "Resolve Person" topic. Include the user’s own name/email as the meeting organizer.
3. Confirm List: After any addition or removal, invoke the "Confirm Attendees" topic.
4. Find Available Times: Once the list is confirmed, run the "Find Slots" topic. Do not run this in the same turn as step 3.
5. Display Results: Present available times in a bulleted list, grouped by date, sorted oldest to newest, in the user’s time zone.
6. Next Actions: Ask the user if they want to modify the list, change the time range, or start over. Always reconfirm the list after a change.

Global Variable Setup

Before building topics, you need a global variable to hold the attendee list. I’ll use a table variable named colAttendees with columns DisplayName (Text) and Email (Text). Initialize it with an empty schema during the conversation start.

powerfxInitialize Empty Attendee Table
FirstN(
  Table(
      {DisplayName: "Placeholder", Email: "placeholder@example.com"}
  ),
  0
)

This returns an empty table that preserves the required columns. Also create a Boolean global isListUpdated and set it to false.

2. Building the Custom Topics

You will build three main custom topics. The system topic Conversation Start will also be modified.

2.1 Conversation Start (System Topic)

In the Conversation Start system topic, add the initialization formulas above. Then send a welcome message and an opening question, such as “Who would you like to invite to the meeting?”

2.2 Resolve Person Topic

The orchestrator invokes this topic when it detects person names in the user’s message. Add a trigger phrase such as "add [personName] to the list" and create an input variable varPersonName of type String with its Identify as setting set to the Person Name prebuilt entity. The orchestrator will pass each name it detects.

Inside the topic, add an Office 365 Users – Search for users (V2) action. Use varPersonName as the search term. Then, using a Modify items in a list node, append the top result to Global.colAttendees:

powerfxAdd Found User to Attendee List
{
  DisplayName: First(Topic.SearchUserV2.value).DisplayName,
  Email: First(Topic.SearchUserV2.value).UserPrincipalName
}

After the append, set Global.isListUpdated to true. If no user is found, do not add a message node – let the orchestrator inform the user and ask for clarification. This keeps the agent responsive without hardcoded text.

2.3 Confirm Attendees Topic

Create a new topic named Confirm Attendees. At the start of this topic, add a Condition node checking that Global.isListUpdated equals true, and redirect to the previous topic if it is false. The orchestrator decides when to invoke this topic based on your instructions, but the flag gives you a runtime guard to prevent it running prematurely.

Generate a readable HTML list from the colAttendees table:

powerfxFormat Attendees as HTML Bulleted List
"<ul>" &
Concat(
  Global.colAttendees,
  $"<li>{DisplayName} ({Email})</li>"
) &
"</ul>"

Assign the result to a string variable. Then send a message like:

Here are the attendees I found: [formattedList] Is this correct?

If the user answers Yes, set Global.isListUpdated to false and redirect the conversation to the Find Slots topic. If No, let the orchestrator handle the follow‑up – it will ask the user to add or remove names.

Trigger Condition

Always use a flag like isListUpdated to control when this topic runs. Without it, the topic could loop unnecessarily after every small change.

2.4 Find Slots Topic (High‑Level)

The Find Slots topic collects the date range and meeting duration. The simplest approach is to ask two questions:

  • “How many minutes should the meeting last?”
  • “What are the start and end dates for the search?”

Then use a connector (e.g., Microsoft Graph via HTTP or a custom connector) to query free/busy information for all attendees. For demonstration, you can simulate the results.

After obtaining the available slots, present them in a bulleted list grouped by date and sorted chronologically.

powerfxSort and Group Available Slots
SortByColumns(
  GroupBy(colAvailableSlots, "Date", "Slots"),
  "Date",
  SortOrder.Ascending
)

Feed this list into a message node.

3. System Topic Adjustments

3.1 Modifying the Conversation Start Topic

As mentioned, add the initialization and welcome message here. Also, ensure the Conversational Boosting system topic remains active and well‑configured.

3.2 Disabling Unnecessary System Topics

Turn off the following system topics to prevent interference with generative orchestration:

  • Escalate
  • Goodbye
  • Next Steps
  • Sign‑out (if present)

Keep On Error and Sign-In as‑is. The orchestrator will handle off‑topic conversations using the instructions you provided.

4. Security and Performance Considerations

  • Microsoft 365 Permissions: The Office 365 Users connector requires admin consent and appropriate Graph API permissions. Ensure the agent is configured to use a connection that can read user profiles.
  • Variable Scoping: Use global variables only for state that must persist across topics. All other data should remain local to a topic.
  • Delegation: If you later switch to Dataverse for storing attendee lists, remember that functions like Concat and GroupBy may not fully delegate. For typical attendee lists (fewer than 100 people), this is rarely a concern.
  • Error Handling: Let generative orchestration handle unexpected responses. Test edge cases: multiple names in one message, misspelled names, empty lists.

5. Common Mistakes and Troubleshooting

MistakeSolution
Variable not initialized before useEnsure the global table is created in the Conversation Start topic before any other topic runs.
Topic fails to triggerDouble‑check trigger conditions and entity types. The input variable must have the correct Identify as setting (e.g., Person Name) so the orchestrator knows what to pass.
Infinite loops or repeated confirmationsReview the isListUpdated flag logic. It should be reset to false only after the list is confirmed.
HTML list not renderingUse a Text variable to store the HTML string, then output it directly in a message node. Copilot Studio renders simple HTML like <ul> and <li>.

Testing tip: Run each topic individually in the test pane before testing the full orchestrated flow. Use the conversation map after a test run to see which topics were invoked and in what order.

6. Final Recommendation

Generative orchestration enables a new class of conversational agents that feel proactive and natural. The key is investing time in clear, structured instructions and letting the AI handle the rest. Start with a well‑scoped scenario like attendee coordination, use global variables to maintain state, and test iteratively. You will be surprised how much you can accomplish with minimal hardcoded logic.

References