Help us shape the future of Go! Take the 2026 Go Developer Survey

agenteval

package module
v0.4.41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 20 Imported by: 0

README

agenteval

agenteval mide qué tan bien se comporta un agente de webtyp. Un modelo de lenguaje no responde siempre igual, así que un test de pasa/no pasa no alcanza. Cada escenario, escrito en Go, se corre N veces contra un modelo local, y el resultado es una tasa de éxito.

Lo vas a usar cuando cambies el prompt de un agente (por ejemplo, Cote en veltylabs/mjosefa-cote), su modelo o su manejo de contexto, y quieras saber si mejoró o empeoró sin revisar las respuestas una por una.

Escribir un escenario

//go:build eval

package evals

// Caso de uso: un funcionario pregunta hasta qué hora se atiende hoy.
func TestHorarioDeHoy(t *testing.T) {
	agenteval.Scenario{
		Given: agenteval.Given{
			At:    agenteval.Moment{Year: 2026, Month: 9, Day: 29, Hour: 10, UTCOffsetMinutes: -180},
			Tools: []agenteval.FakeTool{{
				Name:        "list_business_hours",
				Description: "Opening hours of the clinic for every day of the week.",
				InputSchema: `{"type":"object","properties":{}}`,
				Action:      model.Read,
				Returns:     "Monday to Friday 08:00-18:00. Saturday and Sunday closed.",
			}},
		},
		When: "¿Hasta qué hora atendemos hoy?",
		Then: []agenteval.Check{
			agenteval.Calls("list_business_hours"),
			agenteval.DoesNotModify(),
			agenteval.Contains("18:00"),
			agenteval.Faithful(),
			agenteval.AsksNothingToConfirm(),
		},
		Runs: 10, MinPass: 9,
	}.Run(t, func(env agenteval.Env) (*agent.Agent, error) {
		cfg := env.Config() // el decisor, el redactor y el resto de las piezas del escenario
		cfg.Texts = cote.Texts // las palabras de la aplicación
		return agent.New(cfg)
	})
}

Env trae las dos piezas del agente híbrido: el decisor (decider-0.8b, que elige entre opciones) y el redactor (LFM2.5-350M, que escribe respuestas a partir de datos). Al decisor se le pregunta con el mismo texto que lee en el navegador (qwen.DecidePrompt), así que lo que se mide aquí es lo que corre en la aplicación.

Para correr los escenarios se inician tres llama-server:

# Decisor (AGENTEVAL_DECIDER_URL)
llama-server -m ~/Dev/LMmodels/mradermacher/decider-0.8b-GGUF/decider-0.8b.Q8_0.gguf --port 8080 -np 1 -c 4096

# Redactor (AGENTEVAL_WRITER_URL)
llama-server -m ~/Dev/LMmodels/LiquidAI/LFM2.5-350M-GGUF/LFM2.5-350M-Q8_0.gguf --port 8081 --jinja -np 1 -c 4096 --temp 0.3

# Juez (AGENTEVAL_JUDGE_URL)
llama-server -m decider-4b-v2.1-Q4_K_M.gguf --port 8090 -np 1 -c 4096 -ngl 99

El decisor se lee a temperatura 0 (se toma la probabilidad de cada letra, con la temperatura de decisión 1,03 que usa el navegador). El redactor varía entre intentos: cada intento usa su propia semilla (1, 2, …), así que repetir la suite completa da el mismo resultado.

Cómo se decide si un intento aprobó

  1. Verificadores deterministas en Go: qué tools se llamaron y qué contiene la respuesta.
  2. Un juez local para lo que no es una regla. Hoy es decider-4b sobre llama-server.

Laboratorio: el agente en el navegador

Los escenarios miden el agente contra llama-server. El laboratorio lo corre donde va a correr: en un Web Worker del navegador, con los modelos reales en WebAssembly, y un chat para hablarle. La aplicación pasa mensajes de ejemplo a ui.New y cada respuesta muestra una línea con sus tiempos (qué significa cada número).

