GPT编程开发:产生复杂代码的必要条件
GPT产生复杂代码的必要条件
现在很多人都在尝试使用GPT直接生成代码,试图通过自然语言生成来引导。 GPT完成传统的编码工作。然而,几乎没有人真正认真思考过生成代码的长期维护。
基于可逆计算理论的基本概念,我们可以做出如下论证:
- 如果一段复杂的业务逻辑代码需要能够长时间稳定运行,那么肯定不可能每次都重新生成代码。当需求发生变化时。 我们需要将原来的逻辑改为检查差异的形式,所以我们需要定义Delta差异空间,并且确保程序可以自动执行差异合并操作。进一步推理:如果我们要保证复杂系统差分描述的稳定性,那么它必须定义在具有业务意义的领域模型空间中,而不是在通用的编程语言空间中。一般的编程语言空间太大,与需求空间不匹配。查询中的一个微小变化都可能导致整体编程语言空间的大量变化,影响逻辑表达式的稳定性。
- 考虑到自然语言固有的歧义性,复杂业务逻辑的稳定媒介不应该是自然语言。可以想象,即使当前的自然语言描述在当前上下文中是明确的,但随着社会环境的变化和人们所使用的词语含义的变化,同样的自然语言描述在未来的上下文中也可能是明确的。产生不同的解释。为了稳定、准确地表达业务逻辑,并确保按照指定语义绝对可重复执行,我们必须使用近 100 年来才达到的人类智能的辉煌顶峰:形式语言。要保证GPT自动生成的形式语言逻辑能够被人类理解并快速验证和检查,必须提供与业务需求复杂度相匹配的方法,可以使用工具自动验证,并且描述性语言,可以从中反向提取信息以用于其他用途:领域特定语言 (DSL)。
- 程序代码中捕获了大量的业务知识,但以前的编程技术往往将这些知识困在特定的技术实现中。我们没有通用的技术手段来逆向提取信息。即使 GPT 可以用来理解一段现成的代码并为其生成自然语言解释,但我们也很难生成一个程序来准确地使用我们需要从系统源代码中获取的知识。 GPT生成的代码解释只能作为参考,随时可能因错觉而胡言乱语。但是,如果我们使用 GPT 来构建一个新系统,为什么不从一开始就使用支持可逆分析的结构表达式呢?
- 如果GPT不是通过简单的查询响应的方式来完成任务,而是可以调用外部插件,自行设计更复杂的执行计划,那么我们会从安全的角度或者与外界交互的稳定性的角度来考虑世界。有必要将GPT的指令限制在我们预设的语义空间。
有些程序员可能不注重理论分析,认为大型AI模型的Prompt项目是实践经验积累的问题。我不同意这个观点。基于以上可逆计算理论的分析,我们当然可以得到必要条件使用GPT作为一个严肃的软件生产工具:
GPT的输入和输出必须是差分量化DSL(Domain语言)描述了。
基于统一元模型的DSL森林
很多人认为GPT可以理解复杂的业务逻辑描述并生成准确的通用代码实现,那么为什么GPT不能掌握更简单的结构和更清晰的语义定义呢? DSL 语言?一个常见的误解是DSL语言使用自制的小众语法,大型语言模型没有足够的训练语料库来掌握其语法。但事实上,DSL 真正的重要性在于它所创建的语义空间。它使用特定领域的名词来非常简洁地表达相关业务知识,并且可以自然地链接到用户需求的描述。例如,为了描述用户审批流程,我们只需要使用过程、步骤、操作、审批者等几个概念。DSL中使用的每个令牌都具有业务语义,并且由于某些技术限制而没有添加。 。相反,如果要使用通用语言代码来定义,这必然会涉及到导入依赖包、声明变量作用域等一系列与业务无关、由编程语法限制而产生的细节问题语言。 。
如果我们只关注DSL的语义,我们完全可以使用通用的XML或JSON语法作为DSL的通用语法。简单来说,DSL可以用AST语法树的形式来定义。这种方法类似于LISP语言的S表达式,只不过我们可以使用XML标签,这是一种更易读的表示形式。可以在许多不同的表示之间执行可逆转换。例如,Nop平台定义了多种XML和JSON格式之间的可逆转换方法。本质上相同的 DSL 可以由 JSON 和 XML 表示。
采用统一的表示语法后,不同的DSL可以使用统一的元模型来约束(类似Json Schema),进而形成DSL森林。 DSL-forest 可以使用统一的元模型实现语义一致的理解,并支持多个 DSL 之间的无缝嵌入。
很多程序员传统上有这样的印象:设计一个新的 DSL 需要编写自己的解析器和编译器,还要维护自己的 IDE 插件,这是一个巨大的工作量。但在Nop平台上,你只需要定义Nop平台还可以基于自动生成视觉设计器等自动实现领域对象与Excel模板文件之间的双向转换。 Nop平台提供了所谓的Domain Language Workbench的概念,通过它可以快速开发和扩展DSL语言。设计目标与JetBrains的MPS产品类似,但Nop平台是基于可逆计算理论的概念。技术路线更加简洁清晰,复杂度比MPS低很多。另一方面,它具有灵活性。并且在可扩展性和扩展性方面远远超过MPS。
为什么XML是合适的DSL语法载体?
很多程序员都没有亲自设计过XML格式的DSL语言。他们只听说过XML在古代如何被业界前辈的后起之秀淘汰的传说。这造成了 XML 过于冗长的刻板印象。 ,只能用于机器之间传递信息,不适合人机交互。然而,这是一种错误的偏见,它源于XML原教旨主义对XML的错误使用以及一套国际XML规范对错误使用的推动。
当很多人想到使用XML来表达逻辑时,脑海中浮现出的刻板印象可能是这样的:
<function>
<name>myFunc</name>
<args>
<arg>
<arg>
<name>arg1</name>
<value>3</value>
</arg>
<arg>
<arg>
<name>arg2</name>
<value>aaa</value>
</arg>
</arg>
</arg>
</args>
</function>
但实际上我们可以使用下面的XML格式
<myFunc arg1="3" arg2="aa" />
因为arg1的参数值类型是一个整数,而不是字符串类型,可以扩展 XML 语法以允许直接将数字用作属性值。也可以类似于Vue框架,添加特定的前缀信息来区分是否是字符串。例如前缀@:
表示后面的值符合JSON语法规范,可以按照JSON格式进行解析。
<myFunc arg1=3 arg2="aa" /> 或者
<myFunc arg1="@:3" arg2="aa" />
在Nop平台中,我们指定了JSON和XML之间的双向转换规则。例如,对于以下AMIS页面描述:
{
"type": "crud",
"draggable": true,
"bulkActions": [
{
"type": "button",
"label": "批量删除",
"actionType": "ajax",
"api": "delete:/amis/api/mock2/sample/${ids|raw}",
"confirmText": "确定要批量删除?"
},
{
"type": "button",
"label": "批量修改",
"actionType": "dialog",
"dialog": {
"title": "批量编辑",
"name": "sample-bulk-edit",
"body": {
"type": "form",
"api": "/amis/api/mock2/sample/bulkUpdate2",
"body": [
{
"type": "hidden",
"name": "ids"
},
{
"type": "input-text",
"name": "engine",
"label": "Engine"
}
]
}
}
}
]
}
对应的XML格式为
<crud draggable="@:true">
<bulkActions j:list="true">
<button label="批量删除" actionType="ajax" confirmText="确定要批量删除?">
<api>delete:/amis/api/mock2/sample/${ids|raw}</api>
</button>
<button label="批量修改" actionType="dialog">
<dialog title="批量编辑" name="sample-bulk-edit">
<body>
<form>
<api>/amis/api/mock2/sample/bulkUpdate2</api>
<body>
<hidden name="ids" />
<input-text name="engine" label="Engine" />
</body>
</form>
</body>
</dialog>
</button>
</bulkActions>
</crud>
事实上,XML语法看起来更加紧凑和直观。
这里使用的是没有元模型限制的JSON-XML转换,所以必须使用j:list来标记数组元素,并使用@:前缀来表示非字符串值。如果 XML 文件具有 XDef 元模型定义,则不需要此附加注释信息。
使用 XML 相对于 JSON 格式的另一个优点是它可以轻松引入 XML 扩展标签来生成代码。代码的表示和代码生成的结果形式都是XML格式,这在Lisp语言中是众所周知的。这就是所谓的身份。 目前,JSON 格式缺乏相同的代码生成方法。
<columns>
<c:forEach var="col" items="${entityModel.columns}">
<column name="${col.name}" sqlType="${col.sqlType}" />
</c:forEach>
</columns>
关于XML和JSON的等价,可以在XML、JSON和AST函数的等价中进一步讨论
AI必须理解元模型
大型AI模型之所以引起轰动,本质上是因为它展示了超越简单模式记忆的复杂逻辑推理能力。在这种能力的支持下,大型AI模型将不需要大量的程序语料库来学习DSL语言。只需要告诉它语言固有的结构限制。
元模型和元语言的发现是过去100年来数学界最具革命性的发现之一。元模型和元语言在数学领域占有特殊而重要的地位。范畴论的发展与模型论和元语言的研究密切相关。在程序开发领域,我们应该能够通过元模型将DSL语法结构知识和局部语义知识准确地迁移到大型AI模型中。具体而言,所谓元模型原则上可以看作是类似于JSON Schema的模式定义。
在Nop平台中我们强调元模型和特定模型对象之间的同态关系。也就是说,模式的形式原则上应该与数据本身的结构形式保持一致,而不是将树形结构的域结构分解为大量的对象属性关系,并用完全不同的语法形式来表达它们例如 XML 模式。 。例如,
<entity name="test.MyEntity" table="my_entity">
<columns>
<column name="SID" sqlType="VARCHAR" length="30" />
<column name="TITLE" sqlType="VARCHAR" length="200" />
</columns>
</entity>
对应的XDef元模型定义为:
<entity name="!class-name" table="!string">
<columns xdef:body-type="list" xdef:key-attr="name">
<column name="!prop-name" sqlType="!std-sql-type" length="int" />
</columns>
</entity>
stdDomain 与类型声明类似,但可以使用用户定义的类型声明进行扩展。所有 stdDomain 都维护在字典表中,该字典表可以对字段值施加本地语义约束。例如类名,表示必须满足Java类名的格式要求,不允许全部是字符串。stdDomain 前可以添加感叹号,表示该属性值不能为空。
目前的大型模型都是通过填空来训练的,所以使用这种同态设计方法也可以让大型模型快速掌握元模型。在大型模型应用中,给定少量样本,可以逆向猜测对应的Schema类型约束,但这种猜测肯定是不准确的。例如,我们很难通过样本告诉大模型某些格式的字符 串是非法的,例如它可能不包含 - 作为连接符等。 领域知识可以通过元模型快速有效地转移到大型模型。
所以我认为在大模型的训练过程中,我们应该有意识地加强元模型的训练。元模型必须与常规模型区分开来。值得付出额外的努力来提高大型模型对元模型的精确控制。
使用元模型与GPT交互的具体尝试,请参考我的文章《GPT驱动的低代码平台生成完整应用程序的成熟策略》
Nop平台与GPT结合的具体策略
策略Nop 平台与 GPT 通信如下:
- 通过当前使用的 DSL 的 xdef 元模型,帮助 GPT 更快、更准确地理解 DSL 结构
- 引导 GPT 通过差异合并规则直接返回差异描述可逆计算
- 返回差值。数据被合并到当前模型中并成为新的当前模型。在此基础上你可以不受限制地与GPT通信。
- 由于复杂的逻辑推理,通常不可能通过单个 DSL 一步解决问题。此时我们可以使用多个 DSL 建立差分管道,并将问题拆分为几个步骤。

