gaslit

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 17 Imported by: 0

README

gaslit

A Go framework that stress-tests AI agents by staging a supervised courtroom debate. Gaslit runs a panel of debater agents against a multiple-choice (or open-ended) question, cross-examines them with a moderator, and detects when agents drift or hallucinate.

Built on top of cloudwego/eino.

How it works

  1. Baseline — every agent answers the question with per-option confidences.
  2. Cross-examination — a supervisor moderator challenges panelists using their own claims and any challenges agents raised, via a cross_examine_agent tool.
  3. Re-evaluation — agents give final positions.
  4. Drift detection — flips, confidence erosion, and a pluggable shift metric are measured, and influence is attributed to the swaying agent (or the supervisor).
  5. Grill / CCI — drifted/influential agents are grilled with an inversion probe and scored on a confidence/consistency quadrant.
Quadrant diagnostics
Quadrant Confidence Consistency Meaning
PEAK high high Grounded, resilient knowledge
HALLUCINATING / MALICIOUS high low Confident but fabricating contradictory facts
KB_DEFICIT low high Logically consistent but lacking evidence
WRONG / UNCERTAIN low low Guessing without internal consistency

Install

go get github.com/ItsArnavSh/gaslit

Requires Go 1.26+.

Usage

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ItsArnavSh/gaslit"
	"github.com/cloudwego/eino/adk"
)

