Skip to content

Commit 1a187b0

Browse files
authored
Python: Introduce the Copilot Studio Agent (microsoft#11851)
### Motivation and Context Semantic Kernel Python currently has a demo showing how to interact with a Copilot Studio Agent; however, that uses the `DirectLine API`. This PR introduces a Semantic Kernel `CoilotStudioAgent` that leverages the team's new `microsoft.agents.copilotstudio.client`. One must first have a Copilot Studio Agent created via the portal; and once ready, they can reference it with the proper App Registration and Agent metadata. Interacting with the Agent can leverage Microsoft Copilot Studio's knowledge sources and internal tools. <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> ### Description Add support for the `CopilotStudioAgent`. - Provide getting started samples - Introduces a detailed README on what is required for setup and interacting with the agent. - The current version of the `CopilotStudioAgent` does not support calling another Copilot Studio Agent via a skill. - Authentication for the agent is limited to interactive right now. Once service-to-service auth is support via the client, we will add this into SK. - Closes microsoft#11607 <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [X] The code builds clean without any errors or warnings - [X] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [X] All unit tests pass, and I have added new tests where possible - [X] I didn't break anyone 😄
1 parent 7dcd99e commit 1a187b0

16 files changed

Lines changed: 1804 additions & 355 deletions

‎python/.vscode/launch.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
"configurations": [
77
{
88
"name": "Python: Current File",
9-
"type": "python",
9+
"type": "debugpy",
1010
"request": "launch",
1111
"program": "${file}",
1212
"console": "integratedTerminal",
13-
"justMyCode": false
13+
"justMyCode": false,
14+
"purpose": ["debug-in-terminal"]
1415
},
1516
{
1617
"name": "Python FastAPI app with Dapr",

‎python/pyproject.toml‎

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,15 @@ dependencies = [
5454

5555
### Optional dependencies
5656
[project.optional-dependencies]
57+
anthropic = [
58+
"anthropic ~= 0.32"
59+
]
5760
autogen = [
5861
"autogen-agentchat >= 0.2, <0.4"
5962
]
63+
aws = [
64+
"boto3>=1.36.4,<1.38.0",
65+
]
6066
azure = [
6167
"azure-ai-inference >= 1.0.0b6",
6268
"azure-ai-projects >= 1.0.0b7",
@@ -67,13 +73,24 @@ azure = [
6773
chroma = [
6874
"chromadb >= 0.5,< 1.1"
6975
]
70-
google = [
71-
"google-cloud-aiplatform == 1.89.0",
72-
"google-generativeai ~= 0.8"
76+
# Temporary: use direct Test PyPI links until packages are published to Public PyPI
77+
# Once published, these will simplify to just "microsoft-agents-core" and "microsoft-agents-copilotstudio-client"
78+
copilot_studio = [
79+
"microsoft-agents-core @ https://test-files.pythonhosted.org/packages/17/ae/c87bfb943e75fac50522e2598232fc386b0d7f09a2dd462bbdc63cb83602/microsoft_agents_core-0.0.0a2-py3-none-any.whl",
80+
"microsoft-agents-copilotstudio-client @ https://test-files.pythonhosted.org/packages/47/de/9f9e0a0c57132363154dcf197bf2ce0ed33e9c986a8df8573091bcc79b54/microsoft_agents_copilotstudio_client-0.0.0a2-py3-none-any.whl"
81+
]
82+
dapr = [
83+
"dapr>=1.14.0",
84+
"dapr-ext-fastapi>=1.14.0",
85+
"flask-dapr>=1.14.0",
7386
]
7487
faiss = [
7588
"faiss-cpu>=1.10.0"
7689
]
90+
google = [
91+
"google-cloud-aiplatform == 1.89.0",
92+
"google-generativeai ~= 0.8"
93+
]
7794
hugging_face = [
7895
"transformers[torch] ~= 4.28",
7996
"sentence-transformers >= 2.2,< 5.0",
@@ -82,28 +99,28 @@ hugging_face = [
8299
mcp = [
83100
"mcp ~= 1.6"
84101
]
85-
mongo = [
86-
"pymongo >= 4.8.0, < 4.13",
87-
"motor >= 3.3.2,< 3.8.0"
88-
]
89-
notebooks = [
90-
"ipykernel ~= 6.29"
91-
]
92102
milvus = [
93103
"pymilvus >= 2.3,< 2.6",
94104
"milvus >= 2.3,<2.3.8; platform_system != 'Windows'"
95105
]
96106
mistralai = [
97107
"mistralai >= 1.2,< 2.0"
98108
]
109+
mongo = [
110+
"pymongo >= 4.8.0, < 4.13",
111+
"motor >= 3.3.2,< 3.8.0"
112+
]
113+
notebooks = [
114+
"ipykernel ~= 6.29"
115+
]
99116
ollama = [
100117
"ollama ~= 0.4"
101118
]
102119
onnx = [
103120
"onnxruntime-genai ~= 0.5; python_version < '3.13'"
104121
]
105-
anthropic = [
106-
"anthropic ~= 0.32"
122+
pandas = [
123+
"pandas ~= 2.2"
107124
]
108125
pinecone = [
109126
"pinecone[asyncio, grpc] ~= 6.0"
@@ -119,31 +136,20 @@ redis = [
119136
"types-redis ~= 4.6.0.20240425",
120137
"redisvl ~= 0.4"
121138
]
122-
usearch = [
123-
"usearch ~= 2.16",
124-
"pyarrow >= 12.0,< 20.0"
125-
]
126-
weaviate = [
127-
"weaviate-client>=4.10,<5.0",
128-
]
129-
pandas = [
130-
"pandas ~= 2.2"
131-
]
132-
aws = [
133-
"boto3>=1.36.4,<1.38.0",
134-
]
135-
dapr = [
136-
"dapr>=1.14.0",
137-
"dapr-ext-fastapi>=1.14.0",
138-
"flask-dapr>=1.14.0"
139-
]
140139
realtime = [
141140
"websockets >= 13, < 16",
142141
"aiortc>=1.9.0",
143142
]
144143
sql = [
145144
"pyodbc >= 5.2"
146145
]
146+
usearch = [
147+
"usearch ~= 2.16",
148+
"pyarrow >= 12.0,< 20.0"
149+
]
150+
weaviate = [
151+
"weaviate-client>=4.10,<5.0",
152+
]
147153

148154
[tool.uv]
149155
prerelease = "if-necessary-or-explicit"
@@ -240,4 +246,4 @@ name = "semantic_kernel"
240246

241247
[build-system]
242248
requires = ["flit-core >= 3.9,<4.0"]
243-
build-backend = "flit_core.buildapi"
249+
build-backend = "flit_core.buildapi"
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Semantic Kernel - CopilotStudioAgent Quickstart
2+
3+
This README provides an overview on how to use the [CopilotStudioAgent](../../../semantic_kernel/agents/copilot_studio/copilot_studio_agent.py) within Semantic Kernel.
4+
This agent allows you to interact with Microsoft Copilot Studio agents through programmatic APIs.
5+
6+
> ℹ️ **Note:** Knowledge sources must be configured **within** Microsoft Copilot Studio first. Streaming responses are **not currently supported**.
7+
8+
---
9+
10+
## 🔧 Prerequisites
11+
12+
1. Python 3.10+
13+
2. Install Semantic Kernel with Copilot Studio dependencies:
14+
```bash
15+
pip install semantic-kernel[copilot_studio]
16+
```
17+
3. An agent created in **Microsoft Copilot Studio**
18+
4. Ability to create an application identity in Azure for a **Public Client/Native App Registration**,
19+
or access to an existing app registration with the `CopilotStudio.Copilots.Invoke` API permission assigned.
20+
21+
## Create a Copilot Agent in Copilot Studio
22+
23+
1. Go to [Microsoft Copilot Studio](https://copilotstudio.microsoft.com).
24+
2. Create a new **Agent**.
25+
3. Publish your newly created Agent.
26+
4. In Copilot Studio, navigate to:
27+
`Settings` → `Advanced` → `Metadata`
28+
29+
Save the following values:
30+
- `Schema Name` (maps to `agent_identifier`)
31+
- `Environment ID`
32+
33+
## Create an Application Registration in Entra ID – User Interactive Login
34+
35+
> This step requires permissions to create application identities in your Azure tenant.
36+
37+
You will create a **Native Client Application Identity** (no client secret required).
38+
39+
1. Open [Azure Portal](https://portal.azure.com)
40+
2. Navigate to **Entra ID**
41+
3. Go to **App registrations** → **New registration**
42+
4. Fill out:
43+
- **Name**: Any name you like
44+
- **Supported account types**: `Accounts in this organization directory only`
45+
- **Redirect URI**:
46+
- Platform: `Public client/native (mobile & desktop)`
47+
- URI: `http://localhost`
48+
5. Click **Register**
49+
6. From the **Overview** page, note:
50+
- `Application (client) ID`
51+
- `Directory (tenant) ID`
52+
7. Go to: `Manage` → `API permissions`
53+
- Click **Add permission**
54+
- Choose **APIs my organization uses**
55+
- Search for: **Power Platform API**
56+
57+
If it's not listed, see **Tip** below.
58+
59+
8. Choose:
60+
- **Delegated Permissions**
61+
- Expand `CopilotStudio`
62+
- Select `CopilotStudio.Copilots.Invoke`
63+
9. Click **Add permissions**
64+
10. (Optional) Click **Grant admin consent**
65+
66+
### Tip
67+
68+
If you **do not see Power Platform API**, follow [Step 2 in Power Platform API Authentication](https://learn.microsoft.com/en-us/power-platform/admin/programmability-authentication-v2) to add the API to your tenant.
69+
70+
---
71+
72+
### Configure the Example Application - User Interactive Login
73+
74+
Once you've collected all required values:
75+
76+
1. Set the following environment variables in your terminal or .env file:
77+
78+
```env
79+
COPILOT_STUDIO_AGENT_APP_CLIENT_ID=<your-app-client-id>
80+
COPILOT_STUDIO_AGENT_TENANT_ID=<your-tenant-id>
81+
COPILOT_STUDIO_AGENT_ENVIRONMENT_ID=<your-env-id>
82+
COPILOT_STUDIO_AGENT_AGENT_IDENTIFIER=<your-agent-id>
83+
COPILOT_STUDIO_AGENT_AUTH_MODE=interactive
84+
```
85+
86+
## Create an Application Registration in Entra ID – Service Principal Login
87+
88+
> **Warning**: Service Principal login is **not yet supported** in the current version of the `CopilotStudioClient`.
89+
90+
## Creating a `CopilotStudioAgent` Client
91+
92+
If all required environment variables are set correctly, you don't need to manually create or pass a `client`. Semantic Kernel will automatically construct the client using the environment configuration.
93+
94+
However, if you need to override any environment variables—such as when specifying custom credentials or cloud settings—you should create the `client` explicitly using `CopilotStudioAgent.create_client(...)` and then pass it to the agent constructor.
95+
96+
```python
97+
client: CopilotClient = CopilotStudioAgent.create_client(
98+
auth_mode: CopilotStudioAgentAuthMode | Literal["interactive", "service"] | None = None,
99+
agent_identifier: str | None = None,
100+
app_client_id: str | None = None,
101+
client_secret: str | None = None,
102+
client_certificate: str | None = None,
103+
cloud: PowerPlatformCloud | None = None,
104+
copilot_agent_type: AgentType | None = None,
105+
custom_power_platform_cloud: str | None = None,
106+
env_file_encoding: str | None = None,
107+
env_file_path: str | None = None,
108+
environment_id: str | None = None,
109+
tenant_id: str | None = None,
110+
user_assertion: str | None = None,
111+
)
112+
113+
agent = CopilotStudioAgent(
114+
client=client,
115+
name="<name>",
116+
instructions="<instructions>",
117+
)
118+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
import asyncio
4+
5+
from semantic_kernel.agents import CopilotStudioAgent
6+
7+
"""
8+
This sample demonstrates how to use the Copilot Studio agent to answer questions about physics.
9+
It does not use a thread to maintain context between user inputs.
10+
"""
11+
12+
13+
async def main() -> None:
14+
# 1. Create the agent
15+
agent = CopilotStudioAgent(
16+
name="PhysicsAgent",
17+
instructions="You help answer questions about physics. ",
18+
)
19+
20+
# 2. Create a list of user inputs
21+
USER_INPUTS = [
22+
"Why is the sky blue?",
23+
"What is the speed of light?",
24+
]
25+
26+
# 3. Loop through the user inputs and get responses from the agent
27+
for user_input in USER_INPUTS:
28+
print(f"# User: {user_input}")
29+
response = await agent.get_response(messages=user_input)
30+
print(f"# {response.name}: {response}")
31+
32+
"""
33+
Sample output:
34+
35+
# User: Why is the sky blue?
36+
# PhysicsAgent: The sky appears blue because of the way Earth's atmosphere scatters sunlight.
37+
When sunlight enters the atmosphere, it is made up of different colors, each with different wavelengths.
38+
Blue light has shorter wavelengths and is scattered in all directions by the gases and particles in the
39+
atmosphere more than other colors. This scattered blue light is what we see when we look up at the sky.
40+
This phenomenon is known as Rayleigh scattering.
41+
AI-generated content may be incorrect
42+
# User: What is the speed of light?
43+
# PhysicsAgent: The speed of light in a vacuum is approximately 299,792,458 meters per second (m/s). This is often
44+
rounded to 300,000 kilometers per second (km/s) for simplicity.
45+
AI-generated content may be incorrect
46+
"""
47+
48+
49+
if __name__ == "__main__":
50+
asyncio.run(main())
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
import asyncio
4+
5+
from microsoft.agents.copilotstudio.client import (
6+
CopilotClient,
7+
)
8+
9+
from semantic_kernel.agents import CopilotStudioAgent, CopilotStudioAgentThread
10+
11+
"""
12+
This sample demonstrates how to use the Copilot Studio agent to answer questions from the user.
13+
It demonstrates how to use a thread to maintain context between user inputs.
14+
"""
15+
16+
17+
async def main() -> None:
18+
# As an example, manually create the client and pass it in to the agent
19+
# 1. Create the client
20+
client: CopilotClient = CopilotStudioAgent.create_client(auth_mode="interactive")
21+
22+
# 2. Create the agent
23+
agent = CopilotStudioAgent(
24+
client=client,
25+
name="PhysicsAgent",
26+
instructions="You are help answer questions about physics.",
27+
)
28+
29+
# 3. Create a list of user inputs
30+
USER_INPUTS = [
31+
"Hello! Who are you? My name is John Doe.",
32+
"What is the speed of light?",
33+
"What have we been talking about?",
34+
"What is my name?",
35+
]
36+
37+
# 4. Create a thread to maintain context between user inputs
38+
# If no thread is provided, a new thread will be created
39+
# and returned in the response
40+
thread: CopilotStudioAgentThread | None = None
41+
42+
# 5. Loop through the user inputs and get responses from the agent
43+
for user_input in USER_INPUTS:
44+
print(f"# User: {user_input}")
45+
response = await agent.get_response(messages=user_input, thread=thread)
46+
print(f"# {response.name}: {response}")
47+
thread = response.thread
48+
49+
# 6. If a thread was created, delete it when done
50+
if thread:
51+
await thread.delete()
52+
53+
"""
54+
Sample output:
55+
56+
# User: Hello! Who are you? My name is John Doe.
57+
# PhysicsAgent: Hello, John! I'm an AI assistant here to help you with any questions you might have.
58+
How can I assist you today?
59+
AI-generated content may be incorrect
60+
# User: What is the speed of light?
61+
# PhysicsAgent: The speed of light in a vacuum is approximately 299,792,458 meters per second (m/s).
62+
This is often rounded to 300,000 kilometers per second (km/s) for simplicity. If you have any more questions,
63+
feel free to ask!
64+
AI-generated content may be incorrect
65+
# User: What have we been talking about?
66+
# PhysicsAgent: Sure, John! So far, we've had the following conversation:
67+
68+
1. You introduced yourself and asked who I am.
69+
2. I introduced myself as an AI assistant and asked how I could assist you.
70+
3. You asked about the speed of light, and I provided the information that it is approximately 299,792,458 meters
71+
per second in a vacuum.
72+
73+
If you have any more questions or need further assistance, feel free to ask!
74+
AI-generated content may be incorrect
75+
# User: What is my name?
76+
# PhysicsAgent: Based on our conversation, your name is John Doe. How can I assist you further today?
77+
AI-generated content may be incorrect
78+
"""
79+
80+
81+
if __name__ == "__main__":
82+
asyncio.run(main())

0 commit comments

Comments
 (0)