#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);
}
}