OpenAI今天刚刚开放了ChatGPT-3.5模型的API,写个Shiny APP调用它测试一下。详见其文档及ChatGPT-3.5 API。收费标准是GPT-3.5的10%,特别为聊天优化,所以OpenAI推荐用它。两会期间,暂时不提供Shiny APP试用体验,只是调试好放在那里。
Pricing
It’s priced at $0.002 per 1K tokens, which is 10x cheaper than the existing GPT-3.5 models.
ChatGPT3.5聊天机器人实例
下面是我通过Shiny APP与它的一段对话,截图如上所示:
User: Hi,你好。
AI: 你好!有什么我可以帮助你的吗?
User: 请问ChatGPT-3.5模型与GPT-3模型有什么不同?
AI: ChatGPT-3.5模型是在OpenAI的GPT-3模型的基础上进行微调和改进的结果,与GPT-3模型相比,主要体现在以下几个方面:
1. 增加对话推理:ChatGPT-3.5在GPT-3的基础上加入了对话推理机制。GPT-3针对单纯文本的生成表现出了强大的能力,但在对话生成上还相对薄弱。ChatGPT-3.5在这方面的表现相比GPT-3有了显著提升。
2. 更好的上下文理解:ChatGPT-3.5相比GPT-3能够更好地理解上下文信息,从而产生更加合理和准确的回答。
3. 更高的精确度和流畅性:ChatGPT-3.5在文本生成的精确度和语言流畅性方面优于GPT-3。在生成对话和文本方面,回复更加自然,更具有代入感。
综上所述,ChatGPT-3.5相比GPT-3在对话生成方面的表现更加优秀,拥有更好的上下文理解和更高的精确度和流畅性。
User: 它们的Python API不太相同,一个输入messages字典列表,一个输入prompt字符串。
AI: 是的,ChatGPT-3.5和GPT-3在Python API方面的区别主要体现在输入的格式不同。
GPT-3的Python API使用prompt字符串作为模型输入,而ChatGPT-3.5则使用了一种稍微复杂的格式,需要传入一个包含对话历史记录的消息字典列表。
具体来说,ChatGPT-3.5需要传入以下参数:
- chat_history: 包含对话历史记录的消息字典列表,可以包括文本、电子邮件、Messager等等不同格式。
- max_tokens: 指定生成文本的最大长度。
- temperature: 控制生成文本的多样性和可预测性。
- stop: 如果生成文本中出现了此参数指定的标识符,就强制终止生成过程。
GPT-3的Python API则不需要输入对话历史记录的消息字典列表,只需要传入一个prompt字符串,以及一些其他的参数,比如生成文本的最大长度、使用哪个模型等。由于GPT-3不考虑对话历史记录,所以输入方式相对简单。
因此,在使用时需要根据具体的情况和对预测结果的要求选择适合的模型和API。
User: 我在GPT-3调用中是把对话历史,包括所有输入和输出,拼接成字符串,来实现对话的上下文,效果是一样的。
AI: 是的,将对话历史拼接为字符串的方式确实可以将上下文信息传递给GPT-3,这样GPT-3就能够根据之前的对话内容来生成下一条回复。
不过,与直接传入消息字典列表相比,使用字符串拼接的方法可能需要对生成的文本进行一些后处理,以保证输出的格式和连贯性。此外,对于较长或复杂的对话历史,过多的文本可能会增加处理时间并降低性能。
综上所述,使用消息字典列表的方式可以更加方便和自然地传递上下文信息,同时也不需要额外的后处理步骤。虽然字符串拼接的方法也可以达到相应的效果,但需要更多的手动处理。
User: Get it.
AI: 很好,如果你有其他问题,请随时问我。
User: O.K., see you.
AI: 好的,祝你愉快。如果您有任何问题,请随时联系我。再见!
要升级OpenAI 包到0.27版:
pip install --upgrade openai
具体实现上稍作修改:
1.ChatGPT35.py,Shiny APP通过它来调用ChatGPT模型。
import openai
# 为方便分享源码,把access key放在添加的api_key.py中,只有授权用户可以访问,这样就不需要显示access key。
from openai.api_resources import api_key
# 郑重提示:纯粹技术研究,请在法律法规范围内谨慎受控使用,勿用于违法用途,责任自负。
# Setup proxy for accessing OpenAI API server through VPN on Linux if it is needed.
# Need to down grade urllib3 to older version when access through VPN.
# pip install urllib3==1.25.11
import os
os.environ['http_proxy']="http://127.0.0.1:7890"
os.environ['https_proxy']="http://127.0.0.1:7890"
openai.api_key = api_key.key
# 传入一个R语言data frame "context"
def CallAPI(context, tokens):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages= context.to_dict('records'),
max_tokens=int(tokens)
)
# print(response['choices'][0]['message']['content'])
return response['choices'][0]['message']['content']
下面是Shiny APP的几个R语言程序。
2、global.R。
对话的上下文将用R语言data frame “context”来存放在session中。
library(reticulate)
library(stringr)
# Load OpenAI API
print(getwd())
path<- "../../scripts/ChatGPT35.py"
print(path)
source_python(path)
# 这是一个空的上下文 data frame结构。
context<-data.frame("role"=character(),"content"=character())
context<-rbind(context, list(role="system",content="You are a helpful assistant."))
3、ui.R。
对话记录是只读,可以复制,不能修改,用javascript程序设定。
fluidPage(
sidebarLayout(
# Sidebar
sidebarPanel(
# Application title
tags$h3("ChatGPT-3.5 聊天机器人"),
sliderInput("tokens",
"最大输出单词数:",
min = 1000, max = 5000, value = 500),
checkboxInput("ifContext","是否包括上下文:", value = TRUE),
actionButton("clearContext", "清除上下文", class = "btn-cleear"),
# 1/6
# width = 2
),
# Main
mainPanel(
textAreaInput("context","对话记录",width="100%", rows =20, resize="vertical", value =""),
# 插入javascript,禁止自己修改 context textAreaInput
tags$script(HTML("
var context = document.getElementById('context');
context.readOnly = true;
")),
tags$h6(" "),
textAreaInput("prompt","输入:",width="100%", rows =2, resize="vertical", value =""),
actionButton("sendout", "提交", class = "btn-success"),
# 5/6
# width = 10
)
)
)
4、server.R。
演示性质简单起见,这里每次都将data frame “context”转换成字符串输出,不是最优的处理,最优的处理是每次添加增量的部分,需要使用Shiny.addCustomMessageHandler()在ui.R中注册一个javascript函数来接受并处理在server.R在observeEvent()中通过session$sendCustomMessage()发送的更新对话数据,详细可参阅这篇文章。
function(input, output, session) {
tokens<- reactive({
input$tokens
})
ifContext<- reactive({
input$ifContext
})
prompt<- reactive({
input$prompt
})
observeEvent(input$clearContext,{
session$userData$context<- context
updateTextAreaInput(session, "context", value = "")
})
observeEvent(input$sendout,{
if(ifContext() && ! is.null(session$userData$context)){
temp<-session$userData$context
} else{
temp<- context
}
temp<-rbind(temp, list(role="user",content=prompt()))
# Show notification while querying.
id <- showNotification("正在询问ChatGPT-3.5...", duration = NULL, closeButton = FALSE)
on.exit(removeNotification(id), add = TRUE)
response<- CallAPI(temp, tokens())
temp<-rbind(temp, list(role="assistant",content=response))
session$userData$context<- temp
# 格式化输出,插入对话者身份前缀。
outStr<-""
for(i in 2: dim(temp)[[1]]){
if (i%%2==0){
outStr<-paste(outStr,"User: ",temp[i,2],"\n",sep="")
}else{
outStr<-paste(outStr,"AI: ",temp[i,2],"\n\n",sep="")
}
}
updateTextAreaInput(session, "context", value = outStr)
updateTextAreaInput(session, "prompt", value = "")
})
}
That's all,其实也很简单。