DLAI-大模型配对编程笔记-全-
DLAI 大模型配对编程笔记(全)
001:课程介绍与概述 🚀

在本节课中,我们将要学习如何利用大型语言模型(LLM)作为编程伙伴,来简化和改进我们的编码工作流程。课程将介绍一系列新兴的最佳实践,帮助你更高效地编写、测试、调试和重构代码。
欢迎来到这门与谷歌合作开发的关于使用大型语言模型进行配对编程的简短课程。大型语言模型正在改变我们编写代码的方式。当我为翻译一些DeepLearning.AI内容而开发基于LLM的翻译软件原型时,我需要使用一些不熟悉的Python库。我没有阅读文档,而是让LLM先尝试编写代码,然后我再进行修正。
经验丰富的开发者正以多种方式使用LLM来加速工作。在本课程中,你将学习这套新兴的最佳实践,包括如何让LLM帮助你进行错误处理、性能优化等等。我很高兴本课程的讲师是我的老朋友劳伦斯·莫罗尼,他是谷歌的AI首席倡导者。
非常感谢,安德鲁。我也非常高兴能与你和你的团队合作。在本课程中,你将学习如何使用LLM来简化和改进你的代码、编写测试用例、调试代码以及重构代码。你将学习如何处理复杂的现有代码库,这些代码库可能存在技术债务,而LLM可以帮助你解释、记录和格式化这些现有代码。
劳伦斯将使用PaLM API来讲解这些概念,我自己也使用过它,我相信你也会觉得它很有趣。这正是生成式AI和大型语言模型真正让我兴奋的地方。如果我们仅仅把它们看作是从零开始创造某些东西(比如代码),那么我们实际上错过了它们能带来的大部分价值。

希望我们今天将要探讨的一些例子能为你自己的编程之旅带来启发,并帮助你成为一名更高效的软件工程师。我在使用生成式代码时有过一个有趣的经历:它帮助我发现了那些我不知道自己不知道的东西。来自DeepLearning.AI团队的Ed Shu和Dila Eadin也为本课程做出了贡献。
第一课将介绍如何开始使用PaLM API来改进和简化你的代码。这听起来很棒。让我们开始吧。



本节课中我们一起学习了本课程的总体目标:利用大型语言模型作为编程助手,提升编码效率和质量。我们了解到LLM不仅能辅助编写新代码,更能帮助理解、优化和维护现有复杂代码库。接下来,我们将进入第一课,具体学习如何开始使用相关工具。
002:开始使用PaLM API进行代码生成 🚀


在本节课中,我们将学习如何开始使用PaLM API进行代码生成。我们将介绍必要的设置步骤,并编写一个简单的代码生成示例。
概述


首先,我们需要完成一些必要的设置。我将指导你完成整个过程。
PaLM API及其相关工具在Google的开发者生成式AI网站上持续更新。这包括Maker Suite(一个快速、简单的生成式AI提示原型设计工具)和Vertex AI(提供可扩展性和企业级隐私、安全性等更多功能)。但本课程的重点将放在PaLM API上,它允许你通过编码接口访问Google大型语言模型的许多功能。在本课程中,你将亲自动手使用这个API编写代码。
让我们深入了解你需要准备什么。

准备工作 📋

