@@ -28,140 +28,198 @@ Azure AI Agents supports function calling, which allows you to describe the stru
28
28
29
29
::: zone pivot="csharp"
30
30
31
- ## Define a function for your agent to call
32
-
33
- Start by defining a function for your agent to call. When you create a function for an agent to call, you describe its structure of it with any required parameters in a docstring.
34
-
35
- ``` csharp
36
- // Example of a function that defines no parameters
37
- string GetUserFavoriteCity () => " Seattle, WA" ;
38
- FunctionToolDefinition getUserFavoriteCityTool = new (" getUserFavoriteCity" , " Gets the user's favorite city." );
39
- // Example of a function with a single required parameter
40
- string GetCityNickname (string location ) => location switch
41
- {
42
- " Seattle, WA" => " The Emerald City" ,
43
- _ => throw new NotImplementedException (),
44
- };
45
- FunctionToolDefinition getCityNicknameTool = new (
46
- name : " getCityNickname" ,
47
- description : " Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'." ,
48
- parameters : BinaryData .FromObjectAsJson (
49
- new
50
- {
51
- Type = " object" ,
52
- Properties = new
31
+ ## Using the .NET SDK
32
+
33
+ In this example we are demonstrating how to use the local functions with the agents. The functions can be used to provide agent specific information in response to user question.
34
+
35
+ 1 . First, set up the configuration and create a ` PersistentAgentsClient ` . This client will be used for all interactions with the Azure AI Agents Service. This step also includes all necessary ` using ` directives.
36
+
37
+ ``` csharp
38
+ using Azure ;
39
+ using Azure .AI .Agents .Persistent ;
40
+ using Azure .Identity ;
41
+ using Microsoft .Extensions .Configuration ;
42
+ using System .Text .Json ;
43
+
44
+ IConfigurationRoot configuration = new ConfigurationBuilder ()
45
+ .SetBasePath (AppContext .BaseDirectory )
46
+ .AddJsonFile (" appsettings.json" , optional : false , reloadOnChange : true )
47
+ .Build ();
48
+
49
+ var projectEndpoint = configuration [" ProjectEndpoint" ];
50
+ var modelDeploymentName = configuration [" ModelDeploymentName" ];
51
+ PersistentAgentsClient client = new (projectEndpoint , new DefaultAzureCredential ());
52
+ ```
53
+
54
+ 2 . Next , define the local functions that the agent can call . For each function , create a `FunctionToolDefinition ` that describes its name , purpose , and parameters to the agent . These functions and definitions are used by both synchronous and asynchronous agent operations .
55
+
56
+ ```csharp
57
+ string GetUserFavoriteCity () => " Seattle, WA" ;
58
+ FunctionToolDefinition getUserFavoriteCityTool = new (" getUserFavoriteCity" , " Gets the user's favorite city." );
59
+
60
+ string GetCityNickname (string location ) => location switch
61
+ {
62
+ " Seattle, WA" => " The Emerald City" ,
63
+ _ => throw new NotImplementedException (),
64
+ };
65
+ FunctionToolDefinition getCityNicknameTool = new (
66
+ name : " getCityNickname" ,
67
+ description : " Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'." ,
68
+ parameters : BinaryData .FromObjectAsJson (
69
+ new
53
70
{
54
- Location = new
71
+ Type = " object" ,
72
+ Properties = new
55
73
{
56
- Type = " string" ,
57
- Description = " The city and state, e.g. San Francisco, CA" ,
74
+ Location = new
75
+ {
76
+ Type = " string" ,
77
+ Description = " The city and state, e.g. San Francisco, CA" ,
78
+ },
58
79
},
80
+ Required = new [] { " location" },
59
81
},
60
- Required = new [] { " location" },
61
- },
62
- new JsonSerializerOptions () { PropertyNamingPolicy = JsonNamingPolicy .CamelCase }));
63
- ```
64
-
65
- < ! -- See the [C # file on GitHub ](https :// github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/agents/user_functions.py) for an additional function definition examples. -->
82
+ new JsonSerializerOptions () { PropertyNamingPolicy = JsonNamingPolicy .CamelCase }));
83
+
84
+ string GetWeatherAtLocation (string location , string temperatureUnit = " f" ) => location switch
85
+ {
86
+ " Seattle, WA" => temperatureUnit == " f" ? " 70f" : " 21c" ,
87
+ _ => throw new NotImplementedException ()
88
+ };
89
+ FunctionToolDefinition getCurrentWeatherAtLocationTool = new (
90
+ name : " getCurrentWeatherAtLocation" ,
91
+ description : " Gets the current weather at a provided location." ,
92
+ parameters : BinaryData .FromObjectAsJson (
93
+ new
94
+ {
95
+ Type = " object" ,
96
+ Properties = new
97
+ {
98
+ Location = new
99
+ {
100
+ Type = " string" ,
101
+ Description = " The city and state, e.g. San Francisco, CA" ,
102
+ },
103
+ Unit = new
104
+ {
105
+ Type = " string" ,
106
+ Enum = new [] { " c" , " f" },
107
+ },
108
+ },
109
+ Required = new [] { " location" },
110
+ },
111
+ new JsonSerializerOptions () { PropertyNamingPolicy = JsonNamingPolicy .CamelCase }));
112
+ ```
66
113
67
- In the following sample , we create a helper function to get and parse the resolved tools ' outputs , and return it .
114
+ 3 . Create a helper function , ` GetResolvedToolOutput `. This function takes a ` RequiredToolCall ` ( when the agent determines a local function should be executed ) and invokes the appropriate C # function defined in the previous step . It then wraps the result in a ` ToolOutput ` object for the agent.
68
115
69
- ```csharp
70
- ToolOutput GetResolvedToolOutput (RequiredToolCall toolCall )
71
- {
72
- if (toolCall is RequiredFunctionToolCall functionToolCall )
116
+ ```csharp
117
+ ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall )
73
118
{
74
- if (functionToolCall .Name == getUserFavoriteCityTool .Name )
119
+ if (toolCall is RequiredFunctionToolCall functionToolCall )
120
+ {
121
+ if (functionToolCall .Name == getUserFavoriteCityTool .Name )
122
+ {
123
+ return new ToolOutput (toolCall , GetUserFavoriteCity ());
124
+ }
125
+ using JsonDocument argumentsJson = JsonDocument .Parse (functionToolCall .Arguments );
126
+ if (functionToolCall .Name == getCityNicknameTool .Name )
127
+ {
128
+ string locationArgument = argumentsJson .RootElement .GetProperty (" location" ).GetString ();
129
+ return new ToolOutput (toolCall , GetCityNickname (locationArgument ));
130
+ }
131
+ if (functionToolCall .Name == getCurrentWeatherAtLocationTool .Name )
132
+ {
133
+ string locationArgument = argumentsJson .RootElement .GetProperty (" location" ).GetString ();
134
+ if (argumentsJson .RootElement .TryGetProperty (" unit" , out JsonElement unitElement ))
75
135
{
76
- return new ToolOutput (toolCall , GetUserFavoriteCity ());
136
+ string unitArgument = unitElement .GetString ();
137
+ return new ToolOutput (toolCall , GetWeatherAtLocation (locationArgument , unitArgument ));
77
138
}
78
- using JsonDocument argumentsJson = JsonDocument .Parse (functionToolCall .Arguments );
79
- if (functionToolCall .Name == getCityNicknameTool .Name )
80
- {
81
- string locationArgument = argumentsJson .RootElement .GetProperty (" location" ).GetString ();
82
- return new ToolOutput (toolCall , GetCityNickname (locationArgument ));
139
+ return new ToolOutput (toolCall , GetWeatherAtLocation (locationArgument ));
140
+ }
83
141
}
142
+ return null ;
84
143
}
85
- return null ;
86
- }
87
- ```
144
+ ```
88
145
89
- ## Create a client and agent
146
+ 4 . Now , create the agent . Provide the model deployment name ( retrieved in step 1 ), a descriptive name for the agent, instructions for its behavior, and the list of `FunctionToolDefinition`s (defined in step 2) it can use .
90
147
91
- ```csharp
92
- // note: parallel function calling is only supported with newer models like gpt-4-1106-preview
93
- Response <Agent > agentResponse = await client .CreateAgentAsync (
94
- model : " gpt-4-1106-preview" ,
95
- name : " SDK Test Agent - Functions" ,
148
+ ```csharp
149
+ PersistentAgent agent = client .Administration .CreateAgent (
150
+ model : modelDeploymentName ,
151
+ name : " SDK Test Agent - Functions" ,
96
152
instructions : " You are a weather bot. Use the provided functions to help answer questions. "
97
153
+ " Customize your responses to the user's preferences as much as possible and use friendly "
98
154
+ " nicknames for cities whenever possible." ,
99
- tools : new List <ToolDefinition > { getUserFavoriteCityTool , getCityNicknameTool , getCurrentWeatherAtLocationTool }
100
- );
101
- Agent agent = agentResponse .Value ;
102
- ```
103
-
104
- ## Create a thread
105
-
106
- ```csharp
107
- Response <AgentThread > threadResponse = await client .CreateThreadAsync ();
108
- AgentThread thread = threadResponse .Value ;
109
-
110
- Response <ThreadMessage > messageResponse = await client .CreateMessageAsync (
111
- thread .Id ,
112
- MessageRole .User ,
113
- " What's the weather like in my favorite city?" );
114
- ThreadMessage message = messageResponse .Value ;
115
- ```
116
-
117
- ## Create a run and check the output
118
-
119
- ```csharp
120
- Response <ThreadRun > runResponse = await client .CreateRunAsync (thread , agent );
121
-
122
- #region Snippet:FunctionsHandlePollingWithRequiredAction
123
- do
124
- {
125
- await Task .Delay (TimeSpan .FromMilliseconds (500 ));
126
- runResponse = await client .GetRunAsync (thread .Id , runResponse .Value .Id );
127
-
128
- if (runResponse .Value .Status == RunStatus .RequiresAction
129
- && runResponse .Value .RequiredAction is SubmitToolOutputsAction submitToolOutputsAction )
155
+ tools : [getUserFavoriteCityTool , getCityNicknameTool , getCurrentWeatherAtLocationTool ]);
156
+ ```
157
+
158
+ 5 . Create a new conversation thread and add an initial user message to it . The agent will respond to this message .
159
+
160
+ ```csharp
161
+ PersistentAgentThread thread = client .Threads .CreateThread ();
162
+
163
+ client .Messages .CreateMessage (
164
+ thread .Id ,
165
+ MessageRole .User ,
166
+ " What's the weather like in my favorite city?" );
167
+ ```
168
+
169
+ 6 . Create a run for the agent on the thread and poll for its completion. If the run requires action (e.g., a function call ), submit the tool outputs .
170
+
171
+ ```csharp
172
+ ThreadRun run = client .Runs .CreateRun (thread .Id , agent .Id );
173
+
174
+ do
130
175
{
131
- List <ToolOutput > toolOutputs = new ();
132
- foreach (RequiredToolCall toolCall in submitToolOutputsAction .ToolCalls )
176
+ Thread .Sleep (TimeSpan .FromMilliseconds (500 ));
177
+ run = client .Runs .GetRun (thread .Id , run .Id );
178
+
179
+ if (run .Status == RunStatus .RequiresAction
180
+ && run .RequiredAction is SubmitToolOutputsAction submitToolOutputsAction )
133
181
{
134
- toolOutputs .Add (GetResolvedToolOutput (toolCall ));
182
+ List < ToolOutput > toolOutputs = [];
183
+ foreach (RequiredToolCall toolCall in submitToolOutputsAction .ToolCalls )
184
+ {
185
+ toolOutputs .Add (GetResolvedToolOutput (toolCall ));
186
+ }
187
+ run = client .Runs .SubmitToolOutputsToRun (run , toolOutputs );
135
188
}
136
- runResponse = await client .SubmitToolOutputsToRunAsync (runResponse .Value , toolOutputs );
137
189
}
138
- }
139
- while ( runResponse . Value . Status == RunStatus .Queued
140
- || runResponse . Value . Status == RunStatus .InProgress );
141
- # endregion
142
-
143
- Response < PageableList < ThreadMessage >> afterRunMessagesResponse
144
- = await client . GetMessagesAsync ( thread . Id );
145
- IReadOnlyList < ThreadMessage > messages = afterRunMessagesResponse . Value . Data ;
146
-
147
- // Note: messages iterate from newest to oldest, with the messages[0] being the most recent
148
- foreach ( ThreadMessage threadMessage in messages )
149
- {
150
- Console . Write ($ " {threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: " );
151
- foreach (MessageContent contentItem in threadMessage . ContentItems )
190
+ while ( run . Status == RunStatus . Queued
191
+ || run . Status == RunStatus .InProgress
192
+ || run . Status == RunStatus .RequiresAction );
193
+ ```
194
+
195
+ 7 . After the run completes , retrieve and display the messages from the thread to see the conversation , including the agent 's responses .
196
+
197
+ ``` csharp
198
+ Pageable < ThreadMessage > messages = client . Messages . GetMessages (
199
+ threadId : thread . Id ,
200
+ order : ListSortOrder . Ascending
201
+ );
202
+
203
+ foreach (ThreadMessage threadMessage in messages )
152
204
{
153
- if ( contentItem is MessageTextContent textItem )
205
+ foreach ( MessageContent content in threadMessage . ContentItems )
154
206
{
155
- Console .Write (textItem .Text );
156
- }
157
- else if (contentItem is MessageImageFileContent imageFileItem )
158
- {
159
- Console .Write ($" <image from ID: {imageFileItem.FileId}" );
207
+ switch (content )
208
+ {
209
+ case MessageTextContent textItem :
210
+ Console .WriteLine ($" [{threadMessage .Role }]: {textItem .Text }" );
211
+ break ;
212
+ }
160
213
}
161
- Console .WriteLine ();
162
214
}
163
- }
164
- ```
215
+ ```
216
+
217
+ 8 . Finally , clean up the created resources by deleting the thread and the agent .
218
+
219
+ ```csharp
220
+ client .Threads .DeleteThread (threadId : thread .Id );
221
+ client .Administration .DeleteAgent (agentId : agent .Id );
222
+ ```
165
223
166
224
::: zone - end
167
225
@@ -510,4 +568,4 @@ curl $AZURE_AI_AGENTS_ENDPOINT/threads/thread_abc123/messages?api-version=2024-1
510
568
- H " Authorization: Bearer $AZURE_AI_AGENTS_TOKEN"
511
569
` ` `
512
570
513
- ::: zone-end
571
+ ::: zone-end
0 commit comments