func main() {
	ctx := context.Background()

	// 1. Build your debater agents (any eino adk.Agent).
	econ, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
		Name:        "economic_historian",
		Description: "Analyzes events from a socio-economic lens.",
		Instruction: "You are an economic historian. Argue that economic forces drive history.",
		Model:       myModel,
	})
	pol, _ := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
		Name:        "political_historian",
		Description: "Analyzes events through leadership and military strategy.",
		Instruction: "You are a political and military historian.",
		Model:       myModel,
	})

	// 2. Configure the engine: thresholds, drift metric, attribution, grill
	//    strategy, protocols, prompts, budgets, hooks, persistence, ...
	cfg := gaslit.PresetDefault()
	cfg.SupervisorModel = myModel
	cfg.Agents = []adk.Agent{econ, pol}
	cfg.Thresholds = gaslit.Thresholds{DriftSignificance: 0.15, Confidence: 0.55, Consistency: 0.60}
	cfg.DriftMetric = gaslit.TVDRift
	cfg.AttributionFn = gaslit.ChampionAttribution
	cfg.GrillStrategy = gaslit.GrillSingleSuspect
	cfg.Protocol = gaslit.ProtocolSupervised
	cfg.Concurrency = 4

	engine, err := gaslit.New(ctx, cfg)
	if err != nil {
		log.Fatal(err)
	}

	// 3. Run the debate.
	summary, err := engine.Debate(ctx, "What caused the Roman Empire's collapse?", []string{
		"Economic exhaustion",
		"Military breakdown",
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Outcome: %s\n", summary.OverallOutcome)
	fmt.Printf("Drift: %t (driver: %s)\n", summary.DriftVerdict.HasDrift, summary.DriftVerdict.PrimaryDriver)
	for name, diag := range summary.Diagnostics {
		fmt.Printf("%s → %s (conf %.2f, consistency %.2f)\n", name, diag.Quadrant, diag.ConfidenceScore, diag.ConsistencyScore)
	}
}

Configuration

Everything is set on a Config struct passed to New(ctx, cfg), and any field can be refined with an Option:

  • ThresholdsThresholds{DriftSignificance, Confidence, Consistency}, also per-call via WithThresholdsCall.
  • PresetsPresetDefault(), PresetStrict(), PresetLenient().
  • Drift metricsTVDRift, KLDistance, JSDistance, MaxChange, or any custom DriftMetric function.
  • AttributionChampionAttribution, ShiftWeightedAttribution, CorrelationAttribution, or a custom AttributionFn.
  • Quadrant classifierThresholdClassifier or a custom Classifier.
  • Consistency scoring*LLMScorer (default, uses the supervisor model) or the deterministic *LexicalScorer (zero extra model calls), or a custom ConsistencyScorer.
  • Grill strategyGrillNone, GrillSingleSuspect, GrillInfluencer, GrillAllDrifted, GrillTopN (with GrillCount).
  • ProtocolsProtocolSupervised, ProtocolAdversarialPairs, ProtocolRoundRobin, ProtocolDevilsAdvocate.
  • AggregationMajorityVote, ConfidenceWeightedVote, WeightedVote, ConfidenceWeightedWeightedVote, or a custom AggregationFn.
  • Prompts — override any template via PromptTemplates (initial prompt, debate trigger, grill probe, NLI probe, supervisor system, ...).
  • Answer schema*MCQSchema (default) or *OpenEndedSchema for free-form questions (pass empty options to Debate). Custom schemas implement AnswerSchema.
  • Per-agentAgentConfig{Instruction, Weight, SkipPhases} keyed by name.
  • ExecutionPacingDelay, RequestTimeout, MaxRetries, Concurrency, MaxRounds, OptionLabels.
  • BudgetCostBudget{MaxCalls, MaxTokens, Deadline} caps total work.
  • ObservabilityHooks callbacks, Recorder (e.g. JSONLRecorder), Instrumentation.Stats(), Redact.
  • Moderator toolsExtraTools are added to the supervisor.

Function variants

The pipeline is fully sliced; every stage is callable standalone:

  • Debate(ctx, question, options, opts...) — full pipeline.
  • DebateN(ctx, question, options, rounds, opts...) — multi-round debate.
  • DebateMany(ctx, questions, optionsList, opts...) — batch, reusing context.
  • Baseline(ctx, question, options, opts...) — stage 1 only.
  • CrossExamine(ctx, question, before, opts...) — stage 2 only.
  • Evaluate(ctx, options, opts...) — stage 3 only.
  • DriftAnalysis(before, after, options, opts...) — pure drift verdict.
  • Grill(ctx, agentName, claim, question, opts...) — grill a registered panelist.
  • CCI(ctx, agent, statement, question, opts...)standalone consistency / confidence probe against any adk.Agent, even one outside the panel.
  • Stream(ctx, question, options, opts...) — stream pipeline events as they happen.

All variants accept per-call CallOptions (WithVerboseCall, WithMaxRoundsCall, WithThresholdsCall, WithMetadataCall, WithBudgetCall, ...) that merge over the engine config.

Example

See examples/basic for a full runnable program using Groq's API, including a 429-backoff transport and a "panel of historians" demo.

GROQ_API_KEY=... go run ./examples/basic

License

MIT

Documentation

Index

Constants

View Source
const DefaultJSONInstruction = `` /* 571-byte string literal not displayed */

DefaultJSONInstruction is the output contract agents must follow for multiple-choice answers. Custom AnswerSchema implementations may provide their own instructions instead.

View Source
const OpenEndedInstruction = `` /* 223-byte string literal not displayed */

OpenEndedInstruction is the output contract for free-form (option-less) questions.

Variables

This section is empty.

Functions

func ChampionAttribution added in v0.3.0

func ChampionAttribution(res *PromptResult, labels []string, verdict *DriftVerdict)

ChampionAttribution credits the agent who originally championed the option the biggest sway target moved toward; otherwise it credits the supervisor's cross-examination.

func ConfidenceWeightedVote added in v0.3.0

func ConfidenceWeightedVote(after map[string]*Answer, labels []string, weights map[string]float64) string

ConfidenceWeightedVote weights each panelist's vote by their confidence in their chosen option.

func ConfidenceWeightedWeightedVote added in v0.3.0

func ConfidenceWeightedWeightedVote(after map[string]*Answer, labels []string, weights map[string]float64) string

ConfidenceWeightedWeightedVote combines agent weights with confidence.

func CorrelationAttribution added in v0.3.0

func CorrelationAttribution(res *PromptResult, labels []string, verdict *DriftVerdict)

CorrelationAttribution credits the panelist whose final position is closest to the sway target's new position; otherwise the supervisor.

func JSDistance added in v0.3.0

func JSDistance(labels []string, before, after *Answer) float64

JSDistance is the Jensen-Shannon divergence, a symmetric, bounded variant of KL divergence.

func KLDistance added in v0.3.0

func KLDistance(labels []string, before, after *Answer) float64

KLDistance is the Kullback-Leibler divergence from before to after, with epsilon smoothing to keep it finite.

func MajorityVote added in v0.3.0

func MajorityVote(after map[string]*Answer, labels []string, weights map[string]float64) string

MajorityVote picks the option chosen by the most panelists.

func MaxChange added in v0.3.0

func MaxChange(labels []string, before, after *Answer) float64

MaxChange is the maximum absolute per-option confidence change.

func SaveSummary added in v0.3.0

func SaveSummary(w io.Writer, s *FullDebateSummary) error

SaveSummary writes s to w as indented JSON.

func ShiftWeightedAttribution added in v0.3.0

func ShiftWeightedAttribution(res *PromptResult, labels []string, verdict *DriftVerdict)

ShiftWeightedAttribution credits the panelist whose own confidence in the target's new option rose the most (i.e. who reinforced the target's flip).

func TVDRift added in v0.3.0

func TVDRift(labels []string, before, after *Answer) float64

TVDRift is the total variation distance (L1 distance / 2).

func WeightedVote added in v0.3.0

