📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

How to code Claude Code in ~200 lines of code

The Modern Software Developer44:31

Transcription

好的。大家好。呃,我是 Mi Hy。我想制作这个视频,因为就在本周早些时候,我们为 CS146S,现代软件开发人员,上了一门课。这是我们今年秋天在斯坦福开设的第一门此类课程。这门课完全是关于现代软件开发实践的。基本上,我们如何在软件开发生命周期的每一个环节使用 AI 编码。

现在,在本周一的课程中,我们基本上花了一整节课来讲解如何用大约 200 行代码实现像 Cloud Code、Cursor 或 Warp 这样的高级编码代理的核心。所以,我们浏览了整个源代码,表明它实际上并不需要比基本算法更复杂的东西。

在这个视频中,我想做的是实际进行同样的练习,录制一段我自己编写这样一个代理的屏幕录像,并展示在大约 200 行代码中,我们就拥有了驱动我们一直在使用的这些令人惊叹的编码平台核心的功能。所以,我们开始了。呃,这就是这个录像将要展示的内容。再次强调,这是为我们本周的新课程准备的。如果您有兴趣跟进,课程资料可以在这个网址找到:the modernsoftware.dev。我们发布所有讲座、讲义、作业和阅读材料。所以,欢迎大家随时跟进。

但对于这个视频的其余部分,让我们实际深入研究一些代码,看看我们能多快地构建出像 Cloud Code 这样令人惊叹的工具的核心。所以,我这里有我们将要构建的入门代码。呃,我已经准备好了一些脚手架。大约有 60 或 70 行左右。呃,可以这样想:这里没有什么特别疯狂的事情。我们正在初始化一个 API 客户端,连接到某个底层模型提供商。它可以是 OpenAI,可以是 Cloud,可以是任何东西。但在这里,我们只是为了我们的目的使用 OpenAI。

稍后我们将填写系统提示。所以,这里只是留空。有一些格式化字符串,只是为了让我们的控制台输出更容易着色。所以,您会看到颜色的差异。我们还准备了一个非常简单的实用程序,它只是一个将文件名转换为文件的绝对路径的方法。所以,我们稍后将使用它,它将有助于代理在实现这些功能时实际打开文件。

驱动这个代理的核心实际上将依赖于三个工具,对吧?所以,我们将使用三个工具来实现这一点。呃,我们必须实现的三个工具是读取文件、列出文件和编辑文件。

好的,那么今天任何编码代理的工作方式以及它实际做什么,其实很简单。所以,它是一个聊天,对吧?我与 LLM 交互。当我向 LLM 发出请求时,也许是要求它创建一个新文件之类的,它会返回一些东西给我,它的响应可能包括我应该使用的工具列表或发出的命令。所以,我可能会告诉 LLM,嘿,给我创建一个新文件。而 LLM 可能会发出一个工具,比如编辑文件,并附带我应该编辑的参数。

现在,作为工具的接收者,我将收到,你知道,在这种情况下是 JSON,我们将实现它,但你知道,你可以根据需要格式化它,然后我将实际执行该工具调用,对吧?所以,我本人,用户,正在执行这个工具调用,或者我猜在很多情况下,IDE 或 Cloud Code 正在执行工具调用,然后我们将工具调用的响应返回给 LLM。所以,LLM 然后,我们将这个添加到运行的上下文,就像一个对话上下文,关于当我们发出工具时发生了什么,而 LLM 接收到它,使用该上下文来跟进。我们还可能使用该上下文发出另一个查询,比如一些其他的用户提示,然后循环就无限地继续下去。

所以,这确实是循环的核心,对吧?我向 LLM 发出提示或消息。LLM 回应并检测它是否需要一个工具来服务这个请求。呃,然后基于此,我们要么发出工具调用,执行它,你知道,离线执行,不是由 LLM 执行,然后我们将返回工具调用的响应,或者我们将继续进行正常对话,因为我们每次对 LLM 说的话并不都需要实际与工具交互。但无论如何,所以,这其中的关键是,有一些工具我们需要实现并提供给 LLM,提供给代理实际使用。所以,这些是我们真正需要的三个工具。这就是将驱动这个代理的核心。