以下是开始所需的简单清单。
首先,你需要一个API密钥。在现实世界中,你可以从我上一张幻灯片展示的网站(develop.generativeai.google)获取。为了本课程的目的,你无需担心,我们已经为你准备了一个,但你需要记住这一点。
其次,你需要Google的生成式AI库。在录制本视频时,它们支持Node.js、Swift和Python,以及一个curl接口。但在本课程中,我将使用Python,并向你展示如何进行Pip安装。
当然,不言而喻,你可能需要一些Python技能。如果你没有,可以查看learnPython.org。但我要做的大部分内容都相当基础。
探索模型 🦎
PaLM包含很多内容,包括许多为不同目的设计的后端模型。探索它们,特别是它们的命名,会很有趣。你会看到很多动物名字。一般来说,动物越大,模型也越大。让我们来看看。
首先,为了获取你的API密钥,我们为你提供了一个,但你需要一个工具来获取它。这里有一个辅助函数。我将执行get_api_key来获取那个辅助函数。
在下一个单元格中,我将导入Google的生成式AI库,我们将其称为palm,然后使用API密钥配置它。
现在我已经完成了这些,我将运行这个单元格。我已经导入了密钥。如前所述,我导入了google.generativeai,我将其称为palm,并通过传递api_key参数为get_api_key的结果来配置palm。
现在我们已经准备好了。如果你要在没有后端的自己的系统上做这件事,你将需要通过pip install google-generative-ai来安装。
接下来,我将探索其中一些模型。我将执行models = palm.list_models()。这是PaLM API中的一个函数,允许你列出所有模型。我将粘贴接下来的代码,然后我们将打印名称、描述和支持的方法。
运行这个单元格,看看我们得到了什么。我们看到我们得到了chat-bison、text-bison和embedding-gecko。猜猜哪些是大模型,哪些是小模型?我们可以看到有两个bison和一个gecko。我们今天要做的是生成文本,我们会看到这个chat-bison支持generate_message,而这个text-bison支持generate_text。这很好。我们知道我们将使用这个模型。
但这里还有另一个函数可以使用。随着更多模型得到支持,如果有多个模型支持generate_text,我可以开始这样做:
models = [m for m in palm.list_models() if 'generateText' in m.supported_generation_methods]
我在这里说的是,如果generate_text确实在支持生成方法中,那么我将展示这些模型是什么。运行这个,现在我们可以看到关于这个模型的更多细节。text-bison是唯一真正支持它的模型。
同样,这只是你可以开始使用PaLM API来了解情况的一些方法。随着API的发展和支持模型的增长,你可能会在这里得到不同的结果,并且你可能能够尝试不同的模型,并从中获得更多乐趣。
我们看到最终列出了三个模型:chat-bison-001、text-bison-001和embedding-gecko-001。想象一下,gecko会更小。我提到过,它是基于动物大小的。随着时间的推移,你可能会看到更多模型被添加到这里。
现在你可能会经常听到的一个问题是:chat-bison和text-bison有什么区别?chat-bison的目标是更优化于聊天场景,它会跟踪上下文。你问它一些东西,它会给出答案,你可能会跟进,或者它会给出另一个答案,你可能再次跟进。而text-bison则更优化于单次提示。你给它一个提示,你会得到一个答案,然后你就继续前进。我们今天将使用那个,因为我发现它通常对代码工作得更好。
现在我们只有一个模型支持generate_text,所以我将创建一个指向它的变量。我将其称为model_bison。当然,这是我们模型列表中的第一个。如果我输出model_bison,确保我得到了正确的那个。运行这段代码,我们看到我得到了相同的输出。
创建辅助函数 🔧
现在我们有了模型,让我们创建另一个辅助函数。这个辅助函数将用于生成文本,这样我们就不必一遍又一遍地编写相同的代码。不重复自己总是一个好习惯。
我将从from google.api_core import retry开始。retry库是这样一个东西:当你像我们在这里一样对LLM数据库进行后端调用时,有时事情可能会不同步,有时你的调用可能会在互联网中丢失。与其编写一大堆代码来重试并不断重试,这可以通过一个非常简单的装饰器来完成,比如@retry.Retry()。
然后我们可以编写我们的函数。我们的函数将被称为generate_text。在generate_text中,我将传递我的提示、我想要使用的模型(我们已经创建了一个称为model_bison的东西),然后我将使用一个温度。模型的默认温度是0.7,但使用0.0的温度,它将是一个更具确定性的模型。所以,无论我从提示中得到什么结果,你都应该看到相同的结果。当然,如果你使用更新的版本甚至更新的模型,它可能已经改变了。
完成这些后,我们现在只想返回palm将给我们的东西,所以是palm.generate_text并传递相同的东西:prompt=prompt,model=model,temperature=temperature。这有助于我们在整个代码中拥有一个很好的辅助函数,这样我们就不必不断地重新发明轮子。
运行这个单元格,确保一切正常。看起来不错。



