在人工智能快速发展的今天,多模态AI应用已成为技术前沿的热点。微软开源的Semantic Kernel为C#开发者提供了强大的工具,使创建多模态图像分析应用变得更加简单和高效。本文将深入探讨如何利用C#和Semantic Kernel构建图像分析应用,并提供详细的代码示例。
Semantic Kernel是微软开源的一个轻量级AI开发套件,它能让开发者轻松构建AI Agent,并将最新的AI模型集成到C#、Python或Java代码库中。它的核心优势在于将传统编程语言与大型语言模型(LLM)AI技术相结合的能力,使开发者能够利用熟悉的编程语言,同时享受先进AI技术带来的便利。
多模态是现代LLM的重要能力,特别是在处理图像、文本等不同类型数据时。Semantic Kernel通过集成OpenAI、Azure OpenAI等服务,支持图像分析和其他多模态任务,包括:
这里用的是阿里的通义千问72B,你可以去阿里注册,现在送免费TOKEN。
C#using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel;
using System.Net.Http;
using OpenAI;
using System.ClientModel;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Text;
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace AppMultimodal
{
internal class Program
{
// 最大保存的对话轮数,防止历史记录过长
private const int MaxConversationTurns = 10;
static async Task Main(string[] args)
{
try
{
// 使用Host Builder模式配置应用
var builder = Host.CreateApplicationBuilder(args);
// 从配置文件或环境变量加载API密钥
// 实际使用时请替换为自己的API密钥或通过环境变量配置
builder.Configuration.AddEnvironmentVariables();
var apiKey = builder.Configuration["OpenAI:ApiKey"] ?? "sk-****";
// 配置OpenAI客户端凭证和选项
var openAIClientCredential = new ApiKeyCredential(apiKey);
var openAIClientOption = new OpenAIClientOptions
{
// DeepSeek的API端点
Endpoint = new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1"),
NetworkTimeout = TimeSpan.FromSeconds(60),
};
// 创建OpenAI客户端
var openaiClient = new OpenAIClient(openAIClientCredential, openAIClientOption);
// 注册聊天服务
builder.Services.AddOpenAIChatCompletion("qwen-vl-plus", openaiClient);
// 注册Kernel
builder.Services.AddTransient((serviceProvider) => {
return new Kernel(serviceProvider);
});
var host = builder.Build();
var kernel = host.Services.GetRequiredService<Kernel>();
var chatService = kernel.GetRequiredService<IChatCompletionService>();
// 启动图像分析程序
await RunImageAnalysisAppAsync(chatService);
}
catch (Exception ex)
{
// 全局异常处理
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"发生错误: {ex.Message}");
Console.ResetColor();
}
}
/// <summary>
/// 运行图像分析应用
/// </summary>
static async Task RunImageAnalysisAppAsync(IChatCompletionService chatService)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("欢迎使用多模态图像分析应用");
Console.WriteLine("输入图像路径进行分析,或输入 'exit' 退出程序");
// 创建聊天历史
var chatHistory = new ChatHistory("你是一个专业的图像分析AI助手,擅长分析图像内容并提供详细描述。");
while (true)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("\n请输入图像路径或命令: ");
string input = Console.ReadLine();
if (string.IsNullOrEmpty(input) || input.ToLower() == "exit")
break;
await ProcessImageAnalysisRequestAsync(input, chatHistory, chatService);
}
}
/// <summary>
/// 处理图像分析请求
/// </summary>
static async Task ProcessImageAnalysisRequestAsync(string imagePath, ChatHistory chatHistory, IChatCompletionService chatService)
{
try
{
// 检查文件是否存在
if (!File.Exists(imagePath))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"错误: 找不到文件 '{imagePath}'");
Console.ResetColor();
return;
}
// 获取文件的MIME类型
string mimeType = GetMimeType(imagePath);
if (mimeType == null)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("错误: 不支持的图像格式。请使用JPG、PNG、GIF或BMP格式的图像。");
Console.ResetColor();
return;
}
Console.WriteLine($"正在分析图像: {imagePath}");
// 从文件读取图像数据
byte[] imageBytes = File.ReadAllBytes(imagePath);
// 添加用户消息,包含分析请求和图像
chatHistory.AddUserMessage(
[
new TextContent("请详细分析这张图片中的内容,包括主要对象、场景、颜色和可能的含义。"),
new ImageContent(imageBytes, mimeType)
]
);
// 管理聊天历史长度
ManageChatHistorySize(chatHistory);
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("AI分析结果:");
// 获取AI的响应
var response = await chatService.GetChatMessageContentAsync(chatHistory);
// 打印响应
Console.WriteLine(response.Content);
// 添加AI回复到历史记录
chatHistory.AddAssistantMessage(response.Content ?? string.Empty);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"处理图像时出错: {ex.Message}");
Console.ResetColor();
}
}
/// <summary>
/// 获取文件的MIME类型
/// </summary>
static string GetMimeType(string filePath)
{
string extension = Path.GetExtension(filePath).ToLower();
return extension switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".bmp" => "image/bmp",
_ => null // 不支持的格式
};
}
/// <summary>
/// 管理聊天历史大小,防止过长导致token超限或内存问题
/// </summary>
static void ManageChatHistorySize(ChatHistory chatHistory)
{
// 保留最近的对话,移除较早的对话
// 注意: ChatHistory类的处理方式与List<ChatMessage>不同
while (chatHistory.Count > MaxConversationTurns * 2 + 1) // +1 是为系统消息
{
// 移除最早的用户消息和助手回复
// 从索引1开始移除(保留系统消息)
chatHistory.RemoveAt(1);
if (chatHistory.Count > 1) // 如果还有助手消息,也移除
chatHistory.RemoveAt(1);
}
}
}
}