func WeightedVote(after map[string]*Answer, labels []string, weights map[string]float64) string

WeightedVote weights each panelist's vote by their configured agent weight (default 1.0).

Types

type AgentConfig added in v0.3.0

type AgentConfig struct {
	// Instruction, when non-empty, is a per-agent system instruction that is
	// prepended to every prompt sent to this agent.
	Instruction string
	// Weight is the influence weight used by weighted aggregation functions.
	// Zero means the default weight of 1.0.
	Weight float64
	// SkipPhases lists the phases this agent is excluded from.
	SkipPhases map[Phase]bool
}

AgentConfig customizes a single registered panelist, keyed by agent name.

type AggregationFn added in v0.3.0

type AggregationFn func(after map[string]*Answer, labels []string, weights map[string]float64) string

AggregationFn derives the winning option from the panel's final positions. It returns the winning option label, or "" when there is no clear winner.

type Answer added in v0.3.0

type Answer struct {
	Answer      string             `json:"answer"`
	Confidences map[string]float64 `json:"confidences,omitempty"`
	Reasoning   string             `json:"reasoning"`
	Challenge   *challengeRequest  `json:"challenge,omitempty"`
}

Answer is a single agent's position on a question: its pick, per-option confidences (absent for open-ended questions), reasoning, and an optional nomination of another panelist for cross-examination.

type AnswerSchema added in v0.3.0

type AnswerSchema interface {
	// Instruction is the output-contract text appended to prompts.
	Instruction() string
	// Parse extracts an Answer from raw model output. It must tolerate leading
	// prose, markdown fences, and trailing chatter.
	Parse(raw string) (*Answer, error)
}

AnswerSchema defines the JSON contract agents must follow when answering, and how raw model output is parsed into an Answer.

type AttributionFn added in v0.3.0

type AttributionFn func(res *PromptResult, labels []string, verdict *DriftVerdict)

AttributionFn determines who drove the primary shift. It mutates the verdict with PrimaryDriver and DriverImpact.

type BudgetError added in v0.3.0

type BudgetError struct {
	MaxCalls  int
	Calls     int64
	MaxTokens int
	Tokens    int64
}

BudgetError is returned when a CostBudget is exhausted.

func (*BudgetError) Error added in v0.3.0

func (e *BudgetError) Error() string

type CallOption added in v0.3.0

type CallOption func(*CallOptions)

CallOption customizes a single engine call (Debate, Baseline, Grill, CCI, ...) without mutating the engine's construction-time configuration.

func WithBudgetCall added in v0.3.0

func WithBudgetCall(b CostBudget) CallOption

func WithClaimCall added in v0.3.0

func WithClaimCall(s string) CallOption

func WithConfidenceCall added in v0.3.0

func WithConfidenceCall(c float64) CallOption

func WithGrillCountCall added in v0.3.0

func WithGrillCountCall(n int) CallOption

func WithGrillStrategyCall added in v0.3.0

func WithGrillStrategyCall(s GrillStrategy) CallOption

func WithHooksCall added in v0.3.0

func WithHooksCall(h Hooks) CallOption

func WithMaxRoundsCall added in v0.3.0

func WithMaxRoundsCall(n int) CallOption

func WithMetadataCall added in v0.3.0

func WithMetadataCall(m map[string]any) CallOption

func WithOptionLabelsCall added in v0.3.0

func WithOptionLabelsCall(l []string) CallOption

func WithProtocolCall added in v0.3.0

func WithProtocolCall(p Protocol) CallOption

func WithThresholdsCall added in v0.3.0

func WithThresholdsCall(t Thresholds) CallOption

func WithVerboseCall added in v0.3.0

func WithVerboseCall(v bool) CallOption

type CallOptions added in v0.3.0

type CallOptions struct {
	Verbose       bool
	MaxRounds     int
	OptionLabels  []string
	Thresholds    Thresholds
	GrillStrategy GrillStrategy
	GrillCount    int
	Protocol      Protocol
	Metadata      map[string]any
	Confidence    *float64
	Claim         string
	Hooks         *Hooks
	Budget        CostBudget
}

CallOptions is the resolved per-call configuration.

type Classifier added in v0.3.0

type Classifier func(confScore, consistencyScore float64) (QuadrantState, string)

Classifier maps confidence and consistency scores onto the 2D quadrant grid.

type Config added in v0.3.0

