OpenAI接口接入调试记录
完成的功能
- 制作基于流式传输的OpenAI接口调用工具
- 制作关于剧情生成的提示词
过程中遇到的问题
- 调用流式传输接口等待时间过长
原因HttpCompletionOption指示HttpClient操作是在响应可利用时立即视为已完成,还是在读取包含上下文的整个答案信息之后才视为已完成,默认问HttpCompletionOption.ResponseContentRead,这会使在所有响应结束之后才视为完成,而实际只需要在响应可利用时返回,所以需要修改为HttpCompletionOption.ResponseHeadersRead
//修改前
//此处测试耗时基本在80s以上
var requestTimer = Stopwatch.StartNew();
//PostAsJsonAsync 本身不支持直接传入 HttpCompletionOption
HttpResponseMessage httpResponseMessage = await client.PostAsJsonAsync<AIChat>("", aiChat);
requestTimer.Stop();
//修改后
var requestContent = JsonContent.Create(aiChat, mediaType: new MediaTypeHeaderValue("application/json"));
using var request = new HttpRequestMessage(HttpMethod.Post, "")
{
Content = requestContent,
VersionPolicy = HttpVersionPolicy.RequestVersionExact
};
//此时平均响应在1s左右
var sw = Stopwatch.StartNew();
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
sw.Stop();
- 流式传输的底层逻辑
所谓流式传输就是http建立socket协议之后不立即断开连接,而是按照一定的协议,在socket断开之前一直保持连接,直到服务端发送完毕,客户端接收完毕,才断开连接,即客户端会收到多次返回消息,需按约定格式一一处理
// 持续读取流数据直到结束
while (!reader.EndOfStream)
{
// 异步读取一行数据
var line = await reader.ReadLineAsync();
// 跳过空行处理
if (string.IsNullOrEmpty(line)) continue;
// 检测流结束标记
if (line == "data: [DONE]")
{
Console.WriteLine("\nStream completed");
break; // 终止循环
}
// 处理有效数据行(服务端返回的SSE格式数据)
if (line.StartsWith("data: "))
{
try
{
// 提取有效JSON部分(去除开头的"data: ")
var json = line["data: ".Length..];
// 反序列化JSON为响应对象
var responsejson = JsonSerializer.Deserialize<StreamResponse>(json);
// 解析模型思考过程内容(如模型内部推理过程)
var think = responsejson?.choices.FirstOrDefault()?.delta?.reasoning_content ?? "";
// 处理思考内容输出
if (!string.IsNullOrEmpty(think))
{
// 状态切换:当开始思考时输出提示
if (!bThinking)
{
fileWriter.WriteLine("\nThinking..."); // 写入思考提示
bThinking = !bThinking; // 切换状态标志
}
fileWriter.Write(think); // 持续写入思考内容
}
// 解析实际响应内容(模型最终输出的对话内容)
var chunk = responsejson?.choices.FirstOrDefault()?.delta?.content ?? "";
// 处理最终内容输出
if (!string.IsNullOrEmpty(chunk))
{
// 状态切换:当开始输出正式内容时输出提示
if (bThinking)
{
fileWriter.WriteLine("\nSay:"); // 写入内容提示
bThinking = !bThinking; // 重置状态标志
}
fileWriter.Write(chunk); // 持续写入对话内容
}
}
catch (Exception ex)
{
// 异常处理:输出解析错误信息(保留控制台输出用于调试)
Console.WriteLine($"Parse error: {ex.Message}");
}
}
}
未解决的问题
- 模型输出的文本符合json格式,但是会在json前后添加markdown的json标记,目前暂无法消除,需要进一步研究
- 文档中response_format可以指定模型输出json格式的内容,但是事实上模型经常输出错误的格式