视频教程
.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
(字符串) 必填函数名称。