第一章:了解大型语言模型
本章涵盖
- 介绍生成式人工智能(特别是大型语言模型)
- 生成式人工智能的历史
- 探索生成式人工智能的好处
- 确定何时使用生成式人工智能以及何时不使用
无论你是否意识到这一点,无论你是否愿意承认这一点,你都悄悄地得到了晋升。事实上,每个专业软件工程师都得到了晋升。几乎一夜之间,我们从员工工程师变成了工程经理。你现在拥有世界上最聪明、最有才华的初级开发人员作为你的团队成员。指导、辅导和执行代码审查应该成为你日常工作的一部分。你现在有了生成式人工智能作为你的新编程伙伴。本章将为您提供生成式人工智能的一个子集的概述,称为大型语言模型(LLM),具体包括 Chat GPT、GitHub Copilot 和 AWS CodeWhisperer。
注意
本书将不是一本传统的编程书籍。你不能像使用脚本一样使用它。你将与大型语言模型进行对话,就像与任何对话一样,词语和方向会根据模型和之前的语境而改变。你收到的输出很可能会与本书中打印的内容不同。这不应该让你感到沮丧。相反,你应该探索。旅程与目的地同样重要。你可能会发现自己感到沮丧,因为它们无法跟上。请耐心等待。如果你有纪律(并且有些冒险精神),你可以让 GPT 配合本书的一般主题和目的:学习如何使用生成式人工智能使你成为一名更好的程序员。
1.1 大型语言模型简介
生成式人工智能,特别是大型语言模型(LLMs),正在彻底改变我们思考和开发软件的方式。我们不再设计和编码模块、组件和测试,而是描述我们希望这些人工智能构建的软件,它们将为我们生成这些工作的主体。这是软件工程领域的一个自然趋势:我们的编译器变得更智能(Rust 的编译器是一个很好的例子,它消除了一个整个类别的错误),我们的工具变得更智能(源代码中的 IntelliSense),我们的编程语言变得更具表现力和生产力。虽然这可能使得这些生成式人工智能看起来更像是进化,而不是革命性的;但从某种意义上说,它们既是进化的,也是革命性的。
本书将审视、比较和对比三种大型语言模型:GitHub 的 Copilot、OpenAI 的 ChatGPT 和亚马逊的 CodeWhisperer。后者将获得较少的关注,因为它在很大程度上类似于 Copilot,但更实用,更专注于 AWS 生态系统内的开发。
GitHub Copilot 和 ChatGPT 都在幕后使用由 OpenAI 创建的 GPT-4 Codex 模型。微软从 OpenAI 许可了这个软件,使用了 GitHub 的公共存储库中的源代码(微软拥有)。GitHub 创建了一个服务,它将会接收你在集成开发环境(IDE)中提供的上下文,比如 Visual Studio Code 或 IntelliJ,并将该上下文发送到 GitHub Copilot 服务。该服务将使用 OpenAI Codex 根据你在文件中的注释和代码提供的上下文生成最多十种可能的解决方案。Codex 尝试将这个上下文与它在训练数据语料库中找到的示例进行匹配。这些代码解决方案将被返回到你的集成开发环境,供你选择。你需要审查所有的代码建议,并接受最接近你意图的一个。你在这里提供的监督非常重要:解决方案过时或不准确并不罕见。如果接受了某个解决方案,那么你的“接受”解决方案会被发送回 GitHub Copilot 服务,以进一步增强模型和建议。
微软正在通过 Copilot 大力押注这项技术。谷歌也在通过 Bard 押注。很容易理解为什么。
本书教会你如何管理 GitHub Copilot 等生成式人工智能。琐碎的示例将让位给令你惊叹的难以置信复杂的示例。本书假定你对使用生成式人工智能了解不多。你可能听说生成式人工智能已经有一段时间了。你可能认为这个概念令人兴奋,值得随时研究生成式人工智能。那么,现在是最好的时机。本书将带你了解基础知识,从在集成开发环境中设置到使用它来将你的生产率、输出和希望的编码乐趣提高 10 倍。
本书将展示一个接一个的示例,说明哪种生成式人工智能更适合特定任务。这将帮助你建立直觉,了解何时应该使用其中一种,以及何时应该避免它们。让我们简要了解一下每种生成式人工智能的核心优势。
ChatGPT 擅长生成模仿人类语言和书面语言的回复。因此,它非常擅长文档编写和代码注释。由于它可以处理自然语言(NLP),它还可以执行反向操作:总结文本并捕获情绪。你还可以使用它来改善这些领域:让它重写或重新表达文案。
ChatGPT 可以生成代码片段、函数、应用程序和整个聊天机器人。此外,你可以使用它来自动生成测试。我们将在后续章节中完成所有这些任务。
GitHub Copilot(以及 CodeWhisperer)为开发人员提供了以下好处:它们帮助完成代码、修复错误和重构。它们驻留在开发人员的集成开发环境(IDE)中,可以帮助保持开发人员对手头任务的关注。这将使开发人员在输出方面更加高效(单位时间内的代码行数),但它们也可以自动化重复的任务。鉴于 Copilot 的训练数据是通过整理公共代码库获得的,开发人员将有建议来提高整体代码质量。
Copilot 还可以帮助更好地理解外部代码库。它将提供关于如何浏览该代码库的建议,因为它可以帮助我们更好地理解类和代码之间的关系。
当你使用这些工具时,你会注意到随着你更好地了解你的新编程伙伴的能力和局限性,你的速度会发生巨大的变化。由于它可以“记住”你的编程风格和方法,你的编程伙伴也会变得更加擅长与你合作。与生成式 AI 一起工作将使你能够解决更加复杂的问题。你将编写更好、更干净的代码,bug 更少。所有这些都是以比你想象的更快的速度前进。听起来像是一个梦,或者更糟糕,是空洞的话?它不是。
有人可能会问自己这不就是 IntelliSense 的一个更好的版本吗?在看过前几个例子之后,你可能会问自己这个问题;然而,在下一章结束时,使用生成式 AI,你将能够欣赏到其中的区别。
在图 1.1 中,你会看到 Microsoft Visual Studio Code 提供了 IntelliSense 的自动完成建议来启动 Flask 应用程序。请注意,这是内联的,建议是在我编辑代码时提供的。
图 1.1 IntelliSense 自动完成了 Flask 的运行方法。
图 1.2 显示了 GitHub Copilot 根据方法名称和签名提出了相同的建议。也就是说,在我开始编写实现之前,它就已经写好了代码。生成式 AI 背后的激动在于这种力量:它的预测性质。随着提示变得更加明确,建议变得更加精确。我们将在后面的章节中进一步探讨这一点。
图 1.2 GitHub Copilot 提供了运行 Flask 应用程序的解决方案。
图 1.2 提供了一个简单的示例,并没有提出为什么要使用生成式 AI 的充分理由。然而,在同一个 Flask 应用程序中,如果你需要创建一个能够处理来自 POST 方法的输入的端点,但忘记了语法怎么办?我们需要打开官方文档然后尝试找到如何做吗?不,我们可以直接问 GitHub Copilot。
图 1.3 GitHub Copilot 生成 POST 方法处理程序
您可以看到 Copilot 提供了几个类似的建议来完成这段代码。声明该方法会使第一个建议在我们的 IDE 中自动完成。不需要停下来使用鼠标。这种方法可以让您在不必要的干扰下更长时间地保持在代码和流状态中。现在,如果 Copilot 能给我们带杯咖啡就好了……
1.2 生成式人工智能的历史
值得快速了解一下我们在接下来的几章中研究的技术的起源。
生成式人工智能是人工智能的一个子集。人工智能已经存在并且积极研究了六十多年。逻辑理论家被认为是人工智能的第一个应用,早于“人工智能”这个术语的出现。逻辑理论家是赫伯特·西蒙和艾伦·纽厄尔的杰作,克利夫·肖也做出了一些贡献。西蒙和纽厄尔试图教会计算机思考。
尽管这次尝试没有产生真正思考的机器,但逻辑理论家能够产生比当时的数学家阿尔弗雷德·北·怀特海德和伯特兰德·罗素更好、更详细的数学证明。对逻辑理论家将对数学领域产生什么影响的猜测和推测,与我们今天在新闻中看到的关于 ChatGPT 的情况相呼应。
直到 1956 年,兰德公司举办了达特茅斯夏季人工智能研究项目,人工智能这个词才出现。在此次会议上,著名研究员约翰·麦卡锡(Lisp 的原始作者)和计算机科学家首次提出了“人工智能”这个术语。
对人工智能的研究持续了几十年,随着公众对其兴趣的起伏不定而逐渐改进。加里·卡斯帕罗夫于 1997 年被 IBM 的深蓝战胜了。肯·詹宁斯于 2011 年被 IBM 的沃森在“危险边缘!”中打败。几年后,2014 年,生成式对抗网络(GANs)被发明。GANs 的出现引发了人工智能的新一轮兴趣,因为它能够创建逼真的图像和所谓的深度伪造。
2017 年,一种新型的深度学习模型被发明了:transformer。这为大型语言模型(LLM)奠定了基础:例如 GPT-3。而 GANs 能够从噪声图生成图像,LLMs 能够创建文本和软件。在此期间,OpenAI 公司——ChatGPT 的管理者——成立了。
但是 OpenAI 是为什么成立的呢?他们的目标是什么?让我们问问 ChatGPT。
Q: openai 的历史是什么?
A:OpenAI 是一家研究公司,于 2015 年 12 月由埃隆·马斯克、萨姆·奥尔特曼、格雷格·布罗克曼、伊利亚·苏茨克维尔、沃伊切赫·扎伦巴和约翰·舒尔曼创立。该公司的使命是以惠及全人类为目标开发和推广友好型人工智能。OpenAI 最初专注于开发和推广游戏和机器人技术领域的人工智能,但后来扩大了业务范围,包括了更广泛的行业。2019 年,OpenAI 发布了 GPT-2,这是一个强大的语言处理人工智能模型,2020 年发布了 GPT-3,已被广泛应用于自然语言处理任务,并成为迄今为止最大的人工智能模型之一。2023 年,我们看到了 GPT-4 的发布,这是迄今为止最先进的模型。它具有连接互联网等其他巨大改进的能力。OpenAI 还参与了人工智能安全、机器人技术和机器学习等多个研究项目,并与多个组织和公司建立了合作伙伴关系,推动人工智能研究和发展。
这段文字中一个重要的细节是,有一些公司正在积极致力于使人工智能系统更易于使用。考虑到这种趋势不太可能结束(而且未来可能只会加剧),提前入局不是很有意义吗?这将让你保持在技术发展的前沿。这将使你作为一个受 AI 辅助的开发者的技能变得更加有价值。
1.3 理解生成型人工智能
生成型人工智能是人工智能的一个子集。它们在大型数据集上进行训练,以学习数据的模式和结构。训练完成后,它们将使用这些数据生成类似于训练数据集的新数据,就新数据的结构而言。这就是生成型人工智能名称中的生成部分。
有三种显著和备受瞩目的生成型人工智能:生成对抗网络(GANs)、变分自动编码器(VAEs)和基于变换器的语言模型。ChatGPT 和 OpenAI 的 Codex 就是后者的例子。我们将简要介绍每种类型的生成型人工智能的功能。
图 1.4 生成型人工智能主要有三种类型:生成模型、生成对抗网络和基于变换器的模型。
所有这三种生成型人工智能都利用神经网络来创建输出;无论是文本还是代码生成或图像。神经网络是模仿人类思维方式的,因为神经元之间传递信号。你可以将其视为一个有向图,其中超过一定阈值的数据被传递到图中的下一个节点。
数据编码输入层,称为外层。输出层连接到隐藏层。在隐藏层后面是其他的,通过这些隐藏层必须经过许多隐藏层的数据。神经网络中的所有节点都通过计算得到的数值连接,表示神经元之间连接的强度,使用反向传播(在图 1.5 中表示为线条)进行,具有必须超过阈值才能激活下一层的阈值。如果数据到达输出层,那么数据将从网络返回。但是,不能保证数据将被返回。数据可能会被过滤掉。
图 1.5 可视化神经网络。一个非常微小的网络。外部节点是暴露的,以便它们可以接受输入。当遍历隐藏节点时,这些输入要么被丢弃,要么转发到下一个节点。如果输入到达输出节点,则会返回。
生成式人工智能使用非常大的数据集来训练这些模型。在 GitHub Copilot 的情况下,这个大型数据集是 GitHub 内部公开可访问的存储库的内容。如果你曾经为开源项目做过贡献,那么你可能有 Copilot 训练过的代码。
尽管许多生成式人工智能使用神经网络,但它们的使用方式决定了人工智能的类型。生成对抗网络(GAN)使用两个神经网络:一个称为生成器,一个称为判别器。生成器网络根据训练数据集生成假数据。判别器试图识别假数据。这些网络具有对抗性,因为生成器试图创建与真实数据无法区分的数据,而判别器试图判断数据是真实的还是假的。
变分自动编码器(VAEs)也使用两个网络:一个用于编码,一个用于解码。在某种意义上,编码网络通过将数据简化为低维表示来简化输入。然后,解码网络将这个低维表示映射回原始数据空间。这样做的全部意义在于能够通过抽样生成新的数据。
最后一种类型是基于变换器的模型。变换器模型是一种使用自注意机制处理顺序数据(例如自然语言文本)的前馈神经网络。在训练期间,网络的权重会调整以最小化损失函数,例如交叉熵。
在前馈神经网络中,输入沿着一个方向流动,从输入层到输出层,层与层之间没有反馈连接。此外,没有信息或错误信号从输出返回到输入层。因此,神经网络的输出仅由输入数据和层之间分配的连接权重确定。
自注意机制允许网络基于与当前输出相关性选择性地关注输入序列的不同部分。在 transformer 中,输入序列首先通过嵌入层嵌入到向量空间中。然后将嵌入的输入序列送入编码器网络,该网络由多个前馈神经网络层组成。每个编码器层应用自注意机制来捕捉输入序列的不同部分之间的关系。
自注意机制根据每个输入序列部分与其它序列部分的关系计算一个注意力分数。这些分数用于权重化每个序列部分对编码器网络最终输出的贡献。这使得编码器网络能够有选择性地关注输入序列中最重要的部分,而忽略不相关的部分。
编码器网络的输出被送入一个解码器网络,这个解码器同样由多层前馈神经网络组成。解码器使用自注意机制基于输入序列逐个词生成输出序列。
在 transformer 网络中,编码器和解码器的关系可以用编译器和链接器的类比来理解。就像编译器把高层代码变成低层指令,链接器把这些指令组合成一个可执行程序一样,编码器网络把输入序列分解成有意义的单位,解码器则把这些单位组合成最终的输出序列。transformer 中使用的自注意机制类似于编译器和链接器优化代码性能的方式。
正如之前所说的,很多生成 AI 使用神经网络,但并非所有的都是如此。有些是基于规则的,通过将规则应用于输入来生成输出。仍有其他人是演化的性质,迭代结果,并根据适合度进行选择。
现在我们可以通过一个描述性的例子来演示你如何与 Copilot 交互。当你开始在你最喜欢的 IDE 中(例如本例中的 VS Code)键入时,Copilot 插件将把你的注释或代码(有时候仅需要一个函数名!)发送到 Copilot 服务中。
这个服务可以把你的代码行或注释转换为自然语言提示,然后运行在 OpenAI 的 Codex 模型上。该模型基于训练数据集生成建议。GitHub 称之为代码合成。他们声称这个训练数据集包含来自数十种编程语言的数十亿行代码。
Codex 模型返回的前十个解决方案将会被 Copilot 服务返回给你的编辑器。你可以选择最符合你意图或需求的建议,或者拒绝所有的建议。你的选择将会被返回给 Copilot 服务以更好地训练模型。
图 1.6 你的代码被 Copilot 插件采样和编码。然后将其发送到 OpenAI Codex 模型,生成建议。这些建议然后返回到你的 VS Code 会话中。
GitHub 不断改进他们的 Copilot 服务。最近发布(截至 2022 年 12 月),他们自豪地宣称跨所有编程语言的接受率为 46%,而特别是对于 Java,平均为 61%。^([1])
1.4 何时使用和何时避免生成式人工智能
这些技术引起了人们的兴奋。自 2022 年 11 月底公开发布以来,有数百(可能数千)篇关于 ChatGPT 各个方面的文章。它会破坏教育吗?还需要教育吗?软件工程师还有必要吗?
抱持悲观情绪是很容易的。有很多未知数,这项技术的全部影响尚未被揭示。然而,当你阅读本书时,你应该形成自己的观点。我希望你能看到生成式人工智能的优点,并将其用于正面方面。你将用它来成长为一名程序员。随着你的使用,你会成为一名更好的程序员。
作为开发者成长的最佳途径之一就是阅读优秀的代码。OpenAI 已经为你筛选出了地球上最优秀的代码;它们都近在咫尺。你现在也可以轻松获得一些最糟糕的代码。你可以从好的示例中学习,也可以从坏的示例中学习。能够分辨出其中的区别是成长的关键。
那么,什么时候应该使用生成式人工智能呢?尽可能地利用每一个机会吧!(我们将讨论一些使用例外情况。)与生成式人工智能互动是非常有趣的。你将学会如何更好地使用它们,找到捷径,发现新功能,并且每一刻都会感到心花怒放,就像一个充满好奇心的孩子一样。
尽管在日常工作中使用生成式人工智能似乎是很有道理的(因为它确实如此),但应该注意的是,并非所有情况下都适用。
如果你被要求完成一份带回家的编码考试,你应该使用生成式人工智能来完成这个考试吗?除非明确说明可以使用,否则应该避免。如果测试者没有预料到你会使用它,那么这可能被视为作弊。
他们是否应该预计到你会使用它们?是的,在这一点上他们可能应该。此外,有人可能会认为,考试的目的是评估潜在候选人的编码能力,潜在雇主应该尽力构建真实世界的条件,以最好地评估一个人的能力。这应该包括所有在一天中进行课程的工具和资源。这些工具将包括生成式人工智能。
特别注意不要在学术环境中使用它们。适当的归属涉及到很多复杂的问题,至少如此。生成式人工智能在灵感和抄袭之间的界限非常微妙。如果你的使用被认定为抄袭,后果将是严重且永久的:开除,并可能被其他机构拒绝未来入学。务必小心谨慎。
总的来说,要运用你更好的判断力。如果你有可能触犯任何版权法或行政政策,那么除非你被授予明确的许可,否则不要使用它。最好与工作中的企业 IT 或信息安全团队交流一下,确保你遵守与企业系统和计算机相关的企业政策。
最后需要注意的是:生成式人工智能是工具,像任何工具一样,你需要对自己的操作有一定的了解。你应该对正确答案有一定的了解。你应该在你对领域有一定了解的情况下使用它们。在这样的情况下,你会发现自己在探索领域,加深对领域的理解,以及学习速度更快。
现在我们已经简要探讨了生成式人工智能的历史,看到了一些生成式人工智能的用例,并应用了一些重要的防范措施,我们将在下一章进行实际操作,探讨如何使用这三种生成式工具开始同样的项目。
1.5 总结
- 生成式人工智能既是进化的也是革命性的。从进化的角度来看,它们只是开发人员每天使用的工具之一。从革命性的角度来看,它们将改变我们的工作方式。事实上,它们将改变我们的工作。
- 开发的未来将是生成式人工智能的管理。即使是传说中的 10x 开发者也不会像拥有人工智能伙伴的开发者一样高效;一个由人工智能驱动的开发者将以比不使用人工智能更高的质量和更快的速度、更低的成本产生代码。我们将花更多的时间训练我们的人工智能伙伴,让它按我们想要的方式进行操作,然后我们将不再像以前那样编写代码。
- 尽管有很多生成式人工智能在世界上,我们将探讨其中三种最受欢迎的:
- ChatGPT – 自 2022 年 11 月以来一直成为头条新闻。
- GitHub Copilot – 在集成开发环境中使用最广泛的生成式人工智能。由微软提供资金支持和推广。
- 亚马逊网络服务 CodeWhisperer – 一款类似 Copilot 的产品,由亚马逊支持。
- 一些全球最大的公司正在大力投资生成式人工智能(微软、亚马逊、Alphabet)并使其更易于使用(Open AI)。
- 生成式人工智能利用极其复杂的神经网络,类似于我们的神经网络,将输入过滤和映射到以前未见过的新输出。
- 在使用生成式人工智能进行学校作业之前,你应该先咨询你的教授或老师。
^([1]) 赵舒音。“GitHub Copilot 现在拥有更好的 AI 模型和新功能。” GitHub 博客. github.blog/2023-02-14-github-copilot-now-has-a-better-ai-model-and-new-capabilities/
。上次访问日期:2023 年 2 月 14 日。
第二章:从生成式人工智能入门
本章涵盖
- 使用 ChatGPT 进行交流
- 学习使用 Copilot 的基础知识
- 学习使用 CodeWhisperer 的基础知识
- 对比这三个生成式人工智能工具之间的差异
在本章中,我们将深入研究使用三个重要的生成式人工智能工具:ChatGPT、GitHub Copilot 和 AWS CodeWhisperer。本章的主要重点将是利用这些人工智能工具开发一个模型信息技术资产管理(ITAM)系统,特别关注硬件管理组件。
ITAM 系统,无论是商业的还是开源的,都提供了一系列专门用于组织软件和硬件管理的功能。通过仅限制我们的项目范围仅限于硬件管理组件,我们旨在突出显示我们如何与每个生成式人工智能工具互动,比较它们生成的代码质量,并对比它们的独特特点和局限性。
表面上看,ITAM 系统似乎相对简单,主要关注资产管理、标记和跟踪。然而,正如我们将要揭示的那样,在这些看似简单的要求中隐藏着一层重要的复杂性。这种固有的复杂性使我们选择的项目成为利用生成式人工智能来导航问题空间的理想候选。
所以,让我们踏上这个迷人的建设和理解之旅。我们将从概述我们打算构建的系统以及这些非凡的人工智能工具在这一过程中将扮演的角色开始。
2.1 介绍我们的项目,信息技术资产管理系统
信息技术资产管理(ITAM)系统是一种管理和跟踪硬件设备、软件许可证和其他 IT 相关组件的工具,贯穿它们的生命周期。ITAM 系统通常包括硬件和软件清单工具、许可证管理软件和其他相关软件应用程序。该系统还可能涉及使用 QR 码、条形码或其他物理资产管理技术对 IT 资产进行手动跟踪和物理跟踪。
一般来说,ITAM 系统将拥有一个集中式数据库,该数据库存储与资产类型特定的资产标识符和属性。例如,您可能会为台式电脑存储设备类型、型号、操作系统和已安装的应用程序。对于软件,您可能会保存应用程序的名称、供应商、可用许可证数量以及已安装该软件的计算机。后者确保您的组织符合所有许可证限制。通过监控使用情况,您不应超出已购买的许可证数量。
ITAM 系统还赋予了控制成本的能力。由于您始终知道可用的软件和硬件,因此不应该进行任何不必要的购买。这些系统集中了购买,这有助于批量采购。未使用的硬件可以出售。工作负载未充分利用的硬件可以合并其工作负载。此外,正如我们将看到的,我们可以使用此购买日期信息来计算硬件的折旧价值,并将该价值应用于您的组织税收。
我们将探索 ITAM 系统的更多特性,构建一个迷你版本,专注于硬件管理。我们将从 ChatGPT 开始,使用 Python 构建第一个组件。然后,我们将使用 Copilot 和 AWS CodeWhisperer 复制此练习。通过使用每个这些生成 AI 工具重复此过程,我们可以对比差异并将相似之处联系起来。
首先,从 ChatGPT 开始,我们将专注于构建 Asset 类。接下来,我们将开发一种通过使用 FastAPI 库的表征状态转移(REST)控制器来呈现它的方法。最后,我们将在随后的章节中构建这个,添加功能,扩展设计并添加文档。
为什么选择 Python?
在 2023 年,一个人在应用开发中使用 Python 3 的决定不应该需要进行辩解。然而,鉴于主题的关系,对于选择 Python 作为本书的语言是有意义的值得列举一下。首先,Python 是一个用于原型设计项目的优秀语言。数据科学家和机器学习专家经常使用它。其次,它是表达性和生产性的。几行代码就可以完成大量工作,尤其是考虑到标准库的大小,更不用说像 pandas 和 numpy 这样的常用库了。第三,它易于学习和使用。它的语法侧重于(并强制执行)可读性。最后,它得到了所有生成 AI 平台的支持。
2.2 进入 ChatGPT 的探索
上下文是与 ChatGPT 一起工作的最重要的方面之一。你之前的prompt可以极大地改变你当前提示的结果。在像 ChatGPT 这样的语言模型的背景下,提示是指提供给模型的输入,用于生成响应。它可以是一个句子,一个段落,甚至是一段更长的文本。它充当模型的指令或查询,引导其响应。鉴于提示的质量构成了模型响应的上下文,始终注意当前会话中发布了哪些提示是很重要的。因此,建议每次开始新项目时都从新会话开始。附录 A 将指导您设置帐户,登录 ChatGPT,并编写您的第一个提示。
我们将从简单的提示开始:“请使用 Python 为 ITAM 编写一个 Asset 类。” 值得重申的是,当使用大型语言模型时,相同的输入不会总是产生相同的输出。因此,您的输出可能与以下内容不完全匹配;但希望它至少近似。
列表 2.1 ChatGPT 生成的用于 ITAM 的 Asset 类
class Asset: def __init__(self, asset_id, asset_type, manufacturer, model, purchase_date, warranty_end_date, location): self.asset_id = asset_id self.asset_type = asset_type self.manufacturer = manufacturer self.model = model self.purchase_date = purchase_date self.warranty_end_date = warranty_end_date self.location = location self.assigned_to = None self.status = 'Available' def assign_to_user(self, user): self.assigned_to = user self.status = 'Assigned' def unassign(self): self.assigned_to = None self.status = 'Available' def get_details(self): details = f "Asset ID: {self.asset_id}\n" details += f"Type: {self.asset_type}\n" details += f"Manufacturer: {self.manufacturer}\n" details += f"Model: {self.model}\n" details += f "Purchase Date: {self.purchase_date}\n" details += f "Warranty End Date: {self.warranty_end_date}\n" details += f"Location: {self.location}\n" details += f "Assigned To: {self.assigned_to}\n" details += f"Status: {self.status}\n" return details
除了代码之外,ChatGPT 还将返回此类的文档。
图 2.1 ChatGPT 枚举了 Asset 类中的属性以及类的方法
为了跟踪我们的资产,我们需要一种与它们交互的方式。API 是与系统中的对象交互的一种绝佳方式。REST 是在微服务架构中公开 API 功能的一种方法。因此,我们将要求 ChatGPT 为该类创建一个 REST 控制器,并使用以下提示:“请为此类创建一个 REST 控制器。” ChatGPT 生成的简化代码在列表 2.2 中。
列表 2.2 当要求 ChatGPT 为项目添加 REST 控制器时,ChatGPT 生成的 REST 控制器
from flask import Flask, request, jsonify from asset import Asset, AssetManager app = Flask(__name__) asset_manager = AssetManager() # Get all assets @app.route('/assets', methods=['GET']) def get_assets(): assets = [asset.__dict__ for asset in asset_manager.assets] return jsonify({'assets': assets}) # Get an asset by ID @app.route('/assets/<string:asset_id>', methods=['GET']) def get_asset(asset_id): asset = asset_manager.get_asset_by_id(asset_id) if asset: return jsonify(asset.__dict__) else: return jsonify({'error': 'Asset not found'}) if __name__ == '__main__': app.run(debug=True)
ChatGPT 做出了一些有趣的假设。首先,它使用 Flask 构建 REST 控制器。虽然 Flask 是一种流行的基于 Python 的 Web 框架,绝不是一个坏决定,但我们很想知道为什么选择它。因此,我们可以问 ChatGPT 为什么选择 Flask。使用以下提示:“为什么在没有提供要使用的框架的情况下,你选择了 Flask ?
” 使用大型语言模型的乐趣之一是试图剥开谜题的层,看看它是如何工作的。
图 2.2 ChatGPT 在没有提供要使用的框架时解释了使用 Flask 的理由。它指出 Flask 轻量级且易于使用。
尽管这个理由很好,但它并不是我们想要使用的框架。不久之后,我们将让 ChatGPT 切换应用程序以使用 FastAPI。不过,我们应该确保我们刚刚“编写”的代码能够运行。然而,在我们的项目中缺少一些依赖项。我们可以要求 ChatGPT 为我们创建一个 requirements.txt 文件。
在输入框中输入以下提示:“请为此项目创建一个 requirements.txt 文件。
” ChatGPT 应该会返回类似以下代码片段的内容。
列表 2.3 具有 Flask 支持的 Asset 管理项目的 requiremnts.txt 文件
click==8.0.3 Flask==2.1.1 importlib-metadata==4.8.1 itsdangerous==2.0.1 Jinja2==3.0.2 MarkupSafe==2.0.1 typing-extensions==3.10.0.2 Werkzeug==2.0.1 zipp==3.6.0
一旦我们创建并填充了初始的 app.py 文件(ChatGPT 创建的初始 Python 代码)以及我们刚刚生成的 requirements.txt 文件,我们会注意到 ChatGPT 做出的第二个假设:AssetManager
类。ChatGPT 默默地生成了这段代码,而没有给我们任何线索。这个隐藏的类,我们没有源代码,意味着我们的项目无法运行。
幸运的是,我们可以要求 ChatGPT 给我们这段代码。输入以下提示:“请显示 AssetManager 的源代码。
”
列表 2.4 ChatGPT 生成的 AssetManager 类的类定义
class AssetManager: def __init__(self): self.assets = [] def add_asset(self, asset): self.assets.append(asset) def remove_asset(self, asset_id): for asset in self.assets: if asset.asset_id == asset_id: self.assets.remove(asset) return True return False def get_asset_by_id(self, asset_id): for asset in self.assets: if asset.asset_id == asset_id: return asset return None def assign_asset(self, asset_id, user): asset = self.get_asset_by_id(asset_id) if asset and asset.status == 'Available': asset.assign_to_user(user) return True return False def unassign_asset(self, asset_id): asset = self.get_asset_by_id(asset_id) if asset and asset.status == 'Assigned': asset.unassign() return True return False def get_available_assets(self): return [asset for asset in self.assets if asset.status == 'Available'] def get_assigned_assets(self): return [asset for asset in self.assets if asset.status == 'Assigned']
此外,ChatGPT 还将生成关于类使用以及类的所有方法的文档。如果我们想知道如何使用该类,或者如果我们想要向我们的代码添加注释,这份文档将会很有帮助。
图 2.3 ChatGPT 提供了关于 AssetManager 类的文档,包括属性和方法。
如果您选择将 AssetManager
类存储在单独的 Python 文件中,这是一个好主意,那么您将需要更新 app.py 文件,以以下方式更改导入语句:
列表 2.5 修复文件 app.py 的导入语句
from flask import Flask, request, jsonify from asset import Asset from asset_manager import AssetManager app = Flask(__name__) asset_manager = AssetManager() …
现在,您应该能够运行我们非常简单的 ITAM 系统了。一旦您启动 Flask 应用程序,如果您导航到 http://localhost:5000/assets,您应该会看到 JSON 输出的 Assets。
图 2.4 我们向 AssetManager 添加了三个资产。控制器以 JSON 格式返回了这个集合
有一个我们应该关注的未解决问题。这个项目仍然在使用 Flask。有几个原因使我们更倾向于使用 FastAPI 而不是 Flask。首先是 FastAPI 将为我们提供更好的性能。FastAPI 在基准测试中一直表现得明显更好,因此得名。FastAPI 支持异步非阻塞、高并发编程。异步非阻塞系统在相同的硬件上提供更好的性能,因为计算机在等待 I/O 操作完成时会执行其他任务。然而,我们想在这个项目中使用 FastAPI 的主要原因是为了演示如何使用 ChatGPT 轻松更改我们的中间件。
因此,我们应该转换到 FastAPI。我们可以要求 ChatGPT 为我们做这个。输入以下提示:“请将此项目从使用 Flask 切换到使用 FastAPI
。” ChatGPT 将生成类似于以下 app.py 的代码。请注意,列表 2.6 被删减了,只包括 GET REST 操作。完整的输出将支持 POST、PUT 和 DELETE。
列表 2.6 包含 FastAPI 支持的更新后的 app.py
app = FastAPI() asset_manager = AssetManager() class AssetIn(BaseModel): asset_id: str asset_type: str manufacturer: str model: str purchase_date: str warranty_end_date: str location: str class AssetOut(BaseModel): asset_id: str asset_type: str manufacturer: str model: str purchase_date: str warranty_end_date: str location: str assigned_to: str status: str @app.get("/assets/{asset_id}", response_model=AssetOut) async def get_asset(asset_id: str): asset = asset_manager.get_asset_by_id(asset_id) if asset: return asset else: return {"message": "Asset not found"}
有几件事需要注意。首先是 ChatGPT 通知我们所做的更改。请参见图 2.5。接下来,请注意它创建了两个额外嵌入的类:AssetIn
和 AssetOut
。这被认为是一种良好的做法,因为它提供了客户端应遵守的接口或合同,同时允许我们自由更改我们的领域模型的内部表示。
图 2.5 ChatGPT 解释了将应用程序从 Flask 应用程序更改为 FastAPI 所做的更改。
在更改项目的中间件时,您应该查看领域模型类是否没有发生变化。在这种情况下,Asset
和 AssetManager
都发生了变化。我们应该要求 ChatGPT 展示给我们这些新类。
注意
在设计和开发与 ChatGPT 相关的软件时,定期审查不同类或文件的代码非常重要。这是因为上下文是通过与 ChatGPT 的交互不断建立的。这个审查过程将使您首次亲眼看到这些更改是如何实施的。
首先,让我们看看对 Asset
类所做的更改。要做到这一点,请输入以下提示:“请展示 Asset 类的代码。
”
2.7 更新后的 Asset 类,反映了从 Flask 到 FastAPI 的中间件的更改
from typing import Optional class Asset: def __init__(self, asset_id: str, asset_type: str, manufacturer: str, model: str, purchase_date: str, warranty_end_date: str, location: str): self.asset_id = asset_id self.asset_type = asset_type self.manufacturer = manufacturer self.model = model self.purchase_date = purchase_date self.warranty_end_date = warranty_end_date self.location = location self.assigned_to: Optional[str] = None self.status: str = 'Available' def assign_to_user(self, user: str): self.assigned_to = user self.status = 'Assigned' def unassign(self): self.assigned_to = None self.status = 'Available' def get_details(self): details = f "Asset ID: {self.asset_id}\n" details += f"Type: {self.asset_type}\n" details += f"Manufacturer: {self.manufacturer}\n" details += f"Model: {self.model}\n" details += f "Purchase Date: {self.purchase_date}\n" details += f "Warranty End Date: {self.warranty_end_date}\n" details += f"Location: {self.location}\n" details += f "Assigned To: {self.assigned_to}\n" details += f"Status: {self.status}\n" return details
ChatGPT 添加的最有意义的更改是 Optional
类型提示。这将表明您打算 assigned_to
属性不应为空,从而导致 NoneType
错误。ChatGPT 通常还会提供方法文档。
图 2.6 ChatGPT 解释了更新后的 Asset 类。虽然它没有明确告诉我们它在更新类时进行了哪些更改,但它提供了对类方法的充分文档。
接下来,请输入以下提示:“请展示 AssetManager 的代码。
” 构造函数初始化一个 Assets
的列表。这个列表是 AssetManager
类用来执行 CRUD(创建、读取、更新和删除)操作的数据结构。
2.8 更新后的 AssetManager 类,反映了从 Flask 到 FastAPI 的中间件的更改
from typing import List, Optional from asset import Asset class AssetManager: def __init__(self): self.assets: List[Asset] = [] def add_asset(self, asset: Asset): self.assets.append(asset)
remove_asset
方法将遍历资产列表中的元素。如果找到与方法参数(asset_id)提供的标识符相匹配的资产,则将其从列表中移除。
2.9 AssetManager 的 remove_asset 方法
def remove_asset(self, asset_id: str) -> bool: for asset in self.assets: if asset.asset_id == asset_id: self.assets.remove(asset) return True return False
get_asset
方法遍历列表中的所有元素,查找传入的 asset_id 对应的资产。如果找到该资产,则返回它。该搜索功能与此类中的许多方法非常相似:在 assign_asset
和 unassign_asset
方法中可以再次注意到这一点。如果我们遵循 DRY(不要重复自己)原则(我们应该这么做!),那么我们将重构这个类(或者让 ChatGPT 为我们做这个),整合逻辑,提取一个单一的方法供整个类使用。
2.10 AssetManager 类的常见方法
def get_asset_by_id(self, asset_id: str) -> Optional[Asset]: for asset in self.assets: if asset.asset_id == asset_id: return asset return None def assign_asset(self, asset_id: str, user: str) -> bool: for asset in self.assets: if asset.asset_id == asset_id: if asset.status == 'Available': asset.assign_to_user(user) return True else: return False return False def unassign_asset(self, asset_id: str) -> bool: for asset in self.assets: if asset.asset_id == asset_id: if asset.status == 'Assigned': asset.unassign() return True else: return False return False
同样,get_available_asset
和 get_assigned_asset
方法包含共同的功能。两者都会查找所有状态与参数匹配的资产。这些都可以被重构成一个常用的方法来使用。
2.11 方法:根据给定状态返回资产
def get_available_assets(self) -> List[Asset]: return [asset for asset in self.assets if asset.status == 'Available'] def get_assigned_assets(self) -> List[Asset]: return [asset for asset in self.assets if asset.status == 'Assigned'] def get_all_assets(self) -> List[Asset]: return self.assets
附带这段代码的是与类方法及其用法相关的文档。
图 2.7 ChatGPT 列出了已迁移到使用 FastAPI 的 AssetManager 类中可用的方法
最后,我们需要 ChatGPT 重新生成依赖文件。使用以下提示:“请展示我新的 requirements.txt 文件。
”
列表 2.12 更新后使用 FastAPI 的 requirements.txt 文件
fastapi==0.70.0 uvicorn==0.15.0 pydantic==1.8.2
一旦重新运行了 pip
命令(pip install -r requirements.txt
)以安装更新的依赖项,您应该能够使用以下命令运行项目:uvicorn app:app --reload
。
端口现在已更改为 8000。当我运行这个时,并没有一个方法来显示 AssetManager
中的所有 Assets
。我们可以要求 ChatGPT 添加这样一个方法。“请添加一个方法到 app.py 中,以返回和显示所有 Assets。
”您可能会得到一个异步方法,该方法返回 AssetManager 的所有 Asset
。代码应该类似于以下清单。
列表 2.13 用于显示 AssetManager 中所有资产的方法
@app.get("/assets/", response_model=List[AssetOut]) async def get_assets(): assets = asset_manager.get_all_assets() return assets
FastAPI 中的路由
在这种方法中,我们使用 @app.get 装饰器定义了一个新的路由,URL 路径为 /assets/。我们还使用 response_model 参数指定了 List[AssetOut] 的响应模型,这告诉 FastAPI 验证输出数据并基于 AssetOut 数据模型的架构生成 API 文档。
在方法内部,我们调用 asset_manager 对象的 get_all_assets 方法,该方法返回所有 Asset 对象的列表。然后,我们将此列表返回给客户端,FastAPI 将自动将其转换为 JSON 数组。
您可以使用 Web 浏览器或诸如 curl 之类的工具测试此端点。例如,如果您在本地运行应用程序并使用端口 8000,则可以在 Web 浏览器中打开 http://localhost:8000/assets/,以查看所有资产的 JSON 数组。
让我们通过在浏览器中添加并显示一个新的 Asset
来测试我们的 ITAM 系统。您可以使用 curl 或 Postman 添加一个新的 Asset
。
列表 2.14 使用 curl 将新资产添加到 AssetManager
curl -X POST -H "Content-Type: application/json" -d '{"asset_id": "12345", "asset_type": "Laptop", "manufacturer": "Dell", "model": "Latitude 5400", "purchase_date": "2022-01-01", "warranty_end_date": "2023-01-01", "location": "New York"}' http://localhost:8000/assets/
当您尝试购买这个新的 Asset
时,可能会遇到错误。要使 ChatGPT 生成的代码正常工作,需要进行两处修改。第一步是在 Asset 类中添加一个名为 to_dict()
的新方法。FastAPI 使用库 pydantic 将输入/输出模型(例如 AssetIn
和 AssetOut
)与 Asset
类进行转换。它通过使用字典来分配属性来实现此目的。to_dict()
方法将为我们提供一种简单的方法来提供该字典。
列表 2.15 在 asset.py 中定义的 Asset 类中添加 to_dict 方法
def to_dict(self): return { "asset_id": self.asset_id, "asset_type": self.asset_type, "manufacturer": self.manufacturer, "model": self.model, "purchase_date": self.purchase_date, "warranty_end_date": self.warranty_end_date, "location": self.location, "assigned_to": self.assigned_to, "status": self.status }
我们需要做的第二个更改是将输出模型(AssetOut
)转换为 JSON。我们需要在输出此对象的所有地方进行此操作。我们将检查与将输出更改为 JSON 相关的类中的更改。
列表 2.16 更新后的 app.py 类,输出采用 JSON 编码
@app.get("/assets/", response_model=List[AssetOut]) async def get_assets(): assets = asset_manager.get_all_assets() return JSONResponse(content=[asset.to_dict() for asset in assets]) @app.post("/assets/") async def add_asset(asset: AssetIn): new_asset = Asset(asset.asset_id, asset.asset_type, asset.manufacturer, asset.model, asset.purchase_date, asset.warranty_end_date, asset.location) asset_manager.add_asset(new_asset) return {"message": "Asset added successfully"} @app.delete("/assets/{asset_id}") async def remove_asset(asset_id: str): if asset_manager.remove_asset(asset_id): return {"message": "Asset removed successfully"} else: return {"message": "Asset not found"} @app.get("/assets/{asset_id}", response_model=AssetOut) async def get_asset(asset_id: str): asset = asset_manager.get_asset_by_id(asset_id) if asset: return JSONResponse(content=asset.to_dict()) else: return {"message": "Asset not found"} @app.put("/assets/{asset_id}/assign") async def assign_asset(asset_id: str, user: str): if asset_manager.assign_asset(asset_id, user): return {"message": "Asset assigned successfully"} else: return {"message": "Asset not available"} @app.put("/assets/{asset_id}/unassign") async def unassign_asset(asset_id: str): if asset_manager.unassign_asset(asset_id): return {"message": "Asset unassigned successfully"} else: return {"message": "Asset not assigned"} @app.get("/assets/available/", response_model=List[AssetOut]) async def get_available_assets(): assets = asset_manager.get_available_assets() return JSONResponse(content=[asset.to_dict() for asset in assets]) @app.get("/assets/assigned/", response_model=List[AssetOut]) async def get_assigned_assets(): assets = asset_manager.get_assigned_assets() return JSONResponse(content=[asset.to_dict() for asset in assets])
将中间件从 Flask 切换到 FastAPI 在我们的代码中引起了无数的问题。然而,通过这个过程,我们建立了一个直觉,即我们应该如何以及何时与 ChatGPT 进行交互。ChatGPT 是我们软件设计工具箱中不可或缺的工具。它可以让我们快速地对我们的项目进行根本性的改变,并给我们指明应该采取的方向。它往往在非常精细的任务上失败(至少在撰写本文时是这样)。
一个有趣的实验是你应该尝试(在一个新的会话中)首先创建一个使用 FastAPI 的项目的提示,然后将其与我们迭代创建的项目进行比较。你应该会注意到代码有很大的不同。
在本书的这一部分中,我们深入探讨了 ChatGPT 在软件开发中的实际应用,通过开发一个小型 IT 资产管理(ITAM)项目。我们利用 ChatGPT 自动生成软件的基本元素,包括创建类、开发一个 RESTful 控制器,并生成一个 requirements.txt 文件。这个过程凸显了 ChatGPT 作为一个辅助工具在加快和简化软件开发的复杂过程中的能力,特别是对于像这样的小规模项目。在这种情况下应用 ChatGPT 不仅突显了它在构建基本软件组件方面的实用性,而且展示了它在未来简化软件开发流程方面的潜力。
AI 驱动的开发者(MEAP)(一)(2)https://developer.aliyun.com/article/1516310