TextEvalHandler 返回一个 HTTP handler, 用于处理文本评估请求。 路径示例: POST /v1/evals/text 请求体: { "answer": "Paris is the capital of France.", "reference": "Paris is the capital city of France, a country in Europe.", "keywords": ["paris", "capital", "france", "europe"], "scorers": ["keyword_coverage
()
| 665 | // ] |
| 666 | // } |
| 667 | func (s *Server) TextEvalHandler() http.Handler { |
| 668 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 669 | start := time.Now() |
| 670 | |
| 671 | if r.Method != http.MethodPost { |
| 672 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) |
| 673 | return |
| 674 | } |
| 675 | |
| 676 | var req TextEvalRequest |
| 677 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| 678 | http.Error(w, "invalid JSON body", http.StatusBadRequest) |
| 679 | return |
| 680 | } |
| 681 | |
| 682 | if req.Answer == "" { |
| 683 | http.Error(w, "answer is required", http.StatusBadRequest) |
| 684 | return |
| 685 | } |
| 686 | |
| 687 | // 解析需要运行的 scorer 列表 |
| 688 | scorerNames := req.Scorers |
| 689 | if len(scorerNames) == 0 { |
| 690 | scorerNames = []string{"keyword_coverage", "lexical_similarity"} |
| 691 | } |
| 692 | |
| 693 | for _, name := range scorerNames { |
| 694 | if name != "keyword_coverage" && name != "lexical_similarity" { |
| 695 | http.Error(w, "unsupported scorer: "+name, http.StatusBadRequest) |
| 696 | return |
| 697 | } |
| 698 | } |
| 699 | |
| 700 | ctx := r.Context() |
| 701 | ctx, cancel := context.WithTimeout(ctx, 30*time.Second) |
| 702 | defer cancel() |
| 703 | |
| 704 | input := &evals.TextEvalInput{ |
| 705 | Answer: req.Answer, |
| 706 | Context: req.Context, |
| 707 | Reference: req.Reference, |
| 708 | } |
| 709 | |
| 710 | scores := make([]evals.ScoreResult, 0, len(scorerNames)) |
| 711 | |
| 712 | for _, name := range scorerNames { |
| 713 | switch name { |
| 714 | case "keyword_coverage": |
| 715 | scorer := evals.NewKeywordCoverageScorer(evals.KeywordCoverageConfig{ |
| 716 | Keywords: req.Keywords, |
| 717 | CaseInsensitive: true, |
| 718 | }) |
| 719 | res, err := scorer.Score(ctx, input) |
| 720 | if err != nil { |
| 721 | logging.Error(ctx, "http.evals.text.error", map[string]any{ |
| 722 | "scorer": "keyword_coverage", |
| 723 | "error": err.Error(), |
| 724 | "latency_ms": time.Since(start).Milliseconds(), |
nothing calls this directly
no test coverage detected