Pieza Qué es
ui/ el panel de chat: botón para despertar al agente, progreso de descarga, conversación, confirmar o cancelar cambios. Sirve para cualquier agente que corra en webtyp.com/agentworker
lab/ los archivos de modelos del laboratorio (ArtifactSources()) y Serve
lab/worker/ el agente de prueba: un asistente de consultorio con dos herramientas (leer el horario, cambiarlo)
web/client.go la página: ui.New(lab.Scripts)
web/workers/agent/main.go el Worker: agentworker.Serve(worker.Setup(ids))
cmd/lab build de release + servidor HTTPS

Cómo correrlo:

mkdir -p models
ln ~/Dev/LMmodels/Mapika/decider-0.8b/decider-0.8b.{wtypw,merges} models/
ln ~/Dev/LMmodels/LiquidAI/LFM2.5-350M/lfm2.5-350m.{wtypw,merges} models/
go run ./cmd/lab          # build de release en web/public y https://localhost:8443

Abrir https://localhost:8443 y pulsar Despertar al agente. La primera vez el Worker copia los modelos (≈ 1,2 GB) del servidor a su almacenamiento propio (OPFS) y verifica su SHA-256; después arranca desde ahí sin descargar.

Por qué go run ./cmd/lab y no solo webtyp dev: el servidor de desarrollo compila la página pero todavía no compila los Workers (web/workers/), y los modelos solo se publican en el build de release (/artifacts.json, /artifacts/). Con webtyp dev se ve la interfaz; para hablar con el agente, cmd/lab.

Resultados de las pruebas reales: docs/REAL_TESTS.md.

Documentación

  • docs/JUDGE.md: el modelo juez, cómo se le pregunta, cómo se lee y qué tan confiable resultó.

Documentation

Index

Constants

View Source
const (
	TempChoice = 1.11
	TempYesNo  = 1.56
)
View Source
const (

	// DecideTemperature is decider-0.8b's decision temperature, the one the browser uses
	// (qwen.Config.DecideTemperature).
	DecideTemperature = 1.03
)
View Source
const DefaultRuns = 10
View Source
const (
	MinConfidence = 0.8
)

Variables

View Source
var (
	ErrGivenAtRequired   = errors.New("agenteval: Given.At is required")
	ErrMinPassRequired   = errors.New("agenteval: MinPass is required (1..Runs)")
	ErrWhenRequired      = errors.New("agenteval: When is required")
	ErrThenCheckRequired = errors.New("agenteval: Then needs at least one check")
)
View Source
var (
	ErrFakeToolNeedsName = errors.New("agenteval: FakeTool needs a Name")
)

Functions

func NewDeciderJudge added in v0.1.0

func NewDeciderJudge(url string) llm.Decider

NewDeciderJudge creates a new decider-4b judge client.

Types

type Attempt added in v0.1.0

type Attempt struct {
	Question string // Scenario.When
	At       Moment
	Answer   string         // what agent.Run returned (reply.Text)
	Pending  []llm.ToolCall // what the agent asked the person to confirm; not executed
	Err      error          // what agent.Run returned
	Calls    []ToolCall     // every tool call, in order
}

Attempt is what happened in one run of a scenario.

type Builder added in v0.1.0

type Builder func(Env) (*agent.Agent, error)

Builder builds the agent under test from what the scenario provides.

type Check added in v0.1.0

type Check interface {
	Name() string // how the check reads in the report, e.g. `Contains("18:00")`
	Check(a Attempt, j llm.Decider) Result
}

Check judges one attempt.

func AnswersTheQuestion added in v0.1.0

func AnswersTheQuestion() Check

AnswersTheQuestion checks if the assistant's answer actually answers the user's question.

func AsksNothingToConfirm added in v0.2.0

func AsksNothingToConfirm() Check

AsksNothingToConfirm checks that Pending is empty.

func AsksToConfirm added in v0.2.0

func AsksToConfirm(name string) Check

AsksToConfirm checks that some pending tool call has the given name.

func Calls added in v0.1.0

func Calls(name string) Check

Calls checks that some tool call has the given name.

func Contains added in v0.1.0

func Contains(text string) Check

Contains checks that the answer contains the given substring.

func DoesNotModify added in v0.1.0

func DoesNotModify() Check

DoesNotModify checks that every tool call has Action == model.Read.

func Faithful added in v0.1.0

func Faithful() Check

Faithful checks if the assistant's answer states anything not supported by the tool result.

