微信扫码
与创始人交个朋友
我要投稿
自从ChatGPT发布以来,许多开源LLM(大型语言模型)涌现,但没有一个能完全达到OpenAI的满意度和可靠性。如果你想在应用中使用LLM或自己构建一个,OpenAI仍然是首选。他们的语言模型API有很好的商业模式,其中最便宜且强大的选项gpt-3.5-turbo-0125被广泛使用。但通过巧妙地调用API,实际上可以使其变得更便宜。秘诀在于提示工程,即知道输入什么和期望得到什么。
让我们快速看一下2024年3月的现状。在这篇博客中,我们将使用[gpt-3.5-turbo-0125][4]语言模型。你可以使用其他模型,但对我来说,这个最经济实惠,特别是当我们处理大量文本数据时。让我们看看它的规格,如价格、令牌限制等。
[gpt-3.5-turbo-0125][4]定价(2024年3月)
在开始之前,让我们看看OpenAI如何计算每次API调用的令牌数。
# 从openai模块导入OpenAI类
from openai import OpenAI
# 使用提供的API密钥创建OpenAI类的实例
client = OpenAI(api_key=open_ai_key)
# 将用户输入设置为字符串"Hi"
user_input = "Hi"
# 使用OpenAI客户端创建聊天完成
response = client.chat.completions.create(
model="gpt-3.5-turbo-0125",# 将模型设置为"gpt-3.5-turbo-0125"
messages=[{"role": "user", "content": user_input}]
)
# 打印聊天完成的响应内容
print(response.choices[0].message.content)
######## 输出 ########
你好!今天我能帮你什么?
######## 输出 ########
你可能认为如果将“Hi”作为单个令牌输入,响应将只有7-9个令牌。但事实并非如此。当我们打印响应的使用详情时,我们看到:
# 打印响应的使用情况
print(response.usage)
######## 输出 ########
CompletionUsage(completion_tokens=9, prompt_tokens=8, total_tokens=17)
######## 输出 ########
请注意,prompt_tokens(表示输入令牌)为8。这是因为每次向API发送文本时,[会自动添加额外7个令牌][5]。completion_tokens的值是我们预期的,表示模型生成的响应中的令牌数。
根据我们学到的知识,让我们编写一个函数,它接受用户提示作为输入,并返回四件事:API响应、输入令牌的成本、输出令牌的成本和总成本。
def openai_chat(user_prompt):
# 使用OpenAI客户端创建聊天完成
response = client.chat.completions.create(
# 将模型设置为"gpt-3.5-turbo-0125"
model="gpt-3.5-turbo-0125",
messages=[{"role": "user", "content": user_prompt}]
)
# 聊天完成的响应
answer = response.choices[0].message.content
# 计算输入令牌的价格
input_price = response.usage.prompt_tokens * (0.5 / 1e6)
# 计算输出令牌的价格
output_price = response.usage.completion_tokens * (1.5 / 1e6)
# 计算总价
total_price = input_price + output_price
# 返回答案、输入价格、输出价格和总价
return {
"answer": answer,
"input_price": f"$ {input_price}",
"output_price": f"$ {output_price}",
"total_price": f"$ {total_price}"
}
让我们通过尝试一个简单、简短的提示来查看这个函数如何工作。
# 调用我们的函数,传入提示
openai_chat("中国的首都是什么?")
####### 输出 #######
{
answer: '中国的首都是北京。',
input_price: '$ 7e-06',
output_price: '$ 1.05e-05',
total_price: '$ 1.75e-05'
}
####### 输出 #######
函数的输出应该很清晰。只是要澄清价格是根据我之前向你展示的gpt-3.5-turbo-0215模型的定价表计算的。
# 提问关于100k字文档的问题
openai_chat(100K_words_document_and_one_question)
####### 输出 #######
{
answer: '根据给定的上下文... ',
input_price: '$ 0.18',
output_price: '$ 0.02',
total_price: '$ 0.20'
}
####### 输出 #######
对于大量文本(100k字)和一个问题,估计成本仅为0.20美元。这显示了gpt-3.5-turbo模型的效率。现在我们的函数已经准备好,让我们看看如何降低使用OpenAI API的成本。
假设你有一个包含大量新闻标题的列表,你想对它们进行聚类。虽然使用嵌入式是一种选择,但假设你想使用OpenAI语言模型API,因为它可以以人类方式捕捉意义。在创建提示模板以降低成本之前,让我们先看看新闻标题列表是什么样的。
# 新闻标题列表
news_headlines = [
"donald trump is ... surprise when he arrives",
"Emirates airlines ... takeover of Etihad Airways",
"The US is considering... troops to Afghanistan",
...
]
# 新闻列表长度
len(news_headlines)
####### 输出 #######
1256
####### 输出 #######
我们有超过1200个新闻标题。在创建提示模板之前,让我们将它们合并为一个具有特定格式的单个字符串。这将帮助语言模型更有效地处理它们。
formatted_news = "Given the news headlines:\n"
# 遍历所有新闻标题并将其格式化为单个字符串
for i in range(0, len(user_input)):
formatted_string += f"s{i}: {user_input[i]}\n"
# 打印格式化后的新闻
print(formatted_news)
####### 输出 #######
s0: donald trump is ... surprise when he arrives
s1: Emirates airlines ... takeover of Etihad Airways
s2: The US is considering... troops to Afghanistan
...
s1256: Virat Kholi is ready ... against Srilanka
####### 输出 #######
我用较短的形式表示新闻标题,如s0、s1、s2等。这样做的原因是,当语言模型对它们进行聚类时,它可以简单地使用这些简写(例如,使用s35表示第35个新闻标题),而无需在每个聚类中写出完整的标题。
prompt_template = f'''{formatted_news}
i have these news headlines i want you to perform clustering on it.
you have to answer me in this format:
cluster1: s1,s3 ...
cluster2: s2, s6 ...
...
do not write sentences but write it just like this starting from s0,s1,s2 ...
dont say anything else other than the format
'''
接下来,我定义了提示模板。这个模板指定了我想要语言模型提供的答案格式,以及一些额外的说明信息以提高清晰度。关键在于,我们不是要求模型写出完整的标题,而是使用简写。
我们只需要将此提示模板传递给我们之前创建的函数,看看它在定价和响应质量方面的表现如何。
# 调用我们的函数,传入prompt_template
results = openai_chat(prompt_template)
# 打印响应内容
print(results['answer'])
####### 输出 #######
cluster1: s5, s67, s55, s134, s764 ...
cluster2: s64, s21, s896, s445, s12 ...
...
cluster7: s12, s853, s414, s244, s712 ...
####### 输出 #######
这里我截断了响应,但语言模型成功将我们的1256个新闻标题分成了七个聚类。你可以进一步编辑提示模板以指定所需的聚类数量。让我们看看这个任务的费用是多少。
# 打印完整结果
print(results)
####### 输出 #######
{
answer: 'cluster1: s5, s67, ...',
input_price: '$ 0.020',
output_price: '$ 0.003',
total_price: '$ 0.023'
}
####### 输出 #######
这个任务的总费用为$0.023。如果我们没有使用这个提示模板,我们可能会花费超过两倍的输入费用。这是因为我们将所有标题作为输入发送,而每百万个令牌的输出成本为$1.50,这将适用于返回的完整标题。以下是使用这种方法节省的费用摘要:
这种方法将极大地节省你在处理大量数据时的积分。但我们需要编写一些代码,将简写映射回实际的新闻标题。
# 通过换行符字符分割'answer'字符串
responses = result['answer'].split("\n")
# 使用字典推导式从每一行中提取键值对
clusters = {line.split(": ")[0].strip(): [i.strip() for i in line.split(": ")[1].split(",")] for line in responses if line.strip()}
# 将键(s01, s02, s03等)替换为'news_headlines'中的实际新闻标题
for k, v in clusters.items():
clusters[k] = [news_headlines[int(i[1:])] for i in v]
上述代码将OpenAI的响应映射回实际的新闻标题。以下是clusters字典的样子:
# 打印聚类
print(clusters)
####### 输出 #######
{
'cluster1': ["Sea levels... change", "Arabian sea .. pollution", ...],
'cluster2': ["fifa has banned ... ", "Ronaldo has been ... a row", ...}
...
}
####### 输出 #######
假设你有一个长篇的文本文档,想要构建一个小型的语法修正网络应用。虽然有许多NLP技术可用于此任务,但语言模型,尤其是OpenAI的模型,已经训练了大量数据,因此可能是更好的选择。关键在于如何巧妙地设计提示模板。我们希望API的响应能突出错误的单词并建议正确的拼写,而不是将整个修正后的文本作为输出。让我们看看输入文本:
# 包含拼写错误的我的输入
user_input = '''As the sun beganned to set on the horizen, casting a
warm gloy across the ... '''
# 打印单词总数
print(len(user_input.split()))
####### 输出 #######
500,000
####### 输出 #######
我们有一个包含500k单词的文档,需要进行拼写修正。下一步是创建一个提示模板,引导API仅对有问题的单词提供响应。
prompt_template = f'''给定输入文本:
用户输入:{user_input}
输出必须是以下格式:
拼错的单词:正确单词
...
输出必须仅包含此格式,不包含其他信息
'''
在提示模板中,我们明确定义了期望的响应格式。API应突出显示拼错的单词并提供正确的替换词。我们还指定响应中不应包含除这些单词替换之外的任何其他信息,因为我们将使用代码在原始user_input中替换这些单词。
现在我们只需将此提示模板传递给之前创建的函数,看看它在价格和响应质量方面的表现。
# 调用我们的函数,传入prompt_template
results = openai_chat(prompt_template)
# 打印响应内容
print(results['answer'])
####### 输出 #######
beganned: began
horizen: horizon
gras: grass
...
####### 输出 #######
这里我截断了响应,但语言模型成功识别了拼错的单词并提供了正确的形式。让我们看看这个任务的费用是多少。
# 打印完整结果
print(results)
####### 输出 #######
{
回答: 'beganned: began \n horizen: horizon ...',
输入价格: '$ 0.33333',
输出价格: '$ 0.005135',
总价格: '$ 0.33812'
}
####### 输出 #######
这个任务的总费用为$0.33812,假设500K单词中有8K-10K个拼写错误。如果我们没有使用这个提示模板,费用可能会是输入成本的两倍多,因为我们将全部文本作为输入,而每百万个令牌的输出成本为$1.50,这将适用于返回的完整输入文本。以下是使用这种方法节省的费用摘要:
即使对于只有500个单词的相对较小文本,成本节省也是明显的。想象一下在处理大数据时的财务效益!
但我们需要编写一些代码来将简写映射到实际的标题。
# 通过换行符分割'response1'字符串
response = results['answer'].split("\n")
# 使用列表推导式从每一行提取键值对
result = [(line.split(":")[0], line.split(":")[1]) for line in response if line.strip()]
# 用正确的单词替换拼错的单词
for word, corrected_word in result:
corrected_user_input = user_input.replace(word, corrected_word)
上述代码将拼错的单词映射到它们的正确版本:
# 打印修正后的文本
print(corrected_user_input)
####### 输出 #######
As the sun began to set on the horizon, casting
warm glow across the ...
####### 输出 #######
我们将使用与拼写检查相同的提示模板进行文本清理,因为我们需要突出显示需要清理或完全删除的单词。
我们希望API的响应能突出显示不正确的单词并提出它们的正确拼写,而不是将整个已更正的文本作为输出。让我们看看输入文本:
# 包含拼写错误的输入
user_input = '''The rugge9d pathw&ay winded through the th!ck f0r3st,
obfusca+ting the way forward. The creaking of branches
and rustling of leaves added an ... '''
# 打印单词总数
print(len(user_input.split()))
####### 输出 #######
1,000,000
####### 输出 #######
我们有一个需要清理的100万词文档。下一步是创建一个提示模板,指导API仅提供针对问题单词的响应。
prompt_template = f'''给定输入文本:
用户输入:{user_input}
输出必须是以下格式:
未清理单词: 清理后的单词
...
如果未清理单词没有替代词,请使用以下格式
未清理单词:
输出必须仅包含此格式,不包含其他信息
'''
在提示模板中,我们明确定义了期望的响应格式。API应突出显示未清理的单词并提供它们的正确替换词,如果未清理的单词没有替代词,则应替换为空字符串。我们还规定响应中不应包含除该格式之外的任何其他信息,因为我们将使用代码在原始user_input中替换这些单词。
现在我们需要做的就是将此提示模板传递给我们之前创建的函数,看看它在价格和响应质量方面的表现。
# 调用我们的函数,传入prompt_template
results = openai_chat(prompt_template)
# 打印响应内容
print(results['answer'])
####### 输出 #######
rugge9d: rugged
th!ck: thick
f0r3st: forest
obfusca+ting: obfuscating
s;ense: sense
...
####### 输出 #######
这里我截断了响应,但语言模型成功识别了未清理的单词并提供了它们的正确形式。让我们看看这个任务的费用是多少。
# 打印完整结果
print(results)
####### 输出 #######
{
回答: 'rugge9d: rugged \n th!ck: thick ...',
输入价格: '$ 0.6665',
输出价格: '$ 0.099',
总价格: '$ 0.7665'
}
####### 输出 #######
这个任务的总费用为$0.7665,假设100万个单词中有20,000到30,000个需要清理的错误。如果我们没有使用这个提示模板,我们可能会花费超过输入成本的两倍。以下是使用这种方法节省的费用摘要:
对于100万词的文档,成本节省是明显的。想象一下在大数据处理中能带来的财务效益!
但我们需要编写一些代码来将缩写映射到实际的单词。
# 通过换行符分割'response1'字符串
response = results['answer'].split("\n")
# 使用列表推导式从每一行提取键值对
result = [(line.split(": ")[0], line.split(": ")[1]) for line in response if line.strip()]
# 用正确的单词替换拼写错误的单词
for word, corrected_word in result:
cleaned_user_input = user_input.replace(word, corrected_word)
上述代码将拼写错误的单词替换为正确的单词:
# 打印清理后的文本
print(cleaned_user_input)
####### 输出 #######
The rugged pathway winded through the thick forest,
obfuscating the way forward. The creaking of branches
and rustling of leaves added an ...
####### 输出 #######
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-05-28
2024-08-13
2024-04-26
2024-08-21
2024-07-09
2024-06-13
2024-08-04
2024-04-11
2024-07-18
2024-07-01