使用 Gemini 生成有意义的测试数据
众所周知,在将应用程序部署到生产环境之前,拥有一套合适的测试数据对于确保其可靠性和性能至关重要。这有助于模拟真实场景,并在潜在问题或缺陷影响最终用户之前将其识别出来。 此外,使用具有代表性的数据集进行测试,有助于优化性能、发现瓶颈,并根据需要对算法或流程进行微调。归根结底,拥有一套全面的测试数据有助于交付更高质量的产品,降低生产环境上线后出现问题的可能性,并提升整体用户体验。
在本文中,我们将探讨如何利用生成式人工智能(即谷歌的Gemini)为多个对象的属性生成(希望是)有意义的数据。为此,我将使用RESTful服务生成JSON格式的数据,然后利用收到的数据创建对象。

这引出了一个显而易见的问题:为什么不直接使用 %Library.PopulateUtils 中的方法来生成所有数据呢?其实,如果你看过该类的的方法列表,答案也很明显——其中能生成有意义数据的方法并不多。
那么,让我们开始吧。
由于我将使用 Gemini API,而我之前还没有 API 密钥,因此需要先生成一个。要生成 API 密钥,只需打开aistudio.google.com/app/apikey 并点击“创建 API 密钥”。
.png)
并在新项目中创建一个 API 密钥
.png)
完成这一步后,你只需编写一个 REST 客户端来获取和转换数据,并构思一个发送给 Gemini AI 的查询字符串。小菜一碟 😁
为了便于说明,本示例中我们将使用以下简单类
.png)
Class Restaurant.Dish Extends (%Persistent, %JSON.Adaptor)
{
Property Name As %String;
Property Description As %String(MAXLEN = 1000);
Property Category As %String;
Property Price As %Float;
Property Currency As %String;
Property Calories As %Integer;
}通常情况下,使用内置的 %Populate 机制就能轻松搞定。但在大型项目中,你会遇到许多属性,这些属性很难自动填充有意义的数据。
无论如何,既然我们已经有了这个类,就来思考一下向 Gemini 发送查询的措辞。假设我们编写如下查询:
{"contents": [{
"parts":[{
"text": "Write a json object that contains a field Dish which is an array of 10 elements. Each element contains Name, Description, Category, Price, Currency, Calories of the Restaurant Dish."}]}]}如果我们将此请求发送至https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=APIKEY,将会得到类似以下的结果:
剧透
已经不错了。真的不错!既然查询语句的措辞已经确定,我现在需要尽可能自动生成它,调用它并处理结果。
下一步——生成查询。借助那篇关于如何获取类属性列表的非常实用的文章,我们可以自动生成大部分查询内容。
ClassMethod GenerateClassDesc(classname As %String) As %String
{
set cls=##class(%Dictionary.CompiledClass).%OpenId(classname,,.status)
set x=cls.Properties
set profprop = $lb()
for i=3:1:x.Count() {
set prop=x.GetAt(i)
set $list(profprop, i-2) = prop.Name
}
quit $listtostring(profprop, ", ")
}
ClassMethod GenerateQuery(qty As %Numeric) As %String [ Language = objectscript ]
{
set classname = ..%ClassName(1)
set str = "Write a json object that contains a field "_$piece(classname, ".", 2)_
" which is an array of "_qty_" elements. Each element contains "_
..GenerateClassDesc(classname)_" of a "_$translate(classname, ".", " ")_". "
quit str
}在处理类之间的复杂关系时,使用对象构造函数将不同的对象链接在一起,或者使用 %Library.Ppulate 的内置机制,可能会更简单。
下一步是调用 Gemini RESTful 服务,并处理返回的 JSON 数据。
ClassMethod CallService() As %String
{
Set request = ..GetLink()
set query = "{""contents"": [{""parts"":[{""text"": """_..GenerateQuery(20)_"""}]}]}"
do request.EntityBody.Write(query)
set request.ContentType = "application/json"
set sc = request.Post("v1beta/models/gemini-pro:generateContent?key=<YOUR KEY HERE>")
if $$$ISOK(sc) {
Set response = request.HttpResponse.Data.Read()
set p = ##class(%DynamicObject).%FromJSON(response)
set iter = p.candidates.%GetIterator()
do iter.%GetNext(.key, .value, .type )
set iter = value.content.parts.%GetIterator()
do iter.%GetNext(.key, .value, .type )
set obj = ##class(%DynamicObject).%FromJSON($Extract(value.text,8,*-3))
set dishes = obj.Dish
set iter = dishes.%GetIterator()
while iter.%GetNext(.key, .value, .type ) {
set dish = ##class(Restaurant.Dish).%New()
set sc = dish.%JSONImport(value.%ToJSON())
set sc = dish.%Save()
}
}
}当然,由于这只是一个示例,请不要忘记在必要时添加状态检查。
现在,当我运行它时,数据库中生成了相当令人印象深刻的结果。让我们执行一条 SQL 查询来查看这些数据。
.png)
描述和类别与菜品名称相符。此外,价格和卡路里看起来也正确。这意味着我实际上得到了一张数据库,其中填充了看起来相当真实的数据。而我接下来要运行的查询结果,也将与真实结果非常相似。
当然,这种方法的一个巨大缺点是必须向生成式 AI 编写查询语句,而且生成结果需要花费时间。但实际数据可能值得付出这些代价。总之,这由你来决定 😉
附言
附言二:第一张图片展示了Gemini对“编写程序来生成测试数据的AI”的想象 😆