我们将有一个这样的工具注册表或工具,就像一个从工具到其实现的映射。然后还有另外两个实用程序。获取工具表示。正如我们稍后将看到的,这只是一个实际的方法,可以获取任何给定工具的实现,返回一个字符串化的表示,我们将用它来填充一个完整的系统提示,因为请记住,我们还没有实际实现系统提示,或者我们还没有写它,但最终我们会写的。我们需要有一个所有可用工具的集合。

然后我们将有一个提取工具调用的函数,它将负责接收 LLM 的输出,它可能会告诉我们实际发出一个工具,执行一个工具。所以,我们必须从 LLM 的输出中解析出这些。我们将有一个执行 LLM 调用的函数,这只是一个与我们上面定义的客户端交互的非常简单的函数。

然后我们有了代理循环的核心,编码代理循环。这确实将是运行我所描述的那个序列的工作马。所以,这就是核心。呃,到本视频结束时,我们将实现大约 100 到 150 行代码,来驱动所有这些并实现所有这些。所以,这听起来不错。让我们开始吧,我会一边编码一边讲解。呃,然后,希望这能成为你们可以参考的东西。

好的。首先,我们将先实现循环,对吧?代理的核心循环。为了我们的目的,我将从打印我们的 FTON 提示开始。这对于调试很有用。呃,然后,当我们构建其余组件时,我们将使用它来迭代地查看我们正在更改该提示。所以,我们开始初始化这个对话对象,它将真正跟踪我们发送给 LLM 的所有消息。任何曾经向 GPT 或 Claude 等发出过 API 调用的人可能都熟悉这种格式或 O Lama。呃,这是许多 LLM 目前使用的标准消息格式。

所以,这个特定消息的角色是什么,消息的内容是什么?所以,我们从系统提示开始。所以,我们将发出系统提示,然后循环的核心开始说,好吧,while true,所以你知道,无限循环,开始获取用户输入。好的,所以我们将获取用户输入。我们将在这里使用一些非常简单的方法。呃,您现在可以看到,这就是我们利用了开头那些 ANSI 代码转义字符串。这只会给在这种情况下着色不同的颜色。我们只是这样做。没有什么疯狂的。一点点空间。

然后,如果这不起作用,我们将以防御性的方式编程,并捕获键盘中断和文件错误。呃,这样我们就可以在需要时按 Ctrl+C 和 Ctrl+D 来中断,你知道,在我们调试时,我们将跳出循环。好的,太好了。所以,我们有了用户输入。我们将首先将其附加到对话历史记录中。所以,在这里,角色是用户而不是系统。内容是用户输入,我们将其剥离以清除任何尾随的空白字符。

所以,现在我们收到了用户输入,现在我们必须进入代理正在做什么或 LLM 正在做什么的循环。所以,这将是另一个嵌套的 while true 循环。所以,让我们实际发出,从 LLM 获取响应。所以,我们将使用我们已经构建的对话历史记录来执行 LLM 调用,在循环的第一次运行时,它将只有两条消息。系统提示,然后是用户消息。然后,一旦我们发出 LLM 的响应,它可能包含也可能不包含工具,正如我们所描述的,我们将提取工具的调用。

所以,让我们开始从助手的响应中提取工具调用,正如我们所希望的,我们有两种不同的情况。如果没有调用,如果字符串中没有明确提及任何工具,我们将像往常一样继续。但首先,让我们打印出助手的响应。再次,我们使用了不同的 ANSI 转义字符串,因为我们使用了不同的颜色。好的。然后,我们将在这里提供助手的响应,这就是我们想要的。然后,我们将将其附加到我们正在运行的对话的实际历史记录中。在这种情况下,当然,角色是系统,内容是您期望的系统响应。

