开篇词|知识工程:AI时代的软件工程

你好,我是徐昊,欢迎你和我一起学习 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 引入团队或是组织。