Skip to content

Commit 2b3fc0a

Browse files
authored
Update function-calling.md
Adding persistence agent async local function calling code.
1 parent 8eb2762 commit 2b3fc0a

File tree

1 file changed

+78
-45
lines changed

1 file changed

+78
-45
lines changed

articles/ai-services/agents/how-to/tools/function-calling.md

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@ Azure AI Agents supports function calling, which allows you to describe the stru
3333
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.
3434

3535
```csharp
36-
// Example of a function that defines no parameters
3736
string GetUserFavoriteCity() => "Seattle, WA";
3837
FunctionToolDefinition getUserFavoriteCityTool = new("getUserFavoriteCity", "Gets the user's favorite city.");
39-
// Example of a function with a single required parameter
4038
string GetCityNickname(string location) => location switch
4139
{
4240
"Seattle, WA" => "The Emerald City",
@@ -60,6 +58,34 @@ FunctionToolDefinition getCityNicknameTool = new(
6058
Required = new[] { "location" },
6159
},
6260
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
61+
string GetWeatherAtLocation(string location, string temperatureUnit = "f") => location switch
62+
{
63+
"Seattle, WA" => temperatureUnit == "f" ? "70f" : "21c",
64+
_ => throw new NotImplementedException()
65+
};
66+
FunctionToolDefinition getCurrentWeatherAtLocationTool = new(
67+
name: "getCurrentWeatherAtLocation",
68+
description: "Gets the current weather at a provided location.",
69+
parameters: BinaryData.FromObjectAsJson(
70+
new
71+
{
72+
Type = "object",
73+
Properties = new
74+
{
75+
Location = new
76+
{
77+
Type = "string",
78+
Description = "The city and state, e.g. San Francisco, CA",
79+
},
80+
Unit = new
81+
{
82+
Type = "string",
83+
Enum = new[] { "c", "f" },
84+
},
85+
},
86+
Required = new[] { "location" },
87+
},
88+
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
6389
```
6490

6591
<!--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. -->
@@ -72,15 +98,25 @@ ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall)
7298
if (toolCall is RequiredFunctionToolCall functionToolCall)
7399
{
74100
if (functionToolCall.Name == getUserFavoriteCityTool.Name)
75-
{
76-
return new ToolOutput(toolCall, GetUserFavoriteCity());
77-
}
78-
using JsonDocument argumentsJson = JsonDocument.Parse(functionToolCall.Arguments);
101+
{
102+
return new ToolOutput(toolCall, GetUserFavoriteCity());
103+
}
104+
using JsonDocument argumentsJson = JsonDocument.Parse(functionToolCall.Arguments);
79105
if (functionToolCall.Name == getCityNicknameTool.Name)
80106
{
81107
string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
82108
return new ToolOutput(toolCall, GetCityNickname(locationArgument));
83109
}
110+
if (functionToolCall.Name == getCurrentWeatherAtLocationTool.Name)
111+
{
112+
string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
113+
if (argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unitElement))
114+
{
115+
string unitArgument = unitElement.GetString();
116+
return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument, unitArgument));
117+
}
118+
return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument));
119+
}
84120
}
85121
return null;
86122
}
@@ -89,78 +125,75 @@ ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall)
89125
## Create a client and agent
90126

91127
```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",
128+
PersistentAgent agent = await client.Administration.CreateAgentAsync(
129+
model: modelDeploymentName,
95130
name: "SDK Test Agent - Functions",
96-
instructions: "You are a weather bot. Use the provided functions to help answer questions. "
97-
+ "Customize your responses to the user's preferences as much as possible and use friendly "
98-
+ "nicknames for cities whenever possible.",
99-
tools: new List<ToolDefinition> { getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool }
100-
);
101-
Agent agent = agentResponse.Value;
131+
instructions: "You are a weather bot. Use the provided functions to help answer questions. "
132+
+ "Customize your responses to the user's preferences as much as possible and use friendly "
133+
+ "nicknames for cities whenever possible.",
134+
tools: [getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool]);
102135
```
103136

104137
## Create a thread
105138