但是,然后我们想打破这个。我们想直接打破这个。呃,因为如果我们只是继续,如果 LLM 正在响应,而我们没有工具可以调用,那么我们应该回到用户那里,回到循环的顶部。然后,如果不是这种情况,让我们再次打印 LLM 的响应。这里有点冗余,但你知道,我并不是说这是最干净、最完美的代码,但它相对紧凑。呃,所以这里有点冗余,但我们就再做一次。我们将附加这个。所以,为什么我们不直接复制这个呢?好的,我们在这里是第二种情况,因为我们必须遍历工具,因为第二种情况是,第一种情况是我们没有调用任何工具。第一种情况是没有调用工具。第二种情况是我们实际上有 LLM 说我们应该交互的工具。所以,让我们这样做。

所以,为了做到这一点,我们知道这个对象很可能不是 None,我们将遍历该对象。现在,我们稍后将展示当我们在 extract tool invocations 函数时,它期望返回的格式。但为了让你提前了解一下,或者给你一点即将到来的东西的味道,它将是一个对象元组列表,其中一个是工具的名称,另一个是我们应该调用的工具的参数。所以,它看起来会是这样。我们将打印出来以进行调试,以便我们看到发生了什么。

我们将首先获取工具。所以,我们将使用我们从工具调用中提取的工具名称来获取工具。然后我们有几种不同的情况。好的。如果工具名称是 read file,你知道我们在这里列出了它,这是实际的键值映射。所以,如果它是 read file,我们理论上应该调用 read file 工具。但为了我们的目的,我们只是打印 read file called,因为现有的 read file 实现什么都不做。但如果它是 list files,那么我们将只列出 files called,然后如果它是 edit file,我们将打印 edit files。呃,我们稍后会填补这一点,但我们现在只是留下存根。呃,当然,我们也可以有一个 else 捕获。如果调用了现有的其他工具,我们应该,你知道,抛出错误或告诉用户重试。呃,我们现在不会这样做。只是为了节省时间。好的。

最后,我们将再次附加这个工具调用的输出。这次是因为我们发出的是工具而不是 LLM,我们接收工具名称,发出它,然后返回响应。在这种情况下,角色是用户。所以,实际上是用户消息,内容将是我们定义的特定格式,但让我们这样做。呃,格式如下。当我们返回工具调用的输出时,我们将说,我们将以 tool result 作为前缀,然后我们将说,只需转储实际的 JSON.dumps 响应。需要将其设为 f 字符串,以便它可以做到这一点。我可能应该在这里初始化 restp 为某个值。呃,也许是 rest。我们暂时称它为空。实际上,这可能是一个错误。所以,也许我们应该这样做。当我们实际获得工具调用的响应时,我们将填入响应。好的,我们快要得到有用的东西了。

好的。为了使其有用,因为现在,如果我们运行这个循环,它真的真的什么都不会做。为了使其至少稍微有用,我们将开始通过实现 execute LM call。我们这样做的方式是,我们将再次开始,这就像一个非常简单的函数,因为我们所做的只是与 OpenAI 客户端交互,它完成了大部分实际与 LLM 对话的工作。所以,我们将这样做的方式是,我们真的只会说 response = OpenAI client.completions.create create model = GPT5 message = messages = conversation,正如我们所期望的。然后 max completion tokens max completion tokens = 让我们说 2000,非常激进。这可能比我们需要的要多得多,但你知道,为了安全起见。

然后我们最后返回的是 response.choices[0].message.content。

好的。此时,我们已经实现了与实际 OpenAI 客户端交互的函数,它将发出一个 LLM 调用并返回一个响应。现在,我们必须包含的一件事是,我们实际上将只是初始化,而不是一个说 pass 的存根,用于 get full system prompt,我们实际上将返回一个空字符串。呃,因为我们将其附加到对话中,这就是我们为 LLM 提供的。所以,我们不应该向 LLM 提供 None 消息。但此时,我们实际上已经有了一些东西,我们可以至少与之交谈,而不会做任何有用的事情,因为我们还没有连接工具调用的集成。