基于Nop平台的DSL支持,AI和人类可以通过以下方式协同工作:
- AI根据需求规范生成顶层DSL
- 通过以下方式将DSL扩展到下一个级别:手动编写的代码生成器DSL
- 手册可以使用Delta调整来细化和定制AI生成的DSL
- 最详细的部分可以由AI基于本地知识来细化
简单地说它等于1。 AI 生成原始处理结果 2. 手动精炼 3. AI 打磨
现在很多程序员设想的 AI 代码的生成是软件中通用组件的生成,如接口、类、属性等。但是 Nop 中的方法平台完全不同。正如我反复强调的,类属性的抽象是由于我们受到底层实现技术的限制。它与域内部结构的关系并不完全一致,比如我反复强调的域结构坐标的概念。类型级映射后,信息会丢失,导致无法准确地进行差分校正。 Nop平台中的DSL侧重于树结构,可以一次性生成完整的逻辑树。
有人思考细化之前生成的代码结构。那么AI模型是否可以用来生成一系列API调用并通过这些API来定制模型呢?例如,通过生成从数据模型中删除phone3字段的API。
entityModel.getColumns().remove("phone3“);
如果我们比较一下Nop平台中的Delta合并运算符,我们就可以知道为什么这个API方法是一个未经优化的解决方案。
<columns>
<column name="phone3" x:override="remove" />
<column name="status" sqlType="INTEGER" />
</columns>
Nop的解决方案具有以下优点:
- 可以将多个Delta变更合并为一个结果,并且可以简化合并过程并丢弃重复的变更。使用API的本质是使用变化动作作为Delta,但多个动作不能自动合并或简化。如果不将这些动作在我们的脑海中一一执行,我们无法理解最终的系统将如何调整。这就是可逆计算理论一直强调的。 Delta必须被独立地理解和定义,并且Delta必须遵守结合律并且可以进行局部简化。
- 领域模型的Delta定义可以由程序自动分析,并可以逆向提取其中的信息。当我们使用API实现Delta时,我们没有一个简单的工具来分析Delta的具体构成,在应用Delta之前我们无法准确地知道其影响范围。这种逆向信息提取的能力也是可逆计算理论反复强调的。
- 之所以可以对基础模型应用Delta修改,是为了让我们能够精确定义变化发生的位置,比如实体模型字段集合中名为phone3的字段。这个位置定义必须有明确的业务含义,并且有唯一的路径定义,而MyEntity模型文件中第10行到第20行的定位描述方法实际上是不稳定和不清晰的。 Nop平台的Delta平差解决方案精确使用了领域模型坐标系,API调用方式在函数调用链中深度隐藏了定位坐标的概念。在GPT生成中很有可能使用一些临时定位方法,但却错过了域坐标系中最有效的直接定位方法。
事实上,基于可逆计算理论的指导,主动从元模型、可逆性和差分量化的角度重新审视编程中的具体实际解决方案,我们可以得到很多新的东西获得洞察力并找到进一步改进的迹象。
从可逆计算的角度看提示
一些硬核的提示设计可以很自然地从可逆计算理论的角度来解释,比如HuggingGPT中的TaskPlan:
The AI assistant performs task parsing on user input, generating a list
of tasks with the following format:
[{"task": task, "id", task_id, "dep": dependency_task_ids,
"args": {"text": text, "image": URL, "audio": URL, "video": URL}}].
这个提示词的形式很接近XDef 元模型定义。HuggingGPT 的工作方式是让 GPT 返回满足元模型要求的 DSL 指令。
微软提出的指导项目采用了如下格式的提示:
role_simulator = guidance('''
{{#system~}}
You are a helpful assistant
{{~/system}}
{{#user~}}
You will answer the user as {{role}} in the following conversation. At every step, I will provide you with the user input, as well as a comment reminding you of your instructions. Never talk about the fact that you are an AI, even if the user asks you. Always answer as {{role}}.
{{#if first_question}}You can also start the conversation.{{/if}}
{{~/user}}
{{~! The assistant either starts the conversation or not, depending on if this is the first or second agent }}
{{#assistant~}}
Ok, I will follow these instructions.
{{#if first_question}}Let me start the conversation now:
{{role}}: {{first_question}}{{/if}}
{{~/assistant}}
{{~! Then the conversation unrolls }}
{{~#geneach 'conversation' stop=False}}
{{#user~}}
User: {{set 'this.input' (await 'input')}}
Comment: Remember, answer as a {{role}}. Start your utterance with {{role}}:
{{~/user}}
如果引入标准化、系统化的树表示方法,显然可以说机器会省心,我们的也是如此。
NopPlatform,一个基于可逆计算理论设计的低代码平台,已开源:
- gitee:canonical-entropy/nop-entropy
- github:entropy-cloud/nop-entropy
- 开发示例:文档/教程/tutorial.md
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。