准备工作
在今天的这个案例中,我们创建了一个.NET控制台应用程序,安装了以下NuGet包:
Microsoft.EntityFrameworkCore.Sqlite
Microsoft.Agents.AI.OpenAI
Microsoft.Extensions.AI.OpenAI
我们的配置文件中定义了LLM API的信息:
复制代码
{
"OpenAI": {
"EndPoint": "https://api.siliconflow.cn",
"ApiKey": "******************************",
"ModelId": "Qwen/Qwen2.5-32B-Instruct"
}
}
复制代码
这里我们使用 SiliconCloud 提供的 Qwen2.5-32B-Instruct 模型,你可以通过这个URL注册账号:https://cloud.siliconflow.cn/i/DomqCefW 获取大量免费的Token来进行本次实验。然后,我们将配置文件中的API信息读取出来:
var config = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json", optional: false, reloadOnChange: true)
.Build();
var openAIProvider = config.GetSection("OpenAI").Get<OpenAIProvider>();
数据库初始化
为了将聊天记录持久化,我们定一个CustomChatHistory类来记录每次对话的内容:
复制代码
public sealed class CustomChatHistory
{
public string Id { get; set; }
public string Context { get; set; }
public DateTime CreatedTime { get; set; }
public CustomChatHistory(string context)
{
Id = Guid.NewGuid().ToString();
Context = context;
CreatedTime = DateTime.UtcNow;
}
}
复制代码
然后,基于EF Core实现一个DbContext,这是老操作了:
复制代码
public class ChatHistoryDbContext : DbContext
{
public DbSet<CustomChatHistory> ChatHistories { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=ChatHistoryDb.db");
}
}
复制代码
有了DbContext后续我们就可以往数据库中写数据了。
创建一个AI Agent
通过MAF快速创建一个专注于回答马自达汽车知识的Agent:
var mazdaAgent = new OpenAIClient(
new ApiKeyCredential(openAIProvider.ApiKey),
new OpenAIClientOptions { Endpoint = new Uri(openAIProvider.Endpoint) })
.GetChatClient(openAIProvider.ModelId)
.CreateAIAgent(name: "Powerful Assistant", instructions: "You are a helpful assistant who responds user message in Mazda cars.");
开始第一轮对话
开启第一轮对话,我们想了解下Mazda 3这款车:
var userMessage = "Hello, can you tell me about the Mazda 3?";
Console.WriteLine($"User> {userMessage}");
var thread = mazdaAgent.GetNewThread();
var agentResponse = await mazdaAgent.RunAsync(userMessage, thread);
Console.WriteLine($"Agent> {agentResponse}");
执行结果下图所示:
image
可以看到,它洋洋洒洒地用了很多文字给我们介绍马三这款车。
对话记录持久化
假设我们这时要接一个电话,暂时就不跟Agent对话了。那么,Agent需要将对话记录存下来,这里我们就需要进行AgentThread的序列化并将其存到数据库:
复制代码
var serializedThread = thread.Serialize(JsonSerializerOptions.Web).GetRawText();
var chatConversation = new CustomChatHistory(serializedThread);
var dbContext = new ChatHistoryDbContext();
dbContext.Database.EnsureCreated();
dbContext.ChatHistories.Add(chatConversation);
await dbContext.SaveChangesAsync();
复制代码
这里我们存入的是一个Sqlite文件数据库,主要用于演示,十分便捷。
image
假设过了几分钟,我们接完电话回来准备继续对话了,Agent系统需要再从数据库中将我们的对话记录读取出来,也就是进行AgentThread的反序列化:
var savedConversation = dbContext.ChatHistories.First(c => c.Id == chatConversation.Id);
var loadedThread = JsonSerializer.Deserialize<JsonElement>(savedConversation.Context, JsonSerializerOptions.Web);
var resumedThread = mazdaAgent.DeserializeThread(loadedThread, JsonSerializerOptions.Web);
开始第二轮对话
我们刚刚问了Mazda 3这款车的基本信息,现在我们想了解下这款车的一些特性。这里,我直接问“What are the feautres of this car?”,这个 this car 就需要结合上下文才能知道我问的是哪一款车。
userMessage = "What are the features of this car?";
Console.WriteLine($"User> {userMessage}");
agentResponse = await mazdaAgent.RunAsync(userMessage, resumedThread);
Console.WriteLine($"Agent> {agentResponse}");
执行结果如下图所示:
首先,通过debug我们可以看到反序列化出来的AgentThread的确是包含了我们第一轮的对话记录:
image
其次,Agent在聊天记录的基础上回答这个问题的结果如下:
image
由上图可以看出,Agent准确的分析到了我们所说的 this car 指的是之前聊到的 Mazda 3这款车,并给了我们一些这款车的特性,清晰准确!
小结