你好,我是徐昊,欢迎你和我一起学习 AI 时代的软件工程。
随着 2022 年底大语言模型(Large Language Model)进入技术爆炸期,LLM 也对软件研发产生了巨大的冲击。无论是使用 ChatGPT 直接从头生成应用,还是在 Github Copilot 辅助下在已有代码库中编码,每一次与 LLM 有关的新闻都刺激着从业人士的神经,好像过不了几天,工作就会被 AI 取代了。
通过 ChatGPT 编写生产代码
然而实际使用起来,是怎样的呢?让我们看一个例子,如果要做一个产品目录的服务(Product Catalog),通过 API 的调用返回产品信息。这是一个非常简单的 RESTful API 服务。大多数人可能会想到通过下面的提示词(Prompt),让 ChatGPT 帮助我们编写代码:
目前我们在编写一个产品目录服务,通过API提供所有可售商品的详细信息,使用Java编写。
请提供代码和对应的功能测试。
在这个提示词中,我们简要地描述了需求。因为 ChatGPT 的回答并不是确定的,以下是我得到的结果:
比起可以使用的生产代码,这更像一份在给定上下文(产品目录)下的 Spring Boot 教程。离生产代码还有很大差距。我们需要在现有结果的基础上给予 ChatGPT 反馈,让 ChatGPT 做出调整。比如,告诉 ChatGPT 这个 API 不允许修改或增加目录中的产品,并要求 ChatGPT 补充其他功能测试。
需要注意的是,虽然 ChatGPT 可以通过对话保持一定时间内的上下文信息,但是如果需要更稳定的反馈结果,可以修改原始的提示词,将上下文信息包含在其中,然后重新提问。这样会获得更稳定的反馈和结果。
现在我们把反馈加到提示词里,可以这样写。
目前我们在编写一个产品目录服务,通过API提供所有可售商品的详细信息。此API不能修改或增加可售商品。使用Java编写。
请提供代码和所有功能的功能测试。
当然这个提示词的结果也许依然无法满足最后的需要,我们需要重复这样的过程,给予 ChatGPT 持续的反馈,明确我们需要修改的部分。在经过几次迭代之后,我们就会得到一个好得多的版本,当然也会有一个复杂得多的提示词,比如:
目前我们在编写一个产品目录服务,通过API提供所有可售商品的详细信息;
此API包含列出所有商品,按SKU查看某个商品,按照分类列出商品以及按关键词搜索的功能;
API返回的结果是json格式;
当查找的SKU不存在时,返回404;
按关键搜索功能使用POST而不是GET;
使用Java编写;
为所有功能提供功能测试,包括异常情况。
请编写功能和测试代码。
可以看到,为了让 ChatGPT 给出的结果更贴近生产代码,我们需要在提示词或者对话中提供丰富的上下文信息。比如在上面的提示词中就包含了大量的上下文信息。
目前我们在编写一个产品目录服务,通过API提供所有可售商品的详细信息;(业务上下文)
此API包含列出所有商品,按SKU查看某个商品,按照分类列出商品以及按关键词搜索的功能;(业务功能描述)
API返回的结果是json格式;(技术规范描述)
当查找的SKU不存在时,返回404;(技术规范描述)
按关键搜索功能使用POST而不是GET;(最佳实践)
使用Java编写;(技术栈)
为所有功能提供功能测试,包括异常情况;(代码范围)
请编写功能和测试代码。(最终的交付物要求)
在与 ChatGPT 一起完成代码功能时,我们需要通过上下文信息补充,指导 ChatGPT 生成更接近我们所需的代码。这个过程中,具体的程序编写是由 ChatGPT 完成的,而要做什么、怎么做则是由人来指定的。
从软件工程到知识工程
这可能就是 ChatGPT 出现时带来恐慌的根源,在大多数程序员心中,都认为自己是搞技术的,是写代码的。现在 ChatGPT 把代码都写了,那岂不是自己就可有可无了?是不是未来都要被 AI 取代了?
这恐怕是对软件工程最大的误解了。软件工程不仅仅是编写代码。写代码在软件开发中只占很小的比例,甚至还不是最困难的根本复杂度。实际上,软件工程还涉及问题解决、需求分析、系统设计、测试和维护等方面。
这是因为软件是载体,并不是真正的产品,真正的产品是包含在其中的知识。软件只是知识的可执行形式。软件开发的核心不是产生代码,而是知识的获取与学习。
有效编码的前提是,所需的知识已经被有效地获取了。知识先存在于大脑中,然后才能体现在软件里。如果所有的知识都已经被掌握,那么编码也不会花费太多的气力。否则,开发过程就会迅速地转化为如何寻找并掌握正确的知识。
对于大多数软件开发人员来说,通过会议反复确认需求、构思正确的解决方案、查找文档、到 Stackoverflow 之类的在线社区寻求问题解答、通过调试理解存在于代码中的逻辑构成了开发工作的主要内容。与之相比,真正编码的时间反而不会太多。
而对于高水平的程序员而言,在敲下代码之前,80% 的代码都已经在头脑中构思好了。敲出来,也不过是一种必须的负担而已。所以写代码从来都不是软件开发的核心部分。
但编码的确是一个琐碎且繁杂的活动,它甚至掩盖了我们工作中最有价值的部分:获取、学习并传递知识。顺便说一句,将软件开发的过程看作知识管理的过程,强调知识作为核心产品,并不是什么新的观念。极限编程等敏捷软件开发方法就是源自这种理念的实践。
LLM 正在改变软件开发的面貌,它们可以自动化一些编码工作,但这并不意味着软件工程师的角色会消失。相反,它们将使软件工程师能够更多地专注于复杂问题的解决和创新,专注于更有价值的部分。也就是知识的获取与传递。
回想前面与 ChatGPT 一起写代码的例子,我们的主要工作,从编码转变成为 LLM 提供足够多的上下文信息。虽然我们并不需要提供详细的编码指导,也不需要指定类库或编程语言的用法,但是对于功能需求、业务知识、架构决策、测试策略等关乎“生产代码”的重要信息,还是要依赖我们,才能准确地提供给 LLM。
我们的关注点从如何构造软件,变成了如何提取组织知识,让知识变成 LLM 能够理解的形式。因此,提示词工程(Prompting Engineering)的关键并不在提示词,而在如何组织其中的知识。因而知识工程(Knowledge Engineering)是一个更合理的名字。
随着 LLM 的兴起,我们也应该使用知识工程改造软件工程,即提取知识、组织知识为 LLM 更易于理解的形式,再通过 LLM 将这些知识转化为可工作的软件。
课程设计
这种改变将对个人和团队都带来深远的影响。
对个人而言,从自己编码变成指导 LLM 编码。
人们常说,可以将 ChatGPT 看作一个博学肯干且情绪稳定的毕业生。那么使用类似的 LLM,就相当于公司为你配备了一个编码助理。怎么有效地发挥这个助理的能力,就成了提升效率的关键。这时我们相当于从运动员变成了教练员。对于编码技能的要求降低了,但提高了对知识总结、知识传递、任务分解等技能的要求。正如传奇程序员 Kent Beck 所言,LLM 让我 90% 的技能无用了,却让 10% 的技能放大了 1000 倍。
对团队而言,大量的知识可以通过 LLM 得到有效的沉淀。原本困扰团队的诸多问题,比如,关键岗位人才流失造成大量上下文损失;在团队中难以形成统一的共识;新人培养时间长,难以上手等问题,都会得到极大的缓解。然而直接套用 LLM 并不能有效地实现这一点,需要我们有能力将现有研发流程重新梳理为知识管理的过程,从中捕获关键知识,并通过 LLM 加以沉淀。
从知识管理的角度去看待软件开发,对于大多数人是陌生的,但是对于 AI 时代而言则是必须的。
因此在这门课中,我们主要站在知识工程的角度,阐述如何从知识管理的角度理解软件开发。在软件开发的不同阶段中,提取核心知识及关键载体,将它们表示成易于 LLM 理解的形式。其中编码阶段的知识提取和传递,将是个人与 LLM 合作的核心技能。
我们首先会介绍知识工程的整体框架。从识别软件中不同知识种类开始,讨论这些知识在传递过程中带来的认知行为改变,以及不同的认知行为适合哪种 LLM 交互模式。
然后我们会讲解在不同的软件开发生命周期中,需要传递的关键知识,以及如何将它们组织成 LLM 可以理解的形式。首先是需求分解与传递的过程,这是整个开发流程中最关键的一部分知识,LLM 可以发挥非常重要的作用。
一旦我们完成了对于业务知识的传递,就会将目光聚焦在技术和实现方式上的知识,并通过这些知识构造有效的现场管理框架。
最后是构建 AI 辅助的团队。毕竟,任何改变,只有彻底改变了团队中的人,才算是真正的落地,AI 也不例外。
希望通过这门课的学习,能帮助你更好地利用 LLM 提高效率,还可以站在一个更全面的立场上,讨论如何将 LLM 引入团队或是组织。