Skip to content

Commit

Permalink
feat: generate evaluation test dataset
Browse files Browse the repository at this point in the history
Signed-off-by: bjwswang <[email protected]>
  • Loading branch information
bjwswang committed Jan 12, 2024
1 parent 4737e37 commit e19cd7b
Show file tree
Hide file tree
Showing 18 changed files with 285 additions and 25 deletions.
10 changes: 5 additions & 5 deletions apiserver/pkg/chat/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import (
"github.com/kubeagi/arcadia/api/base/v1alpha1"
"github.com/kubeagi/arcadia/apiserver/pkg/auth"
"github.com/kubeagi/arcadia/apiserver/pkg/client"
"github.com/kubeagi/arcadia/pkg/application"
"github.com/kubeagi/arcadia/pkg/application/base"
"github.com/kubeagi/arcadia/pkg/application/retriever"
"github.com/kubeagi/arcadia/pkg/appruntime"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
"github.com/kubeagi/arcadia/pkg/appruntime/retriever"
)

var (
Expand Down Expand Up @@ -99,12 +99,12 @@ func AppRun(ctx context.Context, req ChatReqBody, respStream chan string) (*Chat
Answer: "",
})
ctx = base.SetAppNamespace(ctx, req.AppNamespace)
appRun, err := application.NewAppOrGetFromCache(ctx, app, c)
appRun, err := appruntime.NewAppOrGetFromCache(ctx, c, app)
if err != nil {
return nil, err
}
klog.FromContext(ctx).Info("begin to run application", "appName", req.APPName, "appNamespace", req.AppNamespace)
out, err := appRun.Run(ctx, c, respStream, application.Input{Question: req.Query, NeedStream: req.ResponseMode.IsStreaming(), History: conversation.History})
out, err := appRun.Run(ctx, c, respStream, appruntime.Input{Question: req.Query, NeedStream: req.ResponseMode.IsStreaming(), History: conversation.History})
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion apiserver/pkg/chat/chat_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

"github.com/tmc/langchaingo/memory"

"github.com/kubeagi/arcadia/pkg/application/retriever"
"github.com/kubeagi/arcadia/pkg/appruntime/retriever"
)

type ResponseMode string
Expand Down
6 changes: 3 additions & 3 deletions config/samples/arcadia_v1alpha1_worker_bge-large-zh-v1.5.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
\apiVersion: arcadia.kubeagi.k8s.com.cn/v1alpha1
apiVersion: arcadia.kubeagi.k8s.com.cn/v1alpha1
kind: Worker
metadata:
name: bge-large-zh
namespace: arcadia
namespace: kubeagi-system
spec:
displayName: BGE模型服务
description: "这是一个Embedding模型服务,由BGE提供"
type: "fastchat"
replicas: 1
model:
kind: "Models"
name: "bge-large-zh-v1.5"
name: "bge-large-zh-v1.5"
16 changes: 8 additions & 8 deletions pkg/application/app_run.go → pkg/appruntime/app_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package application
package appruntime