type Config struct {
	// SupervisorModel is the model that powers the moderator and, by default,
	// the LLM consistency scorer. Required.
	SupervisorModel model.BaseModel[*schema.Message]
	// Agents is the panel of debaters. At least one is required. Agent names
	// (from Agent.Name) must be unique.
	Agents []adk.Agent
	// Logger receives all verbose/progress output. Default slog.Default().
	Logger *slog.Logger

	// PacingDelay throttles each model invocation by sleeping before the call.
	// Zero disables pacing. Default 0.
	PacingDelay time.Duration
	// RequestTimeout bounds a single model invocation. Zero disables. Default 0.
	RequestTimeout time.Duration
	// MaxRetries is how many corrective re-prompts to attempt when an agent
	// returns unparseable output. Default 2.
	MaxRetries int
	// Concurrency is the number of agents queried in parallel during the
	// baseline and evaluation phases. Default 1 (sequential).
	Concurrency int
	// MaxRounds is the number of (cross-examine → evaluate) rounds run by
	// Debate. Default 1.
	MaxRounds int

	// Thresholds drives drift detection and quadrant classification. Zero
	// Thresholds resolves to the defaults.
	Thresholds Thresholds
	// OptionLabels overrides the auto-generated A, B, C... labels used for
	// multiple-choice questions. Leave nil to auto-generate.
	OptionLabels []string
	// AnswerSchema defines the JSON output contract agents must follow. Default
	// is the MCQ schema. Open-ended questions (no options) always use the
	// open-ended schema regardless of this field.
	AnswerSchema AnswerSchema
	// Protocol selects the cross-examination orchestration strategy.
	Protocol Protocol
	// GrillStrategy selects which agents get grilling diagnostics.
	GrillStrategy GrillStrategy
	// GrillCount is the number of agents grilled by GrillTopN. Default 1.
	GrillCount int
	// AggregationFn derives the winning option from the final positions.
	AggregationFn AggregationFn
	// DriftMetric computes the distribution shift between an agent's before and
	// after positions. Only used for multiple-choice questions.
	DriftMetric DriftMetric
	// AttributionFn determines who drove the primary shift.
	AttributionFn AttributionFn
	// Classifier maps confidence/consistency onto the quadrant grid.
	Classifier Classifier
	// ConsistencyScorer evaluates whether two arguments from the same agent
	// contradict each other. Defaults to an LLM scorer over SupervisorModel.
	ConsistencyScorer ConsistencyScorer
	// PromptTemplates overrides any or all of the built-in prompts.
	PromptTemplates PromptTemplates

	// AgentOverrides holds per-agent customization keyed by agent name.
	AgentOverrides map[string]AgentConfig
	// ExtraTools are additional tools given to the moderator alongside the
	// built-in cross_examine_agent tool.
	ExtraTools []tool.BaseTool
	// Hooks are observability callbacks fired at pipeline milestones.
	Hooks Hooks
	// Recorder receives a JSON-serializable Event stream for persistence.
	Recorder Recorder
	// Redact, when non-nil, is applied to every outgoing prompt before it is
	// sent to any model.
	Redact func(string) string
	// Budget caps total work per debate.
	Budget CostBudget
	// Verbose enables default progress logging when a call does not override it.
	Verbose bool
}

Config is the construction-time configuration for a Gaslit engine. The zero value is not usable directly; build one via Preset* helpers and/or New's Option funcs. New applies defaults for every unset field.

func PresetDefault added in v0.3.0

func PresetDefault() Config

PresetDefault returns the recommended all-purpose configuration.

func PresetLenient added in v0.3.0

func PresetLenient() Config

PresetLenient returns a configuration that ignores small shifts and grades agents leniently.

func PresetStrict added in v0.3.0

func PresetStrict() Config

PresetStrict returns a configuration that flags smaller shifts and demands higher confidence/consistency before an agent is graded PEAK.

type ConsistencyScorer added in v0.3.0

type ConsistencyScorer interface {
	Score(ctx context.Context, question, r1, r2 string) (float64, error)
}

ConsistencyScorer evaluates whether two arguments from the same agent contradict each other. Scores range 0.0 (contradicts itself) to 1.0 (clean).

type CostBudget added in v0.3.0

type CostBudget struct {
	// MaxCalls limits the total number of model invocations.
	MaxCalls int
	// MaxTokens is an approximate token budget (characters / 4) consumed by
	// all outgoing prompt content.
	MaxTokens int
	// Deadline bounds the total wall-clock time of a debate.
	Deadline time.Duration
}

CostBudget caps how much work a single debate (or debate batch) may perform. A zero value for any field means "unlimited" for that field.

type DiagnosticResult

type DiagnosticResult struct {
	AgentName        string        `json:"agent_name"`
	ConfidenceScore  float64       `json:"confidence_score"`  // X-axis (0.0 -> 1.0)
	ConsistencyScore float64       `json:"consistency_score"` // Y-axis (0.0 -> 1.0)
	Quadrant         QuadrantState `json:"quadrant"`
	InitialReasonR1  string        `json:"r1_initial_reasoning"`
	CounterReasonR2  string        `json:"r2_counter_reasoning"`
	Recommendation   string        `json:"recommendation"`
}

DiagnosticResult is the outcome of grilling a single agent: where it lands on the confidence/consistency grid and what action is recommended.

type DriftMetric added in v0.3.0

type DriftMetric func(labels []string, before, after *Answer) float64

DriftMetric computes the distribution shift between an agent's before and after confidence distributions over the given option labels.

type DriftVerdict

type DriftVerdict struct {
	// HasDrift reports whether any agent flipped or significantly eroded.
	HasDrift bool `json:"has_drift"`
	// DriftedAgents lists the names of agents that drifted.
	DriftedAgents []string `json:"drifted_agents"`
	// PrimaryDriver is the agent (or the supervisor) responsible for the shift.
	PrimaryDriver string `json:"primary_driver"`
	// DriverImpact is a human-readable description of who swayed whom.
	DriverImpact string `json:"driver_impact"`
	// AgentShifts maps each agent name to its measured shift.
	AgentShifts map[string]float64 `json:"agent_shifts"`
}

DriftVerdict describes whether agents changed their positions over the course of a debate, which agents moved, and who drove the shift.

type Event added in v0.3.0

type Event struct {
	Time  time.Time       `json:"time"`
	Type  EventType       `json:"type"`
	Agent string          `json:"agent,omitempty"`
	Data  json.RawMessage `json:"data,omitempty"`
}

Event is a JSON-serializable record of a pipeline milestone, suitable for persistence or replay.

type EventType added in v0.3.0

type EventType string

EventType identifies a recorded pipeline event.

const (
	EventPhaseStart EventType = "phase_start"
	EventPhaseEnd   EventType = "phase_end"
	EventBaseline   EventType = "baseline"
	EventCrossExam  EventType = "cross_exam"
	EventEval       EventType = "evaluation"
	EventDrift      EventType = "drift"
	EventDiagnostic EventType = "diagnostic"
	EventSummary    EventType = "summary"
	EventDone       EventType = "done"
	EventError      EventType = "error"
)

type FullDebateSummary

type FullDebateSummary struct {
	Question       string                       `json:"question"`
	Options        []string                     `json:"options"`
	OptionLabels   []string                     `json:"option_labels"`
	PromptResult   *PromptResult                `json:"prompt_result"`
	DriftVerdict   DriftVerdict                 `json:"drift_verdict"`
	Diagnostics    map[string]*DiagnosticResult `json:"diagnostics,omitempty"` // Targeted grilling results
	Winner         string                       `json:"winner"`                // Aggregated winning option ("" if none)
	OverallOutcome string                       `json:"overall_outcome"`
	Metadata       map[string]any               `json:"metadata,omitempty"`
}

FullDebateSummary is the complete result of a Debate run: the question and options, pre/post positions, drift verdict, targeted grilling diagnostics, and the aggregated winner.

func LoadSummary added in v0.3.0

func LoadSummary(r io.Reader) (*FullDebateSummary, error)

LoadSummary reads a FullDebateSummary from r.

type Gaslit

type Gaslit struct {
	// contains filtered or unexported fields
}

Gaslit orchestrates a panel of debater agents under a supervising moderator. Instances are built with New and safe for concurrent use across debates.

func New added in v0.3.0

func New(ctx context.Context, cfg Config, opts ...Option) (*Gaslit, error)

New builds a Gaslit engine from a Config. Every unset field receives a default; see Config and the Option helpers. At least one agent is required, and agent names must be unique. Concurrent calls to Debate on the same engine are safe.

func (*Gaslit) Baseline added in v0.3.0

func (g *Gaslit) Baseline(ctx context.Context, question string, options []string, opts ...CallOption) (map[string]*Answer, error)

Baseline collects each agent's initial position on the question.

func (*Gaslit) CCI added in v0.3.0

func (g *Gaslit) CCI(ctx context.Context, agent adk.Agent, statement, question string, opts ...CallOption) (*DiagnosticResult, error)

CCI runs a standalone Confidence/Consistency probe against any agent on a given statement. The agent does not need to be part of the panel. Confidence defaults to the agent's self-reported confidence in the statement; override it with WithConfidenceCall.

func (*Gaslit) CrossExamine added in v0.3.0

func (g *Gaslit) CrossExamine(ctx context.Context, question string, before map[string]*Answer, opts ...CallOption) error

CrossExamine runs one cross-examination round against the given positions.

func (*Gaslit) Debate

func (g *Gaslit) Debate(ctx context.Context, question string, options []string, opts ...CallOption) (*FullDebateSummary, error)

