.net 调用 AI 例子

#load "AppLib"
#load "seqlog"

// https://learn.microsoft.com/en-us/dotnet/core/extensions/artificial-intelligence?tabs=dotnet-cli
// https://www.bilibili.com/video/BV1b96ZYWEHT
// 引用 Microsoft.Extensions.AI
// 引用 Microsoft.Extensions.AI.Ollama

async Task<int> Main(string[] args)
{
    Util.SetPassword("seqKey", "xxx"); // 日志记录

    var app = this.CreateApp(args, $@"./logs/ollama.log", it => EnableSeqLogSystem(it, Util.GetPassword("seqKey"))); // 创建日志

    var logger = this.GetLogger(app); // 创建日志

    logger.LogInformation($"启动: {this.GetType()}: {Assembly.GetEntryAssembly()}");

    using Microsoft.Extensions.AI.IChatClient client = new ChatClientBuilder(
        //new OllamaChatClient(new Uri("http://localhost:11434/"), "qwen2.5") )     
        new OllamaChatClient(new Uri("http://localhost:11434/"), "llama3.2-vision"))
            .UseFunctionInvocation() // 可以调用工具          
            .Use(it => new UseLanguageChatClient(it, "中文")) // 调用中间件
            .Build();

    // 创建一个针对 client  进行对话的函数
    await this.CallCompleteAsync(client);

    // 进行流式对话
    await this.CallCompleteStreamingAsync(client);

    // 调用 function
    await this.CallFunctionToolsAsync(client);

    // 调用中间件
    await this.CallUseAsync(client);

    // 进行图片分析
    await this.CallImageFunAsync(client);

    Console.WriteLine();

    // 向量计算
    {
        this.Title("向量调用");

        // 嵌入
        IEmbeddingGenerator<string, Embedding<float>> generator =
            new OllamaEmbeddingGenerator(new Uri("http://localhost:11434/"), "all-minilm");
        //new OllamaEmbeddingGenerator(new Uri("http://localhost:11434/"), "nomic-embed-text");

        // await generator.GenerateEmbeddingAsync("nice a day"); // 计算一个词的向量

        // 批量计算向量
        var embeddingResult = await generator.GenerateAndZipAsync(new[] { "nice day", "hello" }); // 对一批词进行计算

        // 计算相似度
        var similarity = CalculateCosineSimilarity(embeddingResult[0].Embedding.Vector.ToArray(), embeddingResult[1].Embedding.Vector.ToArray());
        similarity.Dump();
    }

    return 0;

}

[Description("有问题图像的通知")]
static void BadImageNoti([Description("图像的描述")] string imgDesc)
{

    Console.WriteLine($"发现有问题的图像:{imgDesc}");

}

// 进行图片分析
async Task CallImageFunAsync(Microsoft.Extensions.AI.IChatClient client)
{
    Title("进行图片分析");

    var bytes = await File.ReadAllBytesAsync(@"C:\Users\icoms\Desktop\景德镇\景德镇_11.13\DO9A5409.JPG");

    var tool = AIFunctionFactory.Create(BadImageNoti);

    var assistant = this.GetAssistant();

    var messages = new List<ChatMessage>();
    messages.Add(new ChatMessage(ChatRole.System, assistant));
    var message = new ChatMessage(ChatRole.User, "图像中多少女孩");
    message.Contents.Add(new ImageContent(bytes, "image/jpg"));
    messages.Add(message);

    //var response = await client.CompleteAsync<GrilsCount>(messages, new ChatOptions() {Tools = [tool]}); // 有一些智能不支持 tool
    var response = await client.CompleteAsync<GrilsCount>(messages);

    Console.WriteLine($"共有 {response.Result.Count} 位女孩");

}

class GrilsCount
{

    [Description("统计共有多少女孩")]
    public int Count { get; set; }
}

// 调用中间件
async Task CallUseAsync(Microsoft.Extensions.AI.IChatClient client)
{
    Title("调用中间件,见 UseLanguageChatClient 调用");

    // 这里使用英文提示,中间件强制使用中文回复 
    var messages = new List<ChatMessage>();
    messages.Add(new ChatMessage(ChatRole.User, "What is AI?"));

    ChatOptions options = new ChatOptions();

    await foreach (var update in client.CompleteStreamingAsync(messages, options))
    {
        Console.Write(update.Text);
    }

}

void Title(string title)
{
    Console.WriteLine("");
    Console.WriteLine("");
    Console.WriteLine($"== {title} ===================================");
    Console.WriteLine("");
}

[Description("查询天气,返回指定地区天气的文字描述")]
static string QueryWeatherInfo([Description("要查询天气的地区")] string location)
{
    Console.WriteLine($"天气工具被调用,'{location}' ");

    return $"{location} 天气很不好, 不适合外出";
}