看看这个。你好。是的。所以,再次,所有这些做的循环,没有工具调用,实际上只是一个对话循环,我们向 LLM 发出一些东西,LLM 返回一条消息,然后我们就这样无限地继续下去。呃,所以这里没有什么特别疯狂的。它还不是一个编码代理。它真的只是一个聊天机器人。

好的。为了让它变得更有趣,我们必须开始实现其他方面的核心。所以,有几个部分我们将实现,然后我们将再次运行它。我们要实现的第一个部分是这里一个充实过的系统提示,对吧?所以,我们将要实现的是实际将这个系统提示的一些核心内容放入其中,它将描述代理的行为,然后我们将填写其他函数的一些实现。

所以,让我们开始放入系统提示的核心。所以,我们将开始说,你是一个有用的编码助手,你的目标是帮助完成编码任务。好的,这定义了角色。您可以使用一系列工具。您可以执行,即使它不是实际执行的,我们是执行者,但只是告诉它它知道它可以执行什么。以下是您可以使用的一些工具。然后是字符串,这将是我们的工具列表字符串。稍后我们将用另一个函数来填充它,您可能还记得我们已经将其设为存根。

好的。现在,真正重要的部分。我们描述了它应该返回的输出格式,如果它想使用工具。当您想使用工具时,请在格式 tool 的确切一行中回复。所以,一个字符串前缀,然后是具体的工具名称 JSON args,仅此而已。好的,所以,当您想返回工具调用时,实际上只有一行。使用紧凑的单行 JSON,在收到工具结果消息后使用双引号。您应该继续处理任务。如果不需要工具,请正常响应。对吧?所以,如果您想发出一个工具,LLM 的输出格式是这样的,带有工具名称和参数,确保它是单行紧凑的 JSON,然后在我们执行工具并使用此工具结果格式返回工具响应后,您将记住下面的格式,就像这样,然后继续前进。

好的,这就是系统提示的核心。我们只需要告诉它这些。好的,现在让我们开始放入这两个部分。首先是我们想要提供给 LLM 的工具表示。所以,为了获得完整的工具表示字符串,我们将首先开始,你知道,在这个函数中,我们返回,我们被提供了工具的名称。让我们实际获取这个工具,只是这个工具的单个工具表示。所以,我们将这样做。它看起来会是这样的,你知道,它是任意的,我们如何定义它,但为了我们的目的,我们只是必须明确工具的名称是什么,它在做什么,以及它的签名定义。这就是 LLM 需要知道的明确性,以便它能够推理该工具。

所以,我们有名称,我们有描述,这将来自文档字符串。这将使我们必须在定义工具时非常明确我们的文档字符串,正如我们稍后将看到的。然后我们有一个签名。所以,有一个 Python 内置函数允许我们获取这个签名,从定义的工具中。所以,重要的是我们有这些类型定义,无论是输入还是输出。

好的,现在我们得到了,所以这是获取一个工具的字符串表示。但是,当我们想将其注入系统提示和我们定义的那个动态变量时,我们将想要填充整个内容。所以,让我们这样做。我们将一些工具列表。我们将 registry tool list,抱歉,toolster repper = +=,我们只是将其附加到它。我们的格式有点任意,但我们将定义它如下,工具名称。

好的,所以,工具,你知道,那里会有一个新行,有三重等号,添加一个单独的工具表示,然后我们将标记该工具调用的结束。然后,如果我们必须处理多个工具定义,那么我们将确保它们之间有一行分隔。嗯。相当任意。再次,你可以根据自己的意愿格式化它,只要它足够合理,能够让 LLM 解析并理解工具的语义,以及它如何与其他工具的语义区分开来。

最后,抱歉,我们实际上必须这样做。我们将格式化工具字符串,或者抱歉,tool list stir 等于 tool stir repper tool list,而不是 tool。所以,这应该是 tool stir repper。好的。所以,这现在将把我们所有工具的合并定义注入到这里。

