Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Optimize prompt for test case generate #6049

Merged
merged 1 commit into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion internal/pkg/ai-functions/functions/test-case/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
func (f *Function) CompletionOptions() []sdk.PatchOption {
return []sdk.PatchOption{
sdk.PathOptionWithModel("gpt-35-turbo-16k"),
sdk.PathOptionWithTemperature(1),
// 改变温度参数会改变模型的输出。 温度参数可以设置为 0 到 2。 较高的值(例如 0.7)将使输出更随机,并产生更多发散的响应,而较小的值(例如 0.2)将使输出更加集中和具体。
sdk.PathOptionWithTemperature(0.5),

Check warning on line 116 in internal/pkg/ai-functions/functions/test-case/function.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ai-functions/functions/test-case/function.go#L116

Added line #L116 was not covered by tests
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
要求:
- 使用中文返回
- 每次只能生成一个测试用例
- 尽可能多的给出操作步骤和对应的期望结果
- 包含正向验证测试、反向验证测试、边界条件测试、特殊场景或特殊参数值测试等各种可能得场景对应的测试用例
- 尽可能多的给出操作步骤和对应的期望结果
- 最少给出 4 组 操作步骤和对应的期望结果
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strings"
"sync"

"github.com/pkg/errors"
Expand Down Expand Up @@ -116,15 +118,22 @@
IssueID: tp.IssueID,
Prompt: tp.Prompt,
}

bdl := bundle.New(bundle.WithErdaServer())
issue, err := bdl.GetIssue(tp.IssueID)
if err != nil {
return errors.Wrap(err, "get requirement info failed when create testcase")

Check warning on line 125 in internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go#L125

Added line #L125 was not covered by tests
}
// 根据需求内容生成 prompt
if hasDetailInfoInRequirementContent(issue.Content) {
callbackInput.Prompt = tuningPrompt(issue.Title, issue.Content)
} else {
callbackInput.Prompt = issue.Title

Check warning on line 131 in internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go#L131

Added line #L131 was not covered by tests
}
tp.Prompt = callbackInput.Prompt

if len(tp.Req.StepAndResults) > 0 {
// 表示是修改后批量应用应用生成的测试用例,直接调用创建接口,无需再次生成
bdl := bundle.New(bundle.WithErdaServer())
// 根据 issueID 获取对应的需求 Title
issue, err := bdl.GetIssue(tp.IssueID)
if err != nil {
return errors.Wrap(err, "get requirement info failed when create testcase")
}

aiCreateTestCaseResponse, err := bdl.CreateTestCase(tp.Req)
if err != nil {
err = errors.Errorf("create testcase with req %+v failed: %v", tp.Req, err)
Expand All @@ -138,7 +147,6 @@
TestCaseID: aiCreateTestCaseResponse.TestCaseID,
})
} else {
// 表示需要生成
result, err := aiHandlerUtils.GetChatMessageFunctionCallArguments(ctx, factory, req, openaiURL, tp.Prompt, systemPrompt, callbackInput)
if err != nil {
return err
Expand All @@ -164,10 +172,69 @@
if tp.IssueID <= 0 {
return errors.Errorf("AI function functionParams requirements[%d].issueID for %s invalid", idx, aitestcase.Name)
}
if tp.Prompt == "" && len(tp.Req.StepAndResults) == 0 {
return errors.Errorf("AI function functionParams requirements[%d].prompt for %s invalid", idx, aitestcase.Name)
}
}

return nil
}