func NotContains added in v0.1.0

func NotContains(text string) Check

NotContains checks that the answer does not contain the given substring.

type Env added in v0.1.0

type Env struct {
	Decider   llm.Decider      // decider-0.8b on llama-server (AGENTEVAL_DECIDER_URL)
	Writer    llm.Client       // LFM2.5-350M on llama-server (AGENTEVAL_WRITER_URL)
	Tokens    llm.TokenCounter // the decider's tokenizer
	Clock     agent.Clock
	Memory    agent.MemoryStore // fresh for every attempt
	IDGen     model.IDGenerator
	ToolIndex agent.ToolIndex
	Tools     []agent.Tool // the scenario's FakeTools; pass them as Config.LocalTools
}

Env is everything a scenario provides to the agent. A Builder passes each field to agent.Config unchanged and adds only the application's Texts, Templates and Guard.

func (Env) Config added in v0.2.0

func (e Env) Config() agent.Config

Config returns an agent.Config with the environment's pieces. A Builder adds the application's words:

cfg := env.Config()
cfg.Texts, cfg.Templates, cfg.Guard = ...
return agent.New(cfg)

type FakeTool added in v0.1.0

type FakeTool struct {
	Name, Description, InputSchema string
	Action                         model.Action // required: model.Read, model.Create, model.Update, model.Delete or a combination
	Returns                        string       // what the tool answers, whatever the arguments
}

FakeTool is a tool with a fixed answer. Its definition should be the real tool's, so the agent sees exactly what it will see in production.

func (FakeTool) Validate added in v0.1.0

func (ft FakeTool) Validate() error

Validate validates the FakeTool specification.

type Given added in v0.1.0

type Given struct {
	At    Moment     // required: the date and time the user speaks
	Tools []FakeTool // the tools the agent can use in this scenario
}

Given is the situation before the user speaks.

type Moment added in v0.1.0

type Moment struct {
	Year, Month, Day, Hour, Minute int
	UTCOffsetMinutes               int // e.g. -180 for UTC-3
}

Moment is a local date and time.

func (Moment) Now added in v0.1.0

func (m Moment) Now() int64

Now returns the unix nanoseconds of that local moment in UTC.

type Result added in v0.1.0

type Result struct {
	Verdict Verdict
	Reason  string // why, in one line
}

type Scenario added in v0.1.0

type Scenario struct {
	Given   Given
	When    string  // the user's message
	Then    []Check // every check must pass for an attempt to pass
	Runs    int     // attempts; 0 means DefaultRuns
	MinPass int     // required: attempts that must pass, 1..Runs
}

Scenario is one use case of an agent. Run executes it Runs times and fails the test when fewer than MinPass attempts pass.

func (Scenario) Run added in v0.1.0

func (s Scenario) Run(t *testing.T, build Builder)

Run executes the scenario Runs times and fails t when fewer than MinPass attempts pass.

func (Scenario) Validate added in v0.1.0

func (s Scenario) Validate() error

Validate returns validation errors for Scenario.

type ToolCall added in v0.1.0

type ToolCall struct {
	Name, Input, Output string
	Action              model.Action
}

type Verdict added in v0.1.0

type Verdict int
const (
	Pass Verdict = iota + 1
	Fail
	Unsure
)

Directories

Path Synopsis
cmd
lab command
Command lab builds the agent laboratory with the real models and serves it on https://localhost:8443 (README, "Laboratorio").
Command lab builds the agent laboratory with the real models and serves it on https://localhost:8443 (README, "Laboratorio").
lab
Package lab is the agent this repository's laboratory runs (web/client.go, web/workers/agent): its model files and its Worker scripts.
Package lab is the agent this repository's laboratory runs (web/client.go, web/workers/agent): its model files and its Worker scripts.
worker
Package worker is the agent the laboratory runs in its Web Worker: a clinic assistant over two tools (the opening hours, and changing them), in Spanish.
Package worker is the agent the laboratory runs in its Web Worker: a clinic assistant over two tools (the opening hours, and changing them), in Spanish.
web
workers/agent command
The agent laboratory's Web Worker: the lab's agent with the real models (lab/worker).
The agent laboratory's Web Worker: the lab's agent with the real models (lab/worker).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL