C#调用 AI学习从0开始-第2阶段(Function Calling+工具调用智能体)-第11天实战-实现天气工具+混合调用

1. 目标:让AI能查询天气,通过Function Calling调用天气工具。在昨天的基础上学习

2.实现逻辑:提示词写清晰

csharp 复制代码
commonClass.SetSystemPrompt("查询指定城市的天气信息。用户询问天气时必须调用此函数获取准确数据");

天气工具:

csharp 复制代码
        public static object GetWeatherTool()
        {
            return new
            {
                type = "function",
                function = new
                {
                    name = "get_weather",
                    description = "查询指定城市的天气信息。用户询问天气时必须调用此函数获取准确数据。",
                    parameters = new
                    {
                        type = "object",
                        properties = new
                        {
                            city = new
                            {
                                type = "string",
                                description = "城市名称,例如:北京、上海、广州、深圳"
                            }
                        },
                        required = new[] { "city" }
                    }
                }
            };
        }

主要实现代码

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using NCalc;

namespace ConsoleApp1.Common
{
    public class CommonHelper
    {
        private readonly HttpClient _httpClient = new HttpClient();
        private const string AMAP_KEY = "这里写你的天气 高德 天气应用Key";  

        /// <summary>
        /// 执行本地工具
        /// </summary>
        public async Task<string> ExecuteTool(string toolName, string arguments)
        {
            switch (toolName)
            {
                case "get_current_time":
                    return  DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                case "calculator":  //计算器
                    return CalculateExpression(arguments);
                case "get_weather":  //天气
                    return await GetWeather(arguments);
                    
                // 未来可以添加更多工具
                default:
                    return $"未知工具: {toolName}";
            }
        }

        private string CalculateExpression(string arguments)
        {
            try
            {
                // 解析参数
                var doc = JsonDocument.Parse(arguments);
                string expression = doc.RootElement.GetProperty("expression").GetString();

                // 替换中文符号和空格
                expression = expression.Replace("×", "*")
                                       .Replace("÷", "/")
                                       .Replace(" ", "");

                // 使用DataTable计算
                var table = new DataTable();
                var result = table.Compute(expression, "");

                return result.ToString();
            }
            catch (Exception ex)
            {
                return $"计算错误: {ex.Message}";
            }
        }

        private async Task<string> GetWeather(string city)
        {
            try
            {
                // 1. 地理编码:城市名 → adcode
                string geoUrl = $"https://restapi.amap.com/v3/geocode/geo?address={city}&key={AMAP_KEY}";
                var geoResponse = await _httpClient.GetStringAsync(geoUrl);
                var geoDoc = JsonDocument.Parse(geoResponse);

                var adcode = geoDoc.RootElement
                    .GetProperty("geocodes")[0]
                    .GetProperty("adcode")
                    .GetString();

                // 2. 查询天气
                string weatherUrl = $"https://restapi.amap.com/v3/weather/weatherInfo?city={adcode}&key={AMAP_KEY}";
                var weatherResponse = await _httpClient.GetStringAsync(weatherUrl);
                var weatherDoc = JsonDocument.Parse(weatherResponse);

                var live = weatherDoc.RootElement.GetProperty("lives")[0];
                string weather = live.GetProperty("weather").GetString();
                string temperature = live.GetProperty("temperature").GetString();
                string wind = live.GetProperty("windpower").GetString();

                return $"{city}:{weather},{temperature}°C,风力{wind}级";
            }
            catch (Exception ex)
            {
                return $"获取真实天气失败: {ex.Message}";
            }
        }

    }
}

混合调用:

csharp 复制代码
       public static async Task Day()
        {
            try
            {
                Console.WriteLine($"-------------天气工具测试-------------\r\n");

                CommonClass commonClass = new CommonClass();
                //commonClass.SetSystemPrompt("查询指定城市的天气信息。用户询问天气时必须调用此函数获取准确数据");
                // 设置系统提示
                commonClass.SetSystemPrompt(@"你是一个智能助手。
                - 当用户询问时间时,调用 get_current_time
                - 当用户询问数学计算时,调用 calculator
                - 当用户询问天气时,调用 get_weather
                - 如果是闲聊,直接回答,不需要调用工具");


                // 定义工具(天气 + 时间 + 计算器,多工具协同)
                //var tools = new object[] { CommonClass.GetCurrentTimeTool(), CommonClass.GetCalculatorTool(), CommonClass.GetWeatherTool() };  //
                object[] tools = commonClass.GetAllTools();  //获取所有工具(统一注册)

                Console.WriteLine("用户:现在几点了?");
                var reply = await commonClass.CallAPIWithTools("现在几点了?", tools);
                Console.WriteLine($"AI:{reply}\n");

                // 测试1:查询北京天气
                //Console.WriteLine("用户:西安今天天气怎么样?");
                //var reply1 = await commonClass.CallAPIWithTools("西安今天天气怎么样?", tools);
                //Console.WriteLine($"AI回答:{reply1}\n");

                // ===== 测试5:混合意图 =====
                Console.WriteLine("【测试:混合意图】");
                Console.WriteLine("用户:北京天气怎么样?现在几点了?");
                var reply5 = await commonClass.CallAPIWithTools("北京天气怎么样?现在几点了?", tools);
                Console.WriteLine($"AI:{reply5}\n");

                Console.WriteLine("用户:西安美食推荐?");
                var reply1 = await commonClass.CallAPIWithTools("西安美食推荐?", tools);
                Console.WriteLine($"AI回答:{reply1}\n");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"异常:{ex.Message}");
            }
        }

主要设置:请求体的tool_choice设置auto

csharp 复制代码
//请求体 第1次请求:带tools参数
var requestBody = new
{
	model = "qwen-turbo",
    messages = _history,  //MessagesIn
    tools = tools,
    tool_choice = "auto",  //让AI自动决定是否调用工具
    stream = false
};
  1. 注册高德天气应用步骤:调用高德天气,需注册高德应用天气,申请Key。申请步骤详解:
    【1】注册/登录高德开放平台:访问高德开放平台官网,如果没有账号,先点击"注册"完成开发者注册;已有账号则直接登录。
    【2】进入控制台创建应用:登录后进入"控制台",在左侧菜单找到"应用管理" -> "我的应用"。点击右上角的"创建新应用",给你的应用起个名字(比如"我的AI助手"),选择好应用类型,然后提交。
    【3】添加Key并选择"Web服务":进入刚创建的应用,点击"添加Key"。在弹出的对话框里,最关键的步骤是服务平台一定要选择"Web服务"(不是Web端JS API)。之后填好Key名称,勾选同意条款,提交就可以了。
    【4】获取你的Key:添加成功后,你就能在应用详情里看到生成的Key字符串了,复制保存好就行。

⚠️ 避坑小贴士

别选错平台:申请时,服务平台务必选"Web服务"。选错了类型(比如选成iOS/Android SDK),调用天气接口时就会报"INVALID_USER_KEY"这个错。

注意每日调用限额:个人认证的开发者账号,天气接口每日免费调用限额是5,000次。学习和个人项目完全够用,注意别超限就行。