106139
```csharp
107-
Response<AgentThread> threadResponse = await client.CreateThreadAsync();
108-
AgentThread thread = threadResponse.Value;
140+
PersistentAgentThread thread = await client.Threads.CreateThreadAsync();
109141

110-
Response<ThreadMessage> messageResponse = await client.CreateMessageAsync(
142+
await client.Messages.CreateMessageAsync(
111143
thread.Id,
112144
MessageRole.User,
113145
"What's the weather like in my favorite city?");
114-
ThreadMessage message = messageResponse.Value;
115146
```
116147

117148
## Create a run and check the output
118149

119150
```csharp
120-
Response<ThreadRun> runResponse = await client.CreateRunAsync(thread, agent);
151+
ThreadRun run = await client.Runs.CreateRunAsync(thread.Id, agent.Id);
121152

122-
#region Snippet:FunctionsHandlePollingWithRequiredAction
123153
do
124154
{
125155
await Task.Delay(TimeSpan.FromMilliseconds(500));
126-
runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id);
156+
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
127157

128-
if (runResponse.Value.Status == RunStatus.RequiresAction
129-
&& runResponse.Value.RequiredAction is SubmitToolOutputsAction submitToolOutputsAction)
158+
if (run.Status == RunStatus.RequiresAction
159+
&& run.RequiredAction is SubmitToolOutputsAction submitToolOutputsAction)
130160
{
131-
List<ToolOutput> toolOutputs = new();
161+
List<ToolOutput> toolOutputs = [];
132162
foreach (RequiredToolCall toolCall in submitToolOutputsAction.ToolCalls)
133163
{
134-
toolOutputs.Add(GetResolvedToolOutput(toolCall));
164+
ToolOutput? toolOutput = GetResolvedToolOutput(toolCall);
165+
if (toolOutput != null)
166+
{
167+
toolOutputs.Add(toolOutput);
168+
}
135169
}
136-
runResponse = await client.SubmitToolOutputsToRunAsync(runResponse.Value, toolOutputs);
170+
run = await client.Runs.SubmitToolOutputsToRunAsync(run, toolOutputs);
137171
}
138172
}
139-
while (runResponse.Value.Status == RunStatus.Queued
140-
|| runResponse.Value.Status == RunStatus.InProgress);
141-
#endregion
173+
while (run.Status == RunStatus.Queued
174+
|| run.Status == RunStatus.InProgress
175+
|| run.Status == RunStatus.RequiresAction);
142176

143-
Response<PageableList<ThreadMessage>> afterRunMessagesResponse
144-
= await client.GetMessagesAsync(thread.Id);
145-
IReadOnlyList<ThreadMessage> messages = afterRunMessagesResponse.Value.Data;
177+
AsyncPageable<ThreadMessage> messages = client.Messages.GetMessagesAsync(
178+
threadId: thread.Id,
179+
order: ListSortOrder.Ascending
180+
);
146181

147-
// Note: messages iterate from newest to oldest, with the messages[0] being the most recent
148-
foreach (ThreadMessage threadMessage in messages)
182+
await foreach (ThreadMessage threadMessage in messages)
149183
{
150-
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
151-
foreach (MessageContent contentItem in threadMessage.ContentItems)
184+
foreach (MessageContent content in threadMessage.ContentItems)
152185
{
153-
if (contentItem is MessageTextContent textItem)
186+
switch (content)
154187
{
155-
Console.Write(textItem.Text);
188+
case MessageTextContent textItem:
189+
Console.WriteLine($"[{threadMessage.Role}]: {textItem.Text}");
190+
break;
156191
}
157-
else if (contentItem is MessageImageFileContent imageFileItem)
158-
{
159-
Console.Write($"<image from ID: {imageFileItem.FileId}");
160-
}
161-
Console.WriteLine();
162192
}
163193
}
194+
195+
await client.Threads.DeleteThreadAsync(threadId: thread.Id);
196+
await client.Administration.DeleteAgentAsync(agentId: agent.Id);
164197
```
165198

166199
::: zone-end
@@ -510,4 +543,4 @@ curl $AZURE_AI_AGENTS_ENDPOINT/threads/thread_abc123/messages?api-version=2024-1
510543
-H "Authorization: Bearer $AZURE_AI_AGENTS_TOKEN"
511544
```
512545

513-
::: zone-end
546+
::: zone-end

0 commit comments

Comments
 (0)