我们还有一件事要实现,这很重要,在我们可以开始实现实际工具调用之前。这就是提取工具调用的函数。所以,再次,它的作用是,它实际上接收 LLM 的响应,其中可能包含一个或多个工具调用,我们必须解析出这些工具调用,然后用它来确定是否需要发出一个工具调用,包括工具的名称以及参数。

所以,这个函数的工作方式是,它将接收通常提供给它的东西,这将是助手的工具,LLM 的响应,它将返回一个工具名称和参数列表,正如我们之前提到的。然后,因为 LLM 将输出我们明确要求的格式,跨越许多,可能是一行或多行,我们将解析出来。

所以,让我们这样做。让我们看看实际情况。所以,我们将保留一个列表,其中包含我们解析出的所有工具,因为可能有多个,一个或多个。这将是相当多的原始字符串解析。所以,让我们开始获取响应的行。所以,我们将 line strip,如果 line 不以 tool 开头。所以,这不是它不符合我们期望的格式。然后继续,否则,抱歉,我们必须再次,这不可否认地是一些稍微脆弱的字符串解析,但嗯,你可以当然使其更具防御性,并更小心地处理它,但我们将这样做的方式是,我们实际上将只是切掉 LLM 发出的工具调用的前缀。

所以,去掉 tool: 前缀,然后我们将解析出后面的内容,对我们来说,这将是工具的名称和参数。所以,我们需要获取名称,我们需要获取参数。所以,我们将首先获取名称和字符串的其余部分。所以,如果不,如果字符串的其余部分不以开头的冒号,开头的括号结尾,那么这是一个格式不正确的工具调用。我们不想处理它,因为谁知道它是什么。所以,我们将在这里稍微防御一下,说如果它不以这个东西结尾,这个闭括号,那么就继续。不要前进。否则,它应该是格式正确的,对吧?它应该是这样的。

所以,我们将获取直到那个闭括号结尾的所有内容,然后我们将加载它,因为我们期望它是 JSON,因为我们明确要求 LLM 生成 JSON,你知道,紧凑的单行 JSON。然后,我们将把名称和参数都附加到这个正在运行的调用列表中。然后,如果发生任何异常,呃,继续前进。再次,我们可以对异常进行更明确的处理。这显然不是编写此代码的最佳方式,但仅为了我们的目的,我们想看看会发生什么。呃,继续前进,然后我们将返回调用。

好了。我们已经到了拥有循环核心的阶段。现在,我们仍然无法做任何有用的或有趣的事情,因为我们还没有实现任何其他工具。所以,如果我再次运行它,它看起来会非常类似于我们开始时的情况,那就是只是与聊天机器人交谈。

所以,让我们实现一个工具,第一个工具,然后将其连接起来,看看当我们有了这个循环时会发生什么。所以,我们要实现的第一个工具是最简单的,对吧?它是 read file 工具,顾名思义,它允许我们读取文件。所以,这里有几点需要注意。一是,因为当我们为 LLM 获取工具表示时,我们提取文档字符串,我们提取签名,我们必须对其中一些方面非常明确。所以,特别是文档字符串应该非常具有描述性。你知道,这是你在为 LLM 定义工具时通常需要知道的,要明确。不要仅仅期望,哦,如果我定义了函数,那么 LLM 就知道该怎么做。不,它实际上是使用文档字符串来推理工具的作用,以及它是否应该使用它,以及何时应该使用它。

所以,我们将非常详细地描述,对于看起来非常简单的事情,它做了什么。所以,获取用户提供的文件的完整内容。好的?我们定义了参数,在这种情况下只是文件名。要读取的文件名。再次,可能很明显,但我们必须明确。返回值也应该清楚。文件的完整内容。呃,遵循这一点非常重要,以便于解析,对吧?所以,添加这些冒号最终对解析非常有帮助。

现在是实际实现。所以,首先让我们获取完整路径。所以,这就是为什么我们有了这个实用程序来解析绝对路径。呃,我们将把它放在那里,只是为了调试,我们将打印完整路径,然后我们将获取实际内容。所以,让我们看看,呃,正如 f read 返回文件路径内容就是内容。