Debate runs a full panel evaluation on the given question:

  1. Collects each agent's baseline position and confidence.
  2. Cross-examines panelists (moderator-led or protocol-driven).
  3. Re-collects each agent's final position.
  4. Detects drift and attributes influence between agents.
  5. Grills the agents selected by the active GrillStrategy.

Options are labeled A, B, C, ... in order (or via WithOptionLabels). Pass an empty options slice for open-ended questions. When verbose is true, phase progress is logged through the configured logger.

func (*Gaslit) DebateMany added in v0.3.0

func (g *Gaslit) DebateMany(ctx context.Context, questions []string, optionsList [][]string, opts ...CallOption) ([]*FullDebateSummary, error)

DebateMany runs a debate for each (question, options) pair in order, reusing the panel's conversation state so agents carry context across questions.

func (*Gaslit) DebateN added in v0.3.0

func (g *Gaslit) DebateN(ctx context.Context, question string, options []string, rounds int, opts ...CallOption) (*FullDebateSummary, error)

DebateN runs a Debate with a specific number of (cross-examine → evaluate) rounds, overriding the engine's MaxRounds for this call.

func (*Gaslit) DriftAnalysis added in v0.3.0

func (g *Gaslit) DriftAnalysis(before, after map[string]*Answer, options []string, opts ...CallOption) DriftVerdict

DriftAnalysis computes the drift verdict between before and after positions using the engine's configured metric, thresholds, and attribution strategy.

func (*Gaslit) Evaluate added in v0.3.0

func (g *Gaslit) Evaluate(ctx context.Context, options []string, opts ...CallOption) (map[string]*Answer, error)

Evaluate re-collects each agent's final position after cross-examination.

func (*Gaslit) Grill added in v0.3.0

func (g *Gaslit) Grill(ctx context.Context, agentName, claim, question string, opts ...CallOption) (*DiagnosticResult, error)

Grill runs a grilling diagnostic against a registered panelist on a claim.

func (*Gaslit) Stats added in v0.3.0

func (g *Gaslit) Stats() InstrumentationStats

Stats returns a snapshot of the engine's instrumentation counters.

func (*Gaslit) Stream added in v0.3.0

func (g *Gaslit) Stream(ctx context.Context, question string, options []string, opts ...CallOption) (<-chan StreamEvent, error)

Stream runs a full debate and emits events as they occur.

type GrillStrategy added in v0.3.0

type GrillStrategy int

GrillStrategy selects which panelists receive a grilling diagnostic.

const (
	// GrillNone skips grilling entirely.
	GrillNone GrillStrategy = iota
	// GrillSingleSuspect grills the single most drifted/influential agent.
	GrillSingleSuspect
	// GrillInfluencer grills the primary driver when it is a panelist,
	// otherwise the most drifted agent.
	GrillInfluencer
	// GrillAllDrifted grills every agent flagged as drifted.
	GrillAllDrifted
	// GrillTopN grills the GrillCount most drifted agents.
	GrillTopN
)

func (GrillStrategy) String added in v0.3.0

func (s GrillStrategy) String() string

type Hooks added in v0.3.0

type Hooks struct {
	OnPhaseStart func(ctx context.Context, phase Phase)
	OnPhaseEnd   func(ctx context.Context, phase Phase)
	OnBaseline   func(ctx context.Context, agent string, ans *Answer)
	OnCrossExam  func(ctx context.Context, target, prompt string)
	OnEval       func(ctx context.Context, agent string, ans *Answer)
	OnDrift      func(ctx context.Context, verdict *DriftVerdict)
	OnDiagnostic func(ctx context.Context, diag *DiagnosticResult)
}

Hooks are observability callbacks fired at pipeline milestones. Nil fields are skipped. All hooks receive the debate's context.

type InstrumentationStats added in v0.3.0

type InstrumentationStats struct {
	TotalCalls    int64
	Tokens        int64
	AgentCalls    map[string]int64
	PhaseDuration map[string][]time.Duration
}

InstrumentationStats is a point-in-time snapshot of engine usage.

type JSONLRecorder added in v0.3.0

type JSONLRecorder struct {
	// contains filtered or unexported fields
}

JSONLRecorder writes one JSON Event per line to an io.Writer.

func NewJSONLRecorder added in v0.3.0

func NewJSONLRecorder(w io.Writer) *JSONLRecorder

NewJSONLRecorder returns a JSONLRecorder writing to w.

func (*JSONLRecorder) Record added in v0.3.0

func (r *JSONLRecorder) Record(_ context.Context, ev Event) error

Record marshals and writes ev as a single JSON line.

type LLMScorer added in v0.3.0

type LLMScorer struct {
	Model  model.BaseModel[*schema.Message]
	Logger *slog.Logger
	// NLIProbe builds the evaluation prompt. When nil, the engine's configured
	// prompt template is used.
	NLIProbe func(question, r1, r2 string) string
}

