目录
对于那些热衷于 AI 的爱好者来说,Gradio.NET 提供了一个绝佳的机会,通过访问 https://qwen.starworks.cc:88/,让他们能够与通义千问开源模型进行互动。
这个 Web 应用不仅用户体验流畅,还能够记住会话历史,轻松识别语义,这一切都得益于其背后的先进技术。
该项目已开源,源代码地址:https://github.com/sdcb/Sdcb.DashScope(欢迎star)
代码讲解
对于 Web 应用开发者而言,这个项目展示了如何利用 Gradio.Net 和 .NET 开发出高效的 Web 应用。
整个页面的生成过程令人印象深刻,仅需 60 行 C# 代码就能完成,这在传统前端开发中实现同样的效果需要多少行代码(好奇🤔)
通过CreateBlocks
方法,开发者能够快速构建出整个页面,无需编写复杂的前端代码。
static Blocks CreateBlocks(IServiceProvider sp)
{
using Blocks blocks = gr.Blocks();
Button sendButton, resetButton, regenerateButton;
Textbox systemPrompt, userInput;
Chatbot chatBot;
Radio model;
gr.Markdown("# 通义千问开源模型");
model = gr.Radio(["qwen1.5-110b-chat", "qwen1.5-72b-chat", "qwen1.5-32b-chat", "qwen1.5-14b-chat", "qwen1.5-7b-chat", "qwen1.5-1.8b-chat", "qwen1.5-0.5b-chat", "codeqwen1.5-7b-chat"], label: "选择模型", value: "qwen1.5-110b-chat");
using (gr.Row())
{
using (gr.Column(9))
{
systemPrompt = gr.Textbox("你是通义千问模型版本{model},请仔细遵循用户指令,用markdown回复,当前日期:{date}", label: "系统Prompt");
}
resetButton = gr.Button("🔄重置聊天", variant: ButtonVariant.Stop);
}
chatBot = gr.Chatbot(label: "聊天窗口", height: 700, showCopyButton: true, placeholder: "这里显示聊天历史记录");
using (gr.Row())
{
using (gr.Column(scale: 9))
{
userInput = gr.Textbox(label: "用户输入", placeholder: "请输入你的问题或指令...");
}
sendButton = gr.Button("✉️发送", variant: ButtonVariant.Primary);
regenerateButton = gr.Button("🔃重新生成", variant: ButtonVariant.Secondary);
}
using (gr.Row())
{
gr.Markdown("""
## Github: https://github.com/sdcb/Sdcb.DashScope
""");
gr.Markdown("""
## QQ: 495782587
""");
}
sendButton.Click(streamingFn: i =>
{
string model = Radio.Payload(i.Data[0]).Single();
string systemPrompt = Textbox.Payload(i.Data[1]);
IList<ChatbotMessagePair> chatHistory = Chatbot.Payload(i.Data[2]);
string userInput = Textbox.Payload(i.Data[3]);
return Respond(model, systemPrompt, chatHistory, userInput, sp);
}, inputs: [model, systemPrompt, chatBot, userInput], outputs: [userInput, chatBot]);
regenerateButton.Click(streamingFn: i =>
{
string model = Radio.Payload(i.Data[0]).Single();
string systemPrompt = Textbox.Payload(i.Data[1]);
IList<ChatbotMessagePair> chatHistory = Chatbot.Payload(i.Data[2]);
if (chatHistory.Count == 0)
{
throw new Exception("No chat history available for regeneration.");
}
string userInput = chatHistory[^1].HumanMessage.TextMessage;
chatHistory.RemoveAt(chatHistory.Count - 1);
return Respond(model, systemPrompt, chatHistory, userInput, sp);
}, inputs: [model, systemPrompt, chatBot], outputs: [userInput, chatBot]);
resetButton.Click(i => Task.FromResult(gr.Output(new ChatbotMessagePair[0], "")), outputs: [chatBot, userInput]);
return blocks;
}
通过简洁的Respond
方法项目完成和通义千问模型的交互,利用 HttpClient 调用阿里云的模型服务灵积(DashScope),将会话历史转化为 DashScopeChatMessage 数组,从而获取模型的响应。
static async IAsyncEnumerable<Output> Respond(string model, string systemPrompt, IList<ChatbotMessagePair> chatHistory, string message, IServiceProvider sp)
{
IConfiguration config = sp.GetRequiredService<IConfiguration>();
string dashScopeApiKey = config["DashScope:ApiKey"] ?? throw new Exception("DashScope API key is not configured.");
if (message == "")
{
yield return gr.Output("", chatHistory);
yield break;
}
systemPrompt = systemPrompt.Replace("{model}", model).Replace("{date}", DateTime.Now.ToString("yyyy-MM-dd"));
chatHistory.Add(new ChatbotMessagePair(message, ""));
using DashScopeClient api = new(dashScopeApiKey);
DashScopeChatMessage[] msgs =
[
DashScopeChatMessage.FromSystem(systemPrompt),
..chatHistory.SkipLast(1).SelectMany(p => new []
{
DashScopeChatMessage.FromUser(p.HumanMessage.TextMessage),
DashScopeChatMessage.FromAssistant(p.AiMessage.TextMessage),
}),
DashScopeChatMessage.FromUser(message),
];
await foreach (var item in api.TextGeneration.ChatStreamed(model, msgs, new()
{
//EnableSearch = true,
IncrementalOutput = true,
Seed = (ulong)Random.Shared.Next()
}))
{
chatHistory[^1].AiMessage.TextMessage += item.Output.Text;
yield return gr.Output("", chatHistory);
if (item.Output.FinishReason == "stop")
{
string? connectionString = config.GetValue<string>("ConnectionString");
if (connectionString != null)
{
IHttpContextAccessor httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
string clientIp =
httpContextAccessor.HttpContext!.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
httpContextAccessor.HttpContext!.Connection.RemoteIpAddress!.ToString();
DashScopeChatMessage[] combinedMessages = [.. msgs, DashScopeChatMessage.FromAssistant(chatHistory[^1].AiMessage.TextMessage)];
LogClientMessage(clientIp, model, combinedMessages, connectionString);
}
break;
}
}
}
Gradio.Net 的 Chatbot 组件负责记录和传递会话历史,省去了额外的存储代码。此外,为了模拟 ChatGPT 的打字效果,只需确保方法返回IAsyncEnumerable<Output>
类型的值,具体的实现细节由 Gradio.Net 框架负责。
结论
总的来说,这个项目不仅为 AI 爱好者提供了一个与开源模型互动的平台,也为 Web 开发者展示了如何利用现代技术栈简化开发流程。
如果你对创建自己的聊天机器人应用感兴趣,那么现在就是一个好时机。
Gradio.NET(https://github.com/feiyun0112/Gradio.Net/)的目标是成为用于开发 Web 应用的 .NET 开发者的首选框架。它的设计理念是让开发变得更加简单,让每个人都能够参与到Web应用的创造中来。
添加微信GradioDotNet,通过加入技术讨论群,开发者们可以分享经验,解决问题,并共同推动.NET的发展。
Gradio.NET和它的社区将继续成长,推动.NET生态系统的发展,帮助开发者们实现他们的创意和梦想。
如果你是.NET开发者,不妨考虑加入Gradio.NET的行列,体验这个框架带来的变革。