好的,让我们连接起来。我们有了这个实现。所以,还记得我们在这里的循环中留下了存根吗?让我们实际进行工具调用,对吧?呃,这就是我们作为与 LLM 交互的人必须做的。所以,我们得到了工具的名称,我们从这里的注册表中得到了工具。我们将获取文件名,仅此而已。这应该足以实际交互第一个工具。所以,让我们看看会发生什么。

好的。所以,正如您将在这里看到的,现在我们有了系统提示,我们在开始时为调试打印了它。对吧?这个,这个东西在这里。抱歉。非常非常上面。这就是它的样子。对吧?所以,我们有这个,这个,呃,第一部分。哎呀。也许我只需要,哎呀。我只是觉得我开始了一个调试会话。抱歉。呃,也许再运行一次。

所以,您有用的编码助手,正如我们在助手提示中所描述的,你知道,这里是您可以使用的一些工具,然后是这些工具的表示。大多数工具,比如 edit file 和 list files,没有什么特别有趣的地方。呃,所以它只有名称和签名,这是从输入中提取的,以及我使用的输出类型定义。但是,read file 工具实际上有更令人兴奋的东西。它有描述,这是从文档字符串中获取的,对吧?这就是让 LLM 能够推理并知道何时使用 read file 工具的原因。

现在,让我们试试吧,对吧?所以,我们这里有一些东西。我们在这个目录中有几个文件。所以,这个文件特别会打印出来说,好吧,test file.py 里有什么。好的。您看到助手发出了一条 read file 命令,带有我们期望的紧凑单行 JSON。太棒了。它从我的请求中检测到了文件名,将文件名解析为这个路径,然后打印出了文件的内容。文件里是这样吗?让我们看看。是的,就是这样。它只是一个非常简单的两行 main 函数,打印出“这个有效”。太棒了。对吧?所以,这只是我们第一个真正连接到 LLM 的工具,它能够使用它来理解工具的语义,然后知道何时实际发出工具,这取决于用户请求,这很棒。

所以,这是代理的第一步。我们还有两个工具需要实现,然后它才能变得真正、真正令人惊叹。所以,下一个我们将要实现的是 list files 工具,为了节省时间,我们将更快地浏览这些实现。所以,list files 工具只是为了让我们能够指向一个目录,说嘿,这个目录里有什么,然后 LLM 变得更自主,能够推理这些部分。

所以,让我们开始实现它。再次,我们将非常明确文档字符串。这是用户提供的目录中的文件。参数是路径,要从中列出文件的目录的路径。full path = resolve absolute path,就像我们上次一样。我们将创建一个所有文件的列表,然后对于 full path 中的每个文件,all files.append file name。所以,这给了我们文件名,然后我们有一个文件类型,如果它是文件,那么打印文件是一个字符串,否则,然后我们将将其返回给 LLM。呃,再次,我们需要提供返回内容,因为这就是我们在工具结果中提供的。所以,这就是为什么这些非常明确地几乎像 JSON 表示,因为这就是我们将提供并添加到 LLM 上下文的内容。

好的,让我们看看现在这应该可以工作了。实际上,我们需要将其连接到 edit files。好的,这基本上就是我们所需要的。所以,现在我们应该能够,我们有了第二个工具,我们的第二个工具已经连接到 LLM。所以,week two 里有什么?看,它发出了 list files 工具。所以,它将发出它。然后,砰,这就是 week two 里有什么,这太棒了。我们可以,你知道,更进一步,让它,呃,你知道,实际上使用我们的 read files 工具打开那个文件。然后,这将是与 LLM 的两步交互。

但让我们再做一件事来真正完成循环并达到涅槃,那就是实际实现 edit file 工具。所以,让我们这样做。edit file 工具是三个工具中最复杂的。呃,但它并不疯狂。基本上,我们的 edit file 工具将根据提供的输入参数来创建新文件,或者它将替换现有文件,就像在现有字符串中替换它们一样。所以,我们首先替换旧字符串的第一个出现,这是文件中可能存在的旧东西,或者用我们想替换它的新东西。所以,要么我们将用 new stir 替换文件中的 old stir。

