多轮对话功能实现
视频教程
<iframe allowfullscreen="true" data-mediaembed="bilibili" frameborder="0" id="8N5yHjY0-1733735615877" src="https://player.bilibili.com/player.html?aid=113622051199870"></iframe>.Net+AI开发入门HttpClient实现通义千问集成-多轮对话功能实现
实现原理
一直保留更新messages
现在设置的meessages只设置了两条内容
- system:系统消息,给AI设置一个角色,
- user:用户消息,你提的问题
消息的类型
根据OpenAI API官网,消息有以下几种类型
我们现在主要用的就三个:
- System Message :系统消息,用于指定模型的目标或角色(放在messages第一位)
- User Message:用户消息,用户发送给模型的消息。
- Assistant Message:助手消息,模型对用户消息的回复。
实现的效果:
messages:
system:xx
user:xxx
assistant:xxx
user:xxx
assistant:xxx
功能开发
消息类
创建一个消息类
public class ChatMessage
{
public string role { get; set; }
public string message { get; set; }
}
修改请求体
修改请求体,将message内容改成一个占位字符串,用于后面修改
增加一个消息集合messages
用于存储消息
List<ChatMessage> messages = new List<ChatMessage>();
messages.Add(new ChatMessage() { role = "system", content = "你是一个C#高手" });
修改发送请求函数
增加一个result,获取流式输出的content完整内容,返回完整的助手消息内容,用于后续添加到messages中
private static async Task<string> SendPostRequestAsync(
string url,
string jsonContent,
string apiKey
)
{
using (var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"))
{
// 发送请求并获取响应
HttpResponseMessage response = await httpClient.PostAsync(url, content);
// 处理响应
if (response.IsSuccessStatusCode)
{
string result = "";
using (Stream stream = await response.Content.ReadAsStreamAsync())
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
if (string.IsNullOrEmpty(line))
continue;
string data = line.Substring(6);
if (data == "[DONE]")
{
//结束标志
break;
}
var streamObject = JsonSerializer.Deserialize<StreamObject>(data);
if (streamObject.choices.Count() > 0)
{
var contentRes = streamObject.choices[0].delta.content;
Console.Write(contentRes);
result += contentRes;
}
if (streamObject.usage != null)
{
Console.WriteLine(
$"Usage: prompt_tokens:{streamObject.usage.prompt_tokens}, completion_tokens:{streamObject.usage.completion_tokens}, total_tokens:{streamObject.usage.total_tokens}"
);
}
Thread.Sleep(200);
}
Console.WriteLine();
}
return result;
// return await response.Content.ReadAsStringAsync();
}
else
{
Console.WriteLine($"请求失败: {response.StatusCode}");
return $"请求失败: {response.StatusCode}";
}
}
}
将httpclient设置请求头的代码拿到一开始,只设置一次
修改用户消息输入
- 修改用户消息,改成用户直接在控制台上输入,输入之后再加入到消息中
- 然后将消息集合
messages
序列化成字符串,替换掉jsonContent
里面的消息占位符messagesContent
,再发送出去 - 接收到模型返回的助手消息之后,将助手消息也添加到
messages
中去,role
为"assistant" - 然后下次发送,这些消息累加一起再发送
while (true)
{
Console.Write("User:");
var usermessage = Console.ReadLine();
if (string.IsNullOrEmpty(usermessage))
{
continue;
}
if (usermessage == "exit")
{
break;
}
var user = new ChatMessage() { role = "user", content = usermessage };
messages.Add(user);
var str = JsonSerializer.Serialize(messages);
var send = jsonContent.Replace("messagesContent", str);
// 发送请求并获取响应
Console.WriteLine("assistant:");
var result = await SendPostRequestAsync(url, send, apiKey);
messages.Add(new ChatMessage() { role = "assistant", content = result });
}
多轮对话的token
多轮对话的token是持续累加的,第二次发送的时候相当于第一次发送和返回的消息也发送了,都算在第二次发送的token中
消息完整文档
以下详细内容来着OpenAI API翻译
https://platform.openai.com/docs/api-reference/chat/create
消息类型
系统消息 (System message)
object
-
content
(字符串或数组) 必填系统消息的内容。
-
role
(字符串) 必填消息作者的角色,此处为
"system"
。 -
name
(字符串) 可选用于区分相同角色参与者的可选名称。
用户消息 (User message)
object
-
content
(字符串或数组) 必填用户消息的内容。
-
role
(字符串) 必填消息作者的角色,此处为
"user"
。 -
name
(字符串) 可选用于区分相同角色参与者的可选名称。
助手消息 (Assistant message)
object
-
content
(字符串或数组) 可选助手消息的内容。除非指定
tool_calls
或function_call
,否则必填。 -
refusal
(字符串或 null) 可选助手的拒绝消息。
-
role
(字符串) 必填消息作者的角色,此处为
"assistant"
。 -
name
(字符串) 可选用于区分相同角色参与者的可选名称。
-
audio
(对象或 null) 可选与助手之前的音频响应相关的数据。
-
id
(字符串)必填唯一标识模型生成的音频响应。
-
-
tool_call
(数组) 必填模型生成的工具调用列表(例如函数调用)。
-
id
(字符串)必填具调用的唯一 ID。
-
type
(字符串)必填工具类型,目前仅支持
function
。 -
function
(对象)必填模型调用的函数。
-
name
(字符串)必填要调用的函数名称。
-
arguments
(字符串)必填模型以 JSON 格式生成的函数调用参数。
- 注意:模型生成的参数可能无效,或者包含未在函数定义中描述的参数。
- 建议: 在调用函数之前,应在代码中验证参数
-
-
-
function_call
已弃用 (对象 或者 null) 选填模型生成的工具调用列表(例如函数调用)。
-
name
(字符串)必填要调用的函数名称。
-
arguments
(字符串)必填模型以 JSON 格式生成的函数调用参数。
-
工具消息 (Tool message)
object
-
role
(字符串) 必填消息作者的角色,此处为
"tool"
。 -
content
(字符串或数组) 必填工具消息的内容。
-
tool_call_id
(字符串) 必填对应工具调用的消息 ID。
函数消息 (Function message)
(已弃用)
object
-
role
(字符串) 必填消息作者的角色,此处为
"function"
。 -
content
(字符串或 null) 必填函数消息的内容。
-
name
(字符串) 必填函数名称。