摘要
本文主要讲述,以“无需训练模型”的方式实现:AI智能分析功能需求、写代码、review代码解决特定业务问题的实践过程,解决的是一款极其小众的开发语言的问题,模型并无相关领域知识,因此具备一定的代表性。与讲解大模型原理和方法论的文章不同,本文主要站在工程同学视角记录实践经验,例如:如何做RAG优化、rank排序剪枝、small2big、prompt调优、fewshot、避免幻象等,并不会对算法原理做过多剖析,希望能对同样没有算法背景的同学有所帮助,文中内容仅代表个人观点,若有不严谨之处,欢迎指出讨论。
利用AI实现通过功能需求描述生成脚本代码,并具备自动补全、格式化和CodeReview能力:
业务背景
在B端销售技术系统中,决策中心扮演着非常重要的作用,它支持各类复杂规则决策树的快速配置和接入能力,业务系统在自身流程中接入后,可以快速响应各类政策规则的调整上线。例如,毛利管控政策、售卖引导政策、审批规则政策、一户一价灵活报价政策、产品搭配的售卖规则等,还有很多技术类的诸如灰度规则等技术场景,所有复杂的业务规则都可以抽象成一颗决策树来实现。
一言以蔽之,决策中心能通过灵活的规则策略配置能力支持各类复杂业务政策快速落地,给B端系统的灵活扩展性要求提供了强有力的支撑。
在业务流程中,接入规则插件实现灵活运营能力。
从技术架构视角看,决策中心内部是按照,从上至下:策略-规则-指标的层次结构来实现其灵活配置能力的。其中指标层是其灵活性的基础保障,指标层主要基于Aviator脚本[1]实现,技术同学可以通过快速编写Aviator脚本定义出指标给到规则和策略运营配置使用,脚本层可以内聚的实现一些“相对复杂”的业务流程,规则策略层可以配置出复杂的决策树组合,从而实现快速支撑业务政策。
Aviator是一个高性能、轻量级的 java 语言实现的表达式求值引擎, 主要用于各种表达式的动态求值。现在已经有很多开源可用的 java 表达式求值引擎,为什么还需要 Avaitor 呢?
Aviator的设计目标是轻量级和高性能,相比于Groovy、JRuby的笨重, Aviator非常小, 加上依赖包也才 537K,不算依赖包的话只有 70K; 当然, Aviator的语法是受限的, 它不是一门完整的语言, 而只是语言的一小部分集合。
其次, Aviator的实现思路与其他轻量级的求值器很不相同, 其他求值器一般都是通过解释的方式运行, 而Aviator则是直接将表达式编译成 JVM 字节码, 交给 JVM 去执行。简单来说, Aviator的定位是介于 Groovy 这样的重量级脚本语言和 IKExpression 这样的轻量级表达式引擎之间。
github:https://github.com/killme2008/aviatorscript
一段Aviator脚本样例:
use java.io.File; let io = require('io'); let files = count(ARGV) == 0 ? 5 : long(ARGV[0]); let nums = count(ARGV) < 2? 1000 : long(ARGV[1]); let temp_dir = "/tmp"; for i in range(0, files) { let file = "#{temp_dir}#{File.separator}data.#{i}"; let writer = io.writer(io.file(file)); for j in range(0, nums) { write(writer, "#{rand(100000000)}\r\n"); } io.close(writer); p("Generate #{nums} random numbers to file #{file}"); } let testResult = indicator_execute("TEST", "TEST-INDICATOR", seq.map()); if (is_empty(testResult)) { log.log("测试指标调用结果为空"); } let testNameSet = seq.set(); for testData in testResult { seq.add(testNameSet, testData.get("name")); } ...
业务问题
Aviator脚本在带来灵活性的同时,给技术同学也带来一定的困扰:
一、研发脚本困难
语言小众不熟悉
Aviator相对来说是一门比较小众的脚本语言,没有完备的知识文档体系,技术同学几乎都是java背景,除非已经很熟练Aviator脚本开发,过程中常常遇到很多的语法问题,基本上是遇到一个查阅一次文档,非常痛苦。
无成熟IDE环境
虽然Aviator提供了idea插件可以编辑脚本,但技术同学开发时往往更习惯在决策中心进行,基本都在浏览器的编辑框内,语法错误很难发现,非常痛苦。
难以维护
为了更好的运营和维护指标脚本,我们在脚本基础上设计了指标的名称、描述的属性,用来描述其实现功能,便于维护和处理,但实际研发过程中,大家往往不愿意投入较大的精力认真填写和维护,指标的有效性变得越来越差,重复度也越来越高。其中,无效描述(描述字段少于10个中文字符)的指标占比超过55%
二、审阅脚本困难
结合上述第1点问题,我们也预判到了可能存在的研发风险,所以开发了很多稳定性保障功能,如:基于mock数据进行验证预计算、接入变更管控审批、灰度发布和回滚流程等,所有指标开发需要经过:验证->评审->灰度->发布 过程,但评审时也只能使用决策中心浏览器的编辑器进行查看和分析。
评审脚本困难
以历史经验来看,大量脚本提交审批时存在基础错误,如缩进格式不统一、命名风格不正确、异常判空处理不足、注释不足等,其次则是运行逻辑错误。脚本多次提交评审才能上线的占比高达80%以上。
简单来说,指标依赖Aviator脚本,但没有完备的研发工具体系导致效率低下。
实践方案
基于AI,github、aone copilot体系已经具备非常厉害的代码生成、注释描述生成、智能代码评审等能力,借鉴于此,我们也可以探索使用AI大模型的能力来提升指标脚本的研发效率,具体来说,就是在决策中心指标编辑页增加:AI 智能研发助手,帮助解决以上难题。
一、挑战&解题思路
1、模型预训练的知识能力有限
以本文实践为例,“通义千问”所掌握的信息和数据更新至2023年为止,而“Meta-Llama-3-1-405B-Instruct”的训练数据则截止到2023年3月1日。
2、模型对Aviator的泛化能力很差
Aviator是非常小众的脚本语言,当你在“通义千问”中询问Aviator脚本时什么时,它的回答非常“离谱”,当你在“Meta-Llama-3-1-405B-Instruct”模型中询问:Aviator脚本如何做字符串拼接时,答案也是错误的,也就是说已有的大模型对于Aviator来说,几乎是一问三不知,所以会出现“胡扯”的幻象。
3、解题思路
上述两个问题,是大模型在实际应用落地时非常普遍的问题,业界解决的思路都大同小异,ROI由高到底的顺序为:选择合适的模型 > 优化好提示词 > RAG检索增强 > 模型预训练,这里不展开说其原理,通俗的来讲,让模型帮助我们解决问题,类似寻求一位专家老师的帮助,但老师没有特定领域的知识和经验,比如老师是一个很牛的数学专家、逻辑分析能力极强,但没有法律相关的经验,而我们要寻求法律相关的帮助,则需要:
1、选择一个合适的模型,就好像选择一个优秀的老师,它本身的学习能力和知识储备是非常优秀的,如果要解决的问题场景是逻辑分析和推理,我们就不能去找艺术创作能力很强的老师;
2、优化Prompt提示词,就是说我们要把问题问好,要告诉他足够多的背景信息,还有一些实际案例的答案,让老师根据自身的能力来做推断,这就是类似Zero Shot、Few Shot的prompt调优;
3、RAG知识增强,就好像给老师一本参考书,让老师基于问题首先翻阅参考资料,然后用他的能力来进行理解和分析,最终给到我们好的答案;
4、模型预训练,这就好比是让老师“持续深造”,让一个数学专家认真学习法律逻辑推理的专业知识,当然耗时和成本就很高了。
本文在实际落地时,主要选择了1、2、3点来进行调优,模型选择上,选择了“Meta-Llama-3-1-405B-Instruct”,从业界的评测来看,llama 3.1 405B全方位碾压GPT-3.5 Turbo,Llama 3.1 405B 在 NIH/Multi-needle 基准测试的得分为 98.1,虽然仍旧比不上 GPT-4o,但也表明其在处理复杂信息的能力上堪称完美,尤其是其在ZeroSCROLLS/QuALITY 基准测试的得分为 95.2,意味着其具有整合海量文本信息的能力, 对于关注 LLM 在 RAG 方面性能的 AI 应用开发者来说,可谓是相当友好。
再来看代码生成能力,Human-Eval 主要是负责测试模型在理解和生成代码、解决抽象逻辑能力的基准测试,而 Llama 3.1 405B 在与其他大模型的比拼中也是稍占上风。
二、RAG实现Aviator语法知识问答
如前文所述,Aviator虽然具备高性能、轻量级且高度兼容java的特性,但它是一个非常小众的脚本语言,其推广程度和业界熟悉程度都非常低,为此,我们没有办法直接使用LLM来做Aviator语法知识的问答。
1、知识向量化与检索
实现知识库问答最常用的技术方案就是:RAG,RAG的方案架构可以简单抽象为:用户输入->向量检索知识片段->LLM基于知识片段做扩展->输出给用户。
困难:因为Aviator非常小众,仅有知识库就是:aviatorscript语雀知识库[1],我们也无法获取到知识库api token权限,也没有其他完整的文档数据,也就没有办法做知识向量数据库。
这里直接省掉了向量数据库的搭建,而把文档检索简化成了语雀的公共搜索api[2],通过传入搜索词直接在语雀知识库搜索,可以实现基础的文档片段检索,这样可以解决用户提问的基础检索问题。
当然,如果有完备的知识库,则可以自研进行文本分段和向量化存储,并采用一些常用的技术方案实现检索。在大规模实际工程落地时,远比这个架构复杂,涉及到如何做查询意图识别、查询改写优化、向量化调优、模型调优等等。
2、LLM内容增强
在完成基础的文档片段检索后,就可以使用LLM对检索结果进行内容增强,prompt则是采用典型的RAG提示词,例如当我们搜索“如何遍历集合”时,首先会通过语雀的公共api搜索出相关的文档片段摘要,接着会采用如下prompt去询问大模型:
你非常擅长回答关于aviator脚本相关的技术问题,需要按以下步骤和回答用户问题。 # 步骤 - 识别用户输入的问题,问题在<input></input>中; - 理解已有的参考文档,文档在<ref></ref>中; - 对参考文档中的信息进行整理,将相关信息与用户问题进行关联,形成回答内容,提供详细和准确的回答; # 要求 - 你需要在回答中注明参考文档的来源和链接,以便用户可以进一步查看相关资料。 # 限制 - 如果问题与aviator脚本技术并不相关,则直接回复"我只能回答aviator技术相关问题。" - 严格按照参考文档中的内容进行回答,如果没有能回答问题的参考文档,你需要直接回答“没有找到相关文档,我只能回答aviator技术相关问题!”。 - 不能使用js、ruby、python、java等其他语言规范回答问题,不要捏造或创造不正确的答案。 <input> 如何遍历集合 </input> <ref> # 10. 数组、集合和 Sequence ## 摘要 这一章,我们将介绍<em>如何</em>在 AviatorScript <em>如何</em>方便地创建和操作数组、<em>集合</em>。同时介绍在此之上的 Sequence 抽象。AviatorScript 中将数组和<em>集合</em>都抽象成一个序列<em>集合</em> Sequence,在此之上可以用同一套高阶函数方便地... [文档链接](https://www.yuque.com/boyan-avfmj/aviatorscript/zg7bf9) # 用户指南(5.0之前版本) ## 摘要 本文档不再维护,请参考《<em>如何</em>升级到 5.0 大版本(老用户必读)》,升级到 5.0 大版本,阅读《AviatorScript 编程指南》,谢谢。简介 Aviator是一个高性能、轻量级的 java 语言实现的表达式求值引擎,主要用于各种... [文档链接](https://www.yuque.com/boyan-avfmj/aviatorscript/ra28g1) </ref>
最终模型会对搜索出来的文档摘要与问题进行了相关性分析,并进行了总结和整理最终答案。而prompt的调优对于模型工程落地效果,往往是ROI最高的,因此也诞生了一门新的学科:提示词工程[3],还有很多优化prompt的经验和工具可以参考和使用。
Demo
3、下一步优化
- 检索结果Rerank重排序
通过语雀api搜索出来的文档摘要还是存在一些与问题本身相关度较低的内容,如果采用自建向量搜索能力依然会存在类似问题,同时,LLM模型能接受的上下文窗口大小也是存在限制的,如果检索出来的参考文档内容过大,也没有办法直接交给LLM进行内容增强,因此,就需要对检索结果进行Rerank重排序,过滤掉相关度低的内容,避免超出模型窗口大小的限制,也能避免不必要的“幻象”。
- Small to Big
通过渐进式的检索方法,先检索出文档摘要(Small),然后结合摘要Rerank重排序后,按顺序根据链接获取原始文档内容,再对原始文档内(Big)容进行检索查询,直到找到更精准匹配的文档内容,最后再由模型分析总结答案。
三、LLM实现通过功能描述生成脚本代码
在AI营销泛滥的时代,网上充斥着各种AI自动写代码、程序员失业的软文,但事实上,离AI根据prd需求直接生成项目代码并能正常运行的时代还较远,一些公司的团队正在做类似的项目,但也并没有达到可商业化落地的阶段。虽然AI还并不具备通过业务需求生成可商业化项目级代码的能力,但AI已经具备一些通过单代码文件实现简单清晰的function需求的能力了,例如:我们可以让AI帮我们写一个对map数据进行sort by value的操作代码。
1、使用Few Shot,根据指标名称和功能描述生成脚本代码
themis指标脚本在LLM代码生成的可行性就在于,它与常规的项目级代码不同,它的设计初衷就是一个实现简单闭环功能的function,并且按场景进行了划分,每个独立场景内的指标都具备以下特征:1、使用相同的上下文数据结构;2、实现类似的功能。这就使得通过像模型提供指标名称和功能描述,再结合一些已有案例,让模型推断出待实现功能代码具备了可行性。
用户输入需求文档(1、待实现指标脚本的名称;2、功能描述;3、场景),系统找出相同场景的指标名城、功能描述、脚本代码,再结合一些诸如语法规范等约束条件,使用Few Shot的方式组装成Prompt让模型进行分析推理,从而产出相应的脚本代码。
Prompt:
你需要按照以下"步骤"和"要求"来帮助用户按照描述生成新的脚本代码: # 步骤 - 使用aviator脚本语法进行分析、理解和代码生成,不要使用其他开发语言的语法; - 分析理解<prefix></prefix>部分描述的案例,其中描述了不同名称和功能的脚本代码是如何实现的; - 理解<name></name>部分,它代表当前用户需要编写的脚本的名称; - 理解<desc></desc>部分,它代表当前用户需要编写的脚本需要实现的功能说明; - 根据上述信息,生成能实现<name>和<desc>所描述名称和功能的脚本代码; # 要求 - 生成代码要符合aviator语法规范,不要生成与描述无关的内容; - 生成代码变量和函数以驼峰方式进行命名; - 生成代码统一以两个空格进行缩进; - 生成代码时要添加注释,aviator仅支持使用```##```单行注释; - 生成代码做好必要的判空处理,避免发生不预期的异常; - 可以使用内置函数,如indicator_execute、hsf_invoke、db_query等,以originalContext结构体作为输入; - 尽量调用已有脚本而少写重复代码,格式为:indicator_execute($code, $args...),其中$code为被调用脚本编码,$args为参数列表; - 生成内容按名称、功能、代码分段展示,采用markdown格式输出; <prefix> %s </prefix> <name> %s </name> <desc> %s </desc>
困难:这样的方式确实有效,但模型的上下文窗口存在max token限制,如目前使用的模型(未付费,若采用付费版本可以长达128k)限制token数为8k,而prompt里传入大量代码,使得请求很容易超出限制而失败。而同场景的指标脚本,其实也存在一些与带推理脚本并不相关的代码,需要过滤掉他们并尽可能给模型提供相关度更高的脚本代码。
2、使用Rerank对样例数据进行相关度排序和筛选,剪枝上下文
通过rerank模型,可以判断输入的目标文本与待判断文本列表的相似度,并按排序返回。这里使用了BAAI/bge-reranker-v2-m3[4]作为rerank模型,对召回的同场景指标按名称、功能描述与待实现需求的名称和描述进行相似度排序,过滤掉低相关度的代码,再组装prompt让模型推理,大幅提升推理的成功率和精准度。
Demo
3、下一步优化
现在生成的脚本代码,有几率出现代码无法直接编译通过、运行的情况,对此,可以在系统中实现对生成代码的编译运行,并将结果再次喂给模型进行优化,通过迭代式的交互直到生成可编译运行的代码为止。
四、接入AoneCopilot实现脚本代码自动补全
虽然模型根据功能描述能自动生成脚本代码,但依然存在需要对代码进行调整的场景,继续采用上述的思路,已有的指标脚本是可以用来指导我们进行代码的自动补全的。这里可以学习了解一下Aone Copilot以及GitHub Copilot的实现原理,简单来说就是使用高质量的代码库(300多种开发语言)进行模型训练,结合待补全文件的:1、语言类型;2、相关代码(如同路径、import、打开tab的文件等);3、相似代码片段等进行推理来实现代码补全的。而aone copilot也提供了代码补全的api 服务,但接入服务需要传入代码仓库地址、代码prefix、代码suffix以进行项目级别的prompt的构建。
Aone Copilot是一个类似GitHub Copilot的自研智能研发助手,具备代码自动补全等能力。
困难:指标脚本代码没有独立的代码仓库,就没办法进行多文件联动推理补全了。
这里依然利用了指标脚本独立场景和function级实现的特性,直接找出同场景的指标脚本代码,同时全部拼接到prefix的部分,这样就构建了一个“假的”项目级别的prompt上下文给到推理服务,以此复用aone copilot的代码补全能力。
Demo
五、LLM实现通过代码生成功能描述和评审代码
除了自动生成脚本代码外,还需要解决“指标描述无效率高”和“脚本评审效率低”的问题,这个实现就相对来说简单一些,主要是利用prompt优化和LLM本身的能力来进行,例如通过在提示词里给到模型既定的语法规范、评审规则、评审格式、代码本身和修复建议标准,让模型产出具体的评审结果和修改建议。
1、按要求评审脚本并给出修改建议
你拥有高超的代码编写和检查技能,熟悉多种编程语言和设计模式,可以根据代码的具体情况,指出存在的问题。 请你参考<schedule></schedule>中的检查步骤,<tab></tab>中的检查要求,对<code></code>中的代码进行检查。 <tab> - 空指针:指出可能存在未判定空指针的地方,判定方式为```param == nil```。 - 缺乏一致性:使用了不一致的缩进、命名风格、代码注释等,使得代码难以阅读和维护。 - 变量和函数命名不清晰:变量和函数名未使用驼峰命名,使用没有意义或者太过简单的变量和函数名,无法清晰地表达其用途。 - 长方法或函数:方法或函数超过50行,增加了代码的复杂性,难以理解和测试。 - 注释不足或者错误:缺乏注释或者注释与代码不一致,无法理解代码的用途和实现细节。 - 不合理的代码布局:缺乏良好的排版和布局,使得代码难以理解和浏览。 - 过多的重复代码:存在超过10行的代码段,增加了代码冗余和维护难度,可以将重复代码抽成函数。 - 没有错误处理机制:未考虑异常情况,没有适当的错误处理机制,导致程序容易崩溃或者出现不可预料的错误。 </tab> <schedule> - 使用aviator脚本语法进行检查和优化,不要使用其他开发语言的语法,aviator仅支持使用```##```单行注释,统一以两个空格进行缩进,以驼峰方式进行命名,脚本引擎内置了很多函数,如indicator_execute、hsf_invoke、db_query等,脚本以originalContext结构体作为输入; - 若代码都检查了空指针,则指明具体检查的地方; - 若方法或函数代码没有超过50行,则无需检查长方法或函数; - 若重复代码没有超过10行,则无需检查重复代码; - 深入理解要检查的代码,包括其功能、逻辑和结构等方面的特点; - 检查代码,指出其存在的问题,说明检查的过程和结果,明确指出问题代码的位置行数,如果没有问题则无需输出; - 如果可以,针对问题代码给出修改建议,不要针对不同问题重复生成建议代码,而要用一段代码建议解决所有问题,确保不破坏现有功能,不要生成与原代码严重不符的代码建议; - 回答内容不要复述检查步骤和要求,采用markdown格式输出; </schedule> <code> %s </code>
困难:因模型对Aviator语法并不了解,因此多数时候生成的代码评审修复建议是类似js、ruby、python等脚本语言的语法,如if ... then ...,这样的修复建议是完全不符合Aviator规范的。
1.1 优化prompt以代码样例告诉模型Aviator的语法规范
对此,继续采用few shot的思路,在prompt构造时告诉模型Aviator脚本的语法规范,但如果我们将整个知识库的文档都输入给模型,一来上下文长度无法支持,二来也没有原始的文档数据,这里采用一个变通的办法:从github上下载Aviator脚本的样例代码,同样地为了避免超出上下文窗口限制,随机选取部分代码片段喂给模型,从而让模型不要出现生成js、ruby脚本的幻象。
你拥有高超的代码编写和检查技能,熟悉多种编程语言和设计模式,可以根据代码的具体情况,指出存在的问题。 首先你需要从<example></example>中的样例代码中学习aviator脚本语法规范,例如: - 使用```let goodName = "name";```的方式定义变量,无需设定变量类型; - 使用```fn addFunction(a, b) {return a + b;}```的方式定义函数; - 使用```seq.set()```定义集合,使用```seq.list()```定义数组,使用```seq.map()```定义map; - 使用```str.isNotBlank(...)```的方式引用字符串工具; - 使用```json.toJSONString(...)```的方式引用json工具; - 以originalContext结构体作为输入; - 内置了很多函数可以调用,如```indicator_execute(...)```、```hsf_invoke(...)```、```db_query(...)```、```with_cache(...)```、```saveData(...)```等,日志输出只支持```log.log(...)```的方式。 然后你需要参考<tab></tab>中的检查要求、<schedule></schedule>中的检查步骤,对<code></code>中的代码进行检查和优化。 <tab> - 判定空指针:针对使用的变量,必须判定空指针,对函数名无需判定空指针。 - 保持一致性风格:要使用一致的缩进、命名风格、代码注释等,统一用两个空格进行缩进,变量函数要以驼峰方式进行命名; - 变量和函数命名要清晰:变量和函数名必须使用驼峰命名,同时命名必须有意义,能通过命名理解其用途; - 方法和函数不能过长:定义的方法和函数不能超过50行,若存在超过50行的方法或函数,应该拆解成更小的方法和函数; - 注释要充足且有意义:只能使用```##```单行注释,不能用其他如```//```,```/* */```进行注释,不能多行注释,代码不能缺乏注释,注释内容与代码含义必须保持一致; - 合理的代码布局:代码要有良好的排版和布局,使得代码便于理解和浏览; - 不能有过多的重复代码:不能存在超过10行的重复代码段,若存在超过10行的重复代码,应该抽成函数进行调用; - 要有错误处理机制:必须有适当的错误处理机制,增加程序的健壮性; </tab> <schedule> - 深入理解要检查的代码,包括其功能、逻辑和结构等方面的特点; - 按<tab></tab>中的规则检查代码,清晰说明检查过程和结果,不能使用js、ruby、python、java等其他语言规范; - 若不存在违反规则约束的问题,不要捏造问题和修改建议; - 若存在违反规则约束的问题,则首先对问题进行总结分析,要明确指出问题代码的位置和行数,并给出代码修改建议,确保建议代码要符合aviator脚本语法规范,同时与原代码实现的功能和逻辑保持一致,不要对问题以外的代码做修改; - 如果捏造问题将会受到惩罚,如果修改建议代码不符合aviator语法规范将会受到惩罚,如果修改建议代码与原代码严重不符将会受到惩罚; - 采用markdown格式输出,建议代码用```包装起来; </schedule> <example> %s </example> <code> %s </code>
Demo
2、根据指标脚本生成功能描述
你拥有高超的脚本代码理解能力,可以根据代码的具体情况,总结出脚本所实现的功能描述。 请你参考<schedule></schedule>中的步骤,对<code></code>中的代码进行总结。 <schedule> - 使用aviator脚本语法进行分析和理解,不要使用其他开发语言的语法,除aviator基本语法外,脚本引擎内置了很多函数,如indicator_execute、hsf_invoke、db_query等,脚本以originalContext结构体作为输入; - 深入理解代码,包括其功能、逻辑和结构等方面的特点; - 用一到两句话总结代码所实现的功能是什么,然后再总结其实现步骤是什么; - 要简明扼要,不要长篇大论,如果无法理解代码逻辑就无需输出,不要总结出毫不相干的描述; - 回答内容不要复述处理步骤和要求,采用markdown格式输出; </schedule> <code> %s </code>
Demo
3、格式化指标代码
使用浏览器编辑器编辑脚本代码还会遇到格式化的问题,而因语言小众找不到合适的格式化插件,这里也可以利用模型的推理能力帮助进行快速的格式化代码。
你拥有高超的代码编写技能,熟悉多种编程语言和设计模式,可以对代码进行格式化。 首先你需要从<example></example>中的样例代码中学习aviator脚本语法和代码规范,例如: - 使用```let goodName = "name";```的方式定义变量,无需设定变量类型; - 使用```fn addFunction(a, b) {return a + b;}```的方式定义函数; - 使用```seq.set()```定义集合,使用```seq.list()```定义数组,使用```seq.map()```定义map; - 使用```str.isNotBlank(...)```的方式引用字符串工具; - 使用```json.toJSONString(...)```的方式引用json工具; - 以originalContext结构体作为输入; - 内置了很多函数可以调用,如```indicator_execute(...)```、```hsf_invoke(...)```、```db_query(...)```、```with_cache(...)```、```saveData(...)```等,日志输出只支持```log.log(...)```的方式。 然后你需要参考<tab></tab>中的要求,对<code></code>中的代码给出格式化修改建议。 <tab> - 每一行的末尾不能有空格; - 统一用两个空格的方式进行缩进; - 变量函数要以驼峰方式进行命名; - 只能使用```##```单行注释,不能用其他如```//```,```/* */```进行注释,不能多行注释,代码不能缺乏注释,注释内容与代码含义必须保持一致; - 代码要有良好的排版和布局,使得代码便于理解和浏览; - 不能存在超过10行的重复代码段,若存在超过10行的重复代码,应该抽成函数进行调用; - 给出代码格式化建议,确保建议代码要符合aviator脚本语法规范,确保不要对代码逻辑做任何修改; - 如果修改建议代码不符合aviator语法规范将会受到惩罚,如果修改建议代码与原代码严重不符将会受到惩罚; - 采用markdown格式输出,建议代码用```包装起来; </schedule> <example> %s </example> <code> %s </code>
Demo
下一步实践计划
一、效果评估
目前,实践落地还剩下一个非常重要的部分没有完成,也是模型落地应用里非常重要的一个部分:效果评估,AI内容生成是否具备商业化、工业级有效的结果,必须由完备的测评体系来衡量,如果模型生成的代码根本不可用,那这样的应用就毫无意义。
下一步,将针对上述应用场景构建一些评测能力,目前的思路大致按照三个维度去展开:
1、模型交叉评估,如通过meta-llama生成的代码,由qwen或者其他大模型来进行打分评测反馈。
2、系统实现评估,如大模型生成的代码,直接调用引擎进行编译、执行,若编译出错或者执行失败,则进行负向打分反馈;
3、人为评估,对于模型产生的所有内容,建立人工点赞、点踩的快速评价功能,通过低成本的人为反馈来进行评测反馈。
二、智能策略
在指标层的智能生成上若能取得不错的效果,那么规则、策略也可以尝试通过需求描述来生成,因为指标层相对来说是更复杂的代码生成,而规则、策略则更多的是逻辑结构的推理,而这恰恰是大模型的强项,也就可以做到:根据完整的业务需求描述进行分析拆解,转化成策略-规则-指标的智能生成,从而完整实现整个策略的智能化生成。
结语
本文主要以工程视角记录,如何在不训练模型的情况下实现RAG知识库、AI智能生成代码等技术实践,其中还有很多待优化和完善的地方,例如:基于向量检索进行知识召回使得内容更精确,在未训练模型时采用In-Context Learning会存在token资源浪费等,但并不影响以此了解大模型应用背后的技术原理,希望对读者有所启发。
本文实践技术选型
- Aviator语法知识库:语雀知识库
- 文档检索:语雀公共api
- LLM:Meta-Llama-3-1-405B-Instruct
- Rerank:bge-reranker-v2-m3
- 脚本代码补全:aone copilot api
- 提示词及调优:自研
参考链接:
[1]https://www.yuque.com/boyan-avfmj/aviatorscript/ra28g1
[3]https://www.promptingguide.ai/zh
[4]https://huggingface.co/BAAI/bge-reranker-v2-m3
来源 | 阿里云开发者公众号
作者 | 锦还