以下是一个更完整的多模态图像分析应用程序,支持多种分析功能:
C#using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel;
using System.Net.Http;
using OpenAI;
using System.ClientModel;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Text;
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace AppMultimodal
{
internal class Program
{
// 最大保存的对话轮数,防止历史记录过长
private const int MaxConversationTurns = 10;
static async Task Main(string[] args)
{
try
{
// 使用Host Builder模式配置应用
var builder = Host.CreateApplicationBuilder(args);
// 从配置文件或环境变量加载API密钥
builder.Configuration.AddEnvironmentVariables();
var apiKey = builder.Configuration["OpenAI:ApiKey"] ?? "sk-*****";
// 配置OpenAI客户端凭证和选项
var openAIClientCredential = new ApiKeyCredential(apiKey);
var openAIClientOption = new OpenAIClientOptions
{
// DeepSeek的API端点
Endpoint = new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1"),
NetworkTimeout = TimeSpan.FromSeconds(60),
};
// 创建OpenAI客户端
var openaiClient = new OpenAIClient(openAIClientCredential, openAIClientOption);
// 注册聊天服务
builder.Services.AddOpenAIChatCompletion("qwen-vl-plus", openaiClient);
// 注册Kernel
builder.Services.AddTransient((serviceProvider) => {
return new Kernel(serviceProvider);
});
var host = builder.Build();
var kernel = host.Services.GetRequiredService<Kernel>();
var chatService = kernel.GetRequiredService<IChatCompletionService>();
// 启动图像分析程序
await RunAdvancedImageAnalysisAppAsync(chatService);
}
catch (Exception ex)
{
// 全局异常处理
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"发生错误: {ex.Message}");
Console.ResetColor();
}
}
/// <summary>
/// 运行高级图像分析应用
/// </summary>
static async Task RunAdvancedImageAnalysisAppAsync(IChatCompletionService chatService)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("========================================");
Console.WriteLine(" 多模态图像分析应用 - Semantic Kernel");
Console.WriteLine("========================================");
Console.WriteLine("支持的功能:");
Console.WriteLine("1. 常规分析 - 分析图像中的主要内容");
Console.WriteLine("2. 文本提取 - 提取图像中的文本内容");
Console.WriteLine("3. 物体识别 - 识别图像中的物体");
Console.WriteLine("4. 情感分析 - 分析图像的情感基调");
Console.WriteLine("5. 场景描述 - 创意描述图像场景");
Console.WriteLine("6. 自定义问题 - 向图像提问");
Console.WriteLine("输入 'exit' 退出程序");
Console.WriteLine("========================================");
// 创建聊天历史
var chatHistory = new ChatHistory("你是一个专业的图像分析AI助手,擅长分析图像内容并提供详细描述。");
while (true)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("\n请输入图像路径: ");
string imagePath = Console.ReadLine();
if (string.IsNullOrEmpty(imagePath) || imagePath.ToLower() == "exit")
break;
if (!File.Exists(imagePath))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"错误: 找不到文件 '{imagePath}'");
Console.ResetColor();
continue;
}
// 获取文件的MIME类型
string mimeType = GetMimeType(imagePath);
if (mimeType == null)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("错误: 不支持的图像格式。请使用JPG、PNG、GIF或BMP格式的图像。");
Console.ResetColor();
continue;
}
// 读取图像数据
byte[] imageBytes = File.ReadAllBytes(imagePath);
// 显示功能菜单
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("请选择分析功能 (1-6): ");
string option = Console.ReadLine();
// 根据用户选择执行不同的分析功能
await ExecuteImageAnalysisAsync(option, imageBytes, mimeType, imagePath, chatService, chatHistory);
}
}
/// <summary>
/// 执行图像分析
/// </summary>
static async Task ExecuteImageAnalysisAsync(string option, byte[] imageBytes, string mimeType,
string imagePath, IChatCompletionService chatService, ChatHistory chatHistory)
{
// 创建新的聊天历史,以避免之前的分析影响当前结果
var analysisHistory = new ChatHistory("你是一个专业的图像分析AI助手,擅长分析图像内容并提供详细描述。");
string prompt = option switch
{
"1" => "请详细分析这张图片中的内容,包括主要对象、场景、颜色和可能的含义。",
"2" => "请识别并提取这张图片中的所有文本内容,按照原始格式输出。如果没有检测到文本,请说明。",
"3" => "请列出并描述这张图片中所有可识别的物体,并说明它们的位置和特征。",
"4" => "请分析这张图片传达的情感和氛围。考虑色调、构图、主题等因素,给出全面的情感分析。",
"5" => "请以文学性的语言创意描述这张图片展示的场景,使用生动的形容词和比喻。",
"6" => await GetCustomPromptAsync(),
_ => "请详细描述这张图片中的内容。"
};
Console.WriteLine($"正在分析图像: {Path.GetFileName(imagePath)}");
Console.WriteLine($"分析类型: {GetAnalysisTypeName(option)}");
// 添加用户消息,包含分析请求和图像
analysisHistory.AddUserMessage(
[
new TextContent(prompt),
new ImageContent(imageBytes, mimeType)
]
);
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("\nAI分析结果:");
// 获取AI的响应
var response = await chatService.GetChatMessageContentAsync(analysisHistory);
// 打印响应
Console.WriteLine(response.Content);
// 询问是否要保存结果
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("\n是否保存分析结果? (y/n): ");
string saveOption = Console.ReadLine();
if (saveOption?.ToLower() == "y")
{
string resultPath = Path.Combine(
Path.GetDirectoryName(imagePath),
$"{Path.GetFileNameWithoutExtension(imagePath)}_分析结果_{DateTime.Now:yyyyMMdd_HHmmss}.txt"
);
File.WriteAllText(resultPath, response.Content, Encoding.UTF8);
Console.WriteLine($"结果已保存到: {resultPath}");
}
}
/// <summary>
/// 获取自定义提示
/// </summary>
static async Task<string> GetCustomPromptAsync()
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("请输入您想问关于图片的问题: ");
return Console.ReadLine();
}
/// <summary>
/// 获取分析类型名称
/// </summary>
static string GetAnalysisTypeName(string option)
{
return option switch
{
"1" => "常规分析",
"2" => "文本提取",
"3" => "物体识别",
"4" => "情感分析",
"5" => "场景描述",
"6" => "自定义问题",
_ => "未知分析类型"
};
}
/// <summary>
/// 获取文件的MIME类型
/// </summary>
static string GetMimeType(string filePath)
{
string extension = Path.GetExtension(filePath).ToLower();
return extension switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".bmp" => "image/bmp",
_ => null // 不支持的格式
};
}
}
}

这个实现充分利用了Semantic Kernel的多模态能力,可以帮助您快速构建具有图像分析功能的应用程序。您可以根据自己的需求进一步扩展这个基础框架。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!