代码生成的第一步 💻
现在我们将使用PaLM API进行代码生成的第一步。有一个我喜欢的模式,我们将在这个短期课程的所有示例中遵循这个过程。
这个过程如下所示:
首先,我们将创建一个提示。我们将从一个非常简单的静态字符串开始,稍后将展示如何模板化它以使其更复杂和强大。这包含我们想要发送给LLM以指示其为我们生成一些输出的基本命令。
然后,我们将使用我们刚刚创建的generate_text函数和我们选择的模型来获取一个完成。值得注意的是,通常在使用LLM时,模型的输出文本是它预测的下一个标记集。因此,当你传入一个问题时,下一个标记集通常是一个答案。因此,它完成了你以引号开始的字符串。这就产生了“完成”这个术语,而不是“答案”或其他不太准确的词。
当然,最后,在完成输出之后,由你决定如何操作。在本课程中,我们将在Jupyter笔记本中打印出来,但在真实应用程序中,你可能将代码注入IDE、保存到仓库或做许多其他事情。
让我们探索这些步骤的代码,用PaLM进行基本的代码生成。
基本代码生成示例
现在我们已经完成了这些,让我们做一些非常非常基本的代码生成。
我将给它一个非常简单的提示:
prompt = "How about show me how to iterate across a list in Python"
这将是我的提示。这里假设了很多东西,对吧?因为它假设有一个列表,假设是用Python完成的,假设了很多事情。那么,这会是什么样子呢?让我们看看。
如果我说completion = generate_text(prompt),你认为会发生什么?让我们看看。运行它。这需要一点时间,因为它正在实例化API,正在调用后端,后端正在为我们生成东西。完成后,如果你不是在观看录制视频,而是在自己的笔记本中操作,你会在旁边看到一个小星星。当那个星星变成单元格的编号时,你就可以继续了,然后你就可以做类似print(completion.result)的事情。
结果出来了!我们将看到类似这样的结果。它给出了在Python中迭代列表的方法。你可以使用for循环,语法看起来像这样。例如,如果你有一个包含A、B、C的列表,你的for循环for item in my_list: print(item)将输出A、B、C。
但关于这一点的一个非常有用的地方是,它也找到了其他方法来做这件事。例如,有enumerate函数。你可以看到enumerate函数在列表上工作。但当你查看代码时,你会看到所有这些是如何为你工作的。同样,这是非常非常简单、非常基本的代码生成。
但我们不仅仅是创建代码,因为我要求它“展示如何在Python中迭代列表”。所以它给了我代码并解释了它,不仅仅是给我代码。这是第一部分,这是非常非常基本的代码生成。
现在,正如我所展示的,我要求它“展示如何在Python中迭代列表”,所以我们得到了很多额外的东西。当然,你典型的代码生成可能只是类似这样的东西:
prompt = "Write code to iterate across a list in Python"
运行那个,然后让我偷懒一下,复制这些。我的completion是generate_text(prompt)。然后print(completion.result)。让我们看看那给了我什么。现在你可以看到它只给了我代码。我有一个小注释,创建列表['apple', 'banana', 'cherry'],然后就是for item in list: print(item)。它只给了我一种使用for循环迭代的方法。

所以有时候,这就是你提示的方式,并思考如何措辞。你的大型语言模型会非常非常字面地理解你的要求。所以,如果你要求它写代码,它会写代码;如果你要求它展示如何做某事,你可能会得到一些更有价值的东西,就像我们在这里得到的那样,它给了我各种选项并向我解释。
为了好玩,我们也可以取一些它输出的代码,比如这里Python给了我一个列表['A', 'B', 'C'],然后只是遍历它。我们可以把它粘贴到一个单元格里,看看会发生什么?它给了我一个错误,因为我用反引号引入了这些东西。让我去掉那些,然后运行代码。现在我们可以看到它实际上工作了。如果我改变一些东西,比如['apple', 'banana', 'cherry'],希望它实际上在运行代码,而不仅仅是打印A、B、C。我们可以看到它实际上为我打印出了apple、banana、cherry。
所以,像这样非常非常简单的代码会工作,但我们在课程中将大量讨论的一件事是,输出代码容易出现幻觉,所以在任何严肃或生产环境中使用之前,你真的应该彻底测试你的代码。

轮到你了!🎯
现在轮到你了。也许你可以尝试一些东西,比如prompt = "show me how to...",然后是你想看到的东西。它可能是一些常见的计算机科学问题,比如排序或计数元素之类的。实验它,也实验提示本身的英语语言,就像我们在这里做的那样,我们用“show me”而不是“write code to do something”。想想所有这些不同的场景,想想如果你有一个能做事但非常字面理解指令的人,你会如何措辞你的指令。实验它,玩得开心,我们很乐意了解你使用它时的体验。

总结

