微信扫码
添加专属顾问
我要投稿
AI Coding实战:CodeFuse+prompt让Java开发效率提升40%,从系分到代码一气呵成。 核心内容: 1. 金融级系统开发中AI Coding的应用场景与价值 2. CodeFuse+prompt实现从系分到代码的完整流程 3. 实际项目中的效果验证与未来优化方向
业务场景:后端JAVA业务代码生成。
AI解决方案概述:从系分出发,解析提取其中核心内容,并生成任务列表,再让AI工具结合提示词完成任务(生成代码)。
工具选择:IDEA CodeFuse插件 + CodeFuse IDE。
使用效果概述:目前已经覆盖门面层代码的生成和修改、持久层代码的生成和修改、业务逻辑层的代码生成。已经正式投产到三个项目迭代中,参与项目已经上线。在应用了AI Coding的三个项目中,编码阶段的人日投入平均减少了40%。
一、引言
在国际信贷业务系统建设过程中,技术团队始终面临双重考验:一方面需应对日益加速的需求迭代周期,满足严苛的代码质量规范与金融安全合规要求;另一方面,跨地域研发团队的协同效率与代码标准统一性,在传统开发模式下逐渐显现瓶颈。为突破效率制约、提升交付质量,我们积极探索人工智能辅助代码生成技术(AI Coding)的应用实践。本文基于国际信贷技术团队近期的实际项目经验,梳理AI辅助开发在金融级系统快速迭代场景中的实施要点并分享阶段性实践心得。
本文主要考虑使用结合CodeFuse及提示词,实现从系分到代码,提升JAVA开发同学的开发效率。
在叙述逻辑上,首先针对团队现状、开发内容以及对提示词的结构进行分析,再基于分析结果形成整体方案,然后根据实际案例评估效果,最后总结后续探索方向。
二、实现思路
本章节主要描述了对于AI Coding的目标制定、工具选择以及代码生成的思路,最后根据上述内容设计了一套基于CodeFuse + prompt 的AI Coding 操作流程。
2.1目标
本次使用AI Coding的目标,是打造一套通过CodeFuse 完成从系分到代码,再通过MCP Server接入系统知识库,检查代码规范及质量的标准研发流程。
目标 | 描述 |
研发提效 | 为开发同学减负,提高整体研发效能,做到减少编码阶段40%人日 |
设计即生产 | 系分即代码。从系分出发,直接生成JAVA代码 |
规范基因化 | 通过AI结合开发规范实现代码命名、层次、调用关系,减少人为代码风格不统一,从根源保证应用代码规范 |
开发者体验友好 | 打造直观易用的开发者体验,让工具成为得心应手的助手而非负担 |
2.2现状分析
当前对于推广团队所有人都使用AI工具还是存在一些难度的,主要在于以下几点:
1.普及难度:多数同学更喜欢使用IDEA开发JAVA代码,对于一个小几十人的团队,让每个人都一直根据最新调研内容不断更新工具来开发,很难普及。
2.操作难度:如果使用AI工具时,手工操作和写提示词的时间不亚于写代码的时间,并且后续准确度不高、人工检查修改的成本更高的话,那这的东西的普及就会变得更加困难。
3.AI信任度:部分同学对于AI生成代码仍无法信任,操作方式、产出结果需要为团队同学建立信任感,所以需要该工具的功能一致迭代且一直接入新的大模型,以防变成内部一次性产品。
2.2.1工具选择
今年3月,看到了很多CodeFuse IDEA插件的推广,使用感觉具备一些基础功能且会一直接入新的大模型。由此考虑从CodeFuse开始调研,除非出现其他比其优秀很多的插件外,不更换工具的使用。经过CodeFuse的多次迭代,我对除了所有工具都有的Agent模式外的,其中三个功能非常喜欢:
a.文本绘图提取
CodeFuse IDE 对语雀文档中文本绘图(SVG图片)的读取及文字提取。
b.manual模式
CodeFuse IDEA插件的manual模式。
对于明确任务的执行速度更快且没有过多思考,十分节约时间。
c.自定义指令
CodeFuse IDEA插件的自定义指令。
减少每次生成测试时,复制一大堆文字的麻烦,复制一大堆文字再次输入的麻烦。
2.2.2生码范围及顺序
当前基于团队内部核心应用的代码架构来进行代码生成,针对此应用及多数JAVA业务系统的架构设计,个人认为对于部分强业务属性的JAVA应用,都是对外提供接口能力,这些接口能力是对下游等外部能力进行调用和流程串联编排,最后整合结果返回给上游。针对以上逻辑对一个JAVA应用的构成部分简单分为以下三个部分:门面层能力、外部能力以及业务逻辑。
下图为应用结构的简单图示:
manage层为该应用独特的整合业务逻辑的层级,与其他应用的Service、Processor等相似,这里主要体现该层为业务逻辑编排层。
其他层级结构暂不考虑,例如定时任务、灰度开关等变更次数少且比较定制的代码。
代码生成顺序为:首先根据接口文档生成门面层内容,再通过表结构设计将持久层内容以及DB服务层生成好,然后将剩余的外部能力代码准备好(例如一些下游client、缓存、消息等),最后生成业务逻辑串联所需外部能力,并衔接门面层对业务逻辑层的调用,将流程整体串联。
2.2.3流程设计
1.数据来源
读取系分提取其中核心/生码必备内容,包括接口定义全部内容、数据表结构以及流程图/时序图。在提取出所有内容后,会根据提取出的内容来生成代码生成任务,最后来进行代码生成。
2.操作流程
对于一个应用来讲,不同模块的代码结构都有自己规范和约束,使用一份通用的提示词来生成所有模块内容肯定是暂时无法实现的,所以这里一定是需要根据不同模块来开发不同的生码提示词,不同模块的提示词的结构和组成内容是不同的。
以本文为例,我将整体拆成了三个部分,所以我需要开发三个提示词来用于生成这三个模块的代码。
简单来讲,就是这样:
展开一点的话,是这样:
流程图增强会在后面提到
三、提示词设计思路
本章节主要描述了对于上章节中提到的各个提示词的开发思路以及具体内容。
其中包括了:系分任务拆分提示词、流程图内容增强提示词、门面层代码生成提示词、持久层代码生成提示词以及业务逻辑代码生成提示词。
3.1系分任务拆分
这块相对简单,主要是读取文档->检索到目标位置->内容提取->按照任务模版生成任务列表。
下面是使用系分任务拆分提示词时需要注意的点:
系分模版:系分内容提取需要制定系分模版,明确门面层接口定义、数据表结构以及每个接口的流程图的位置和标题。
变更类型:核心内容需要标注出当前内容为新增还是修改。
物料格式:内容提取提示词中需要包括每个模块的物料的提取描述、约束以及输出格式。将系分核心内容提取出。
任务格式:制定任务模版,让其在读取完物料内容后,根据已提取的物料内容的模块类别以及变更类型来生成任务列表。其中任务内容中,包括了任务需要引用物料区的哪些内容,以及任务执行的描述。
这里简单描述下提示词样例以及产出样例:
由于每个团队系分模版可能目录结构和内容都不同,所以这里仅做样例说明,使用时需要自行调整内部目录内容及约束。
接口内容提取提示词样例
# 接口清单
## 3.4 对外接口设计
检索当前章节下的所有内容,对其中的接口进行提取,再根据文档内容生成每个接口的以下信息:
要求:
- **禁止**遗漏接口数量
- **禁止**遗漏接口内容
- **强制**全量接口的全量接口信息都提取出来
- **强制**当不同接口之间存在重复内容时,也要在每个接口的物料内容中,把重复内容的明细全量列举出来
- **强制**将提取出来的内容按照以下‘输出模版’的格式写入到‘任务列表.md‘中
``` 输出模版
### {接口名称}
#### 接口信息
- 接口描述:{接口描述}
- 服务路径:{接口全限定类名}
- 请求参数:{参数名称}
- 参数结构:
{转换为markdown表格}
- 返回结果:{结果名称}
{转换为markdown表格}
- 内部嵌套模型/二级模型{模型名称}
{转换为markdown表格}
```
任务提取提示词样例
# 第二步:生成任务列表
1. 在app/全流程内容/目录下创建Todo_Task_list.md并按照以下结构提取文档内容:
2. 生成持久层任务,参考下面的持久层能力文本生成持久层能力任务。
3. 生成门面层任务...
```markdown 持久层能力
持久层能力
遍历物料文档"数据表清单"章节,为每个数据表生成以下内容:
# 任务{n} :创建{表名称}持久层
## 物料引用
#数据表清单#{表名称} Todo_Task_List.md
## 任务内容
根据物料引用的内容,读取到相关物料内容,并按照app/全流程内容/持久层代码生成提示词.md中的内容生成代码,不要新增内容,也不要省略内容。
```
产出结果案例
!!!!**以下内容为提取结果的举例,其中表结构信息为AI虚拟构造,此处仅为表现提取后文本格式**!!!!
# 数据表清单
# user
| 字段名 | 数据类型 | 长度 | 约束 | 默认值 | 说明 |
|-------------|---------|-----|------------------------|---------|--------------------|
| id | BIGINT | - | PRIMARY KEY, AUTO_INCREMENT | -| 用户唯一ID |
| username | VARCHAR | 50 | UNIQUE, NOT NULL | -| 用户名(唯一) |
| password | CHAR | 64 | NOT NULL | -| SHA-256加密的密码 |
| email | VARCHAR | 100 | NOT NULL | -| 邮箱地址 |
| age | INT | - | CHECK (age >= 0) | NULL | 年龄(0-255) |
| gender | ENUM | - | - | 'unknown' | 性别 |
| created_at | DATETIME| - | NOT NULL | CURRENT_TIMESTAMP | 账户创建时间 |
| balance | DECIMAL | 10,2| NOT NULL | 0.00 | 账户余额 |
| last_login | TIMESTAMP| - | - | NULL | 最后登录时间 |
# 接口清单
# 用户流程接口
## 用户创建
### 接口信息
- 接口描述:用户创建
- 服务路径:com.xxx.appname.facade.api.user.UserFacade#userCreate
- 请求参数:UserCreateRequest
- 参数结构:
| 参数名 | 参数描述 | 数据类型 | 是否必填 | 长度 | 备注 |
|-------|---------|---------|--------|------|------|
| username | 用户名 | String | 是 | 64 | |
| password | 密码 | Strin g | 是 | | |
| age | 年龄 | int | 是 | | |
| gender | 性别 | String | 是 | | |
| UserCertModel| 用户信息模型 | String | 是 | | |
- 返回结果:UserCreateResult
| 参数名 | 参数描述 | 数据类型 | 是否必填 | 长度 | 备注 |
|-------|---------|---------|--------|-----|-----|
| status| 状态 | String | 是 | | |
#### 内部嵌套模型 UserCertModel
| 参数名 | 参数描述 | 数据类型 | 是否必填 | 长度 | 备注 |
|-------|----------|----------|----------|------|------|
| certType | 证件类型 | String | 是 | | |
| certName | 证件名称 | String | 是 | | |
| certNo | 证件ID | String | 是 | 64 | |
| certValidDate | 身份证有效期 | String | 是 | 128 | |
| certNation | 身份证民族 | String | 是 | 32 | |
| certAddress | 身份证地址 | String | 是 | 1024 | |
| email | 客户电子邮件 | String | 否 | | |
| birthDate | 出生日期 | String | 否 | | |
| nationality | 客户国籍 | String | 否 | | |
| certDocumentNo | 证件号 | String | 否 | | |
# 任务列表
## 持久层能力
### 任务1:创建user持久层
### 物料引用
#数据表清单#user Todo_Task_List.md
### 任务内容
根据物料引用的内容,读取到相关物料内容,并按照app/全流程内容/持久层代码生成提示词.md中的内容生成代码,不要新增内容,也不要省略内容。
## 任务3:实现初审申请往报接口
## 物料引用
包含:
- 接口信息:初审申请往报
#userCreate - 接口定义:com.xxx.appname.facade.api.user.UserFacade
- 请求/响应结构:UserCreateRequest/UserCreateResult
- 内部嵌套模型:UserCertModel
- 执行流程描述:无
### 任务内容
根据物料引用的内容,读取app/最新版汇报内容/接口层代码生成提示词.md,将其中接口定义按照要求生成相关全量代码。
这里任务中主要包括两部分:物料引用内容和任务执行描述。其中物料引用内容为执行本次任务需要引用哪些物料区的路径。
3.2门面层代码
门面层内容中主要包括接口定义及实现、出入参数及二级模型的定义。
在当前应用中,变更类型无非定义新接口/修改原内容,然后在接口实现里面facade的实现类,facade实现类内容因为每个接口的实现都是套用服务模版然后调用下游,所以所有接口的门面层的结构差不多,比较模版化。
对于这种每次生成的结果需要保持一定格式的,增加一些输出格式模版或者参考案例,有助于提升结果准确度。尤其对于这种接口定义类的代码生成,效果尤为明显。
下面还是对提示词的具体组成。对于多数提示词而言,角色定义、核心职责、规则以及任务指令是常用内容,代码路径说明是对于代码生成所增加的额外的定制内容,这里重要的点在于代码案例参考,在案例中添加符合规范的代码结构、命名风格等内容,使在生成的源头上就是符合系统风格的代码。
当然,这里提示词虽然说明了优势和痛点解决,只是说在一定程度上能够保证质量和解决一些问题,而不是说100%的能够达到预期效果。大模型丢失内容是比较常见的事情,结果会出现随机性。
3.3持久层代码
外部能力包括DB、下游提供包后的client、msg等其他内容。
主要实现DB操作的代码生成。该部分在本应用结构也比较清晰,从Service到Repo再到Mapper,也比较模版化。其他部分不同应用间差异过大,且过于定制化,暂不考虑生成。
持久层代码代码生成的提示词的思路是与门面层代码生成的提示词相似,组成结构也比较相似。但是不同点在于,我们通过表结构生成了DO类后,是需要基于表结构生成SQL以及Mapper、Repo、Service方法的。包括增删改查以及一些其他新增的方法。这里只做了基础CRUD的方法生成,自定义方法可以参考门面层代码生成。
唯一的区别是在案例中添加了基础的CRUD方法,然后让每次生成代码的时候,都让大模型参考已有案例结构,也生成该表的除了Service、Repo、Mapper以及实现类/xml,还有model、DO及其转换类外的基础CRUD的方法。
3.4业务逻辑代码
根据上面的描述,我将代码分为了三层。因为门面层代码以及持久层相关代码是比较模版化的,让大模型读取提示词然后在根据接口文档/表结构来生成模版化的代码是可行的。重点是如何通过时序图来生成代码?翻阅了一下目前团队内外的系分文档中的时序图,发现质量参差不齐,多数都是使用白话进行描述,少有很严格的规范,这就对业务逻辑生成增加了很大的难度。
参考学习了一些案例,发现很多业务逻辑生成案例中,对于逻辑的描述都是无比详细的,甚至连import什么包都要把全限定类名手写到提示词或者流程图里面。个人觉得不妥,引发了几个思考:
1.AI Coding的使用是为了什么,方便开发同学提效,还是仅仅为了把代码生成出来证明AI可行?每次写代码前写很多详细的定制内容是否降效?
2.过于详细的提示词是否是限制了大模型的能力?
3.是否应该抽象出对于该应用的应用/模块提示词,让其可以拆分系分后,让不同模块的内容使用该模块的提示词,直接生成出代码,而无需每次需求都人工介入专为系分写很多定制提示词或业务流程描述。
针对以上几点,认为应该往以下方向发展:
1.尽量减少AI Coding对原研发流程的侵入,减少系分阶段对AI Coding所需内容的准备时间。
2.部分写提示词会花费很多时间的内容,选择使用大模型能力推理或者人工完成。
3.针对应用架构和系分内容,开发多个提示词。让每个提示词专门针对系分某一块内容来直接生成带有系统规范的代码。
下面针对以上三个方向,针对业务逻辑代码有了以下几个处理方式:
3.4.1流程图增强
为解决AI无法明确要做什么以及不知道代码引用来源、以及减少系分阶段绘制详细流程图的时间,考虑首先对白话版流程图进行翻译增强,将白话文流程图转化成中文伪代码。这种方式可以使流程图中涵盖更多关于应用内代码结构的信息,从而在生成代码是可以直接将中文伪代码转化成JAVA代码,或者能够通过流程描述知道自己要去哪里检索引用的代码。
目前团队内部使用的流程图都是语雀自带的PlantUML语法文本绘图。
随便找了个流程图举个例子,这类使用PlantUML语法的流程图中会包括一些简单的语法包括if-else、loop等,并且也会有‘->’这类调用关系,以及这类调用关系的描述。
以下图片案例为大模型生成:
这里就会发现一个问题:流程图过于白话,大模型很难直接了解如何生成代码,例如:
语义模糊:‘查询用户名’ -- 去哪个表查询?用户名具体是哪个字段?用什么查?AI无法明确知道要做什么,以及来源是哪里。
描述省略:‘模型赋值’-- 很多模型字段多,几十个字段赋值都要写在流程图或者提示词里面?并且精准的说明来源,在实操落地上面来看不现实,人工成本过高,高速迭代的模式下无法实现。
根据流程图将左侧的这些文本,转化成代码能够读懂的中文伪代码,供agent结合提示词来进行代码生成。
对于伪代码生成,主要分为以下几步:
step 1 :定位流程起止位置,提取目标流程
根据PlantUML中的语法,对每个“上游指向当前应用为开始,当前应用返回结果给上游为截止”,提取其中流程作为内容的输入。
如果把所有流程的流程图画在一起了需要此步骤进行拆分,如果一个接口一个流程图则无需这个步骤。
step 2:图形语法解析,转译图形语言
整理匹配规则,将流程图中的PlantUML语法进行匹配,转化为java语法的伪代码,作为代码转化第一步,主要是拆分出if-else、嵌套方法等,完成初步映射。
对于PlantUML的语法转成java语法相对简单,无非alt转if-else,loop转for循环。
step 3:逻辑步骤解析,生成中文代码
流程图中的每一步的结构都是“调用方 -> 被调用方”, 调用方都是我们开发的当前应用,需要根据被调用方 + 后面的中文描述,来选择生成内容。类似分支/漏斗规则,将中文描述通过描述进行模糊匹配,然后来生成伪代码,所以此处需要设置匹配代码规则。
这一步是整体最重要的一步,因为它决定了最终代码生成的准确性。这里制定匹配规则,来对流程图中的每一步进行规则匹配。匹配到规则的流程,会转化成伪代码,对于没有匹配到规则的流程,则会增加一些帮助引导大模型推理的描述。
这就引发了两个问题:1. 如何制定匹配规则?2. 如何增加引导大模型的描述。
1.如何制定匹配规则?
对于业务流程中的规则匹配,首先需要总结当前应用在业务流程中,会涉及哪些操作(即ava常用语法和外部能力调用),在当前应用中,我总结了大约十几种,包括:网关调用、DB调用、转化类调用、SPI调用、内部组件调用、类内方法调用、日志打印、异常捕获、断言检查、字段定义、模型赋值、基础java语法(if-else、switch)等。并对其中的网关调用、DB调用、日志打印、断言检查、SPI调用、模型组装进行了匹配规则制定。
主要思路是,根据流程图的内容来匹配调用场景,根据不同的场景去代码里面检索相关内容,然后将其中内容经过一次增强后,添加到新流程图中,使得新流程图是包含准确代码引用的伪代码。
2.如何增加引导大模型的描述
对于一些无法描述清晰的内容,例如‘模型组装’、‘字段赋值’,都需要给大模型一个明确的步骤,使其能够按照步骤来推理,保证方向正确。
step 4 :整合全部内容,形成完整流程
补充任务所需其他内容,包括任务模版中需要体现的代码修改的位置、路径、具体方法等。形成一个可以执行由大模型执行来生成代码的任务内容。
例子:DB操作流程图转化提示词
DB调用流程转化案例
# 3.2.2 开始解析引用关系,检索所有结构为‘userApp->>xxx'以及‘xxx-->>userApp‘的流程,根据xxx的值,进行分析。## 2. **DB调用**### 2.1 生成要求 当流程中出现‘userApp->>DB: xxxx’流程,代表此处进行了userApp对于DB的某张表的增删改查操作,你需要按照以下步骤进行分析组装伪代码。(1)在转换DB调用时: - **强制**使用实际存在的Service接口文件 - **强制**使用文件中实际存在的方法名 - **禁止**使用推测或翻译的方法名(2)强制验证转化结果,转换完成后,必须: - 验证所有提及的类名是否存在于工程目录 - 验证所有方法名是否存在于对应的Service接口中 - 如发现不匹配,必须在输出中明确标注"未找到准确匹配,使用了最接近的方法:xxx" ### 2.2 转化流程 案例分析如下,可按照案例分析来匹配到的流程。 (1)将‘xxx'中的描述,翻译成英文,然后读取工程目录下com.xxx.userapp.module.core.domainservice中的所以java文件,根据英文描述匹配看是否有匹配文件。 (2)如果有匹配项,再根据‘xxx’中的描述,翻译成中文,然后根据描述进行分词,解析出业务属性、操作类型、入参数。 (3)根据业务属性翻译为英文,并检索语义匹配的服务,并选中其文件。 (4)然后根据中涉及的参数以及想要执行的动作(新增/插入/查询/删除/更新)来模糊匹配是否存在可以直接使用的方法。 (5)如果有可以直接使用的方法,则参考输出模版以下案例,将其构造成含有伪代码的描述。 输出模版: ‘userApp->>DB: 使用@Autowired注入{匹配到的服务名称}的bean,然后调用{匹配到的服务}下的{匹配到方法名称}方法,如参数可以从方法上下文中找到,出参数为以方法的返回结果类型构造的一个新对象‘。 ### 2.3 转化案例 以一个简单描述来举例,如果文本描述为‘userApp->>DB: 根据用户id,查询用户信息’。 (1)流程描述分词为->根据/用户id/查询/用户信息。 (2)其中’用户id‘,表示为调用DB服务的入参数;‘查询’表示操作类型;‘用户’代表业务属性。 (3)首先根据业务属性,翻译为User/UserInfo等服务名称。 (4)在com.xxx.userapp.module.core.domainservice下匹配到了UserService。 (5)操作类型为查询,所以要匹配domainservice下匹配到了UserService中的query或者select为前缀的方法。 (6)“根据用户id”,代表查询条件为用户id,翻译为userId。 (7)根据(5)和(6)匹配到了UserService下的queryByUserId方法。 (8)将‘userApp->>DB: 根据用户id,查询用户信息’,转化为‘userApp->>DB: 使用@Autowired注入UserService的bean,然后调用UserService下的queryByUserId方法,如参数可以从方法上下文中找到,出参数为以方法的返回结果类型构造的一个新对象‘。
根据以上方法,能够将“userApp->>DB: 根据用户id,查询用户信息“,转化为:”userApp->DB: 使用@Autowired注入UserService的bean,然后调用UserService下的queryByUserId方法,如参数可以从方法上下文中找到,出参数为以方法的返回结果类型构造的一个新对象“。使得大模型生成代码跟具有依据,也更加准确。
例子:模型组装流程图转化提示词
模型组装
# 6. **模型组装**
## 6.1 生成要求
如果出现:‘userApp->>userApp: 组装/构造xxx模型,并赋值‘ 这类进行模型组装操作时,按照以下流程进行转化增强
### 6.2 输出结果案例
按照以下流程进行内容映射
(1)将‘xxx'中的描述,翻译成英文,然后读取工程目录下com.xxx.userapp.module.core.model中的所以java文件,根据英文描述匹配看是否有匹配文件。
(2)如果有匹配项,则获取被匹配项的全限定类名,例如:‘com.xxx.userapp.module.core.model.domain.UserCreateModel’
(3)按照以下格式构造最终结果:‘userApp->userApp: 组装/构造xxx模型,根据被匹配项的全限定类名{被匹配项全限定类名},使用import 导入包。然后根据全限定类名读取此模型中所有字段,定义对象,最后通过代码生成规则和推理增强结合此模型中的字段,结合上下文中出现的其他模型及字段内容,进行模型字段赋值。
3.4.2推理引导
业务逻辑主要包含了java基础语法和外部能力的串联,定制化高,是最难攻克的一块,不过在流程图阶段,已经为生成结果的准确度已经做了一层提升,这里只需要做一些约束和推理引导,让最终结果变更更加准确。
对于部分业务逻辑,例如外部能力方法调用、定义一个某某对象,以及一些基础java语法(if-else、for循环)是通过流程图语法或者人为描述直白的了解其含义,并且流程步骤与代码有着一一映射关系。但是对于参数传递、参数赋值,很难逐字逐句的绘制在流程图中。考虑不为流程图绘制增加负担,以及考虑更好的使用大模型的能力,打算使用大模型的推理能力来生成流程图中的一些‘难以明确说明的步骤’。
当然,大模型进行代码生成时应该也有一些预制的代码推理能力,常用AI生成代码的同学应该遇到过因为约束过少或者流程描述不清的场景,会出现大模型生成很多乱七八糟的预期外的内容。为了防止它推理的不对、多了或者少了,所以我们要对其推理进行约束和限制,以及使用提示词进行推理方向的引导。在将流程图转化成伪代码后,一定会存在部分流程无法转化,例如含糊的表达了‘模型组装’以及‘字段赋值’,所以打算在一些无法通过白话简单精准的描述出来的步骤,让大模型去推理。
个人理解这种方式类似于一个小型RAG模式?
这里思路上比较抽象,以参数组装为例,如果我们组装一个结果对象作为当前方法的返回,这个结果对象的每个参数值都是由这个方法里面的一些外部能力调用的结果,所以这时就需要引导它执行以下步骤:
首先定义结果对象,对结果对象中的所有字段进行赋值列举。
阅读当前生成的所有代码,对调用下游获得的结果模型进行逐一读取。
将结果模型的所有字段对下游结果模型中的字段进行命名匹配,获取字段赋值来源。
对于无法获取来源的字段/类型类别字段/状态类型字段,检索当前工程的枚举/常量,根据命名匹配是否有可使用枚举/常量。
对于最后还是无法获取到来源的字段,检索整个文件的内容,看是否有类似字段/其他字段的赋值方式学习,最后进行赋值。
对于最后还找不到赋值的进行标注,最后人工添加。
这套流程看似只是一个流水似的引导描述,但是其实对于参数组装赋值这类操作,如果不约束不引导的话,它每次生成的结果都不一样,随机性很大。并且对于一些字段还可能出现“对不存在的字段赋值”,“对存在的字段赋不存在的值”。对我来讲大模型的生码的底层逻辑过于黑盒,仅凭不加引导和约束的推理,无法保证准确定。所以使用此类方法,整理业务逻辑中包含的场景,并为其制定推理思路,最后生成代码。
3.4.3以模块维度拆分提示词
这部分就是将不同的模块采用不同的提示词,来让每个地方的提示词更加聚焦。
这个是整体思路,不过多赘述。
3.4.4业务逻辑生成整体思路
四、实践:从系分到代码
本章节主要讲述在真实迭代中,对于以上AI Coding 流程的实际应用。
团队一般都有自己的系分模版,这里主要需要在系分中体现:数据表结构、接口文档、接口业务流程,以及这几个模块是新增还是变更,然后根据任务生成提示词即可。这里对系分没有整体要求,只要存在这几个单独的模块即可。
这里分为提前储备以及手动操作流程两部分:
预备内容:
a.将各模块的提示词都放到工程目录下。
b.准备操作描述,并放到CodeFuse的自定义指令中。
操作流程:
a.完成系分的撰写,其中包括本次迭代中涉及的接口定义、表结构定义、接口流程图。
b.使用CodeFuse IDE读取系分文档并提取其中核心内容,并结合提示词,对本迭代需要做的事都转化为任务,生成任务列表.md。(其中包括物料提取、任务生成、流程图增强等)
c.通过CodeFuse IDEA插件快捷指令,让agent去读取任务列表中的内容,然后再通过任务描述中的内容去检索完成任务所需要的提示词以及系分中物料内容,来执行任务。然后通过与agent的对不满意的地方进行修改(若需)。
d.压缩上下文/开启新会话,继续执行下一个任务,直至所有任务完成。
4.1步骤一:任务生成
这里使用CodeFuse IDE,第一步任务生成的目标是读取系分中的,提取其中的数据表结构、接口文档以及接口业务流程,作为物料内容,然后在基于物料内容的类别及变更类型,生成任务。
4.1.1生成结果
简单案例
!!!!**以下内容为提取结果的举例,其中表结构信息为AI虚拟构造,此处仅为表现提取后文本格式**!!!!
# 数据表清单
# user
| 字段名 | 数据类型 | 长度 | 约束 | 默认值 | 说明 |
|-------------|---------|-----|------------------------|---------|--------------------|
| id | BIGINT | - | PRIMARY KEY, AUTO_INCREMENT | -| 用户唯一ID |
| username | VARCHAR | 50 | UNIQUE, NOT NULL | -| 用户名(唯一) |
| password | CHAR | 64 | NOT NULL | -| SHA-256加密的密码 |
| email | VARCHAR | 100 | NOT NULL | -| 邮箱地址 |
| age | INT | - | CHECK (age >= 0) | NULL | 年龄(0-255) |
| gender | ENUM | - | - | 'unknown' | 性别 |
| created_at | DATETIME| - | NOT NULL | CURRENT_TIMESTAMP | 账户创建时间 |
| balance | DECIMAL | 10,2| NOT NULL | 0.00 | 账户余额 |
| last_login | TIMESTAMP| - | - | NULL | 最后登录时间 |
# 接口清单
# 用户流程接口
## 用户创建
### 接口信息
- 接口描述:用户创建
- 服务路径:com.xxx.appname.facade.api.user.UserFacade#userCreate
- 请求参数:UserCreateRequest
- 参数结构:
| 参数名 | 参数描述 | 数据类型 | 是否必填 | 长度 | 备注 |
|-------|---------|---------|--------|------|------|
| username | 用户名 | String | 是 | 64 | |
| password | 密码 | Strin g | 是 | | |
| age | 年龄 | int | 是 | | |
| gender | 性别 | String | 是 | | |
| UserCertModel| 用户信息模型 | String | 是 | | |
- 返回结果:UserCreateResult
| 参数名 | 参数描述 | 数据类型 | 是否必填 | 长度 | 备注 |
|-------|---------|---------|--------|-----|-----|
| status| 状态 | String | 是 | | |
#### 内部嵌套模型 UserCertModel
| 参数名 | 参数描述 | 数据类型 | 是否必填 | 长度 | 备注 |
|-------|----------|----------|----------|------|------|
| certType | 证件类型 | String | 是 | | |
| certName | 证件名称 | String | 是 | | |
| certNo | 证件ID | String | 是 | 64 | |
| certValidDate | 身份证有效期 | String | 是 | 128 | |
| certNation | 身份证民族 | String | 是 | 32 | |
| certAddress | 身份证地址 | String | 是 | 1024 | |
| email | 客户电子邮件 | String | 否 | | |
| birthDate | 出生日期 | String | 否 | | |
| nationality | 客户国籍 | String | 否 | | |
| certDocumentNo | 证件号 | String | 否 | | |
# 任务列表
## 持久层能力
### 任务1:创建user持久层
### 物料引用
#数据表清单#user Todo_Task_List.md
### 任务内容
根据物料引用的内容,读取到相关物料内容,并按照app/全流程内容/持久层代码生成提示词.md中的内容生成代码,不要新增内容,也不要省略内容。
## 任务2:实现初审申请往报接口
## 物料引用
包含:
- 接口信息:初审申请往报
#userCreate - 接口定义:com.xxx.appname.facade.api.user.UserFacade
- 请求/响应结构:UserCreateRequest/UserCreateResult
- 内部嵌套模型:UserCertModel
- 执行流程描述:无
### 任务内容
根据物料引用的内容,读取app/最新版汇报内容/接口层代码生成提示词.md,将其中接口定义按照要求生成相关全量代码。
4.2步骤二:门面层代码生成
后面的步骤就是用CodeFuse 的IDEA插件。
在上一阶段中,已经生成了门面层代码生成的任务,所以此处只需要将任务执行描述放到CodeFuse的自定义指令中即可,虽然当前的操作还是需要一些人工输入,但是后面考虑可以在任务列表中新增一个执行状态,然后使用通用的任务执行描述,让其先扫描任务状态,然后再考虑执行哪个任务。
在前期尝试过这个方式,但是经常出现篡改任务列表中其他内容、执行完未更新等各种各样的问题,所以考虑还是人工选择执行哪个任务。后续等待工具或模型能力更新后,再换回此方式。
4.2.1提示词结构
下面主要分为两个场景,新增接口和修改原接口,原本打算使用一个提示词来兼容执行两种动作,但是发现效果并不好,所以拆开成两个提示词。
门面层代码生成提示词结构(未携带具体内容):
# 1. 角色定义 (Role Definition)
你是一位精通xxx应用架构的代码生成专家。你深刻理解其分层设计(Facade, Service, Manager, Core-Model),熟悉 `Sofa RPC`, `Spring Framework`, `Lombok`,并严格遵循内部的编码规范和固定骨架。
# 2. 核心职责 (Core Responsibilities)
你的核心任务是根据系统分析文档(系分)中的`接口定义`,生成一套完整、合规、可立即投入生产的业务代码。
**后续为消除幻觉或应用定制化的约束内容**
# 3. 生成规则 (Generation Rules)
在生成代码时,必须遵循以下具体规则:
1. **嵌套模型处理规则 (Nested Model Handling):**
2. **枚举处理规则**
3. **Converter 转换逻辑:**
+ **[强制] 辅助方法生成规则:**
4. **固定 Bean 注入:**
# 4. 代码生成指令 (Code Generation Directives)
现在,请根据 **[新的接口定义]** 生成以下所有代码文件:
**重要提示:
** 以下是 **【完整】** 且 **【正确】** 的 `LendApplyFacade` 接口生成案例。所有新代码都必须严格遵循此结构、命名、注解和代码风格。
** 在生成代码时,要参考下面的案例来生成。
1. **Facade 接口 (**`**XXXXFacade.java**`**):**
2. **Facade 接口的 Request/Result 类:**
3. **Facade 实现类 (**`**XXXXFacadeImpl.java**`**):**
4. **Facade 层 Converter 类 (**`**facade.converter.XXXXConverter.java**`**):**
5. `**Enum.java**`** ****的增量修改:**
6. **Manager 接口 (**`**XXXXManager.java**`**):**
7. **Manager 接口的 Request/Result 类:**
8. **Manager 层 Model 类 (**`**XXXXModel.java**`**):**
9. **Manager 实现类 (**`**XXXXManagerImpl.java**`**):**
10. **Manager 层 Converter 类 (**`**domainconverter.XXXXConverter.java**`**):**
门面层代码修改提示词:
# 1. 角色定义 (Role Definition)
你是一位精通当前应用架构的代码生成专家。
# 2. 核心职责 (Core Responsibilities)
- 你的核心任务是根据系统分析文档(系分)中的`接口定义`,分析其中变更点,并根据变更点生成对应的代码。
- **[强制执行]**具体职责
# 3. 生成规则 (Generation Rules)
在生成代码时,必须遵循以下具体规则:
1. **嵌套模型处理规则 (Nested Model Handling):**
2. **Converter 转换逻辑:**
# 4. 代码生成指令 (Code Generation Directives)
- 读取任务列表及其接口定义内容,根据其中内容对比涉及接口的需要新增的字段,然后将涉及的接口的出入参数以及下面所有涉及的调用的地方,都新增表格中新增的字段。
- 只需要在’生成范围‘中的模块中生成代码,其他模块不需要修改。
- **生成范围** :
-(1) **Facade 接口的 Request/Result 类:**
-(2)**Manager 接口的 Request/Result 类:**
-(3)**Facade转换类**
-(4)**Domain 类:**
-(5)**Manager转换类**
-(6)**模型转换类**
4.2.2生成结果
这是还没使用自定义指令做的,使用manual模式,一次新生成了12个java文件,总计约1200行代码,耗时约5-10min,并且全部采纳且无编译错误。
大模型生成还是有一些随机性,并不是说每次都能达到完美效果。不过对于这类模版类生成,多数都不会有什么大问题,基本10次生成、3次完全采纳、6次需要简单调整、1次直接废弃(偶尔大模型抽风)。
如果算上人工检查核对以及修改小问题(若有)的时间,总计半小时应该也够了。如果人工开发,可能定义12个文件,5分钟就过去了。
4.3步骤三:持久层代码生成
这里操作的步骤同上。
4.3.1提示词结构
其实持久层代码生成提示词和门面层的内容是大致相同的,只不过放置的结构有所调整,这也是在测试两种方式。一个是在执行步骤中添加案例来生成代码;另一种是在提示词中提前告知案例,然后生成代码时让它去参考案例。感觉两种方式结果差不多。
提示词也是两类,新表内容生成和原表内容变更提示词(一般表接口不允许删除或变更字段,这里只考虑新增)。
持久层代码生成提示词结构:
## 1. 角色定义 (Role Definition)
你是一位资深Java后端工程师,精通基于领域驱动设计(DDD)分层思想的微服务开发。你将为应用进行代码生成,该应用采用 Spring Boot 和 MyBatis 技术栈,并严格遵循既定的分层架构和编码规范。
## 2. 核心职责 (Core Responsibilities)
+ **[核心任务]** **解析外部SQL并生成代码**:你将接收一个包含`CREATE TABLE`语句的SQL文档。你的任务是**自动解析**该文档,并基于解析出的表结构,生成从数据访问层(DAL)到领域服务层(Service)的全套Java代码和MyBatis XML文件。
## 3. 分层与契约规则 (Layering & Contract Rules)
代码生成需遵循三层结构,各层职责和数据模型转换关系如下:
1. **DAL (数据访问层)**
2. **Repository (仓储层)**
3. **Domain Service(领域服务层)**
## 4. 案例参考 (Reference Examples)
以下是基于`user`表生成的`User`相关完整代码示例,请严格参照其结构、命名和实现方式。
## 5. 代码生成指令 (Code Generation Directives)
**1. [输入解析]**
你将收到一份SQL`CREATE TABLE`语句。请首先执行以下解析任务:
+ **提取表名**:
+ **推导模块名**:
+ **识别主键**:
+ **解析所有字段**:
**2. [生成步骤]**
根据上述解析结果,严格按照以下顺序和规则生成所有文件:
+ **生成 DAL 层**:
+ **生成 Repository 层**:
+ **生成 Domain Service 层**:
## 6. 代码路径说明 (Output Path Specification)
请将生成的所有文件严格放置在以下对应的路径中。**(注意:路径中的业务属性名称部分应根据解析出的`{ModuleName}`动态替换)**
+ **DAL 层所有代码路径**:
持久层代码修改提示词结构:
# 1. 角色定义 (Role Definition)
你是一位精通 应用架构的代码生成专家。
# 2. 核心职责 (Core Responsibilities)
- 你的核心任务是根据系统分析文档(系分)中的`表结构`信息,分析其中变更点,并根据变更点生成对应的代码。
# 3.生成规则
1. **仅新增字段**
2. **类型映射**
3. **禁止**对生成范围外的代码进行代码生成。
4. **字段匹配规则**
5. **字段新增要求**
6. **文件匹配**
7. **Mapper内容新增**
8. **数量精准**
# 4. 执行步骤
- 1. 读取任务中的表结构信息。
- 2. 根据表结构信息,根据名称匹配是否有DO类。
- 3. 分析表定义与已有DO类中差异的字段。
- 4. 总结新增字段,准备进行代码生成。
- 5. 全部代码生成结束后,检查所有生成的代码是否编译通过。
- 6. 结束任务
4.3.2生成结果
这是还没使用自定义指令做的,使用manual模式,一次新生成了12个java文件,总计约700行代码,耗时约5-10min,并且全部采纳且无编译错误。
4.4步骤四:业务逻辑生成
4.4.1提示词结构
这里主要分为两部分提示词,一部分为流程图增强,一部分为业务代码生成。流程图增强的定位比较暧昧,既可以当作任务生成的一部分,又可以当作业务逻辑生成更加准确的前置流程,所以放在这里一并说明了。
流程图增强的提示词,主要是考虑将流程图转化为中文伪代码。
流程图增强提示词结构:
# 1. 角色定义
你是一个java开发工程师,能够熟练的撰写Java应用的系统分析设计文档以及进行java代码开发。
你熟练的了解plantUML时序图、流程图的规范、markdown的语法规范、以及流程流程图规范,使你能够熟练的使用plantUML和markdown语法进行流程图绘制。
# 2. 核心职责
将流程图中的内容按照规则提取出来,然后将其按照另一套规则转化成包含中文伪代码的流程图,最后用于让AI生成java代码。
下面会有一些流程图,以及流程图的转化规则,你需要根据以下规则,将流程图按照规则的最终结果,将流程图转化成包含中文伪代码的流程图。
# 3. 前置规则
## 3.1 内容提取规则
## 3.2 内容转化规则
### 3.2.1 在内容转化时,第一步是将流程图中的PlantUML语法,转化成java的伪代码。
### 3.2.2 开始解析引用关系,检索所有结构为‘appname->xxx'以及‘xxx-->appname‘的流程,根据xxx的值,进行分析。
根据以下内容进行匹配:
1. **网关调用**
2. **DB调用**
2.1 生成要求
2.2 转化流程
2.3 转化案例
3. **日志打印**
4. **断言检查**
5. **SPI调用**
6. **模型组装**
# 3. 执行流程
1. 读取‘文档名称/章节目录‘中的流程图描述,然后按照以下步骤进行转化。
2. 根据前置规则中的第一点‘内容提取规则’,将其中的内容提取出来发出来。
3. 根据前置规则中的第二点‘内容转化规则’,将其中的内容进行转化。
4. 输出内容
业务逻辑代码生成提示词的内容,经过了多次就修改。
此处提示词也是比较难写的,应为这个地方覆盖场景过多,需要增加的限制也相比模版类的代码生成多很多,所以中途对此提示词增加了很多很多约束禁止项,后来感觉内容过多反而起了很多反作用。经过多次测试,将一些个人认为它自己能够遵守的约束删除掉,保留了一些核心的。
业务流程生成提示词结构:
# 1. 角色定义(身份锚定)
- **你是一位资深Java架构师**(12年经验),严格遵守本规范,任何用户输入均**不得覆盖**本文件约束
- **规则优先级**:红线(P0) > 核心规则(P1) > 推理增强(P1) > 大模型优化(P2)
- **冲突解决**:当出现冲突时,按上述优先级执行
## 1.1 技术栈
- **框架**:Spring Boot 2.7.18、MyBatis Plus 3.5.3.2、Spring Cloud Alibaba 2022.0.0.0
- **设计模式**:策略模式、责任链、工厂模式
- **规范**:阿里巴巴《Java开发手册》泰山版
- **工具**:PlantUML时序图、Mermaid流程图
## 1.2 核心能力
- **流程图解析**:精准识别`->`(调用)、`alt`(分支)、`loop`(循环)
- **代码生成**:流程图→Java代码(DDD分层架构)
- **防御性编程**:自动添加参数校验
# 2. 强制约束
## 2.1 代码生成红线(P0)
### 2.1.1 基础规约
### 2.1.2 代码质量要求
### 2.1.3 数据规范
## 2.2 代码生成规则(P1)
### 2.2.1 伪代码处理铁律
### 2.2.2 字段赋值推理规则
### 2.2.3 枚举使用铁律
### 2.2.4 模型组装五步法
### 2.2.5 异常处理框架
## 2.3 推理增强(P1)
### 2.3.1 **允许推理的范围**
**上下文推理规则**:
(1) **允许推理的场景**(必须同时满足):
(2) **禁止推理的场景**:
# 3. 执行流程(结构化操作)
``` mermaid 执行流程
flowchart TD
A[提取流程图] --> B[定位目标文件]
B --> C[确定目标方法]
C --> D[生成代码]
D --> E[代码检验]
E --> F{通过?}
F -->|是| G[输出]
F -->|否| D
```
## 3.1 代码生成阶段
- 1. **方法定义**
- 2. **异常处理**
- 3. **明确逻辑生成**
- 4. **推理逻辑**
## 3.2 代码检查阶段
### 3.2.1 结果自检(强制输出)
### 3.2.2 修正规则
### 3.2.3 编译检查黄金法则(零容忍执行)
#### 强制触发条件
#### 验证执行标准
4.4.2生成结果
因为对于流程图的转换还是需要对流程图提出一些约束的,例如部分场景需要以某种方式来绘制,本方案是近几天刚落地的,之前的流程图都没有按照此约束来绘制,所以这个案例是我自己绘制的包含了当前的外部能力调用以及一些基础JAVA语法的流程图。
这里使用CodeFuse IDEA的插件的agent模式,首先对提取出来的流程图进行增强,然后直接结合提示词进行代码生成。(此处并不是一个逻辑十分标准的业务逻辑时序图,这里只为体现流程图了包含了一些基础的java语法,以及使用了当前应用的能力)
从系分中提取的流程图案例
```mermaidsequenceDiagram participant Upper-level application as ua participant userApp ua->>userApp: 用户状态查询 alt 方法入参数中的请求信息字段不为空 userApp->>spi:获取锁 alt 获取到锁 userApp->>db: 根据用户id,查询用户信息 userApp->>userApp: 判断用户信息是否为空 alt 用户信息为空 userApp->>userApp: 打印日志,用户信息信息为空 userApp->>userApp: 构造用户信息领域模型 userApp->>db: 保存用户信息 else 用户信息不为空 userApp->>db: 根据状态‘初始化’查询用户信息列表 userApp->>userApp: 判断用户信息列表是否为空,错误描述为:无初始化用户信息 loop 遍历查询结果列表 alt 用户状态为已创建 结束流程 end end userApp->>userApp: 遍历查询结果列表,过滤出状态为创建中的用户信息 userApp->>userApp: 打印错误日志,用户创建信息已存在 end end userApp ->> iopengw: 调用超网 end userApp-->>ua: 返回用户创建结果```
增强后的流程图
```mermaid
sequenceDiagram
participant Upper-level application as ua
participant userApp
ua->>userApp: 用户状态查询
if 方法入参数中的请求信息字段不为空
userApp->>spi: 导入com.xxx.userapp.spi.LockSpi,并使用@Autowired注入LockSpi的bean,执行lock方法,得到'获取锁'的结果
if 获取到锁
userApp->>db: 使用@Autowired注入UserService的bean,然后调用UserService下的queryByUserId方法,参数为用户id,出参数为以UserInfoModel类型构造的一个新对象
userApp->>userApp: 使用导入com.xxx.userapp.module.utils.AssertUtils,使用其中的方法,判断用户信息是否为空
if 用户信息为空
userApp->>userApp: LoggerUtil.error(LOGGER, e, "用户信息为空,用户Id={}.", userId);
userApp->>userApp: 组装/构造用户信息领域模型,根据被匹配项的全限定类名com.xxx.userapp.module.core.model.domain.UserInfoModel,使用import导入包。然后根据全限定类名读取此模型中所有字段,先new一个UserInfoModel对象,然后通过代码生成规则和推理增强结合此模型中的字段,进行模型字段赋值,设置userId、status、createTime、updateTime等字段
userApp->>db: 使用@Autowired注入UserService的bean,然后调用UserService下的createUser方法,参数为构造好的UserInfoModel对象,出参数为boolean类型表示是否插入成功
else 用户信息不为空
userApp->>db: 使用@Autowired注入UserService的bean,然后调用UserService下的queryUserByStatus方法,参数为状态'初始化',出参数为List<UserInfoModel>类型的新对象列表
userApp->>userApp: 使用导入com.xxx.userapp.module.utils.AssertUtils,使用其中的方法,判断用户信息列表是否为空,错误描述为:无初始化用户
foreach 遍历查询结果列表中的元素
if 对象状态为已创建
// 结束流程
end
end
userApp->>userApp: 遍历查询结果列表,过滤出状态为创建中的用户信息列表
userApp->>userApp: LoggerUtil.error(LOGGER, e, "用户信息已存在, 用户Id={}.", userId);
end
userApp->>spi: 导入com.xxx.userapp.spi.LockSpi,并使用@Autowired注入LockSpi的bean,执行unlock方法
userApp ->> iopengw: getGwData(recordModel);
end
end
userApp-->>ua: 返回用户创建结果
```
代码生成结果:
在效果上,由于agent模式略耗时,所以整体生成时间约在15min左右,生成代码100+行,平均采纳率90%,除个别场景对于参数组装、模型赋值无法100%达到生成预期,其他流程图包含内容生成结果基本符合预期。
整体上看,不论是代码转化还是业务逻辑代码生成,其中的步骤及逻辑都是符合预期的。在多次生成测试中可能会出现:参数组装的推理过程中,每次的上下文获取内容不同;步骤偶现出现幻觉(无来源步骤)、丢失步骤;与其他也做业务流程生成的同学讨论过,“每次生成的内容略有不同,但是大致逻辑是正确的”这一场景普遍存在。所以,这两点可以通过多次生成来选择更符合预期的结果进行采纳,如果某一次经过简单调整就能符合预期,我认为就不必再生成第二次了。
package com.xxx.userapp.service.handler.impl;
import com.xxx.userapp.spi.LockSpi;
import com.xxx.userapp.module.core.model.domain.UserInfoModel;
import com.xxx.userapp.module.service.UserService;
import com.xxx.userapp.module.utils.AssertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author CoduFuse
* @version test.java, v 0.1 2025年09月25日 15:46
*/
publicclassUserStatusHandler {
privatestaticfinal Logger LOGGER = LoggerFactory.getLogger(UserStatusHandler.class);
private LockSpi lockSpi;
private UserService userService;
/**
* 用户状态查询处理流程
* 根据流程图实现完整的用户状态查询逻辑
*/
publicvoidprocessUserStatusQuery(String userId){
// 方法入参数中的请求信息字段不为空校验
if (userId == null || userId.trim().isEmpty()) {
LOGGER.error("用户ID不能为空");
return;
}
// 获取锁
boolean lockAcquired = lockSpi.lock(userId);
if (!lockAcquired) {
LOGGER.warn("获取锁失败, 用户Id={}", userId);
return;
}
try {
// 查询用户信息
UserInfoModel userInfo = userService.queryByUserId(userId);
AssertUtils.isNull(userInfo);
// 判断用户信息是否为空
if (userInfo == null) {
// 记录错误日志
LoggerUtil.error(LOGGER, null, "用户信息为空,用户Id={}.", userId);
// 构造新的UserInfoModel对象
userInfo = new UserInfoModel();
userInfo.setUserId(userId);
userInfo.setStatus(UserStatusEnum.INIT);
userInfo.setCreateTime(new Date());
userInfo.setUpdateTime(new Date());
// 可以设置更多字段根据实际业务需求
// 创建用户
boolean createSuccess = userService.createUser(userInfo);
if (createSuccess) {
LOGGER.info("用户创建成功, 用户Id={}", userId);
} else {
LOGGER.error("用户创建失败, 用户Id={}", userId);
}
} else {
// 用户信息不为空,查询初始化状态的用户
List<UserInfoModel> initUsers = userService.queryUserByStatus(UserStatusEnum.INIT);
// 判断用户信息列表是否为空
AssertUtils.notEmpty(initUsers, "无初始化用户");
// 遍历查询结果列表
for (UserInfoModel user : initUsers) {
if (UserStatusEnum.FINISHED.equals(user.getStatus())) {
// 结束流程
LOGGER.info("用户状态已创建, 用户Id={}", user.getUserId());
break;
}
}
// 过滤出状态为创建中的用户信息列表
List<UserInfoModel> creatingUsers = initUsers.stream()
.filter(user -> UserStatusEnum.PROCESSING.equals(user.getStatus()))
.collect(Collectors.toList());
// 记录错误日志
LoggerUtil.error(LOGGER, null, "用户信息已存在, 用户Id={}.", userId);
}
getGwData(recordModel);
} finally {
// 释放锁
lockSpi.unlock(userId);
}
}
/**
* LoggerUtil工具类用于日志记录
*/
publicstaticclassLoggerUtil {
publicstaticvoiderror(Logger logger, Exception e, String format, Object... arguments){
if (e != null) {
logger.error(format, arguments, e);
} else {
logger.error(format, arguments);
}
}
}
/**
* 主方法用于测试
*/
publicstaticvoidmain(String[] args){
// 创建处理器实例(实际使用时由Spring注入)
UserStatusHandler handler = new UserStatusHandler();
// 测试用户状态查询
String testUserId = "test123";
handler.processUserStatusQuery(testUserId);
}
}
五、代码检查
在完成代码生成后,需要保证代码的质量,只靠提示词中的约束和检查只是一部分,最好还是需要一些额外的措施来进行质量及规范检查。这里主要考虑的方式是使用规则匹配代码,然后根据规则匹配代码的最终产出检查报告,然后根据检查报告反向修改代码,得到终版代码。
这部分还在实验阶段,这里只描述进展和思路。
这里主要包含三方面检查
静态规则校验
动态校验
技术风险
5.1静态校验
在这里,我定义了三类静态校验规则,下面围绕这三类规则简单展开。
应用开发规范
JAVA代码规范
信贷术语规范
5.1.1规则规范
这三类规则主要是为了检查当前生成的代码是否符合当前应用的代码风格以及架构要求,以及在一些内容定义上是否符合业务背景,其次就是因为虽然当前很多大模型已经集成了《阿里JAVA开发规范》,但是对于使用者来讲,到底集成了哪些、是否生成的代码一定符合规范、直接使用大模型的java规范进行检查是否全面,都是难解的问题,所以这里选择自己定义一些JAVA开发规则,来进行代码检查。
系统规范案例
- **rule_code**: "FACADE_PATH_DEFINE_CHECK" - **description**: "Facade接口检查,Facade接口必须位于`app/facade/src/main/java/com/xxx/userapp/facade/api/业务名称`的目录下" - **check_list**: - "检查文件路径是否符合规范" - **matching_rule**: "^app/facade/src/main/java/com/xxx/userapp/facade/api/" - **example**: - **valid**: "app/facade/src/main/java/com/xxx/userapp/facade/api/user/UserFacade.java" - **invalid**: "app/core/src/main/java/com/xxx/userapp/facade/UserFacade.java"
阿里JAVA开发规范
- **rule_code**: "NAMING_ABSTRACT_CLASS" - **description**: "抽象类命名使用Abstract或Base开头" - **check_list**: - "检查抽象类名是否以Abstract或Base开头" - **matching_rule**: "^Abstract[A-Z][a-zA-Z]+|^Base[A-Z][a-zA-Z]+" - **example**: - **valid**: "AbstractClass", "BaseClass" - **invalid**: "Class", "Abstractclass"
信贷术语规范
- **rule_code**: "NAMING_CREDIT_APPLICATION"- **description**: "授信申请字段命名应包含Credit或具体授信相关的英文术语"- **check_list**: - "检查字段注释是否包含‘授信申请’或相关中文术语" - "检查字段名是否包含Credit或具体授信相关的英文术语(如CreditApplicationID、CreditLimit等)"- **matching_rule**: "^[a-zA-Z]*Credit[a-zA-Z]*$|^[a-zA-Z]*(Application|Limit|Available|Used)[a-zA-Z]*$"- **example**: - **valid**: - "CreditApplicationID" (授信申请ID) - "AvailableCreditBalance" (可用授信余额) - **invalid**: - "Credit_Application_ID" (不符合驼峰命名) - "creditapplicationid" (未符合驼峰规则)
5.1.2应用规则库
对于这种规范如果作为提示词或者一个markdown文件放到本地,也行,但是管理上总归不太规范,所以这里选择放到KIS平台中存储,然后通过MCP Server来进行使用。
5.1.3检查结果
这里可以使用CodeFuse IDE,它集成了很多MCP Server,或者将规则放到本地都可以,在引用java文件后让其按照规则进行检查。最后可以生成一份报告,我们可以通过报告来修改检查出来的问题。
5.2动态校验
目前我们对于代码检查自测,基本用的都是单元测试和集成测试。
这里推荐使用一个工具--EvoTest来生成单测和集成测试用例,前段时间一直和其团队合作,优化使用流程。
5.3技术风险
对于生成出来的代码进行校验,其中一个校验方式就是先看其出入参数是否有问题,这里我们使用Agent编排平台做了一个契约对比Agent,用于对比我们接口调用结果和系分中的接口契约是否一致。
对于契约对比Agent的使用,首先我们在接口设计中,定义了接口契约的约束,然后将实际调用的结果JSON输入到Agent中,最终获得对比结果表格。在表格中可以输出哪些字段不符合规范或者缺失,由此来检查代码问题。
5.4总结
这里限制过多,当规则变多、以及被检查的代码量变多后,所有规则是否都被检查到、所有代码是否都被检查就很难保证,不过在初期阶段,规则范围小且对少量代码进行检查时,还是可以检查出一些问题的。
目前正常尝试使用此方式来检查代码的规范和语法是否符合要求,后面会探索更加规范的的流程,来通过检查来保证AI Coding的最终质量。
六、经验总结
本章节主要描述在提示词开发与测试的过程中,总结出来的经验。
部分内容可能在前面已经提到,这里做下整体总结。
1.提示词调试经验:提示词调试,调2-3次效果最好,无效立刻改提示词。
在使用AI生成代码时,个人使用上发现个规律:首次生成基本达不到标准,第2、3次效果最佳,再往后使用就会出现更多各种各样的骚操作,所以一般两三次的效果还是比较可观的,就可以考虑采纳了;如果效果一直不好,那就考虑改改提示词吧。
2.被操作/读取内容结构也很重要,被操作/读取内容结构混乱,也会影响最终效果。
任务提取阶段是最容易出现问题的,因为它可能因为标题内容与系分中其他位置的文案一致,而因为文字匹配问题导致提取内容有误,需要根据标题名称在全文搜索是否出现次数过多而影响大模型的结果读取。
最好的方式是制定一个独特不重复的标题,使得内容提取时更好的锚定到目标内容。
3.模版类代码生成经验:添加代码案例提升代码准确度以及保证代码风格。
在生成门面层和持久层代码时,其实其内容大致差不多,如果通过添加约束以及各种架构文档描述来让它生成代码,个人认为会很难控制它的幻觉。所以在生成此类代码时,我建议直接添加代码案例到提示词中,说这提供代码目录让其参考,这样生成出来的代码不仅准确,且对于命名、方法抽象等代码风格都在可控范围内。
在提示词中添加案例后,门面层和持久层的生成相对问题较少,一般两次生成即可完全采纳且无问题。常见场景一般是某个复杂模型的convert有问题、JSON和复杂对象的转化偶现问题,或者忘记import包,一般简单调整即可采纳。或者在案例中重点提示此处注意事项,也能减少小问题的出现。
4.非100%采纳是否要重新生成:人工修改时间和重新生成时间的各自的对比后决策。
一些简单流程(模型赋值和组装的上下文清晰;业务逻辑执行所需外部能力在代码里已经基本都定义好了,且也不会对外部能力的返回结果做很多复杂的操作),个人测试基本没什么问题。
如果流程过于复杂,新生成的业务流程代码是否采纳,就需要人工分析‘人工修改时间和重新生成时间的各自占比’。如果生成出来的内容在我们看来已经差不多了,错误内容通过几分钟内容人工调整就可以完成、且遗漏/错误内容觉得AI通过现有内容也无法生成/修改成功,那我们就可以选择采纳了。如果不然,可以分析是大模型不行、还是提示词不行、还是流程图不够细致,从而从中修改来重新生成。
5.分步执行,将最准确的内容提供给大模型来生成结果。
一个人尽皆知的点:给Ai的描述越准确,它生成的内容越准。所以我们要做到喂给大模型的内容是最全面且准确的,当我们无法直接做到这一步时,就需要将不明确的内容转化为明确的内容,那就需要在整体流程中添加很多”增强层“,来提高最终准确性。
在业务逻辑代码生成时,一定是要依赖一段流程描述或者流程图的。让开发同学绘制流程图的时候使用伪代码属实不妥,使用白话文描述流程更加清晰直白,由此发现从流程图到代码生成中,是缺少一步的。所以要给予流程图应用内部知识,使白话文流程图转化为带有系统知识的伪代码流程图。
6.如何减少大模型推理的幻觉:定义推理范围以及推理约束,引导大模型推理方向,减少莫名其妙的yy。
在代码生成时,如果不添加约束,多数都会出现一些大模型yy的额外步骤。并且对于我们来讲大模型很黑盒,哪怕看了一些文档知道其“大概原理”,也是很难通过提示词很好的约束大模型的幻觉产生。个人推到此类幻觉都是大模型凭借自己“经验”进行“推理”产生的。
这里我的看法是,提前定义推理范围,让其在指定场景进行推理,并且告知其推理方向进行引导,从而使其的推理尽量在我们的掌控中。
在业务代码以及流程图增强中,都使用了此方法,没有让其直接根据业务术语自己去yy一些对象定义以及服务的使用,都是引导按照我们的思路去寻找答案,提高最终准确性的同时,减少幻觉。
7.点对点解决幻觉:有果必有因。
在给大模型很明确的指令以及案例参考时,大模型很容易因为某些点‘我们以为我们描述清楚了’,但是‘大模型理解的是另一个意思’而导致最终结果不好,并且此类问题很难发现问题原因。
在经过不断的和大模型交互问答‘为什么会出现这个问题?‘,以及不断修改提示词,最后发现了这个结论。就是有时候大模型理解的和我们描述的真的可能不是一个意思。所以如果频繁出现同一幻觉,或者添加约束也解决不了问题时,可能不是大模型的问题,要仔细检查自己是不是哪描述的容易歧义,然后进行修改。
8.提示词与大模型交互经验:提示词大而全并非好,要尝试与大模型交互看效果;结构化提示词,避免提示词前后冲突与内容分散。
因为一些幻觉或者其他被叫做“保底回答”的原因,使得我们总是需要添加一些约束,最后变成了提示词列了几十条约束,而且还约束不住。后来通过多次和大模型的交互,可以简单的发现一些大模型自带的约束以及规则。此类规则就可以从我们的规则和约束就可以从我们的提示词中删掉了。
当内容过多时,很容易出现前后语义不一,内容分散、约束冲突等问题,反而降低效果。所以对于提示词一定要定义一套标准结构,各章节边界清晰。
七、整体流程图
八、实际效果
当前将内部两个应用作为AI Coding 为试点,已经在两个项目中使用以上内容,实际案例及效果如下:
项目一
生成内容:新增2个接口及2张数据表,总计生成代码约2300行左右(AI统计),占比约70%。
生成效果:AI生成代码总计耗时1h,无人工修改,直接采纳且通过测试,QA未提交生成内容相关的缺陷,现已上线。
项目二
生成内容:新增4个接口,总计生成代码约2800行左右(研发效能平台统计),占比约60%。
生成效果:预期开发8人日,实际开发4.5人日,减少45%人日。
项目三
生成内容:新增35个接口及9张DB表,生成接口定义类内容(interface、request、result、DTO)、领域模型定义、接口实现基础内容、转换类,以及持久层中从mapper、repo、service到其中DO和领域模型的转换类等能力共计生成代码30000+行。
生成效果:总计2人日完成全量生成门面层及持久层全量代码生成,减少机械式编码时间约80%,帮助项目快速启动。
九、后续方向
最终还是向着更流畅的串联从系分到代码这条路前进。当前AI Coding中,业务逻辑中的参数赋值、模型组装等内容还是通过引导大模型推理来生成的,目前还做不到100%采纳,可能存在部分字段赋值缺失或错误。和团队同学共同研究后,认为可以通过TDD的方式辅助AI Coding也是一个很好的方式。在生成代码后,根据系分以及已有的代码生成全链路覆盖的测试用例,并执行检查预期测试结果,然后再根据测试结果反向验证AI生成代码的结果是否准确,再将检查出来问题进行二次修改,达到最终生成结果的准确性。做到从设计-编码-执行-测试-修改的动作闭环,完成真正的自主式Java开发智能体。
对于一些其他工作,包括代码检查和配置生成,个人认为可以更好的应用MCP Server存储规范知识库来进行代码检查,以及使用工作流来进行配置生成,让其中各个步骤串联的更加顺畅,并且更多的使用外部工具来辅助流程串联及生码。
做AI生成代码最终想要达到的结果是通过系分将其中本迭代内所有的内容按照规范的流程陆续生成出来,现在因为工具和模型还没达到“最预期”内的状态,所以还需要优化提示词的同时等待工具和模型的升级。最终达到读取系分后生成所有任务,然后按照步骤陆续生成的最终结果。
对于其中提示词,考虑可以放到一些知识库的MCP Server里面。但是主要考虑两点比较容易影响结果,一方面是从知识库MCP Server中获取内容,可能因为工具原因而造成获取内容被截断。第二方面是知识库构造方式也很多,如果把不同提示词的内容因为名称相似给匹配成相关内容,会影响最终生成代码的效果。所以目前是放在本地的。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2025-10-21
有效的 Context 工程(精读、万字梳理)|见知录 004
2025-10-21
Agent与Workflow的技术落地实践与思考
2025-10-21
从大脑解码 AI,对话神经网络先驱谢诺夫斯基
2025-10-21
一眼读完一本书?别笑,真有人在干这事
2025-10-21
“AI优先”的时代必然性:揭秘企业坚定投资AI的真实商业回报
2025-10-20
万字长文深度解析最新Deep Research技术:前沿架构、核心技术与未来展望
2025-10-20
端到端的多模态文档解析模型-DeepSeek-OCR架构、数据、训练方法
2025-10-20
Prompt 工程进阶——让 DeepSeek 学会你的测试风格
2025-08-21
2025-08-21
2025-08-19
2025-09-16
2025-07-29
2025-09-08
2025-09-17
2025-08-19
2025-10-02
2025-09-29
2025-10-20
2025-10-20
2025-10-19
2025-10-18
2025-10-18
2025-10-18
2025-10-16
2025-10-16