现在,如果 old stir stir 是空的,那么我们将用 new stir 创建覆盖文件。所以,这只是我们将使用的一个约定。如果 old stir 只是空字符串,那么我们将创建一个新文件。添加要编辑的文件的路径,要替换的字符串,new stir,要替换的字符串,返回一个字典,其中包含文件的路径和采取的操作。再次,这只是为了在响应中明确,以便 LLM 知道发生了什么。path = resolve absolute path path。

如果 old stir 是这个东西,即空字符串,那么我们将写入文本。我们将写入我们的字符串。为了安全起见,我们将明确编码。在这种情况下,响应应该是创建文件。所以,这是我们从头开始创建文件的情况。否则,我们可以,我们将读取旧文本,然后如果我们首先需要找到要替换的旧字符串。所以,我们将找到它的位置。但如果找不到,如果调用 find 并返回负一,那么你知道,我们需要知道我们甚至无法替换这个。我们无法用 new stir 替换 old stir。所以,我们将直接告诉 LLM,嘿,找不到 old stir。你知道,如果我们发现找不到 old stir,我们可以做一些更复杂的事情。你知道,我们可以提示用户告诉我们他们想替换什么,或者或者,你知道,做一些隐含的假设。

这就是像这些 IDE 和 AI 编码平台通常所做的,根据工具结果,它们可能会内置自己的回退行为。但为了我们的目的,我们将保持它非常简单。好的。但如果我们找到了它,那么我们就必须编辑。所以,我们将要做的是,我们将用 new stir 替换 old stir 的第一个实例。然后我们将写入它。然后我们将再次返回它。我们刚才采取了什么行动?我们刚刚编辑了文件。太好了。

所以,这就是实现的 edit file 工具。然后我们需要将其实际连接到,你知道,在那个百万编码代理循环中。所以,我们将要做的是,我们将获取,我们必须为这个特定工具提供三个不同的参数。一个是路径,对吧?所以,我们将获取路径。另一个是 old stir,我们可以获取。最后是 new stir。这就够了。

好的。所以,既然我们实现了这一点,我们就可以实际测试它了。让我们看看会发生什么。所以,如果我运行这个,我现在应该能够说,给我创建一个名为 hello.py 的新文件,并在其中实现 hello world。Hello 是路径。Old stir 是空的。New stir 是打印 hello world。完成。这正是你所期望的。

编辑 hello.py 并为其添加一个函数来乘以两个任意数字。所以,我们首先获取 hello.py。我们读取它。砰。我们看到了它之前是什么。我们编辑它。对吧?所以,旧字符串是这个打印 hello world。新字符串是带有新函数的修改。然后我们返回这个编辑的响应。说嘿,我们刚刚添加了一个 multiply 函数。你还好吗?我们很好。看看它。我们刚刚在代码之上生成了代码。我们编辑了现有文件。这太疯狂了。

这就是编码代理循环的核心。此时,我们可以继续创建新文件。我们可以继续编辑现有文件。这就是全部。这就是像 Cloud Code 这样的东西在做的事情。这就是 Cursor 等等在做的事情。我的意思是,当然,还有更复杂的,但所有这些都是填充物,都是它们在其之上添加的装饰,只是为了让 UI 更流畅。但如果你看看我们刚刚所做的,这是 27、28 行代码,我们就拥有了一个功能齐全的编码代理,我们可以通过与它交谈来编写代码。绝对疯狂。

我希望你们喜欢这个视频。再次强调,这是为我们在斯坦福课程现代软件开发人员的讲座准备的,我们讨论了软件开发生命周期的所有方面,以及它如何因 AI 编码而改变。所以,请继续关注。呃,网址在这里。我也会分享这段代码。呃,你可以在这个特定的网页上看到我们所有的讲座材料、作业、阅读材料等。请继续关注 modern software dev。

感谢大家的观看。希望您喜欢。