LLMScorer implements ConsistencyScorer by having a model run a logical entailment / contradiction check between two arguments.

func (*LLMScorer) Score added in v0.3.0

func (s *LLMScorer) Score(ctx context.Context, question, r1, r2 string) (float64, error)

Score runs the entailment probe and parses the consistency_score. It falls back to a neutral 0.5 on any parse failure.

type LexicalScorer added in v0.3.0

type LexicalScorer struct{}

LexicalScorer implements ConsistencyScorer deterministically with zero extra model calls: it returns the Jaccard similarity of the two arguments' word sets. It is best used as a cheap approximation when an LLM evaluator is not available.

func (*LexicalScorer) Score added in v0.3.0

func (s *LexicalScorer) Score(ctx context.Context, question, r1, r2 string) (float64, error)

Score returns the lexical overlap between the two arguments as a consistency proxy.

type MCQSchema added in v0.3.0

type MCQSchema struct{}

MCQSchema is the default schema for multiple-choice questions: it requires an option label and a confidence for every option.

func (*MCQSchema) Instruction added in v0.3.0

func (s *MCQSchema) Instruction() string

func (*MCQSchema) Parse added in v0.3.0

func (s *MCQSchema) Parse(raw string) (*Answer, error)

type OpenEndedSchema added in v0.3.0

type OpenEndedSchema struct{}

OpenEndedSchema is the schema for free-form questions: agents return their answer text with no option labels or confidences.

func (*OpenEndedSchema) Instruction added in v0.3.0

func (s *OpenEndedSchema) Instruction() string

func (*OpenEndedSchema) Parse added in v0.3.0

func (s *OpenEndedSchema) Parse(raw string) (*Answer, error)

type Option added in v0.3.0

type Option func(*Config)

Option mutates a Config before the engine is constructed. All fields left unset still receive their defaults from New.

func WithAgentConfig added in v0.3.0

func WithAgentConfig(name string, ac AgentConfig) Option

func WithAggregation added in v0.3.0

func WithAggregation(fn AggregationFn) Option

func WithAnswerSchema added in v0.3.0

func WithAnswerSchema(s AnswerSchema) Option

func WithAttribution added in v0.3.0

func WithAttribution(fn AttributionFn) Option

func WithBudget added in v0.3.0

func WithBudget(b CostBudget) Option

func WithClassifier added in v0.3.0

func WithClassifier(cl Classifier) Option

func WithConcurrency added in v0.3.0

func WithConcurrency(n int) Option

func WithConsistencyScorer added in v0.3.0

func WithConsistencyScorer(s ConsistencyScorer) Option

func WithDriftMetric added in v0.3.0

func WithDriftMetric(m DriftMetric) Option

func WithExtraTools added in v0.3.0

func WithExtraTools(tools ...tool.BaseTool) Option

func WithGrillCount added in v0.3.0

func WithGrillCount(n int) Option

func WithGrillStrategy added in v0.3.0

func WithGrillStrategy(s GrillStrategy) Option

func WithHooks added in v0.3.0

func WithHooks(h Hooks) Option

func WithLogger added in v0.3.0

func WithLogger(l *slog.Logger) Option

func WithMaxRetries added in v0.3.0

func WithMaxRetries(n int) Option

func WithMaxRounds added in v0.3.0

func WithMaxRounds(n int) Option

func WithOptionLabels added in v0.3.0

func WithOptionLabels(labels []string) Option

func WithPacingDelay added in v0.3.0

func WithPacingDelay(d time.Duration) Option

func WithPromptTemplates added in v0.3.0

func WithPromptTemplates(t PromptTemplates) Option

func WithProtocol added in v0.3.0

func WithProtocol(p Protocol) Option

func WithRecorder added in v0.3.0

func WithRecorder(r Recorder) Option

func WithRedact added in v0.3.0

func WithRedact(fn func(string) string) Option

func WithRequestTimeout added in v0.3.0

func WithRequestTimeout(d time.Duration) Option

func WithThresholds added in v0.3.0

func WithThresholds(t Thresholds) Option

func WithVerbose added in v0.3.0

func WithVerbose(v bool) Option

type Phase added in v0.3.0

type Phase int

Phase identifies a pipeline stage. It is used by hooks, instrumentation, and per-agent phase skipping.

const (
	PhaseBaseline Phase = iota
	PhaseCrossExamination
	PhaseEvaluation
	PhaseDriftAnalysis
	PhaseGrilling
)

func (Phase) String added in v0.3.0

func (p Phase) String() string

type PromptResult

type PromptResult struct {
	Before map[string]*Answer
	After  map[string]*Answer
}