在你亲自尝试并想出一些有趣的代码之后,看到你做了什么会非常有趣,我们很乐意得到你的反馈。在下一课中,我们将看到如何使这个提示更高效,我们将使用一种叫做字符串模板的东西来实现。
003:提示词模板化 🧩

在本节课中,我们将学习如何通过“模板化”你的提示词字符串,使其更加强大和结构化。我们将把提示词分解为三个核心部分,并通过一个具体的例子来演示如何构建和使用模板。


概述
与大型语言模型交互的一个有效方法是,通过你的提示词来“引导”它表现出特定的行为模式。这意味着,与其简单地要求模型“生成执行某功能的代码”,不如构建一个更结构化的提示,例如:“你是一位擅长编写清晰、工程化Python代码的专家,请生成执行某功能的代码,并为每一行代码添加注释。” 通过将你的具体需求嵌入到这个主提示模板中,你可以获得更好的输出结果。
环境准备
在开始模板化之前,我们需要重新设置环境依赖。以下是准备步骤:
- 导入API密钥并设置PaLM模型。
- 从PaLM支持的模型中选择文本生成模型,例如
text-bison-001。 - 复用上一课中的
generate_text辅助函数,以便轻松地向模型后端发送请求并调整参数(例如将默认温度值temperature从0.7改为0.0)。
提示词的三元结构
我认为一个有效的提示词通常由三个部分组成:
- 引导部分:这部分类似于给墙面刷漆前的“底漆”,用于为模型设定角色和预期行为模式。
- 问题部分:这是提示的核心,即你希望模型完成的具体任务,例如“用Python创建一个双向链表”。
- 修饰部分:这部分指示模型如何处理输出,例如“为每一行代码添加注释”或“逐步展示工作过程”。(注意:此处的“修饰”并非Python中的装饰器概念。)
接下来,我们通过一个例子来具体看看。
构建第一个模板
我们将按照上述结构构建一个提示词模板。
首先,定义引导文本:
priming_text = “你是一位擅长编写清晰、简洁Python代码的专家。”
接着,定义问题部分。我们先从一个简单任务开始:
question = “创建一个双向链表。”
然后,定义修饰部分。最初,我们尝试一个可能更适合解谜而非编码的指令:
decorator = “逐步完成,展示你的工作过程,每一步占一行。”
现在,我们使用Python的字符串格式化功能将它们组合成完整的提示词:
prompt_template = “{priming}\n\n{question}\n\n{decorator}”
my_prompt = prompt_template.format(priming=priming_text, question=question, decorator=decorator)
打印出 my_prompt,我们可以看到完整的提示词内容。将其发送给模型后,我们得到了包含逐步推理过程的输出。然而,对于代码生成任务,“逐步展示工作过程”的指令可能不是最合适的。
优化修饰指令
为了使输出更符合编程场景,我们将修饰部分修改为:
decorator = “为每一行代码插入注释。”
更新提示词模板并重新生成提示。这次,模型的输出为双向链表的代码,并且每一行都附带了清晰的注释,这更符合我们的期望。这个例子说明,在构建提示时,指令的具体性和与任务的匹配度非常重要。
模板化的优势在于,你可以固定引导和修饰部分,只更换问题部分来尝试不同的任务,非常灵活。


尝试更复杂的场景
为了进一步测试,我们提出一个更复杂的问题:
question = “在Python中创建一个非常大的随机数列表,然后编写代码对该列表进行排序。”




我们保持引导和修饰部分不变,仅更新问题部分,生成新的提示词并发送给模型。
模型生成的代码首先使用 random.randint 创建了一个包含10万个随机数的列表,然后使用内置的 sorted() 函数对其进行排序,并且每一行都有注释。我们在笔记本中实际运行这段生成的代码,它成功地创建并排序了列表,验证了代码的有效性。
这个例子展示了如何通过一个结构良好的提示模板,让模型生成复杂且可工作的代码,同时提供解释。
总结






本节课我们一起学习了提示词模板化的方法。我们将提示词分解为引导、问题和修饰三个部分,并通过字符串格式化灵活地构建提示。这种方法使得提示词更结构化、易于维护,并能通过调整特定部分(尤其是更具体的修饰指令)来显著提升模型输出的质量。在下一节课中,我们将探讨如何利用大型语言模型来分析和改进现有的代码。
004:SC-Laurence_L3_v04.zh - GPT中英字幕课程资源


在本节课中,我们将学习如何将大型语言模型(LLM)用作一个能响应你编码需求的宝贵助手,而不是一个取代程序员的工具。我们将通过一系列具体场景来探索LLM在代码改进、简化、测试、优化和调试等方面的应用。请注意,生成的代码可能存在“幻觉”,因此在投入实际使用前务必彻底测试。同时,由于模型的随机性,你得到的代码可能与示例有所不同。
环境设置与准备工作 🛠️
在开始之前,我们需要完成一些环境设置工作。这包括导入必要的库、配置API密钥以及设置一个辅助函数来生成文本。

以下是设置步骤:
- 导入API密钥:首先,我们需要导入PaLM的API密钥。
- 导入库并配置:我们将使用Google的生成式AI库,并将其配置为使用我们的API密钥。
- 选择模型:我们需要找到支持生成文本的模型。在本例中,我们将使用
models/text-bison-001。 - 设置生成函数:我们将创建一个包装函数来调用PaLM的文本生成功能,并设置一个确定性的温度值(
temperature=0.0),以获得更稳定的输出。
以下是核心的代码设置:
import google.generativeai as palm
palm.configure(api_key=‘YOUR_API_KEY’)
models = [m for m in palm.list_models() if ‘generateText’ in m.supported_generation_methods]
model = models[0].name # 例如 ‘models/text-bison-001’
from google.api_core import retry
@retry.Retry()
def generate_text(prompt, **kwargs):
return palm.generate_text(prompt=prompt, model=model, **kwargs)
# 设置确定性输出
generate_text = lambda prompt: palm.generate_text(prompt=prompt, model=model, temperature=0.0)
现在,我们已经完成了所有准备工作,可以开始探索LLM如何协助编码任务了。
改进现有代码 ✨
上一节我们完成了环境设置,本节中我们来看看如何利用LLM改进现有的代码。当你使用一门不太熟悉的语言编写代码时,代码可能“能用”,但未必是最佳实践。LLM不仅能帮你修正代码,还能解释原因,从而帮助你成为更好的程序员。
我们将使用一个简单的提示模板,其中包含“引导语”(priming)和“修饰语”(decorator)。
prompt_template = “””
I don‘t think this code is the best way to do it in Python. Can you help me?
{question}
Please explain in detail what you did to improve it.
“””
以下是我们要改进的代码示例:
question = “””
def print_array(array):
for i in range(len(array)):
print(array[i])
“””
运行生成函数后,LLM可能会建议使用Python的星号(*)操作符来解包列表,这是一种更简洁高效的方法。
# LLM建议的改进版本
def print_array(array):
print(*array)
为了探索更多可能性,我们可以修改提示,要求LLM提供多种解决方案。
new_prompt_template = “””
I don‘t think this code is the best way to do it in Python. Can you help me?
{question}
Please explore multiple ways of solving the problem and explain each.
“””
运行后,LLM可能会列出多种方法,例如列表推导式、enumerate函数和map函数,并比较它们的优缺点。
我们甚至可以询问LLM哪种方式最“Pythonic”(最符合Python风格)。
pythonic_prompt_template = “””
I don‘t think this code is the best way to do it in Python. Can you help me?
{question}
Please explore multiple ways of solving the problem and tell me which is the most pythonic.
“””
LLM可能会指出,使用列表推导式([print(element) for element in array])是最Pythonic的,因为它最简洁、可读性最高。通过这种方式,LLM不仅能生成和改进代码,还能提供观点,帮助我们学习最佳实践。
简化复杂代码 🧹
在学习了如何改进代码风格后,我们来看看如何简化功能正确但结构复杂的代码。通常,代码审查员会提出简化建议,但如果没有审查员,LLM可以充当这个角色。
我们将以一个Python实现的简单链表类为例。虽然功能正确,但代码不够优雅。
以下是初始的提示模板:
simplify_prompt_template = “””
Can you please simplify this code for a linked list in Python?
{question}
Explain in detail what you did to modify it and why.
“””
初始的链表代码如下:
question = “””
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.nextval = None
class SLinkedList:
def __init__(self):
self.headval = None
list1 = SLinkedList()
list1.headval = Node(“Mon”)
e2 = Node(“Tue”)
e3 = Node(“Wed”)
list1.headval.nextval = e2
e2.nextval = e3
“””
LLM在简化后,可能会引入一个辅助函数来从列表创建链表,使代码更清晰、更易于维护。
为了获得更详细的输出,我们可以强化提示,要求LLM扮演专家角色并为每行代码添加注释。
expert_prompt_template = “””
You are an expert in Pythonic code. Can you please simplify this code for a linked list in Python?
{question}
Please comment each line in detail and explain in detail what you did to modify it and why.
“””
这样,LLM生成的代码会包含详细的注释和解释,例如添加add_to_head和print_list等方法。虽然生成的方法名可能不够准确(如add_to_head),但整体上代码结构得到了显著改善,类似于人类代码审查员给出的建议。这提醒我们,对LLM的输出仍需保持审慎,但它的确能提供巨大的帮助。
为代码生成测试用例 🧪
代码简化之后,确保其健壮性至关重要。好的自动化测试是第一步,而创建单元测试可能很耗时。LLM可以帮助我们快速生成测试用例。
我们将为上一节中简化后的链表代码生成测试用例。
以下是生成测试用例的提示模板:
test_prompt_template = “””
Can you please create test cases in code for this Python code?
{question}
Explain in detail what you‘re doing and what these test cases are designed to achieve.
“””
我们将把简化后的链表代码作为问题传入。LLM可能会生成一个使用Python unittest框架的测试类,包含测试链表创建、节点插入和节点删除等方法。
# LLM可能生成的测试框架示例
import unittest
class TestSLinkedList(unittest.TestCase):
def test_creation(self):
# 测试链表创建
pass
def test_insertion(self):
# 测试节点插入
pass
def test_deletion(self):
# 测试节点删除(即使原代码没有此功能,LLM也可能建议)
pass
有趣的是,LLM生成的测试可能包含原代码中没有的功能(如删除节点),这可以启发你完善原始代码。因此,使用LLM生成测试不仅是为了自动化,更是为了发现你可能遗漏的边界情况和功能需求,激发你的编程灵感。
提高代码效率 ⚡
除了简化代码和生成测试,LLM还能从一个中立的视角分析代码,发现效率问题。例如,一个使用递归实现的二分查找树算法,可能没有意识到递归会消耗较多内存。
我们将使用一个递归实现的二分查找算法,并请LLM优化其效率。
提示模板如下:
efficiency_prompt_template = “””
Can you please make this code more efficient?
{question}
Explain in detail what you‘re doing, what you‘ve changed and why.
“””
递归二分查找的示例代码:
question = “””
def binary_search(arr, x):
if len(arr) == 0:
return -1
mid = len(arr) // 2
if arr[mid] == x:
return mid
elif arr[mid] < x:
return binary_search(arr[mid+1:], x)
else:
return binary_search(arr[:mid], x)
“””
LLM可能会建议使用Python内置的bisect模块,它提供了高效的二分查找算法。
# LLM建议的优化版本(需注意“幻觉”)
import bisect
def binary_search(arr, x):
index = bisect.bisect_left(arr, x)
if index != len(arr) and arr[index] == x:
return index
else:
return -1
这是一个需要警惕“幻觉”的典型例子。LLM的解释可能说“使用bisect函数查找数组中间元素的索引”,这并不准确。实际上,bisect执行的是完整的二分查找。此外,它可能提到使用了break语句,而生成的代码中并没有。因此,虽然LLM给出了更高效的代码(直接使用库函数),但我们必须仔细验证其解释和代码逻辑,不能盲目相信。
调试与查找Bug 🐛
最后,我们探索LLM在调试方面的能力。有些Bug不会导致程序崩溃,只在特定情况下出现,难以发现。LLM可以分析代码,找出潜在问题。
我们以一个从在线教程获取的双向链表代码为例,并故意引入一个Bug(在打印函数中未处理空节点,可能导致空指针错误)。
调试提示模板:
debug_prompt_template = “””
Can you please help me to debug this code?
{question}
Explain in detail what you found and why you think it was a bug.
“””
有Bug的双向链表代码示例:
question = “””
# … (包含故意引入的打印空节点Bug的双向链表代码) …
“””
LLM很可能会准确地指出Bug位于list_print函数中,因为它在打印节点数据前没有检查节点是否为None。然而,它提供的修复代码可能不完全正确(例如,检查放在了错误的位置)。这正是一个绝佳的学习机会:我们可以利用LLM的分析来理解问题所在,然后自己动手写出正确的修复代码,而不是直接复制粘贴。
此外,我们可以通过调整生成文本时的temperature参数来观察输出的变化。较低的temperature(如0.0)使输出更确定,较高的值(如默认的0.7)会引入更多随机性,有时可能帮助我们从不同角度理解问题。
总结 📝
本节课中,我们一起探索了大型语言模型作为配对编程助手在多个场景下的应用:
- 改进代码:LLM可以将代码变得更符合语言规范(如Pythonic),并解释原因。
- 简化代码:LLM可以重构复杂代码,使其更清晰、更易于维护。
- 生成测试:LLM能快速创建单元测试用例,甚至启发你发现未实现的功能。
- 优化效率:LLM能从算法角度提出改进建议,但需警惕其解释可能存在的“幻觉”。
- 调试找Bug:LLM可以分析代码,定位潜在错误,但最终的修复需要你结合理解来完成。

关键在于,LLM是一个强大的灵感来源和辅助工具,它能加速开发流程并提升代码质量,但并不能替代开发者自身的思考、验证和决策。希望这些示例能启发你思考如何在自己的项目中利用LLM。
005:利用LLM应对技术债务

概述
在本节课中,我们将学习如何利用大型语言模型来理解和处理软件开发中常见的“技术债务”问题。我们将通过一个具体的Swift代码示例,演示如何使用LLM来解释复杂代码的功能,并自动生成技术文档。


技术债务与LLM的潜力
虽然没有解决所有技术债务的万能方案,但大型语言模型在克服技术债务方面能提供巨大的帮助。
技术债务通常源于复杂的代码在开发者之间长期传递。你必须维护这些代码,但往往难以理解它们,因为它们可能年代久远、过于复杂,或者依赖过多你不敢轻易改动、生怕导致整个系统崩溃的组件。
那么,LLM能在这方面提供帮助吗?让我们通过一个场景来探索。
环境设置与API配置
首先,我们需要设置API和必要的库。我们将导入所需的模块并配置API密钥。
以下是设置步骤:
# 导入获取API密钥的函数
from utils import get_api_key
# 导入Google的生成式AI库,并简称为“palm”
import google.generativeai as palm
# 配置Palm库的API密钥
palm.configure(api_key=get_api_key())
配置完成后,我们需要查看可用的模型。在Palm中,有多个具有不同功能的模型,我们需要选择具有文本生成能力的模型。
# 列出所有可用模型
models = [m for m in palm.list_models() if 'generateText' in m.supported_generation_methods]
model = models[0].name # 例如:'models/text-bison-001'
print(model)
请注意,录制本课程时可能只有特定模型可用。你应该根据自己可用的模型选择最合适的一个。目前,我们将使用列表中的第一个模型。
最后,我们设置一个辅助函数generate_text,用于调用模型生成文本。为了获得确定性的输出并减少随机性,我们将温度参数覆盖为0.0。
# 导入重试装饰器
from google.api_core import retries
# 定义生成文本的辅助函数
@retries.Retry()
def generate_text(prompt, model=model, temperature=0.0):
completion = palm.generate_text(
model=model,
prompt=prompt,
temperature=temperature # 覆盖默认温度值,确保输出稳定
)
return completion.result
现在,我们已经准备好开始探索代码了。
分析复杂代码示例
接下来,我们将分析一段代表“技术债务”的复杂代码。这段代码是Swift代码,摘自一本关于在移动设备上运行机器学习模型的书籍。它的功能是从iOS设备获取图像,将其传递给TensorFlow Lite模型进行分类。
这段代码的复杂性在于,为了转换图像数据,它必须读取图像的基础内存。在移动开发中,直接访问内存存在风险,操作系统可能认为你在进行恶意操作,导致应用商店拒绝上架。因此,代码使用了非常复杂且安全的API来安全地访问设备内存。
这段代码很长,涉及将图像的RGB数据转换为张量,并安全地访问iPhone的内存。对于继承此应用程序的开发者来说,理解其工作原理是一项挑战。这正是LLM可以大显身手的地方。
使用LLM解释代码
首先,我们使用一个提示词模板,要求LLM详细解释这段代码的工作原理。
# 定义提示词模板
prompt_template = """
请解释以下代码是如何工作的。请提供大量细节,并尽可能清晰地说明。
{code_block}
"""
# 将我们的复杂Swift代码填入模板
code_explanation_prompt = prompt_template.format(code_block=complex_swift_code)
# 调用LLM生成解释
explanation = generate_text(code_explanation_prompt)
print(explanation)
运行后,LLM会输出非常详细的解释。它会说明ModelDataHandler类是一个TensorFlow模型处理器,列出其属性和初始化方法,并解释每个方法的功能。特别有趣的是,它还能识别并解释Swift特有的扩展方法(如extension Data和extension Array),这些部分涉及高级的内存缓冲区复制和“不安全指针”操作,正是代码安全访问内存的核心。
通过这种方式,我们快速获得了对整个复杂代码块的全面理解。
使用LLM生成技术文档
除了解释代码,LLM还能帮助我们自动生成技术文档。这对于团队知识传承和降低维护成本至关重要。
我们创建一个新的提示词,要求LLM为这段代码编写技术文档,并让非Swift开发者也能理解。同时,我们要求输出格式为Markdown。
# 定义生成技术文档的提示词模板
doc_prompt_template = """
请为以下代码编写技术文档。目标是让不熟悉Swift的开发者也能理解。
请以Markdown格式输出结果。
{code_block}
"""
# 生成文档提示词
documentation_prompt = doc_prompt_template.format(code_block=complex_swift_code)
# 调用LLM生成Markdown格式的文档
tech_docs = generate_text(documentation_prompt)
print(tech_docs)
生成的文档会以Markdown格式呈现,包括标题、项目符号列表等。它会概述ModelDataHandler类的作用(处理所有数据预处理、调用、对给定帧运行推理等),详细记录公共属性,并以清晰的章节结构进行组织。这样,我们就得到了一个可以直接用作该类技术文档起点的Markdown文件。
实践案例与总结
在我撰写《设备端开发的AI与机器学习》一书时,我需要走出舒适区,为iOS设备编写Swift代码。虽然Swift功能强大,但作为一个Swift新手,我遇到了不少困难。例如,为了将图像传递给移动神经网络,我需要将CVPixelBuffer格式的底层数据转换为原始RGB数据,这涉及到读取内存。由于移动设备的安全限制,这项工作非常棘手。
最终,在大量帮助下,我写出了笔记本中展示的rgbDataFromBuffer函数。几年后,我面临着维护这段几乎遗忘的代码的技术债务。
因此,我在笔记本中分享了这段代码以及两个使用场景:一是向继承此代码的人解释它,二是完成显而易见的文档化工作,甚至输出Markdown格式。你可以自己尝试,看看能利用LLM创造出什么。
本节课中,我们一起学习了如何利用大型语言模型来应对技术债务。我们通过一个具体的Swift代码示例,演示了如何使用LLM来:
- 解释复杂代码:快速理解遗留代码的功能和结构。
- 自动生成文档:创建清晰、结构化的技术文档,便于知识共享和维护。