// tuningPrompt 提取需求的内容,整理成标准格式,如:
// 需求名称: 注册需求
// 需求描述:
// 1. xxx
// 2. xxx
func tuningPrompt(title, content string) string {
result := make([]string, 0)
inputs := strings.Split(content, "###")
result = append(result, "需求名称: "+title)
result = append(result, "需求描述:")
item := 1

for i := 1; i < len(inputs); i++ {
// 意向用户 和 链接/参考 不作为需求的详细描述内容
if strings.Contains(inputs[i], "意向用户*") || strings.Contains(inputs[i], "链接/参考") {
continue
}

for _, r := range strings.Split(inputs[i], "\n") {
if r == "" || strings.Contains(r, "用户故事/要解决的问题*") || strings.Contains(r, "用户体验目标*") {
continue
}
r = strings.TrimSpace(r)
// 删除常见标题性质的内容
if (strings.HasPrefix(r, "功能需求") && len(r) <= 21) ||
(strings.HasPrefix(r, "安全需求") && len(r) <= 21) ||
(strings.HasPrefix(r, "性能需求") && len(r) <= 21) ||
(strings.HasPrefix(r, "兼容性需求") && len(r) <= 21) ||
(strings.HasPrefix(r, "业务需求") && len(r) <= 21) ||
(strings.HasPrefix(r, "用户需求") && len(r) <= 21) ||
(strings.HasPrefix(r, "系统需求") && len(r) <= 21) {
continue
}
result = append(result, fmt.Sprintf("%d. ", item)+r)
item++
}
}

return strings.Join(result, "\n")
}

func hasDetailInfoInRequirementContent(input string) bool {
result := make([]string, 0)
inputs := strings.Split(input, "\n")

for i := 0; i < len(inputs); i++ {
if inputs[i] == "" {
continue
}
result = append(result, inputs[i])

Check warning on line 229 in internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go#L229

Added line #L229 was not covered by tests
}

input = strings.Join(result, "\n")
fmt.Printf("\nInput now:\n%s\n", input)

if len(result) == 4 {
return false

Check warning on line 236 in internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ai-functions/handler/handler_ai_function_create_testcase.go#L236

Added line #L236 was not covered by tests
}

return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,31 @@ func Test_validateParamsForCreateTestcase(t *testing.T) {
})
}
}

func Test_tuningPrompt(t *testing.T) {
type args struct {
title string
content string
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test_01",
args: args{
title: "注册需求",
content: "注册需求,### 用户故事/要解决的问题*\n功能需求:\n用户访问特定网站或应用程序,并点击注册按钮。\n系统显示一个注册页面,用户在用户名和密码的相应文本框中输入准确的凭据,其中用户名为英文字母,不能是中文,不能超过20个字符;密码是8-16为的大写字母、小写字母、数字组成,密码不能是包括空格,但是能包括@和$符号\n用户点击“注册”按钮或按下回车键来提交注册请求。\n如果提供的凭据是错误的,系统可能会显示错误消息提示用户重新输入准确的信息。\n安全需求��\n密码框应该显示*,密码不能够在页面源码模式下被查看,可以支持复制粘贴,\n在不登录系统的情况下,直接输入后台页面地址不能访问页面\n性能需求:\n在单用户注册场景中,系统响应时间应该小于 3 秒;在高并发场景下,用户注册的响应时间不超过 5 秒\n兼容性需求:\n不同移动设备终端和不同浏览器页面的显示以及功能正确性应该一致\n\n### 意向用户*\n\n\n### 用户体验目标*\n\n\n### 链接/参考\n\n\n",
},
want: "需求名称: 注册需求\n需求描述:\n1. 用户访问特定网站或应用程序,并点击注册按钮。\n2. 系统显示一个注册页面,用户在用户名和密码的相应文本框中输入准确的凭据,其中用户名为英文字母,不能是中文,不能超过20个字符;密码是8-16为的大写字母、小写字母、数字组成,密码不能是包括空格,但是能包括@和$符号\n3. 用户点击“注册”按钮或按下回车键来提交注册请求。\n4. 如果提供的凭据是错误的,系统可能会显示错误消息提示用户重新输入准确的信息。\n5. 密码框应该显示*,密码不能够在页面源码模式下被查看,可以支持复制粘贴,\n6. 在不登录系统的情况下,直接输入后台页面地址不能访问页面\n7. 在单用户注册场景中,系统响应时间应该小于 3 秒;在高并发场景下,用户注册的响应时间不超过 5 秒\n8. 不同移动设备终端和不同浏览器页面的显示以及功能正确性应该一致",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tuningPrompt(tt.args.title, tt.args.content); got != tt.want {
t.Errorf("tuningPrompt() = %v, want %v", got, tt.want)
}
})
}
}
Loading