我做了第一个ChatGPT .net api聊天库
最近这个ChatGPT很火啊,看了B站上很多视频,自己非常手痒,高低自己得整一个啊,但是让我很难受的是,翻遍了github前十页,竟然没有一个C#的ChatGPT项目,我好难受啊!那能怎么办?自己搞一个吧。
但是,等等,现在的ChatGPT项目基本都是网页逆向获取几个token,我不会啊,我都不知道哪些cookie是重要的,那我只能找一个其它语言的ChatGPT API项目,自己造(翻译)一个c#的ChatGPT API库了。
ChatGPT是啥?
先了解GTP,摘自官网的一段话“A set of models that can understand and generate natural language”,其实,就是一个自然语言处理的模型。所以ChatGPT顾名思义就是基于GPT3的一个聊天AI。
但是想让他做苦力帮忙写代码的话所需要的其实不是这个模型,而是Codex这个模型,按照官网所说“Most capable Codex model. Particularly good at translating natural language to code. In addition to completing code, also supports inserting completions within code.”。真的是碉堡了!
准备
第一步:注册OpenAI账号
因为对大陆以及中国香港地区不开放,所以我们需要小小的科学一下。
来一个我就是参照着申请账号的园子的文章:
https://www.cnblogs.com/chatgpt/p/how-to-register-chatgpt-in-china.html
当然手机那里可以淘宝找,几块钱就能帮忙注册手机,账号里面默认还有18美元余额。
注意点:如果搭建了科学还是提示不对你的国家提供服务的话,尝试清空浏览器缓存或者打开浏览器的无痕窗口。Chrome默认在右上角三个点打开就能找到“打开新的无痕式窗口”。
万事具备,直接撸代码
1.网页获取所需token和cookie
我们需要三个东西:UserAgent,CfClearance,Session_token
我们需要先打开ChatGPT官方网站:https://chat.openai.com/chat 然后按下F12打开浏览器的开发者模式
UserAgent在网络里(只需要复制UserAgent:后面的值):
CfClearance和Session_token在应用程序->cookie里面
2.创建一个session用来表示一个会话
public OpenAISession(string session_token,string cfClearance,string userAgent)
{
Session_token = session_token;
CfClearance = cfClearance;
UserAgent = userAgent;
Headers = new Dictionary<string, string>();
Cookies = new Dictionary<string, string>();
Proxies = new Dictionary<string, string>();
}
Session可以刷新自己的AccessToken和Session_token
public async Task RefreshSessionAsync()
{
if (string.IsNullOrEmpty(Session_token))
{
throw new Exception("No tokens provided");
}
// Set cookies
Cookies.Put("__Secure-next-auth.session-token", Session_token);
Cookies.Put("cf_clearance", CfClearance);
string cookiesString = GetCookiesString();
Dictionary<string, string> map = new Dictionary<string, string>();
Headers.Put("User-Agent", UserAgent);
Headers.Put("cookie", cookiesString);
Headers.Put("Cookie", cookiesString);
var response = await GetAsync("https://chat.openai.com/api/auth/session");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine("err code: " + response.StatusCode);
Console.WriteLine("cf_clearance: " + CfClearance);
Console.WriteLine("token: " + Session_token);
Console.WriteLine("userAgent: " + UserAgent);
Console.WriteLine("请检查以上参数是否正确,是否过期。");
throw new Exception("无法获取token!请重试");
}
try
{
string name = "__Secure-next-auth.session-token=";
var cookies = response.Headers.GetValues("Set-Cookie");
var stoken = cookies.FirstOrDefault(x => x.StartsWith(name));
Session_token = stoken == null ? Session_token : stoken.Substring(name.Length, stoken.IndexOf(";") - name.Length);
Cookies.Put("__Secure-next-auth.session-token", Session_token);
var result = await response.Content.ReadAsStringAsync();
AccessToken = JsonSerializer.Deserialize<Profile>(result, _jsonSerializerOptions)?.AccessToken;
RefreshHeaders();
}
catch (Exception ex)
{
Console.WriteLine($"Error {nameof(RefreshSessionAsync)}:{ex}");
throw new Exception($"Error {nameof(RefreshSessionAsync)}", ex);
}
}
获取到的最新的AccessToken更新到header里,Session_token更新到cookie里
private void RefreshHeaders()
{
Headers.Put("Host", "chat.openai.com");
Headers.Put("Accept", "text/event-stream");
Headers.Put("Authorization", $"Bearer {AccessToken}");
Headers.Put("User-Agent", UserAgent);
Headers.Put("X-Openai-Assistant-App-Id", string.Empty);
Headers.Put("Connection", "close");
Headers.Put("Accept-Language", "en-US,en;q=0.9");
Headers.Put("Referer", "https://chat.openai.com/chat");
}
string name = "__Secure-next-auth.session-token=";
var cookies = response.Headers.GetValues("Set-Cookie");
var stoken = cookies.FirstOrDefault(x => x.StartsWith(name));
Session_token = stoken == null ? Session_token : stoken.Substring(name.Length, stoken.IndexOf(";") - name.Length);
Cookies.Put("__Secure-next-auth.session-token", Session_token);
3.创建机器人绑定一个会话
public Chatbot(OpenAISession openAISession)
{
OpenAISession = openAISession;
ResetConversation();
}
/// <summary>
/// 重置Conversation,开启一个新的会话
/// </summary>
public void ResetConversation()
{
_conversationId = null;
_parentMessageId = Guid.NewGuid().ToString();
}
如何保持上下文聊天以及多用户隔离?
上下文聊天已经内置,会自动与机器人的回复进行关联,当需要重新开启一个对话的时候,可以调用ResetConversation,这样对应的机器人对象发过去就是开启了一个新的对话。
如何多用户隔离?比如联合微信机器人使用,可以将一个人或者一个群对应一个Chatbot对象,并且利用id与机器人做关联。
当然这个_clientID需要使用者做唯一性管理
//可以作为隔离不同客户端聊天上下文的凭据,即一个机器人绑定一个客户端
//客户端编号可以是,微信id,qq id,飞书id,亦或者自己开发的软件的用户id
private readonly string _clientID;
public Chatbot(OpenAISession openAISession, string clientID)
{
OpenAISession = openAISession;
_clientID = clientID == null ? Guid.NewGuid().ToString() : clientID;
ResetConversation();
}
最后构造对话对象,发送到对应api
public async Task<Reply> GetChatReplyAsync(string prompt)
{
var conversation = new Conversation();
conversation.Conversation_id = _conversationId;
conversation.Parent_message_id = _parentMessageId;
conversation.Messages = new Message[]
{
new Message()
{
Content = new Content
{
Parts = new string []{ prompt }
}
}
};
return await GetChatResponseAsync(conversation);
}
/// <summary>
/// 获取响应
/// </summary>
/// <param name="conversation"></param>
/// <returns></returns>
/// <exception cref="Exception">服务器返回非200</exception>
private async Task<Reply> GetChatResponseAsync(Conversation conversation)
{
using (var client = new HttpClient())
{
var response = await OpenAISession.PostAsync(_conversation, JsonSerializer.Serialize(conversation, _jsonSerializerOptions));
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Faild to request.StatusCode:{response.StatusCode}");
}
var msg = await response.Content.ReadAsStringAsync();
var data = msg.Split("\n")?.ToList().Where(x => !string.IsNullOrEmpty(x) && !x.Contains("data: [DONE]")).LastOrDefault()?.Substring(5);
var reply = JsonSerializer.Deserialize<Reply>(data, _jsonSerializerOptions);
_conversationId = reply.Conversation_id;
return reply;
}
}
效果截图(仅测试)
OpenAISession openAIOptions = new OpenAISession(Session_token, CfClearance, UserAgent);
await openAIOptions.RefreshSessionAsync();
Chatbot chatbot = new Chatbot(openAIOptions);
Console.WriteLine("用c++写个冒泡查询");
var reply = await chatbot.GetChatReplyAsync("用c++写个冒泡查询");
Console.WriteLine(reply.Message.Content.Parts.FirstOrDefault());
源码(欢迎star)
https://github.com/BruceQiu1996/NChatGPTRev
我做了第一个ChatGPT .net api聊天库的更多相关文章
- 我用开天平台做了一个字符串检查API,hin 简单~~
摘要:本文使用了工作台的API全生命周期管理和函数管理功能,编写字符串检查的函数,实现了API的快速创建和发布. 本文分享自华为云社区<[我用开天平台做了一个字符串检查API>,作者:人类 ...
- 如何设计一个优秀的API(转载)
最近在整理框架的一些 API,觉得很有必要总结一下 API 兼容性的设计.下图是我自己当下的一些总结,慢慢维护: 网上搜索了一下,一个多月前,“标点符”已经发布了下面这篇文章,觉得写得非常不错,转载于 ...
- 如何设计一个优秀的API(转)
到目前为止,已经负责API接近两年了,这两年中发现现有的API存在的问题越来越多,但很多API一旦发布后就不再能修改了,即时升级和维护是必须的.一旦API发生变化,就可能对相关的调用者带来巨大的代价, ...
- 并发编程 —— 自己写一个异步回调 API
1. 前言 在并发编程中,异步回调的效率不言而喻,在业务开发中,如果由阻塞的任务需要执行,必然要使用异步线程.并且,如果我们想在异步执行之后,根据他的结果执行一些动作. JDK 8 之前的 Futur ...
- 如何设计一个优秀的API
如何设计一个优秀的API - 文章 - 伯乐在线 http://blog.jobbole.com/42317/ 如何设计一个优秀的API - 标点符 https://www.biaodianfu.co ...
- 我的第一个netcore2.2 api项目搭建(三)续
上一章快速陈述了自定义验证功能添加的过程,我的第一个netcore2.2 api项目搭建(三) 但是并没有真正的去实现,这一章将要实现验证功能的添加. 这一章实现目标三:jwt认证授权添加 在netc ...
- 我的第一个netcore2.2 api项目搭建(三)
上一章快速添加了swagger文档管理功能,我的第一个netcore2.2 api项目搭建(二) 这一章实现目标三:api添加身份验证功能 在实现该目标之前,先得理解netcore运行机制. 这是微软 ...
- 一个扩展搜索API的优化过程
概述 API 是一个服务的门面,就像衣装是人的形象一样. 优雅的 API 设计,能让业务方使用起来倍儿爽,提升开发效率,降低维护成本:糟糕的 API 设计,则让业务方遭心,陷入混沌. 本文将展示一个扩 ...
- 如何使用 Gin 和 Gorm 搭建一个简单的 API 服务 (一)
介绍 Go 语言最近十分火热,但对于新手来说,想立马上手全新的语法和各种各样的框架还是有点难度的.即使是基础学习也很有挺有挑战性. 在这篇文章中,我想用最少的代码写出一个可用的 API 服务. ...
- 通过beego快速创建一个Restful风格API项目及API文档自动化
通过beego快速创建一个Restful风格API项目及API文档自动化 本文演示如何快速(一分钟内,不写一行代码)的根据数据库及表创建一个Restful风格的API项目,及提供便于在线测试API的界 ...
随机推荐
- 使用pip的方式安装docker-compose
# 国内开启pip 下载加速:http://mirrors.aliyun.com/help/pypi mkdir ~/.pip/ cat > ~/.pip/pip.conf <<'E ...
- 使用docker-compose方式部署es和kibana以及cerebro
使用的镜像可以从这个网站查看最新的:https://hub.docker.com/ 参考极客时间上的教程转发来的 使用步骤:安装docker和docker-compose 运行: docker-com ...
- 通过开启swap分区来解决小内存阿里云服务器的内存瓶颈
swap分区大小设置 阿里云的linux云服务器默认是没有启用swap分区(交换分区)的.一般情况下swapswap分区的大小可以参考以下规则进行设定: 内存大小 swap大小 MEM_SIZE &l ...
- 【Java8新特性】- Lambda表达式
Java8新特性 - Lambda表达式 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! ...
- git中 gitignore 忽略文件操作
通常,.gitignore文件被放置在存储库的根目录中.根目录也称为父目录和当前工作目录.根文件夹包含组成项目的所有文件和其他文件夹.也就是说,您可以将它放在存储库中的任何文件夹中.你甚至可以有多个. ...
- [题解] Atcoder Regular Contest ARC 151 A B C D E 题解
点我看题 昨天刚打的ARC,题目质量还是不错的. A - Equal Hamming Distances 对于一个位置i,如果\(S_i=T_i\),那么不管\(U\)的这个位置填什么,对到\(S\) ...
- Android自动化测试工具调研
原文地址:Android自动化测试工具调研 - Stars-One的杂货小窝 Android测试按测试方式分类,可分为两种:一种是传统逻辑单元测试(Junit),另外一种则是UI交互页面测试. 这里详 ...
- JavaScript基本语法(JavaScript代码嵌入方式与声明和使用变量)
.JavaScript代码嵌入方式 #①HTML文档内 JavaScript代码要写在script标签内 script标签可以写在文档内的任意位置 为了能够方便查询或操作HTML标签(元素)scrip ...
- 一个电器工厂可以生产多种类型的电器,如海尔工厂可以生产海尔电视机、海尔空调等,TCL工厂可以生产TCL电视机,TCL空调等,相同品牌的电器构成一个产品族,而相同类型的电器构成了一个产品等级结构,现使用
一个电器工厂可以生产多种类型的电器,如海尔工厂可以生产海尔电视机.海尔空调等,TCL工厂可以生产TCL电视机,TCL空调等,相同品牌的电器构成一个产品族,而相同类型的电器构成了一个产品等级结构,现使用 ...
- Vue中组件化编码 完成任务的添加、删除、统计、勾选需求(实战练习三完结)
上一个章节实现数据在组件之间的传递 .这一章主要是完成添加任务到任务栏.删除任务栏.统计任务完成情况.主要还是参数在各个组件之间的传递. 上一章节的链接地址:https://blog.csdn.net ...