PromptResult captures each agent's position before and after the debate.

type PromptTemplates added in v0.3.0

type PromptTemplates struct {
	// SupervisorSystem is the moderator system prompt. %s is replaced with the
	// comma-joined panelist names.
	SupervisorSystem string
	// CrossExamineLead is formatted with (challenge prompt, schema
	// instruction) when the moderator or a protocol challenges an agent.
	CrossExamineLead string
	// FinalEvalPrompt precedes the schema instruction when re-collecting final
	// positions.
	FinalEvalPrompt string
	// InitialPrompt builds the baseline question prompt.
	InitialPrompt func(question string, options []string, labels []string, instr string) string
	// DebateTrigger builds the moderator's cross-examination briefing.
	DebateTrigger func(question string, names []string, before map[string]*Answer) string
	// GrillProbe builds the adversarial inversion probe.
	GrillProbe func(question, claim string) string
	// NLIProbe builds the consistency-evaluation prompt.
	NLIProbe func(question, r1, r2 string) string
	// ChallengePrompt builds a direct peer challenge used by the non-supervised
	// protocols.
	ChallengePrompt func(from, claim, reasoning string) string
	// ConfidenceProbe asks an agent to self-report confidence in a claim.
	ConfidenceProbe func(claim string) string
}

PromptTemplates lets callers override any or all of the prompts the engine builds. Function fields default to the built-in builders; string fields default to the built-in templates. Zero fields are resolved by New.

type Protocol added in v0.3.0

type Protocol int

Protocol is the cross-examination orchestration strategy.

const (
	// ProtocolSupervised has the moderator agent run the cross-examination.
	ProtocolSupervised Protocol = iota
	// ProtocolAdversarialPairs pairs panelists and runs a direct challenge
	// exchange within each pair.
	ProtocolAdversarialPairs
	// ProtocolRoundRobin has every panelist challenge every other panelist.
	ProtocolRoundRobin
	// ProtocolDevilsAdvocate confronts each panelist with the strongest
	// opposing argument available in the panel.
	ProtocolDevilsAdvocate
)

func (Protocol) String added in v0.3.0

func (p Protocol) String() string

type QuadrantState

type QuadrantState string

QuadrantState is the 2D classification of an agent based on its confidence (x-axis) and semantic consistency (y-axis) under cross-examination.

const (
	// QuadrantPeak indicates high confidence and high consistency.
	QuadrantPeak QuadrantState = "PEAK (Grounded Knowledge)"
	// QuadrantHallucinating indicates high confidence with fabricated contradictions.
	QuadrantHallucinating QuadrantState = "HALLUCINATING / MALICIOUS"
	// QuadrantKBDeficit indicates consistent but low-confidence answers.
	QuadrantKBDeficit QuadrantState = "KB_DEFICIT (Incomplete Info)"
	// QuadrantWrong indicates low confidence and inconsistent internal logic.
	QuadrantWrong QuadrantState = "WRONG / UNCERTAIN"
)

func ThresholdClassifier added in v0.3.0

func ThresholdClassifier(confScore, consistencyScore float64) (QuadrantState, string)

ThresholdClassifier is the default classifier, using the configured confidence/consistency thresholds.

type Recorder added in v0.3.0

type Recorder interface {
	Record(ctx context.Context, ev Event) error
}

Recorder receives an Event stream for persistence.

type StreamEvent added in v0.3.0

type StreamEvent struct {
	Type       EventType         `json:"type"`
	Phase      Phase             `json:"phase,omitempty"`
	Agent      string            `json:"agent,omitempty"`
	Prompt     string            `json:"prompt,omitempty"`
	Answer     *Answer           `json:"answer,omitempty"`
	Verdict    *DriftVerdict     `json:"verdict,omitempty"`
	Diagnostic *DiagnosticResult `json:"diagnostic,omitempty"`
	Metadata   map[string]any    `json:"metadata,omitempty"`
	Error      error             `json:"-"`
}

Stream runs Debate and streams each milestone over a channel as it happens. The caller must drain the channel; it is closed when the debate finishes.

type Thresholds added in v0.3.0

type Thresholds struct {
	// DriftSignificance is the minimum shift (TVD or lexical distance) or
	// confidence erosion required to flag an agent as drifted. Default 0.15.
	DriftSignificance float64
	// Confidence is the x-axis (confidence) threshold for quadrant
	// classification. Default 0.55.
	Confidence float64
	// Consistency is the y-axis (consistency) threshold for quadrant
	// classification. Default 0.60.
	Consistency float64
}

Thresholds are the tunable cut-offs used across drift detection and quadrant classification. All fields are floats in the range [0.0, 1.0].

Directories

Path Synopsis
examples
basic command

Jump to

Keyboard shortcuts

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