虽然本书主要讨论将生成式人工智能(尤其是LLMs)集成到软件应用程序中,但在本章中,我们将讨论如何利用LLMs来帮助软件开发。这是一个庞大的主题;软件开发已被几家咨询公司(如毕马威和麦肯锡)的报告强调为受生成式人工智能影响最大的领域之一。
首先,我们将讨论LLMs如何在编码任务中发挥作用,并提供一个概述,看看我们在自动化软件开发方面取得了多大的进展。然后,我们将尝试使用几个模型,定性评估生成的代码质量。接下来,我们将实现一个完全自动化的软件开发任务代理。我们将详细介绍设计选择,并展示使用LangChain编写的仅有几行Python代码的代理实现的一些结果。我们将提到许多可能的扩展方法。
在整个章节中,我们将致力于自动软件开发的不同实用方法,您可以在书籍的GitHub存储库(huggingface.co/spaces/bigc… 这个截图展示了该模型在Hugging Face Spaces的一个实验场中的情况:
我们可以在不同的模型之间进行选择:StarCoder、StarCoderPlus和StarCoderBase。请注意,正如描述中所说,StarCoder模型没有进行指令调整,这意味着我们不能给它任务。我们不能说“编写一个包含……的类”,但我们可以要求它完成一段文本,其中我们使用“# dataclass of customer including an alphanumeric id, a name, and a birthday”来提示模型 – 让我们试试这个!
我们可以切换温度、最大新标记、前n个和重复惩罚的设置。对于任何复杂的任务,我们需要增加“最大新标记”设置。 我们应该在网页上得到一个代码片段作为响应。我得到了以下代码,为我们的假设客户提供了一个有用的数据模型:
@dataclass(frozen=True) # frozen means that the class cannot be modified after creation. This is useful for storing data in databases or other persistent storages (e.g., files).
class Customer:
cust_id : str = field() # this defines how to create instances from the class; it's called "field" because we're creating fields within our object/instance
firstname : str = "" # these are default values if no value was provided when instantiating objects using this class
lastname : str = "" # note that there can only ever exist one instance variable per line - you'll get errors otherwise!
def __post_init__(self):
"""This method runs automatically once upon instantiation"""
self._validate_cust_id() # validate input before saving anything into memory
self.__set_fullname__() # set full name based on given inputs
@property # getter function used by Python interpreter whenever trying to access property directly instead of through. notation e.g.: myobj.firstname vs just myobj.firstname()
def fullname(self)->str:
return f"{self.lastname}, {self.firstname}"
## private methods below here
### validation functions go here
def _validate_cust_id(self):
assert len(self.cust_id)==8,
ValueError("Customer ID must have exactly eight characters")
try:
int(self.cust_id[0]) == True
except Exception as err:
raise TypeError('First character of CustID should be numeric') from None
#### setter functions go here
def __set_fullname__(self):
'''sets the customers' full name'''
names = [x.strip().title() for x in re.split("[W]+", self.firstname)] + [''] * max((1,-len([y.strip().title() for y in re.split("[W]+", self.lastname)])) )
print(names)
self.firstname=' '.join(filter(None,[n[:3]for n in reversed(names)])+[''])
self.lastname =''.join([' '*(4-len(l))+' '+ l[-2:]if not l=='I' else '' for l in filter(None,[n[:-2].upper()+','*bool(re.search('[aeiou]',n))+n[-2:].upper() for n in reversed(names)])]).rstrip(',')
前述的代码缺少通常会出现在注释提示之前的导入部分,所以我不能因此指责模型。我们需要在顶部添加这两行:
import re
from dataclasses import dataclass, field
即使按原样运行它,也不会抛出错误,这很令人印象深刻。然而,仔细查看时,可以发现其中有一些不太理想甚至是错误的地方。 我不喜欢注释的风格和缺乏文档字符串。代码中留下了一些print()语句,通常这是为了调试目的而这样做的。这不应该出现在生产代码中。将firstname和lastname的默认值设为空字符串并不理想。我期望客户ID是基于散列函数分配的 – 我看不出为什么ID在验证属性的函数中要求具有确切的八个字符。 这段代码有更多问题,影响其正确性和可读性,还有一些其他更关键的问题,包括试图编写只读属性。在数据类中,由于它们被冻结,无法在创建后更改firstname和lastname。使用正则表达式以非标准语法从firstname和lastname创建变量名的逻辑至少是奇怪的 – 而且是错误的。在反转的名称上进行的过滤也极具怀疑性。
我就说这么多。这给我们带来了一些有希望的想法和一个开始的结构,但它不是生产级别的代码。它甚至无法工作。您可以在书的GitHub存储库中的software_development文件夹下的customer.py文件中查看此代码。
让我们再次尝试从模型中获取一些更有用的东西。我们开始得有些不对劲。我们以错误的语法开始了代码片段,并期望能够得到有效的代码。这不现实。让我们再试一次,并以一个更符合标准的提示开始:
"""Customer data model.
Here we implement our data model for customers, a dataclass with
fields firstname, lastname, customer_id (a hash).
"""
我们得到以下结果:
@dataclass(frozen=True) # frozen means that the object cannot be modified after creation!
class Customer:
"""A class representing one single customer."""
firstname: str = ""
lastname: str = ""
id_: int = field(default=-1024, init=False)
def __post_init__(self):
self.__hash__()
@property
def fullname(self)->str:
return f"{self.firstname} {self.lastname}"
def __eq__(self, other:"Customer")->bool:
if isinstance(other, type(self)):
return all([
getattr(self, attr)==getattr(other,attr)
for attr in ["firstname", "lastname"]])
else:
raise TypeError("Cannot compare different types")
def __lt__(self, other:"Customer"):
try:
assert len({type(x) for x in [self, other]})==1
if not hasattr(self,"fullname"):
setattr(self, 'fullname',f'{self.firstname}{self.lastname}')
if not hasattr(other,'fullname'):
setattr(other, 'fullname',f'{other.firstname}{other.lastname}')
return self.fullname < other.fullname
except AssertionError as e:
print('Error:',e )
def __repr__(self):
return f"Customer('{self.firstname}', '{self.lastname}','{hex(abs(self._customer_id))[-6:]})"
def __hash__(self):
hsh = abs(hash((self.firstname+self.lastname))) % ((sys.maxsize + 1)*2)+ sys.maxsize*3
self.id_=hsh
return hsh
很高兴看到客户ID是使用哈希创建的,正如我们预期的那样。有一些用于比较两个不同客户对象的样板代码。然而,同样存在前面的问题。首先,它缺少导入部分,这是我不理解的,因为我们的提示应该是文件开头的模块文档字符串。导入语句应该紧跟在这之后。其次,它再次试图在类的初始化后设置应该是冻结的属性,显示了对冻结属性的理解不足。
在解决这两个问题之后,我们得到了我们的第一个Customer()。但然后又出现了另一个问题,客户ID引用了错误的名称,显示了一种缺乏一致性的情况。在解决此问题后,我们可以初始化我们的客户,查看属性,并将一个客户与另一个客户进行比较。我可以看出,这种方法开始变得对编写样板代码很有用。
您可以在书的GitHub存储库中的software_development文件夹下的customer2.py文件中查看此代码。
StarChat
让我们尝试一种指令调整的模型,这样我们就可以给它一些任务了!基于StarCoder的StarChat可以在Hugging Face上找到,链接为 huggingface.co/spaces/bigc…
您还可以找到其他人提供的许多游乐场,例如:
- Sanjay Wankhede提供的StarCoder游乐场:huggingface.co/spaces/sanj…
- Code Llama模型的游乐场:huggingface.co/spaces/code…
- Joshua Lochner的AI Code游乐场,允许在三个模型之间切换,包括CodeGen-Mono 350M:huggingface.co/spaces/Xeno…
这个截图展示了StarChat中的一个例子,但请注意并非所有的代码都可见:
你可以在GitHub上找到完整的代码清单。
对于这个通常在大一计算机科学课程中涵盖的例子,不需要导入。算法的实现很简单。它立即执行并给出了预期的结果。在LangChain中,我们可以像这样使用HuggingFaceHub集成:
from langchain import HuggingFaceHub
llm = HuggingFaceHub(
task="text-generation",
repo_id="HuggingFaceH4/starchat-alpha",
model_kwargs={
"temperature": 0.5,
"max_length": 1000
}
)
print(llm(text))
在这种情况下,text
是您想要提供给模型的任何提示。
截至2023年底,这个LangChain集成在处理超时方面存在一些问题 – 希望这很快就会得到解决。在这里我们不打算使用它。
Llama 2
Llama 2不是编码方面最好的模型之一,其在 pass@1 上的准确率约为29%;然而,我们可以在Hugging Face的聊天中尝试一下:
请注意,这仅仅是输出的开始部分。Llama 2实现得很好,解释也非常准确。干得好,StarCoder 和 Llama 2!或者说这只是太容易了吗?
小型本地模型
有很多方法可以实现代码完成或生成。我们甚至可以尝试一个小型本地模型:
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
checkpoint = "Salesforce/codegen-350M-mono"
model = AutoModelForCausalLM.from_pretrained(checkpoint)
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
pipe = pipeline(
task="text-generation",
model=model,
tokenizer=tokenizer,
max_new_tokens=500
)
text = """
def calculate_primes(n):
"""
Create a list of consecutive integers from 2 up to N.
For example:
>>> calculate_primes(20)
Output: [2, 3, 5, 7, 11, 13, 17, 19]
"""
"""
# 从管道中获取输出
completion = pipe(text)
print(completion[0]["generated_text"])
或者,我们可以通过LangChain集成包装这个管道:
from langchain import HuggingFacePipeline
llm = HuggingFacePipeline(pipeline=pipe)
llm(text)
这有点冗长。还有更方便的构造方法,即 HuggingFacePipeline.from_model_id()
。
我得到了与StarCoder输出类似的结果。我不得不添加一个 import math
,但这个函数是有效的。
我们可以在LangChain代理中使用这个管道;但请注意,这个模型没有经过指令调整,所以您不能给它任务,只能完成任务。您还可以使用这些模型进行代码嵌入。
其他经过指令调整并可用于聊天的模型可以作为您的技术助手,帮助提供建议、记录和解释现有代码,或将代码翻译成其他编程语言 – 对于最后一个任务,它们需要在这些语言中有足够的样本进行训练。
请注意,这里采取的方法有点天真;然而,尽管如此,这是一个很好的入门方式。这个讨论应该作为使用LLMs进行代码生成的介绍性概览,从提示考虑到执行和实际可行性。像GPT-3这样的公开可用模型可以根据提示生成初始代码,但结果通常需要在使用之前进行细化,因为可能出现不正确的逻辑等问题。专门针对编程任务进行微调显著提高了控制、准确性和任务完成度。像StarCoder这样经过编程提示训练的模型可靠地生成与代码匹配的有效提示和约定。较小的模型也是轻量级代码生成的可行选择。
现在,让我们尝试为代码开发实施一个反馈循环,在其中验证和运行代码,并根据反馈进行更改。
自动化软件开发
在LangChain中,我们有几个用于代码执行的集成,如LLMMathChain,它执行Python代码以解决数学问题,以及BashChain,它执行Bash终端命令,可以帮助执行系统管理任务。然而,尽管对于解决问题很有用,但这些并没有解决更大的软件开发过程。
通过代码解决问题的这种方法,然而,可以相当好地工作,正如我们将在这里看到的:
from langchain.llms.openai import OpenAI
from langchain.agents import load_tools, initialize_agent, AgentType
llm = OpenAI()
tools = load_tools(["python_repl"])
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
result = agent("What are the prime numbers until 20?")
print(result)
我们可以看到,在OpenAI的LLM和Python解释器之间,质数计算得到了很好的处理:
Entering new AgentExecutor chain...
I need to find a way to check if a number is prime
Action: Python_REPL
Action Input:
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
Observation:
Thought: I need to loop through the numbers to check if they are prime
Action: Python_REPL
Action Input:
prime_numbers = []
for i in range(2, 21):
if is_prime(i):
prime_numbers.append(i)
Observation:
Thought: I now know the prime numbers until 20
Final Answer: 2, 3, 5, 7, 11, 13, 17, 19
Finished chain.
{'input': 'What are the prime numbers until 20?', 'output': '2, 3, 5, 7, 11, 13, 17, 19'}
我们得到了有关质数的正确答案。LLM可以生成正确的质数计算。代码生成方法可以适用于简单情况。但是真实世界的软件需要模块化、良好结构化的设计,需要关注点的分离。
为了自动化软件创建而不仅仅是解决问题,我们需要更复杂的方法。这可能涉及一个交互式循环,其中LLM生成草稿代码,人类提供反馈以引导它朝着可读性强、易维护的代码方向发展,而模型则相应地调整其输出。人类开发人员提供高层次的战略指导,而LLM处理编写代码的繁重工作。
下一个前沿是开发框架,以实现人类-LLM协作或更一般地说,为高效、健壮的软件交付构建反馈循环。对此有一些有趣的实现。
例如,MetaGPT库通过代理模拟来实现这一点,其中不同的代理代表公司或IT部门中的工作角色:
from metagpt.software_company import SoftwareCompany
from metagpt.roles import ProjectManager, ProductManager, Architect, Engineer
async def startup(idea: str, investment: float = 3.0, n_round: int = 5):
"""Run a startup. Be a boss."""
company = SoftwareCompany()
company.hire([ProductManager(), Architect(), ProjectManager(), Engineer()])
company.invest(investment)
company.start_project(idea)
await company.run(n_round=n_round)
这是MetaGPT文档中的一个示例。您需要安装MetaGPT才能使其工作。
这是代理模拟的一个鼓舞人心的用例。另一个用于自动化软件开发的库是Andreas Kirsch的 llm-strategy,它使用装饰器模式为数据类生成代码。
关键步骤涉及LLM通过提示将软件项目拆分为子任务,然后尝试完成每个步骤。例如,提示可以指导模型设置目录、安装依赖项、编写样板代码等。
在执行每个子任务之后,LLM然后评估是否成功完成。如果没有成功,它会尝试调试问题或重新制定计划。这个规划、尝试和审查的反馈循环允许它迭代地优化其过程。
Paolo Rechia的Code-It项目和Anton Osika的GPT Engineer项目都遵循类似于Code-It(来源:github.com/ChuloAI/cod…中所示的模式。
这些步骤中的许多都包括向LLM发送特定提示的操作,其中包含将项目拆解或设置环境的指令。通过所有这些工具实现完整的反馈循环相当令人印象深刻。
LLM自动化软件开发的项目还可以通过Auto-GPT或Baby-GPT等项目进行探索。然而,这些系统通常陷入失败循环。Agent架构对系统的鲁棒性至关重要。
在LangChain中,我们可以以各种方式实现简单的反馈循环,例如使用PlanAndExecute链、ZeroShotAgent或BabyAGI。我们在第5章《构建类似ChatGPT的聊天机器人》中讨论了这两种Agent架构的基础知识。让我们选择相当常见的PlanAndExecute,代码在GitHub上提供了不同的实现选项。
主要思想是建立一个链并执行它,目标是编写软件,示例如下:
from langchain import OpenAI
from langchain_experimental.plan_and_execute import load_chat_planner, load_agent_executor, PlanAndExecute
llm = OpenAI()
planner = load_chat_planner(llm)
executor = load_agent_executor(
llm,
tools,
verbose=True,
)
agent_executor = PlanAndExecute(
planner=planner,
executor=executor,
verbose=True,
handle_parsing_errors="Check your output and make sure it conforms!",
return_intermediate_steps=True
)
agent_executor.run("Write a tetris game in python!")
由于我只是想展示这个想法,现在我省略了定义工具的部分,我们马上会来解决这个问题。正如前面提到的,在GitHub上的代码中有许多其他实现选项;例如,那里也可以找到其他Agent架构。
这个实现还有一些细节,但像这样简单的工作可能已经能够编写一些代码,这取决于我们给出的指令。
我们需要的是清晰的指令,让语言模型以特定形式编写Python代码,我们可以参考语法规范,例如:
from langchain import PromptTemplate, LLMChain, OpenAI
DEV_PROMPT = (
"You are a software engineer who writes Python code given tasks or objectives. "
"Come up with a python code for this task: {task}"
"Please use PEP8 syntax and comments!"
)
software_prompt = PromptTemplate.from_template(DEV_PROMPT)
software_llm = LLMChain(
llm=OpenAI(
temperature=0,
max_tokens=1000
),
prompt=software_prompt
)
在使用LLMs进行代码生成时,选择一个专门用于生成软件代码的模型架构是很重要的。对于更一般的文本数据进行训练的模型可能无法可靠地生成符合语法正确和逻辑上合理的代码。我选择了更长的上下文,这样我们不会在函数的中间被截断,还有一个较低的温度,以防止结果过于离散。
我们需要一个LLM,它在训练期间看到了许多代码示例,因此可以生成连贯的函数、类、控制结构等。像Codex、PythonCoder和AlphaCode这样的模型专为代码生成而设计。
然而,仅仅生成原始代码文本是不够的。我们还需要执行代码来测试它,并向LLM提供有意义的反馈。这允许我们迭代地优化和提高代码质量。
对于执行和反馈,LLM本身没有保存文件、运行程序或与外部环境集成的固有能力。这就是LangChain的工具发挥作用的地方。
执行者的tools参数允许指定Python模块、库和其他资源,这些资源可以扩展LLM的功能。例如,我们可以使用tools将代码写入文件,用不同的输入执行它,捕获输出,检查正确性,分析样式等等。
根据工具的输出,我们可以向LLM提供关于代码的哪些部分起作用以及哪些需要改进的反馈。然后,LLM可以生成改进的代码,吸收这个反馈。
通过多代人-LLM循环,可以创建符合所需规格的结构良好、健壮的软件。LLM提供原始的编码生产力,而工具和人类监督确保质量。
让我们看看如何实现这一点,让我们按照承诺定义tools参数:
from langchain.tools import Tool
from software_development.python_developer import PythonDeveloper, PythonExecutorInput
software_dev = PythonDeveloper(llm_chain=software_llm)
code_tool = Tool.from_function(
func=software_dev.run,
name="PythonREPL",
description=(
"You are a software engineer who writes Python code given a function description or task."
),
args_schema=PythonExecutorInput
)
PythonDeveloper类包含了关于将以任何形式给定的任务转化为代码的逻辑。其主要思想是提供一个流水线,从自然语言任务描述到生成的Python代码,再到安全地执行该代码、捕获输出并验证其运行。LLM链支持代码生成,而execute_code()方法负责运行它。
这个环境使得从语言规范到编码和测试的开发周期自动化成为可能。人提供任务并验证结果,而LLM则负责将描述转化为代码。以下是实现:
class PythonDeveloper():
"""Execution environment for Python code."""
def __init__(
self,
llm_chain: Chain,
):
self.llm_chain = llm_chain
def write_code(self, task: str) -> str:
return self.llm_chain.run(task)
def run(
self,
task: str,
) -> str:
"""Generate and Execute Python code."""
code = self.write_code(task)
try:
return self.execute_code(code, "main.py")
except Exception as ex:
return str(ex)
def execute_code(self, code: str, filename: str) -> str:
"""Execute a python code."""
try:
with set_directory(Path(self.path)):
ns = dict(__file__=filename, __name__="__main__")
function = compile(code, "<>", "exec")
with redirect_stdout(io.StringIO()) as f:
exec(function, ns)
return f.getvalue()
我再次省略了一些细节,特别是这里的错误处理非常简单。在GitHub上的实现中,我们可以区分我们遇到的各种错误,例如:
- ModuleNotFoundError:这意味着代码尝试使用我们没有安装的包。我已经实现了安装这些包的逻辑。
- NameError:使用不存在的变量名。
- SyntaxError:代码中的括号没有关闭或者根本不是代码。
- FileNotFoundError:代码依赖不存在的文件。我发现有时代码试图显示虚构的图像。
- SystemExit:如果发生了更严重的事情,Python崩溃了。
我已经实现了安装ModuleNotFoundError的包的逻辑,对一些问题提供了更清晰的消息。在缺少图像的情况下,我们可以添加一个生成图像模型来创建它们。将所有这些作为丰富的反馈返回给代码生成,会产生越来越具体的输出,例如:
Write a basic tetris game in Python with no syntax errors, properly closed strings, brackets, parentheses, quotes, commas, colons, semi-colons, and braces, no other potential syntax errors, and including the necessary imports for the game
Python代码本身在子目录中进行编译和执行,我们重定向Python执行的输出以捕获它;这是通过Python上下文实现的。
在使用大型语言模型生成代码时,要小心运行该代码,特别是在生产系统上。涉及到几个安全风险:
- LLM可能会由于其训练而无意中或者由于恶意操作而生成具有漏洞或后门的代码。
- 生成的代码直接与底层操作系统交互,允许访问文件、网络等。它没有进行沙箱化或容器化。
- 代码中的错误可能导致在主机机器上崩溃或产生不希望的行为。
- 资源使用,如CPU、内存和磁盘,可能不受检查。
因此,从LLM执行的任何代码都对本地系统有重大的控制权。这使得与在笔记本或沙箱等隔离环境中运行代码相比,安全性成为一个重要的问题。
有一些工具和框架可以沙箱生成的代码并限制其权限。对于Python,选择包括RestrictedPython、pychroot、setuptools的DirectorySandbox和codebox-api等。这些工具允许将代码封装在虚拟环境中或限制对敏感操作系统功能的访问。
理想情况下,LLM生成的代码应在在运行在生产系统之前经过彻底的检查,资源使用配置文件、漏洞扫描和功能单元测试,并在运行之前。我们可以实现安全和样式的防护栏,类似于我们在第5章《构建类似ChatGPT的聊天机器人》中讨论的内容。
现在,让我们定义工具:
ddg_search = DuckDuckGoSearchResults()
tools = [
codetool,
Tool(
name="DDGSearch",
func=ddg_search.run,
description=(
"用于研究和了解目标背景的工具。"
"输入:一个目标。"
"输出:有关目标的背景信息。"
)
)
]
添加互联网搜索是值得的,以确保我们正在实现与我们的目标相关的内容。在使用这个工具时,我曾经看到一些实现Rock,Paper,Scissors而不是Tetris,所以了解目标是很重要的。
当我们用实现Tetris的目标运行我们的代理执行程序时,每次结果都有些不同。我们可以在中间结果中看到代理的活动。从中我观察到了对需求和游戏机制的搜索,并且代码一再地生成和执行。
我在这里发现了pygame库的安装。以下代码片段并非最终产品,但它弹出了一个窗口:
# 这段代码按照PEP8的语法编写,并包含了用于解释代码的注释
# 导入必要的模块
import pygame
import sys
# 初始化pygame
pygame.init()
# 设置窗口大小
window_width = 800
window_height = 600
# 创建窗口
window = pygame.display.set_mode((window_width, window_height))
# 设置窗口标题
pygame.display.set_caption('My Game')
# 设置背景颜色
background_color = (255, 255, 255)
# 主游戏循环
while True:
# 检查事件
for event in pygame.event.get():
# 如果用户关闭窗口,则退出
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# 用背景颜色填充背景
window.fill(background_color)
# 更新显示
pygame.display.update()
就语法而言,这段代码还算不错 – 我想这个提示一定帮助了。然而,在功能上,它离Tetris还有很大的距离。
这个完全自动的软件开发代理的实现仍然相当实验性。它也非常简单和基础,仅包括约340行Python代码,包括导入部分,你可以在GitHub上找到。
我认为一个更好的方法是将所有功能分解为函数并维护一个要调用的函数列表,可以在所有后续代码生成的代际中使用。然而,我们的方法的一个优点是它很容易调试,因为所有步骤,包括搜索和生成的代码都被写入一个日志文件中。
我们还可以定义其他工具,如将任务拆解为函数的规划器。你可以在GitHub仓库中看到这一点。
最后,我们可以尝试采用测试驱动的开发方法,或者由人类提供反馈,而不是完全自动化的过程。
LLMs可以从高层描述中产生合理的测试用例集。但是,人类的监督是必不可少的,以捕捉微妙的错误并验证完整性。首先指定期望的行为,审核测试用例,然后创建通过的代码,这是正确流程。该过程分为小步骤 – 生成一个测试,审查和增强它,然后使用最终版本的更改来通知下一个测试或代码生成。明确提供反馈有助于LLM在迭代中改进。
总结
在这一章中,我们讨论了源代码的LLMs以及它们如何在软件开发中发挥作用。有很多领域LLMs可以在软件开发中发挥作用,主要是作为编码助手。我们应用了一些用于代码生成的模型,采用了一些简单的方法,并从定性的角度进行了评估。在编程中,正如我们所见,编译器错误和代码执行的结果可以用来提供反馈。或者,我们可以使用人类反馈或者实施测试。
我们看到了建议的解决方案表面上看似乎是正确的,但却不能完成任务或者充满了错误。然而,我们可以感觉到 – 在正确的架构设置下 – LLMs可能有望学会自动化编码流水线。这可能对安全性和可靠性产生重大影响。目前来说,高层设计上的人类指导和严格的审查似乎是不可或缺的,以防止微妙的错误,未来可能涉及人类与人工智能之间的协作。
在本章中,我们没有实施语义代码搜索,因为它与前一章中聊天机器人的实现非常相似。在第7章《用于数据科学的LLMs》中,我们将使用LLMs进行数据科学和机器学习应用。