基于AI大文本模型的智慧对话开发设计及C#源码实现,实现智能文本改写与智慧对话
驯龙高手_追风 2024-06-22 12:31:02 阅读 75
文章目录
1.AI 大模型发展现状2.基于AI服务的智慧对话开发2.1 大模型API选择2.2 基于C#的聊天界面开发2.3 星火大模型API接入2.4 优化开发界面与显示逻辑 3.源码工程Demo及相关软件下载参考文献
1.AI 大模型发展现状
端午假期几天,关注到国内的AI大模型厂商近乎疯狂地打起了价格战,这边阿里云刚宣布降价97%,那边百度就宣布两款模型全面免费,好不热闹!据不完全统计,国内已有7家大模型企业“参战”,包括字节跳动、阿里云、百度、腾讯云等互联网大厂,智谱AI、深度求索等AI创企,以及垂直赛道头部玩家科大讯飞,纷纷争夺“最便宜”“最高性价比”大模型这块蛋糕。
▲国内大模型厂商参与价格战情况(智东西制表,统计于2024年5月27日)
总的来看,各大厂商对降价原因的解释无外乎以下几点:技术突破了,推理成本降低了;为开发者兜底,降低大模型的使用门槛;提升产品竞争力,积累客户。但大模型价格战对产业的影响具有两面性,既能够促使产业格局变化和商业模式创新,也为开发者带来机遇,有利于爆款应用的开发和大模型私有化部署。
2.基于AI服务的智慧对话开发
由于工作关系,涉及到相关文字材料的编制,对于某些材料,选用baidu的文心一言或者Aliyun的通义千问对于简单的工作来说可以提升部分效率,但是基于网页端还是存在一些限制,于是计划假期间利用C#语言+VS2015的win界面开发优势,配合大模型API实现快速的文本改写、文字降重以及智慧对话的功能
2.1 大模型API选择
选择讯飞星火大模型,其SparkLite免费为开发者开放,且不限tokens和有效期,提供各类开发的Demo源码,对于新手开发的话也比较友好。
2.2 基于C#的聊天界面开发
聊天界面开发计划基于Panel、RichTextBox、Button、PictureBox组成。其中Button主要负责模拟发送。
值得说明的是:Panel部分的聊天窗口由设计如下控件组成,且要支持右键菜单的复制和全选的功能,方便获取消息内容。
功能 | 控件 |
---|---|
头像 | PictureBox |
昵称 | Label |
发送时间 | Label |
信息详情 | RichTextBox |
右键菜单 | ContextMenuStrip |
实现的效果如下:
主窗体源码如下:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace WindowsFormsApplication1{ public partial class Form1 : Form { public Form1() { InitializeComponent(); } //新建聊天控件全局变量 ChatBubble tsps_chat; private void button1_Click(object sender, EventArgs e) { Image touxiang = (WindowsFormsApplication1.Properties.Resources._001); Image touxiang2 = (WindowsFormsApplication1.Properties.Resources._002); //发送 tsps_chat.AddMsg(touxiang2, richTextBox1.Text, ChatBubble.MsgPlace.Right, "测试"); //模拟接收 tsps_chat.AddMsg(touxiang, "你好,我有一个帽衫...",ChatBubble.MsgPlace.Left,"TSPS"); } private void Form1_Load(object sender, EventArgs e) { panel1.VerticalScroll.Visible = true; panel1.AutoScroll = true; Font ft = new Font("黑体", 12, Font.Style & ~FontStyle.Italic); //创建新的聊天窗口(传入panel和font) tsps_chat = new ChatBubble(panel1, ft); } }}
Panel相关代码如下:
using System;using System.Collections.Generic;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace WindowsFormsApplication1{ /// <summary> /// 聊天窗口展示类 /// </summary> class Class1 { } class ChatBubble { /// <summary> /// 生成菜单项 /// </summary> /// <param name="txt"></param> /// <param name="img"></param> /// <returns></returns> private ToolStripMenuItem GetMenuItem(string txt, Image img) { ToolStripMenuItem menuItem = new ToolStripMenuItem(); menuItem.Text = txt; menuItem.Image = img; return menuItem; } /// <summary> /// 菜单项事件响应 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void toolStripMenuItem_Click(object sender, ToolStripItemClickedEventArgs e) { //ToolStripMenuItem menuSend = sender as ToolStripMenuItem; //string selectText = ((RichTextBox)menuSend.).SelectedText; //MessageBox.Show(menu.Text); //获取对应控件的值 ContextMenuStrip menu_now = (ContextMenuStrip)sender; RichTextBox tb = ((RichTextBox)(menu_now).SourceControl); if (((ContextMenuStrip)sender).Items[0] == e.ClickedItem)//全选 { tb.Focus();//设置先焦点定位到当前活动的RichTextBox, tb.SelectAll(); } else if (((ContextMenuStrip)sender).Items[1] == e.ClickedItem)//复制 { Clipboard.SetDataObject(tb.SelectedText); } } public ChatBubble(Panel panel, Font font) { if (panel.Controls.Count != 0) throw new Exception("指定Panel控件不为空!"); ChatPlace = panel; BubbleFont = font; Context_caidan = new ContextMenuStrip(); MsgMaxLength = panel.Width - (6 * 4 + 35*2 ); // 其中, 四个6为 图片与(容器以及消息文本框)的距离 ,两个35为两侧图片的大小. //右键菜单编辑 Context_caidan.Items.Add("全选");//添加到右键菜单 Context_caidan.Items.Add("复制");//添加到右键菜单 //绑定消息 Context_caidan.ItemClicked += new ToolStripItemClickedEventHandler(toolStripMenuItem_Click);//添加事件 //Control.ContextMenuStrip = Context_caidan; } readonly Panel ChatPlace; readonly Font BubbleFont; readonly ContextMenuStrip Context_caidan;//右键复制菜单 int NowY = 7; readonly int MsgMaxLength; public enum MsgPlace { Left, Right } // 气泡创建的位置 /// <summary> /// 根据文本内容设置textbox高度等属性 /// </summary> /// <param name="txt1"></param> private void SettxtHeight(RichTextBox textBox1,string showString) { //属性 textBox1.Text = showString; textBox1.Multiline = true; textBox1.WordWrap = true; textBox1.Font = BubbleFont; textBox1.Width = MsgMaxLength; textBox1.BorderStyle = BorderStyle.None; textBox1.ContextMenuStrip = Context_caidan;//为文本框添加右键菜单 textBox1.ReadOnly = true; //尺寸参数 int txtHeight = 22;//设置单行的行高 int MaxLineCount = 20;//设置最大行数 Size size = TextRenderer.MeasureText(textBox1.Text, textBox1.Font); int itxtLine = size.Width / textBox1.Width + textBox1.Lines.Count() + 1; if (itxtLine > MaxLineCount) { itxtLine = MaxLineCount; } itxtLine -= 1; textBox1.Height = txtHeight * itxtLine; } public void AddMsg(Image Photo, string Text, MsgPlace Place, string Name) { if (ChatPlace.Controls.Count > 4*51) //仅保留近51条消息 { ChatPlace.Controls.Clear(); NowY = 7; } if (ChatPlace.Controls.Count != 0) { NowY = ChatPlace.Controls[ChatPlace.Controls.Count - 2].Location.Y + ChatPlace.Controls[ChatPlace.Controls.Count - 1].Height + 35;//间隔控制 } PictureBox photo = new PictureBox(); // 头像 Label nickname = new Label(); // 昵称 Label sendtime = new Label(); // Label msg = new Label(); RichTextBox msg = new RichTextBox(); photo.Size = new Size(35, 35); photo.SizeMode = PictureBoxSizeMode.StretchImage; photo.Image = Photo; nickname.AutoSize = true; nickname.MaximumSize = new Size(0, 0); nickname.Font = BubbleFont; nickname.Text = Name; //获取当前时间 string timenow = System.DateTime.Now.ToString("T"); sendtime.AutoSize = true; sendtime.MaximumSize = new Size(0, 0); sendtime.Font = BubbleFont; sendtime.Text = timenow; //msg.AutoSize = true; //msg.Font = BubbleFont; //msg.MaximumSize = new Size(MsgMaxLength, 0); // msg.Text = Text; //msg.BorderStyle = BorderStyle.None; SettxtHeight(msg,Text);//自动调整文本框大小 ChatPlace.Controls.Add(nickname); ChatPlace.Controls.Add(sendtime); ChatPlace.Controls.Add(photo); ChatPlace.Controls.Add(msg); if (Place == MsgPlace.Left) { msg.BackColor = Color.LightGreen; photo.Location = new Point(7, NowY); sendtime.Location = new Point(ChatPlace.Width/2-45, NowY); nickname.Location = new Point(photo.Location.X + photo.Width + 6, NowY); msg.Location = new Point(nickname.Location.X, NowY + nickname.Size.Height); } else { msg.BackColor = Color.LightSkyBlue; photo.Location = new Point(ChatPlace.Width - 7 - 35 - 10, NowY); sendtime.Location = new Point(ChatPlace.Width/2 -45 , NowY); nickname.Location = new Point(photo.Location.X - 7 - nickname.Width - 17, NowY); msg.Location = new Point(photo.Location.X - msg.Width - 10, NowY + nickname.Size.Height); // 这里的减去10是除去滚动条的宽度 } //panel滚动条到最下方 Point newPoint = new Point(0, ChatPlace.Height - ChatPlace.AutoScrollPosition.Y); ChatPlace.AutoScrollPosition = newPoint; } } // 简易聊天气泡}
2.3 星火大模型API接入
使用如下C#代码接入讯飞星火大模型API
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Net;using System.Security.Cryptography;using System.Text;using System.Threading.Tasks;using System.Collections;using Newtonsoft.Json;using System.Net.WebSockets;using System.Threading;using Newtonsoft.Json.Linq;using System.Text.Json;/** * 星火认知大模型 WebAPI 接口调用示例 接口文档(必看):https://www.xfyun.cn/doc/spark/Web.html * 错误码链接:https://www.xfyun.cn/doc/spark/%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E.html (code返回错误码时必看) * @author iflytek */namespace Webiat{ class Program { static ClientWebSocket webSocket0; static CancellationToken cancellation; // 应用APPID(必须为webapi类型应用,并开通星火认知大模型授权) const string x_appid = "XXXXXXXX"; // 接口key(webapi类型应用开通星火认知大模型后,控制台--我的应用---星火认知大模型---相应服务的apikey) const string api_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // 接口密钥(webapi类型应用开通星火认知大模型后,控制台--我的应用---星火认知大模型---相应服务的apisecret) const string api_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; static string hostUrl = "https://spark-api.xf-yun.com/v1.1/chat"; async public static void Tasker() { string authUrl = GetAuthUrl(); string url = authUrl.Replace("http://", "ws://").Replace("https://", "wss://"); using (webSocket0 = new ClientWebSocket()) { try { await webSocket0.ConnectAsync(new Uri(url), cancellation); JsonRequest request = new JsonRequest(); request.header = new Header() { app_id = x_appid, uid = "12345" }; request.parameter = new Parameter() { chat = new Chat() { domain = "general",//模型领域,默认为星火通用大模型 temperature = 0.5,//温度采样阈值,用于控制生成内容的随机性和多样性,值越大多样性越高;范围(0,1) max_tokens = 1024,//生成内容的最大长度,范围(0,4096) } }; request.payload = new Payload() { message = new Message() { text = new List<Content> { new Content() { role = "user", content = "你是谁" }, // new Content() { role = "assistant", content = "....." }, // AI的历史回答结果,这里省略了具体内容,可以根据需要添加更多历史对话信息和最新问题的内容。 } } }; string jsonString = JsonConvert.SerializeObject(request); //连接成功,开始发送数据 var frameData2 = System.Text.Encoding.UTF8.GetBytes(jsonString.ToString()); webSocket0.SendAsync(new ArraySegment<byte>(frameData2), WebSocketMessageType.Text, true, cancellation); // 接收流式返回结果进行解析 byte[] receiveBuffer = new byte[1024]; WebSocketReceiveResult result = await webSocket0.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), cancellation); String resp = ""; while (!result.CloseStatus.HasValue) { if (result.MessageType == WebSocketMessageType.Text) { string receivedMessage = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count); //将结果构造为json JObject jsonObj = JObject.Parse(receivedMessage); int code = (int)jsonObj["header"]["code"]; if(0==code){ int status = (int)jsonObj["payload"]["choices"]["status"]; JArray textArray = (JArray)jsonObj["payload"]["choices"]["text"]; string content = (string)textArray[0]["content"]; resp += content; if(status != 2){ Console.WriteLine($"已接收到数据: { receivedMessage}"); } else{ Console.WriteLine($"最后一帧: { receivedMessage}"); int totalTokens = (int)jsonObj["payload"]["usage"]["text"]["total_tokens"]; Console.WriteLine($"整体返回结果: { resp}"); Console.WriteLine($"本次消耗token数: { totalTokens}"); break; } }else{ Console.WriteLine($"请求报错: { receivedMessage}"); } } else if (result.MessageType == WebSocketMessageType.Close) { Console.WriteLine("已关闭WebSocket连接"); break; } result = await webSocket0.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), cancellation); } } catch (Exception e) { Console.WriteLine(e.Message); } } } // 返回code为错误码时,请查询https://www.xfyun.cn/document/error-code解决方案 static string GetAuthUrl() { string date = DateTime.UtcNow.ToString("r"); Uri uri = new Uri(hostUrl); StringBuilder builder = new StringBuilder("host: ").Append(uri.Host).Append("\n").// Append("date: ").Append(date).Append("\n").// Append("GET ").Append(uri.LocalPath).Append(" HTTP/1.1"); string sha = HMACsha256(api_secret, builder.ToString()); string authorization = string.Format("api_key=\"{0}\", algorithm=\"{1}\", headers=\"{2}\", signature=\"{3}\"", api_key, "hmac-sha256", "host date request-line", sha); //System.Web.HttpUtility.UrlEncode string NewUrl = "https://" + uri.Host + uri.LocalPath; string path1 = "authorization" + "=" + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(authorization)); date = date.Replace(" ", "%20").Replace(":", "%3A").Replace(",", "%2C"); string path2 = "date" + "=" + date; string path3 = "host" + "=" + uri.Host; NewUrl = NewUrl + "?" + path1 + "&" + path2 + "&" + path3; return NewUrl; } public static string HMACsha256(string apiSecretIsKey, string buider) { byte[] bytes = System.Text.Encoding.UTF8.GetBytes(apiSecretIsKey); System.Security.Cryptography.HMACSHA256 hMACSHA256 = new System.Security.Cryptography.HMACSHA256(bytes); byte[] date = System.Text.Encoding.UTF8.GetBytes(buider); date = hMACSHA256.ComputeHash(date); hMACSHA256.Clear(); return Convert.ToBase64String(date); } static void Main(string[] args) { Tasker(); Console.ReadLine(); } }}//构造请求体public class JsonRequest{ public Header header { get; set; } public Parameter parameter { get; set; } public Payload payload { get; set; }}public class Header{ public string app_id { get; set; } public string uid { get; set; }}public class Parameter{ public Chat chat { get; set; }}public class Chat{ public string domain { get; set; } public double temperature { get; set; } public int max_tokens { get; set; }}public class Payload{ public Message message { get; set; }}public class Message{ public List<Content> text { get; set; }}public class Content{ public string role { get; set; } public string content { get; set; }}
2.4 优化开发界面与显示逻辑
最终调整了实时显示以及界面展示逻辑如下效果
1.获取的数据可以实时显示并自动调整控件大小;
2.可以正常通过按钮进行交互控制;
3.增加进度条用于显示实时状态;
4.支持个人的账号登录。
3.源码工程Demo及相关软件下载
下载1:讯飞星火大模型C#接入Demo
下载2: C#聊天窗口界面Demo开发
下载3: 打包好的程序,可直接使用: TSPS V32程序,支持讯飞大模型等API接入、论文降重、文本改写、智慧AI对话 或 蓝奏云下载
注:对于上述(3)中打包的程序,若程序打开时显示“Window已保护你的电脑”可以:
1.点击弹窗的“更多信息”
2.点击仍要运行
3.在弹窗中点击“是”即可打开
参考文献
https://www.thepaper.cn/newsDetail_forward_27521760
https://blog.csdn.net/qq_20051033/article/details/104889215?spm=1001.2014.3001.5506
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。