Files
2026-07-13 12:59:43 +08:00

281 lines
12 KiB
C#
Executable File

#!/usr/bin/dotnet run
#:package Microsoft.Extensions.AI@10.*
#:package Microsoft.Agents.AI.OpenAI@1.*-*
#:package Azure.AI.OpenAI@2.1.0
#:package Azure.Identity@1.13.1
using System.ComponentModel;
using System.Text.Json;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Azure.AI.OpenAI;
using Azure.Identity;
// ============================================================================
// TOOL USE DESIGN PATTERN DEMONSTRATION
// ============================================================================
// This sample demonstrates the key concepts from the Tool Use lesson:
// 1. FUNCTION SCHEMAS: Clear descriptions and typed parameters
// 2. MULTIPLE TOOLS: Agent selects the right tool for each task
// 3. TOOL COMPOSITION: Tools can work together to solve complex requests
// 4. PARAMETER HANDLING: Functions with different parameter types
// ============================================================================
// ----------------------------------------------------------------------------
// TOOL 1: Random Destination Generator (No Parameters)
// ----------------------------------------------------------------------------
// Demonstrates: Simple tool with no parameters
[Description("Provides a random vacation destination when the user hasn't specified one.")]
static string GetRandomDestination()
{
var destinations = new List<string>
{
"Paris, France",
"Tokyo, Japan",
"New York City, USA",
"Sydney, Australia",
"Rome, Italy",
"Barcelona, Spain",
"Cape Town, South Africa",
"Rio de Janeiro, Brazil",
"Bangkok, Thailand",
"Vancouver, Canada"
};
var random = new Random();
int index = random.Next(destinations.Count);
Console.WriteLine($"\n[Tool Called] GetRandomDestination() -> {destinations[index]}");
return destinations[index];
}
// ----------------------------------------------------------------------------
// TOOL 2: Weather Lookup (Single Parameter)
// ----------------------------------------------------------------------------
// Demonstrates: Tool with a required parameter - the LLM must extract
// the location from the user's message and pass it to this function
[Description("Gets the current weather conditions for a specified location. Use this when planning activities or packing suggestions.")]
static string GetWeather(
[Description("The city and country to get weather for, e.g., 'Paris, France'")] string location)
{
// Simulated weather data for demonstration
var weatherConditions = new Dictionary<string, (string condition, int tempC, int tempF)>
{
{ "paris", ("Partly Cloudy", 18, 64) },
{ "tokyo", ("Sunny", 24, 75) },
{ "new york", ("Rainy", 15, 59) },
{ "sydney", ("Sunny", 28, 82) },
{ "rome", ("Clear", 22, 72) },
{ "barcelona", ("Sunny", 26, 79) },
{ "cape town", ("Windy", 19, 66) },
{ "rio", ("Hot and Humid", 32, 90) },
{ "bangkok", ("Tropical", 33, 91) },
{ "vancouver", ("Overcast", 12, 54) }
};
var locationLower = location.ToLower();
foreach (var (key, weather) in weatherConditions)
{
if (locationLower.Contains(key))
{
var result = $"Weather in {location}: {weather.condition}, {weather.tempC}°C ({weather.tempF}°F)";
Console.WriteLine($"\n[Tool Called] GetWeather(\"{location}\") -> {result}");
return result;
}
}
var defaultResult = $"Weather in {location}: Mild conditions, around 20°C (68°F)";
Console.WriteLine($"\n[Tool Called] GetWeather(\"{location}\") -> {defaultResult}");
return defaultResult;
}
// ----------------------------------------------------------------------------
// TOOL 3: Destination Information (Multiple Parameters)
// ----------------------------------------------------------------------------
// Demonstrates: Tool with multiple parameters including an enum-like category
[Description("Gets detailed information about a destination including attractions, cuisine, and travel tips. Use this after selecting a destination to provide rich details.")]
static string GetDestinationInfo(
[Description("The destination city to get information about")] string destination,
[Description("The category of information: 'attractions', 'cuisine', 'tips', or 'all'")] string category = "all")
{
Console.WriteLine($"\n[Tool Called] GetDestinationInfo(\"{destination}\", \"{category}\")");
// Simulated destination data
var info = new Dictionary<string, Dictionary<string, string>>
{
{ "paris", new() {
{ "attractions", "Eiffel Tower, Louvre Museum, Notre-Dame, Champs-Élysées" },
{ "cuisine", "Croissants, French onion soup, Coq au vin, Macarons" },
{ "tips", "Metro is efficient, book museums in advance, tip 10-15%" }
}},
{ "tokyo", new() {
{ "attractions", "Senso-ji Temple, Shibuya Crossing, Tokyo Tower, Akihabara" },
{ "cuisine", "Sushi, Ramen, Tempura, Wagyu beef" },
{ "tips", "Get a Suica card, bow when greeting, shoes off indoors" }
}},
{ "rome", new() {
{ "attractions", "Colosseum, Vatican, Trevi Fountain, Pantheon" },
{ "cuisine", "Pasta Carbonara, Pizza al taglio, Gelato, Tiramisu" },
{ "tips", "Book Vatican early, carry water bottle, siesta 1-4pm" }
}}
};
var destLower = destination.ToLower();
foreach (var (key, data) in info)
{
if (destLower.Contains(key))
{
if (category.ToLower() == "all")
{
return JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
}
if (data.TryGetValue(category.ToLower(), out var value))
{
return $"{category} in {destination}: {value}";
}
}
}
return $"Information about {destination}: A wonderful destination with rich culture and experiences.";
}
// ----------------------------------------------------------------------------
// TOOL 4: Trip Cost Estimator (Numeric Parameters)
// ----------------------------------------------------------------------------
// Demonstrates: Tool with numeric parameters for calculations
[Description("Estimates the total trip cost based on destination, duration, and budget level. Returns a cost breakdown.")]
static string EstimateTripCost(
[Description("The destination city")] string destination,
[Description("Number of days for the trip")] int days,
[Description("Budget level: 'budget', 'moderate', or 'luxury'")] string budgetLevel = "moderate")
{
Console.WriteLine($"\n[Tool Called] EstimateTripCost(\"{destination}\", {days}, \"{budgetLevel}\")");
var dailyRates = new Dictionary<string, int>
{
{ "budget", 100 },
{ "moderate", 250 },
{ "luxury", 500 }
};
var rate = dailyRates.GetValueOrDefault(budgetLevel.ToLower(), 250);
var accommodation = rate * days;
var food = (rate / 2) * days;
var activities = (rate / 3) * days;
var total = accommodation + food + activities;
return $"""
Trip Cost Estimate for {destination} ({days} days, {budgetLevel}):
- Accommodation: ${accommodation}
- Food & Dining: ${food}
- Activities: ${activities}
- Estimated Total: ${total}
""";
}
// Azure OpenAI with the Responses API (stable v1 endpoint). Sign in with `az login`.
var azureEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deployment = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") ?? "gpt-4o-mini";
var azureClient = new AzureOpenAIClient(new Uri(azureEndpoint), new AzureCliCredential());
// Agent Identity
var AGENT_NAME = "TravelAgent";
// ============================================================================
// AGENT INSTRUCTIONS - Tool Use Expert
// ============================================================================
var AGENT_INSTRUCTIONS = """
You are a travel planning AI Agent with access to multiple specialized tools.
## Available Tools
You have access to these tools - use them appropriately:
1. **GetRandomDestination**: Suggests a random destination (no parameters needed)
2. **GetWeather**: Gets weather for a location (requires: location)
3. **GetDestinationInfo**: Gets attraction/cuisine/tips (requires: destination, optional: category)
4. **EstimateTripCost**: Calculates costs (requires: destination, days; optional: budgetLevel)
## Tool Usage Guidelines
- **Chain tools together** for comprehensive responses (e.g., destination weather info cost)
- **Extract parameters** from the user's message to pass to tools
- **Choose the right tool** based on what the user is asking
- When the user asks for a "complete trip plan", use multiple tools
## Response Format
After using tools, synthesize the information into a helpful, well-organized response.
Always mention which tools you used so users understand the agent's capabilities.
""";
// Create AI Agent with Multiple Tools
// This demonstrates the Tool Use Design Pattern with a variety of tool types
AIAgent agent = azureClient
.GetOpenAIResponseClient(deployment)
.CreateAIAgent(
name: AGENT_NAME,
instructions: AGENT_INSTRUCTIONS,
tools: [
AIFunctionFactory.Create(GetRandomDestination),
AIFunctionFactory.Create(GetWeather),
AIFunctionFactory.Create(GetDestinationInfo),
AIFunctionFactory.Create(EstimateTripCost)
]
);
// Create Conversation Session
AgentSession session = await agent.CreateSessionAsync();
// ============================================================================
// DEMONSTRATION 1: Tool Selection - Agent picks the right tool
// ============================================================================
Console.WriteLine("=== Tool Use Design Pattern Demonstration ===\n");
Console.WriteLine("--- Demo 1: Tool Selection ---");
Console.WriteLine("User: What's the weather like in Tokyo?\n");
Console.WriteLine("Agent Response:");
await foreach (var update in agent.RunStreamingAsync("What's the weather like in Tokyo?", session))
{
await Task.Delay(10);
Console.Write(update);
}
Console.WriteLine("\n");
// ============================================================================
// DEMONSTRATION 2: Parameterized Tool Calling
// ============================================================================
Console.WriteLine("--- Demo 2: Parameterized Tool with Multiple Parameters ---");
Console.WriteLine("User: How much would a 5-day luxury trip to Rome cost?\n");
Console.WriteLine("Agent Response:");
await foreach (var update in agent.RunStreamingAsync("How much would a 5-day luxury trip to Rome cost?", session))
{
await Task.Delay(10);
Console.Write(update);
}
Console.WriteLine("\n");
// ============================================================================
// DEMONSTRATION 3: Tool Composition - Multiple tools for complex requests
// ============================================================================
Console.WriteLine("--- Demo 3: Tool Composition ---");
Console.WriteLine("User: Plan me a complete trip - suggest a destination and give me all the details including weather and costs for a 3-day moderate budget trip.\n");
Console.WriteLine("Agent Response:");
await foreach (var update in agent.RunStreamingAsync(
"Plan me a complete trip - suggest a destination and give me all the details including weather and costs for a 3-day moderate budget trip.",
session))
{
await Task.Delay(10);
Console.Write(update);
}