// 调用 function tool
async Task CallFunctionToolsAsync(Microsoft.Extensions.AI.IChatClient client)
{
    Title("工具类设备");

    var weatherTool = AIFunctionFactory.Create(QueryWeatherInfo); // 加入天气查询工具

    var assistant = this.GetAssistant();

    var messages = new List<ChatMessage>();
    //messages.Add(new ChatMessage(ChatRole.System, assistant));
    messages.Add(new ChatMessage(ChatRole.User, "杭州天气?"));

    var response = client.CompleteStreamingAsync(
    messages,
    new()
    { Tools = new[] { weatherTool } });

    await foreach (var update in response)
    {
        Console.Write(update.Text);
    }

}

// 进行流式对话
async Task CallCompleteStreamingAsync(Microsoft.Extensions.AI.IChatClient client)
{
    Title("会话流式调用");

    var assistant = this.GetAssistant();

    var messages = new List<ChatMessage>();
    messages.Add(new ChatMessage(ChatRole.System, assistant));
    messages.Add(new ChatMessage(ChatRole.User, "What is AI?"));

    ChatOptions options = new ChatOptions();

    await foreach (var update in client.CompleteStreamingAsync(messages, options))
    {
        Console.Write(update.Text);
    }
}

// 返回一个系统提示词
string GetAssistant()
{
    var assistant = @"
- Role: 中文沟通专家
- Background: 用户需要与人工智能进行高效的中文交流,确保信息的准确传达和理解。
- Profile: 你是一位精通中文的沟通专家,擅长用中文清晰、准确地表达复杂的概念和信息。
- Skills: 你具备深厚的中文语言功底,能够理解并运用中文的语境、修辞和表达习惯,确保与用户的沟通无障碍。
- Goals: 提供一个流畅、自然的中文交流环境,帮助用户以中文高效地获取信息和解决问题。
- Constrains: 所有回复必须使用中文,避免使用英文或其他语言,确保沟通的纯粹性和专业性。
- OutputFormat: 纯中文文本回复,必要时可包含中文标点符号和表情符号以增强表达效果。
- Workflow:
  1. 理解用户的问题或请求,确保完全把握其意图。
  2. 使用中文组织回答,确保语言的准确性和表达的清晰性。
  3. 根据需要,提供额外的解释或例子,以中文进一步阐明答案。
- Examples:
  - 例子1:用户问:“今天天气怎么样?”
    回答:“今天天气晴朗,气温适宜,适合外出。”
  - 例子2:用户问:“这个单词的中文意思是什么?”
    回答:“这个单词的中文意思是……,它通常用于描述……。”
  - 例子3:用户问:“你能帮我解释一下这个数学公式吗?”
    回答:“当然可以,这个数学公式表示的是……,它在……情况下使用。”
- Initialization: 在第一次对话中,请直接输出以下:您好!我是你的 AI 助手,很高兴能用中文与您交流。请问有什么可以帮助您的?    "
    ;

    return assistant;

}

// 进行对话 
async Task CallCompleteAsync(Microsoft.Extensions.AI.IChatClient client)
{
    Title("会话调用");

    var assistant = this.GetAssistant();

    var messages = new List<ChatMessage>();
    messages.Add(new ChatMessage(ChatRole.System, assistant));
    messages.Add(new ChatMessage(ChatRole.User, "What is AI?"));

    ChatOptions options = new ChatOptions();

    var result = await client.CompleteAsync(messages);

    // 这里是底层响应类,如果有的话,可以进行转型,调用特有数据
    result.RawRepresentation?.GetType().Dump();

    Console.WriteLine(result.Message.Text);

}

// 相似度计算
static double CalculateCosineSimilarity(float[] vectorA, float[] vectorB)
{
    double dotProduct = vectorA.Zip(vectorB, (a, b) => a * b).Sum();
    double magnitudeA = Math.Sqrt(vectorA.Sum(a => a * a));
    double magnitudeB = Math.Sqrt(vectorB.Sum(b => b * b));

    if (magnitudeA == 0 || magnitudeB == 0)
        return 0; // 防止除以零

    return dotProduct / (magnitudeA * magnitudeB);
}

//  一个语言中间件
private class UseLanguageChatClient(Microsoft.Extensions.AI.IChatClient innerClient, string language) : DelegatingChatClient(innerClient)
{
    public override IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync(IList<ChatMessage> chatMessages, ChatOptions? options = null, CancellationToken cancellationToken = default)
    {
        chatMessages.Add(new ChatMessage(ChatRole.Assistant, $"请一直使用 {language} 进行回复"));

        return base.CompleteStreamingAsync(chatMessages, options, cancellationToken);
    }
}
上一篇
下一篇