← Library AI AI-Powered

AIBestMatchOp

Selects the single best-matching candidate string for a query using Claude.

Inputs

Field Type Description
Query *string The query or question to match against candidates
Candidates *[]string The list of candidate strings to choose from

Outputs

Field Type Description
Result int 0-based index of the best matching candidate

Reasoning is captured by WithReasoningLog — no graph output needed.

Params

Key Type Default Description
max_retries string "3" Number of retries on parse error

Typical Use Cases

  • FAQ routing — map a user question to the closest FAQ entry
  • Category matching against a dynamic list
  • Intent-to-handler mapping for chatbots
  • Nearest-option selection from a retrieved candidate set

Returns an index, not the string. Use SliceAtOp with the Result index to retrieve the actual candidate value for use downstream.

Pairs well with retrieval. A common pattern is to retrieve N candidate strings via an HTTP or embedding step, pass them as Candidates, and let AIBestMatchOp select the most relevant one.

Complete Runnable Example

Matches a support question to the best FAQ entry from a candidate list, then retrieves the winning text via SliceAtOp.

main.go
package main

import (
	"context"
	"encoding/json"
	"log"
	"os"
	"time"

	"github.com/akennis/sparsi-go/library"
	_ "github.com/akennis/dagor/operator/builtin"

	"github.com/panjf2000/ants/v2"
	"github.com/akennis/dagor"
	"github.com/akennis/dagor/graph"
)

func main() {
	query := "How do I reset my password?"
	candidates := []string{
		"How to update billing information",
		"Password reset and account access",
		"Cancel or pause my subscription",
		"Contact support by phone or chat",
	}

	library.RegisterConst("query_val", query)
	library.RegisterConst("candidates_val", candidates)

	g, err := graph.NewBuilder("best_match_demo").
		Vertex("q_src").Op("query_val").
		Output("Result", "query_wire").

		Vertex("c_src").Op("candidates_val").
		Output("Result", "candidates_wire").

		Vertex("matcher").Op("AIBestMatchOp").
		Input("Query", "query_wire").
		Input("Candidates", "candidates_wire").
		Output("Result", "best_index").

		Vertex("fetch").Op("SliceAtOp").
		Input("Input", "candidates_wire").
		Input("Index", "best_index").
		Output("Result", "best_candidate").

		Build()
	if err != nil {
		log.Fatalf("build: %v", err)
	}

	pool, err := ants.NewPool(4)
	if err != nil {
		log.Fatalf("pool: %v", err)
	}
	defer pool.Release()

	eng, err := dagor.NewEngine(g, pool)
	if err != nil {
		log.Fatalf("engine: %v", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()

	if err := eng.Run(ctx); err != nil {
		log.Fatalf("run: %v", err)
	}

	out := map[string]any{}
	if raw, ok := eng.GetOutput("best_index"); ok {
		out["best_index"] = *raw.(*int)
	}
	if raw, ok := eng.GetOutput("best_candidate"); ok {
		out["best_candidate"] = *raw.(*string)
	}

	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	enc.Encode(out)
}