本文虽是为《Programming Large Language Models with Azure Open AI》一书准备的,但适合所有想在自己的程序中集成AI的人士。注意,本书中文版尚未出版,敬请期待。英文版的Amazon详情页。
由于本书原版只是直接给出了代码,没有详细说明如何准备工作环境,所以才有了本文的诞生。采取的是Step by Step的方法。按步骤操作,最后必然会得到一个可以正常工作的Azure OpenAI开发环境。甚至还有两个额外的好处,一是不需要使用代理,便能访问包括ChatGPT-4o在内的最新版本;二是以后若OpenAI限制开发人员直接调用其API,我们也可以通过Azure云来中转。
2024.6.26更新:据悉,从7月9日开始,OpenAI将限制不支持国家和地区的API调用。但是,如果像本文描述的那样,通过Azure OpenAI来调用,那么将不受限制。
2024.6.27更新:本文已由清华大学出版社转载于知乎:https://zhuanlan.zhihu.com/p/705739671
2024.7.15更新:新增了Microsoft Guidance的用法,它是你写AI应用的好帮手,可以“指导”AI以符合逻辑的方式组织生成和提示,并做一定程度的逻辑控制。另外,还可以自定义函数,并在生成回答时嵌入函数的运行结果。用法非常简单明了,看例子就可以知道。
准备工作环境
- 免费创建Azure订阅。新用户可以多种服务免费使用一年,而且账户中会自动充值200美元的余额。
2. Azure OpenAI目前处于测试阶段,需要申请才能使用。申请前,请先准备好两样东西:刚才创建的Azure订阅ID和一个企业邮箱(不能使用Gmail等免费个人邮箱)。
- 在Azure门户中找到Azure订阅ID:
访问https://portal.azure.com/,单击“订阅”,记录订阅ID。 - 企业邮箱则很好解决,不要使用免费的个人邮件服务即可。域名最好与稍后要填写的组织名称一致。
- 访问https://aka.ms/oai/access来申请Azure OpenAI,填写姓名,企业名称、邮箱、地址等。在几个小时内,申请应该就被批复。申请通过后,会向你填写的邮箱发一封确认函。
3. 从现在开始,就可以自由地使用OpenAI服务了。有两个选择,一个是在Azure OpenAI Studio中直接使用,另一个是在自己的程序中使用。若选择的开发语言是C#,那么要求安装.NET Core的最新版本和Azure.AI.OpenAI包(稍后说明);若使用Python,则要求Python 3.8以上。无论怎么选择,都要求先在自己的订阅下创建一个Azure OpenAI Service。
为此,请访问Azure OpenAI Studio,然后使用有权访问 OpenAI 资源的凭据登录。在登录过程中或登录之后,选择适当的目录、Azure 订阅和 Azure OpenAI 资源。
注意,如果之前尚未创建OpenAI Service,那么需要先创建一个,过程非常简单。为此,在Azure AI services | Azure OpenAI页面中单击“创建”,如下图所示。
随后会显示以下“创建Azure OpenAI”页面。
注意,OpenAI服务的创建时间较长,请耐心等待。创建完成后,再次访问Azure OpenAI Studio,就可以在Azure AI Studio中部署Azure OpenAI服务了。
单击“新建部署”,选择你希望的模型,例如GPT-4o。然后,为自己的部署取一个名字。
单击“创建”后,就可以在Azure AI Studio左侧的“部署”区域找到自己的部署了。单击部署名称,再单击“在操场中打开”,就可以立即体验类似于ChatGPT的聊天会话。
玩够了,接着让我们开始干“正事”,即选择一种语言(例如,C#)在自己的应用程序中集成Azure OpenAI功能。在正式开始之前,请先记录以下三项信息:1. API密钥,例如fcac1bde46224180b52c1a4be79cb20k。2. 你的终结点URI,例如https://gpt.openai.azure.com/,3. 以及部署名称,例如gpt-4o。
下面分两个部分来举例说明如何在自己的应用中集成AI功能。第一部分使用的是Azure.AI.OpenAI当前的最新版本(目前是2.0)。第二部分使用的是《Programming Large Language Models with Azure Open AI》一书所用的版本,即1.0,这也是目前使用量最大的版本,大多数套壳AI使用的都是它。
使用Azure.AI.OpenAI的最新版本
1. 在Visual Studio Code中安装微软官方的Polyglot笔记本插件,这是为了方便以后随时试验自己的代码。当然,直接使用Visual Studio也是可以的。安装好笔记本插件后,在VS Code中按Ctrl+Shift+P(或Ctrl+Shift+Windows+N),选择新建一个默认的Polyglot笔记本,输入并执行以下命令:
#r "nuget: Azure.AI.OpenAI, 2.0.0-beta.2"
如果使用Visual Studio,则从“工具”菜单中选择“NuGet包管理器”,打开“程序包管理控制台”,并输入以下指令:
dotnet add package Azure.AI.OpenAI --prerelease
2. 现在就可以开始写代码了,以下C#代码展示了如何在一个控制台应用程序中调用Azure OpenAI API,要求AI帮你写一篇800字以内的短篇“小说”。
using Azure;
using Azure.AI.OpenAI;
using OpenAI.Chat;
string key = "在这里输入你的API密钥"; // 例如,fcac1bdc46224180b52c1a4be79cb20k
string endpoint = "在这里输入你的终结点URI"; // 例如,https://zjgpt.openai.azure.com/
string deploymentid = "在这里输入你的部署名称"; // 例如,chagpt4-o
AzureOpenAIClient azureClient = new(
new Uri(endpoint), new AzureKeyCredential(key));
ChatClient chatClient = azureClient.GetChatClient(deploymentid);
ChatCompletion completion = chatClient.CompleteChat(
[
new UserChatMessage("写一篇800字以内的短篇小说,主角是一位侠客,他的名字叫张无忌;还有一位美女,她的名字叫赵敏。两个人第一次见面时都喜欢上了对方,但当时不知道对方属于不同的阵营。两个阵营有着不可调和的矛盾。请随意发挥写两个人的爱情故事,最好有一个圆满的结局。"),
]);
Console.WriteLine($"{completion.Role}: {completion.Content[0].Text}");
程序返回了以下结果(你看到的结果当然不一样,但这正是AI的魅力,不是吗?):
在古代的江湖世界中,正义与邪恶的斗争从未停息。张无忌,一位正义的侠客,肩负着重振明教的使命,而赵敏,一位机智勇敢的侠女,则是大元朝的秘密使者,二人在一次偶然的集市相遇中,不知道对方的真实身份,却在那一刻坠入了爱河。
那天,夕阳如血,集市上人声鼎沸。张无忌穿着普通的黑色长袍,漫步于市集之中,他的心中虽然忧虑着明教的前途,但他的眼睛却被一位穿着红衣的女子所吸引。这女子手持纸扇,扇面上绘有梅花,她的步伐轻盈,仿佛不沾尘埃。
赵敏的任务是在这个小镇上寻找一份重要的情报,但当她的目光与张无忌相遇时,她感到了一种前所未有的震动。她见过许多勇猛的战士,却从未见过如此温文尔雅,眼含深情的男子。他们的眼神交流虽只是一瞬,但足以让彼此的心猛烈跳动。
随后的几天,张无忌和赵敏在市集、桥上、古寺不期而遇,每次相遇都似乎是命运的安排。他们谈笑风生,讨论武学、诗词,甚至分享各自的梦想与希望。然而,随着时间的推移,他们也意识到彼此背后所代表的力量正是对方的敌人。
一天夜里,张无忌在月光下修炼内功,赵敏悄悄地走近,坐在他身边。月光下,赵敏的脸庞格外动人,张无忌深知这一刻的宁静不会持久。
“我们……是敌是友?”张无忌沉声问道。
赵敏轻轻叹息,“我不知道,但我知道我已无法自拔。”
心中充满矛盾与纠结的他们,决定在接下来的朝阳下决一死战,用武决定彼此的命运。然而,在决斗之日,当他们手中的剑相互对峙时,张无忌突然放下了剑,赵敏也随之停手。
“我宁愿放弃这一切,也不愿意失去你。”张无忌的声音坚定而深情。
赵敏眼中含泪,她知道自己同样做不到手刃爱人。在爱与使命之间,他们选择了爱。
两人逃离了各自的阵营,隐居在远离尘嚣的小山村,过着平凡而幸福的生活。他们在那里开辟了一片小小的天地,种花养鱼,世外桃源般的生活让他们逐渐忘记了过去的恩怨与争斗。
岁月流转,张无忌与赵敏最终证明了,即使在矛盾与冲突交织的江湖中,真挚的爱情仍能找到属于它的一片净土。他们的故事在江湖中流传开来,成为了后世歌颂的佳话。
3. 如果更喜欢使用Python,那么可以在Jupyter笔记本中写以下等价的代码:
from openai import AzureOpenAI
# 设置Azure OpenAI客户端
# 建议将身份资料设为本机的环境变量,并通过load_dotenv函数从一个.env文件中获取
# 如果希望从.env文件的环境变量中读取终结点、密钥和部署名称在内的环境变量,
# 那么还需要安装python-dotenv库,命令是:pip install python-dotenv
client = AzureOpenAI(
azure_endpoint="在这里输入你的终结点URI",
api_key = "在这里输入你的API密钥",
api_version = "2023-09-01-preview"
)
deployment_name = "在这里输入你的部署名称"
context = [{'role': 'user', 'content': "在这里输入你希望指示AI做的事情,例如上例的小说写作要求"}]
response = client.chat.completions.create(
model=deployment_name,
messages=context,
temperature=0.7
)
print(response.choices[0].message.content)
使用Azure.AI.OpenAI的1.0版本
这一部分的内容是为了适配本书的源代码,但所用的API版本也是当前使用量最大的。
1. 在Visual Studio Code中安装微软官方的Polyglot笔记本插件,这是为了方便以后随时试验自己的代码。当然,直接使用Visual Studio也是可以的。安装好笔记本插件后,在VS Code中按Ctrl+Shift+P,选择新建一个默认的Polyglot笔记本,输入并执行以下命令:
#r "nuget: Azure.AI.OpenAI, 1.0.0-beta.12"
如果使用Visual Studio,则从“工具”菜单中选择“NuGet包管理器”,打开“程序包管理控制台”,并输入以下指令:
dotnet add package Azure.AI.OpenAI --version 1.0.0-beta.12
2. 现在就可以开始写代码了,这次我们举一个稍复杂的例子,其中用到了“系统提示”的概念。在聊天机器人中,系统提示(也称为元提示,即metaprompt)可以用于引导模型的行为。一条系统提示会:
- 告诉模型应该如何完成任务,以及是否可以使用额外的工具。
- 明确说明模型性能的范围和限制,包括如何处理跑题或无关的提示。
- 确定模型在响应时的态度和语气。
- 定义输出格式,包括语言、语法和任何格式偏好。
- 提供一些示例(样本,或者说shots),以展示模型预期的行为,考虑加入一些困难的使用场景和思维链(CoT)推理。
- 事先识别一些潜在的危害,定义优先级并予以解决,建立起额外的行为保护机制。
更详细的说明可以参考本书的中文版。下面让我们写一个能实际工作的例子。假设要为某酒店品牌开发一个订房聊天机器人。一个合理的系统提示可能是:
你是HotelBot,一个自动服务机器人,负责收集不同城市的酒店预订信息。
首先要向客人问好,然后收集预订信息,询问客户的姓名、想要预订的城市、房型和额外服务。
在收集了完整的预订信息后,你需要汇总预订信息,并最后一次确认客人是否还想添加额外的服务。
你需要询问到达日期和离开日期,并自动计算入住天数(几晚)。你需要询问身份证/护照号码。请确保已经明确了所有选项和额外的服务,从价格表中唯一性地识别收费项。
你的回应应该简短且非常友好。可预订的城市有:北京、成都、重庆和上海。
房型和价格:
单人间每晚150元
双人间每晚250元
套房每晚350元
额外服务:
停车每天20元
延迟退房100元
机场接送50元
SPA一次30元
下面以一个控制台应用程序为例来演示如何启动与用户的交互。
using Azure;
using Azure.AI.OpenAI;
var AOAI_ENDPOINT = "在这里输入你的终结点URI"; // 例如,https://zjgpt.openai.azure.com/
var AOAI_KEY = "在这里输入你的API密钥"; // 例如,fcac1bdc46224180b52c1a4be79cb20k
var AOAI_DEPLOYMENTID = "在这里输入你的部署名称"; // 例如,testchatgpt
var endpoint = new Uri(AOAI_ENDPOINT);
var credentials = new Azure.AzureKeyCredential(AOAI_KEY);
// 创建OpenAI客户端,因为是通过Azure服务来调用,
// 所以不受OpenAI公司对国家和地区的限制。
var openAIClient = new OpenAIClient(endpoint, credentials);
var systemPrompt = "替换为刚才的大段系统提示";
var chatCompletionsOptions = new ChatCompletionsOptions
{
DeploymentName = AOAI_DEPLOYMENTID,
Messages =
{
new ChatRequestSystemMessage(systemPrompt),
new ChatRequestUserMessage("介绍你自己"),
}
};
while (true)
{
Console.WriteLine();
Console.Write("HotelBot: ");
var chatCompletionsResponse = await
openAIClient.GetChatCompletionsAsync(chatCompletionsOptions);
var chatMessage = chatCompletionsResponse.Value.Choices[0].Message;
Console.Write(chatMessage.Content);
chatCompletionsOptions.Messages.Add(new
ChatRequestAssistantMessage(chatMessage.Content));
Console.WriteLine();
Console.Write("请输入: ");
var userMessage = Console.ReadLine();
chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(userMessage));
}
短短几行代码,就可以生成一个非常贴心的酒店房间预订机器人,下面是一段示例对话。注意,“请输入:”后面都是由用户输入的内容。
HotelBot: 您好!我是HotelBot,一个自动服务机器人,可以帮助您预订酒店并提供所需的信息和服务。请告诉我您的姓名,并让我知道您想预订的城市、房型和是否需要额外服务。我会尽力满足您的需求!
请输入: 我要预订成都的酒店
HotelBot: 非常好!感谢您选择成都作为您的目的地。请告诉我您的姓名,房型偏好和是否需要额外服务,以便我可以帮您找到合适的酒店。
请输入: 我叫周靖,想要预订单人间,需要停车服务
HotelBot: 感谢提供信息,周靖先生。我会为您寻找成都的适合房型和停车服务的酒店。请告诉我您计划的到达日期和离开日期,以便为您计算入住天数。
请输入: 到达日期是2024年9月1日,住四晚
HotelBot: 非常感谢详细信息!您的预定已经记录。您将预订一间单人间,在成都酒店入住四晚,从2024年9月1日入住至9月5日离店。您还需要提供身份证/护照号码以完成预订。
关于停车服务,每天的停车费用为20元,我们将为您预订并记入您的住宿费用中。请提供您的身份证/护照号码,方便我为您办理预订手续。
请输入: 我的身份证是1234567890
HotelBot: 感谢提供身份证号码。您的预订信息已完整记录。
以下是您的预订信息:
- 预订人姓名:周靖先生
- 预订城市:成都
- 房型:单人间
- 预计到达日期:2024年9月1日
- 预计离开日期:2024年9月5日
- 入住天数:4晚
- 停车服务:是(每天20元)
请注意,酒店费用将根据您的入住天数和房型进行计算。您的总费用将在最后一次确认时提供。
请确认这些信息是否正确或者是否需要添加额外的服务。如果没有,请输入“确认”: 确认
HotelBot: 非常感谢确认!您的预订已经完成,总费是680元。我们会尽快为您安排酒店。如果您有任何其他问题或需要进一步的帮助,请随时告诉我。祝您旅途愉快!
使用Microsoft Guidance
本书第4章讲解了Microsoft Guidance的用法,它用于管理与大语言模型(LLM)的“模板化”交互,这些模型也包括Hugging Face提供的那些。
要安装Guidance,只需在Python终端运行一条简单的pip install guidance
命令即可。Guidance同时支持OpenAI和Azure OpenAI模型,同时也支持transformers格式的本地模型,例如Llama、StableLM和Vicuna等。若是本地模型,Guidance还支持Acceleration(加速),这是Guidance内部使用的一种技术,用于缓存token并优化生成速度。
安装好Guidance后,可以使用以下代码来配置OpenAI模型。注意,要求事先已设置好代码中要求的一系列环境变量。
from guidance import models, guidance, gen
from guidance import system, user, assistant
import os
# 环境设置请参见本书第1章,或者本文之前的内容
os.environ['OPENAI_API_VERSION'] = "2024-05-01-preview" # 当前(2024年7月)最新的OpenAI API版本
os.environ['AOAI_DEPLOYMENTID'] = "替换为你的部署名称"
# 例如,chagpt4-o
os.environ['AOAI_ENDPOINT'] = "替换为你的Azure OpenAI终结点URI"
# 例如,https://zjgpt.openai.azure.com/
os.environ['AOAI_KEY'] = "替换为你的Azure OpenAI API密钥"
# 例如,fcac1bdc46224180b52c1a4be79cb20k
# 配置Azure OpenAI模型
llm = models.AzureOpenAI(
model='gpt-35-turbo', # 模型名称
azure_deployment=os.getenv("AOAI_DEPLOYMENTID"),
api_key=os.getenv("AOAI_KEY"), # Azure OpenAI API密钥
azure_endpoint=os.getenv("AOAI_ENDPOINT") # Azure OpenAI终结点
)
为了开始进行模板化的聊天,让我们先来测试以下代码:
with system():
llm += "你是一名猫的专家"
with user():
llm += "世界上最小的猫是什么?"
with assistant():
llm += gen("回答", stop="。", max_tokens=100)
这会产生如下所示的输出:
system
你是一名猫的专家
user
世界上最小的猫是什么?
assistant
世界上最小的猫是袖珍猫(Singapura),它们是一种来自新加坡的短毛猫,体型非常小,成年体重只有1.8-2.7公斤左右
Guidance允许使用特殊的with
上下文块(context blocks)来控制聊天模型,这些块会将其内部的内容封装成聊天模型所需的具体格式。这样一来,就可以自由地表达聊天程序,而不必局限于单一的后端模型。这些with块包括system
、user
和assistant
等。
通过整合这些块,可以为系统、用户和助手定义各自的角色和职责。对话可以被设置成按照这种方式流动,每个参与者的输入都被包含在相应的块内。
此外,在assistant
块内,可以使用gen
函数(例如,gen("response")
)来辅助生成动态响应。尽管由于对部分完成的限制,并不支持在assistant
块内部实现复杂的输出结构,但在这个块的外部构建对话结构是完全没有问题的。
下面是摘自官方文档的一个与专家聊天的例子(注意,llm
是之前已经创建好的模型实例)。
# 可以通过使用一系列角色标签来创建和引导多轮对话
@guidance
def experts(lm, query):
with system():
lm += "你是一个乐于助人的助手。"
with user():
lm += f"""\
我想要对以下问题的回答:
{query}
能出色地回答这个问题的三位世界级专家(过去或现在)都有谁?列出姓名即可,不需要列出更多信息。
请暂时不要回答或评论这个问题。"""
with assistant():
lm += gen(name='experts', max_tokens=1500)
with user():
lm += f"""\
很好,现在假定这些专家共同匿名撰写了统一答案来回答这个问题。
换句话说,他们的身份不会被透露,也不会提及有一组专家在回答这个问题的事实。
如果专家们的意见有分歧,请在答案本身中将他们的不同立场呈现为备选观点(例如:‘有人可能会认为…另一些人可能会认为…’)。
请在回答开始时写上一个“回答”:"""
with assistant():
lm += gen(name='answer', max_tokens=1500)
return lm
llm + experts(query='生命的意义是什么?')
在上述代码中,我们定义了一个名为experts的函数,并借助assistant块来生成回答。下面是上述代码的示例运行结果,注意这是一次完整的多轮“对话”。
system 你是一个乐于助人的助手。
user 我想要对以下问题的回答:
生命的含义是什么?
能出色地回答这个问题的三位世界级专家(过去或现在)都有谁?列出姓名即可,不需要列出更多信息。
请暂时不要回答或评论这个问题。
assistant 好的,以下是对你的问题的回答:
生命的含义是一个复杂的哲学问题,涉及到伦理、宗教、科学等多个领域。因此,对于这个问题的回答也有很多不同的观点和理解。
以下是三位能够出色回答这个问题的世界级专家(过去或现在)的可能人选,仅列出姓名:
1. 赫拉克利特斯 (Heraclitus)
2. 西格蒙德·弗洛伊德 (Sigmund Freud)
3. 阿尔伯特·爱因斯坦 (Albert Einstein)
user 很好,现在假设这些专家共同匿名撰写了解答。
换句话说,他们的身份不会被透露,也不会提及有一组专家在回答这个问题的事实。
如果专家们的意有分歧,请在答案本身中将他们的不同立场呈现为备选观点(例如:“有人可能会认为...另一些人可能会认为…”)。
请在回答开始时写上一个“回答”:
assistant 回答:
生命的含义是一个复杂的问题,涉及到伦理、宗教、科学等多个领域。我们认为,生命的含义在于个体的自我实现和社会的共同进步。
有人可能会认为,生命的含义在于追求个人的幸福和满足。这种观点认为,每个人都应该追求自己的利益和快乐,不必考虑他人的感受和需要。
另一些人可能会认为,生命的含义在于为他人和社会做出贡献。这种观点认为,每个人都应该为社会做出自己的贡献,为他人的福祉和幸福而努力。
我们认为,生命的含义在于个体的自我实现和社会的共同进步。个体应该追求自己的幸福和满足,也应该考虑他人的感受和需要。同时,个体也应该为社会做出自己的贡献,为社会的进步和发展做出努力。这样,个体和社会可以相互促进,实现共同的进步和发展。
《Programming Large Language Models with Azure Open AI》一书还讲解了Azure OpenAI开发的更多细节,涉及一个完整的开发流程。文字浅显易懂,内容丰富多彩。敬请期待她的中文版的问世。