这只是LLM在减少技术债务方面的一个应用示例。使用相同的技术,你还可以探索许多其他方法来降低自己的技术债务。期待看到你用它构建出怎样的成果。
006:总结与展望 🎯

在本节课中,我们将一起回顾并总结大型语言模型(LLM)在软件开发中的应用价值,并展望其如何帮助开发者提升效率与技能。

关于生成式代码,目前存在许多恐惧、不确定性和疑虑。同时,也有大量炒作声称LLM将使开发者失业。
我个人不同意这种观点。我想明确指出,LLM可以成为开发者的得力助手,让你变得更高效。在本课程中,我逐步介绍了多种使用LLM的方法,这些方法超越了简单的代码生成,能够帮助你成为一名更优秀的软件工程师。
我希望这些内容能激发你思考许多应用场景,让你了解如何成为一名更出色的开发者。😊
上一节我们探讨了LLM在编程中的具体应用,本节中我们来对整个课程进行总结。
以下是本课程的核心要点回顾:


- LLM不仅是代码生成工具,更是提升开发效率与质量的综合助手。
- 通过配对编程、代码审查、调试和文档生成等多种方式,LLM能帮助开发者更专注于复杂问题。
- 正确使用LLM的关键在于将其视为增强能力的伙伴,而非替代品。



本节课中我们一起学习了如何将大型语言模型作为强大的编程伙伴。我们了解到,LLM能够通过多种方式辅助开发工作,从而让开发者变得更高效、更出色。关键在于转变观念,积极利用这项技术来增强自身能力,而非担忧被其取代。


浙公网安备 33010602011771号