import (
"container/list"
Expand All @@ -28,12 +28,12 @@ import (
"k8s.io/utils/strings/slices"

arcadiav1alpha1 "github.com/kubeagi/arcadia/api/base/v1alpha1"
"github.com/kubeagi/arcadia/pkg/application/base"
"github.com/kubeagi/arcadia/pkg/application/chain"
"github.com/kubeagi/arcadia/pkg/application/knowledgebase"
"github.com/kubeagi/arcadia/pkg/application/llm"
"github.com/kubeagi/arcadia/pkg/application/prompt"
"github.com/kubeagi/arcadia/pkg/application/retriever"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
"github.com/kubeagi/arcadia/pkg/appruntime/chain"
"github.com/kubeagi/arcadia/pkg/appruntime/knowledgebase"
"github.com/kubeagi/arcadia/pkg/appruntime/llm"
"github.com/kubeagi/arcadia/pkg/appruntime/prompt"
"github.com/kubeagi/arcadia/pkg/appruntime/retriever"
)

type Input struct {
Expand Down Expand Up @@ -62,7 +62,7 @@ type Application struct {
// return app.Namespace + "/" + app.Name
//}

func NewAppOrGetFromCache(ctx context.Context, app *arcadiav1alpha1.Application, cli dynamic.Interface) (*Application, error) {
func NewAppOrGetFromCache(ctx context.Context, cli dynamic.Interface, app *arcadiav1alpha1.Application) (*Application, error) {
if app == nil || app.Name == "" || app.Namespace == "" {
return nil, errors.New("app has no name or namespace")
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
"k8s.io/klog/v2"

"github.com/kubeagi/arcadia/api/app-node/chain/v1alpha1"
"github.com/kubeagi/arcadia/pkg/application/base"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
)

type LLMChain struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import (
"k8s.io/klog/v2"

"github.com/kubeagi/arcadia/api/app-node/chain/v1alpha1"
"github.com/kubeagi/arcadia/pkg/application/base"
appretriever "github.com/kubeagi/arcadia/pkg/application/retriever"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
appretriever "github.com/kubeagi/arcadia/pkg/appruntime/retriever"
)

type RetrievalQAChain struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

"k8s.io/client-go/dynamic"

"github.com/kubeagi/arcadia/pkg/application/base"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
)

type Knowledgebase struct {
Expand Down
2 changes: 1 addition & 1 deletion pkg/application/llm/llm.go → pkg/appruntime/llm/llm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"k8s.io/client-go/dynamic"

"github.com/kubeagi/arcadia/api/base/v1alpha1"
"github.com/kubeagi/arcadia/pkg/application/base"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
"github.com/kubeagi/arcadia/pkg/langchainwrap"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"k8s.io/client-go/dynamic"

"github.com/kubeagi/arcadia/api/app-node/prompt/v1alpha1"
"github.com/kubeagi/arcadia/pkg/application/base"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
)

type Prompt struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package retriever

import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
Expand All @@ -34,7 +35,7 @@ import (

apiretriever "github.com/kubeagi/arcadia/api/app-node/retriever/v1alpha1"
"github.com/kubeagi/arcadia/api/base/v1alpha1"
"github.com/kubeagi/arcadia/pkg/application/base"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
"github.com/kubeagi/arcadia/pkg/langchainwrap"
pkgvectorstore "github.com/kubeagi/arcadia/pkg/vectorstore"
)
Expand All @@ -52,6 +53,14 @@ type Reference struct {
LineNumber int `json:"line_number" example:"7"`
}

func (reference Reference) String() string {
bytes, err := json.Marshal(&reference)
if err != nil {
return ""
}
return string(bytes)
}

type KnowledgeBaseRetriever struct {
langchaingoschema.Retriever
base.BaseNode
Expand Down
109 changes: 109 additions & 0 deletions pkg/arctl/eval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2024 KubeAGI.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package arctl

import (
"bytes"
"context"
"io"
"os"

basev1alpha1 "github.com/kubeagi/arcadia/api/base/v1alpha1"

Check failure on line 25 in pkg/arctl/eval.go

View workflow job for this annotation

GitHub Actions / Lint Go code

File is not `gci`-ed with --skip-generated -s standard -s default -s prefix(github.com/kubeagi/arcadia) -s blank -s dot --custom-order (gci)
"github.com/kubeagi/arcadia/apiserver/graph/generated"
"github.com/kubeagi/arcadia/apiserver/pkg/common"
"github.com/kubeagi/arcadia/pkg/evaluation"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"

Check failure on line 32 in pkg/arctl/eval.go

View workflow job for this annotation

GitHub Actions / Lint Go code

File is not `gci`-ed with --skip-generated -s standard -s default -s prefix(github.com/kubeagi/arcadia) -s blank -s dot --custom-order (gci)
)

func NewEvalCmd(kubeClient dynamic.Interface, namespace string) *cobra.Command {
var app string

cmd := &cobra.Command{
Use: "eval",
Short: "Manage evaluations",
}

cmd.Flags().StringVar(&app, "app", "", "The application that is going to be evaluated")

cmd.AddCommand(EvalGenTestDataset(kubeClient, namespace, app))

return cmd
}

func EvalGenTestDataset(kubeClient dynamic.Interface, namespace string, appName string) *cobra.Command {
var file string
var questionColumn string
var groundTruthsColumn string

cmd := &cobra.Command{
Use: "gen_test_dataset",
Short: "Generate a test dataset for evaluation",
RunE: func(cmd *cobra.Command, args []string) error {
// read file content
f, err := os.Open(file)
if err != nil {
return err
}
data, err := io.ReadAll(f)
if err != nil {
return err
}

// read files
app := &basev1alpha1.Application{}
obj, err := common.ResouceGet(context.Background(), kubeClient, generated.TypedObjectReferenceInput{
APIGroup: &common.ArcadiaAPIGroup,
Kind: "Application",
Namespace: &namespace,
Name: appName,
}, v1.GetOptions{})
if err != nil {
return err
}

err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), app)
if err != nil {
return err
}

generator, err := evaluation.NewRagasDatasetGenerator(context.Background(), kubeClient, app)
if err != nil {
return err
}

_, err = generator.Generate(
context.Background(),
bytes.NewReader(data),
evaluation.WithQuestionColumn(questionColumn),
evaluation.WithGroundTruthsColumn(groundTruthsColumn),
)
if err != nil {
return err
}
return nil
},
}

cmd.Flags().StringVar(&file, "file", "", "The file(CSV) which provides question and ground_truths")
cmd.Flags().StringVar(&questionColumn, "question-column", "q", "The column name which provides questions")
cmd.Flags().StringVar(&groundTruthsColumn, "ground-truths-column", "a", "The column name which provides the answers")

return cmd
}
114 changes: 113 additions & 1 deletion pkg/evaluation/evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,116 @@ limitations under the License.

package evaluation

// TO BE DEFINED
import (
"context"
"io"

Check failure on line 22 in pkg/evaluation/evaluation.go

View workflow job for this annotation

GitHub Actions / Lint Go code

File is not `gci`-ed with --skip-generated -s standard -s default -s prefix(github.com/kubeagi/arcadia) -s blank -s dot --custom-order (gci)
"github.com/kubeagi/arcadia/api/base/v1alpha1"
"github.com/kubeagi/arcadia/pkg/appruntime"
"github.com/kubeagi/arcadia/pkg/appruntime/base"
pkgdocumentloaders "github.com/kubeagi/arcadia/pkg/documentloaders"
"k8s.io/client-go/dynamic"

Check failure on line 27 in pkg/evaluation/evaluation.go

View workflow job for this annotation

GitHub Actions / Lint Go code

File is not `gci`-ed with --skip-generated -s standard -s default -s prefix(github.com/kubeagi/arcadia) -s blank -s dot --custom-order (gci)
)

type RagasDataRow struct {
Question string `json:"question"`
GroundTruths string `json:"ground_truths"`
Contexts []string `json:"contexts"`
Answer string `json:"answer"`
}

type RagasDatasetGenerator struct {
cli dynamic.Interface

app appruntime.Application
}

func NewRagasDatasetGenerator(ctx context.Context, cli dynamic.Interface, app *v1alpha1.Application) (*RagasDatasetGenerator, error) {
runapp, err := appruntime.NewAppOrGetFromCache(ctx, cli, app)
if err != nil {
return nil, err
}
return &RagasDatasetGenerator{cli: cli, app: *runapp}, nil
}

type genOptions struct {
// questionColumn in csv file
questionColumn string
groundTruthsColumn string

output Output
}

func defaultGenOptions() *genOptions {
return &genOptions{
questionColumn: "q",
groundTruthsColumn: "a",
output: &PrintOutput{},
}
}

func WithQuestionColumn(questionColumn string) GenOptions {
return func(genOpts *genOptions) {
genOpts.questionColumn = questionColumn
}
}

func WithGroundTruthsColumn(groundTruthsColumn string) GenOptions {
return func(genOpts *genOptions) {
genOpts.groundTruthsColumn = groundTruthsColumn
}
}

func WithOutput(output Output) GenOptions {
return func(genOpts *genOptions) {
genOpts.output = output
}
}

type GenOptions func(*genOptions)

// Generate a test dataset from a file(csv)
func (eval *RagasDatasetGenerator) Generate(ctx context.Context, csvData io.Reader, genOptions ...GenOptions) ([]RagasDataRow, error) {
ctx = base.SetAppNamespace(ctx, eval.app.Namespace)

// set generation options
genOpts := defaultGenOptions()
for _, o := range genOptions {
o(genOpts)
}

// load csv to langchain documents
loader := pkgdocumentloaders.NewQACSV(csvData, "", genOpts.questionColumn, genOpts.groundTruthsColumn)
langchainDocuments, err := loader.Load(ctx)
if err != nil {
return nil, err
}

// convert langchain documents to ragas dataset
var ragasRows = make([]RagasDataRow, len(langchainDocuments))
for docIndex, doc := range langchainDocuments {
ragasRow := RagasDataRow{
Question: doc.PageContent,
GroundTruths: doc.Metadata[genOpts.groundTruthsColumn].(string),
}

// chat with application
out, err := eval.app.Run(ctx, eval.cli, nil, appruntime.Input{Question: ragasRow.Question, NeedStream: false, History: nil})
if err != nil {
return nil, err
}
ragasRow.Answer = out.Answer

// handle context
contexts := make([]string, len(out.References))
for refIndex, reference := range out.References {
contexts[refIndex] = reference.String()
}
ragasRow.Contexts = contexts

// set ragasRows
ragasRows[docIndex] = ragasRow
}

return ragasRows, nil
}
Loading

0 comments on commit e19cd7b

Please sign in to comment.