Deliberative aggregation

Voting among many judges at evaluation time

These systems collect many evaluations after training and combine them with a voting rule rather than an average, instead of building the aggregation into training. Several models from different model families, or one compact reward model fitted to each annotator, each score the candidate outputs and vote. The same idea treats existing benchmark and tournament results as ballots and searches for the ranking of agents that contradicts the fewest of them. Because the voting happens at evaluation time, who votes can be changed without retraining anything. This is what separates it from the approach that writes a voting rule into the training objective.

The method (highlighting differences with Deliberative aggregation)

Scroll the diagram sideways to see all of it.

Use it in codefrom aiag.procedural import Jury
from aiag import LM, Judged
from aiag.procedural import Jury

lm = LM("Qwen/Qwen2.5-1.5B-Instruct")
ask = "How long can cooked rice sit out?"
replies = ["Toss it after two hours at room temperature.", "Rice keeps forever.", "Refrigerate it within an hour."]

# LMs and reward functions both sit on the jury; a Borda count, not an average, combines their ballots
jury = Jury([lm, Judged(lm, "Is this response accurate?"), Judged(lm, "Is this response clear?")])
scores = jury(ask, replies)  # Borda score per reply
best = replies[jury.best(ask, replies)]

pip install aiagEvery method in aiag Library on GitHub

Papers

arXiv:2605.01642MethodPartialAuthority: Developer (open)

Learns a compact reward model for each annotator by low-rank decomposition over a shared reward basis. These models then vote as a jury over candidate outputs under a social-choice rule. As values shift over time, the jury adapts by fitting new annotator weights on the fixed bases. A proof-of-concept on the PRISM dataset with simulated historical annotators finds that jury composition and the choice of voting rule can substantially affect outcomes where jury preferences are heterogeneous.

CodeAdaptivePluralistic
from aiag import LM
from aiag.data import pref_data
from aiag.procedural import AdaptivePluralistic

lm = LM("Qwen/Qwen2.5-1.5B-Instruct")
ask = "Should the city allow food trucks downtown?"
first = pref_data([ask] * 2, ["Yes, with permits.", "Yes, on side streets."], ["No.", "No, protect restaurants."],
                  annotator=["ana", "ben"])
later = pref_data([ask], ["Only on weekends."], ["Yes, everywhere."], annotator=["caz"])

ap = AdaptivePluralistic(lm).fit(first)  # shared basis rewards, and each annotator's mix of them
ap.adapt(later, period=1)                # a new period's annotators fit only their own mix
jury = ap.convene()                      # a jury drawn equally from each period
winners = ap(ask, ["Yes, with permits.", "Only on weekends.", "No."])  # 1 for each reply instant runoff can elect
  • AdaptivePluralisticProcedureaiag/procedural/deliberative_aggregation.py:994 Adaptive pluralistic alignment: each annotator's reward a simplex-weighted mix of shared basis rewards (a low-rank decomposition), a jury drawn equally from each period ranking candidates by those rewards and electing by instant runoff, and new periods joining by fitting only new weights on the frozen bases (Freedman, 2026).

pip install aiagEvery method in aiag Library on GitHub

Background

Work this approach builds on that falls outside the inclusion criteria (e.g., a single-objective baseline). It is listed for reference and not counted anywhere.

arXiv:2411.00119MethodBuilt

Ranks agents by treating benchmark and tournament results as votes and searching for the ranking that mispredicts the fewest pairwise comparisons. Across 865 PrefLib preference profiles, its rankings are on average 0 to 0.043 in normalized Kendall-tau from the optimal ranking. On held-out test sets from 31,049 games of seven-player Diplomacy played by 52,958 people, it gives the best approximation to the optimal ranking.

CodeSoftCondorcet
from aiag.procedural import SoftCondorcet

agents = ["agent A", "agent B", "agent C"]
games = [(0, 1), (0, 2), (1, 2), (2, 1), (1, 2), (0, 1)]  # (winner, loser) of each head-to-head evaluation

# Ratings that mispredict as few of the outcomes as possible, the count smoothed by a sigmoid
ratings = SoftCondorcet()(len(agents), games)
ranking = [agents[i] for i in ratings.argsort()[::-1]]  # -> ['agent A', 'agent B', 'agent C']

pip install aiagEvery method in aiag Library on GitHub

arXiv:2404.18796BenchmarkBuilt

Scores model outputs with a panel of several smaller models drawn from disjoint model families, instead of one large judge. Across three judge settings and six datasets, it reports better performance than a single large judge, less intra-model bias, and over seven times lower cost.

CodePanelOfJudges
from aiag import LM
from aiag.procedural import PanelOfJudges

lm = LM("Qwen/Qwen2.5-1.5B-Instruct")
panel = PanelOfJudges([lm, lm, lm])  # in the paper, three small judges from different model families

# Max voting: an answer counts as correct when most judges say it matches the reference
verdicts = panel.correct("What is the capital of Australia?", ["Canberra", "Sydney"], reference="Canberra")
scores = panel("Explain a solar eclipse to a child.", ["The moon hides the sun for a bit.", "Eclipses are rare."])  # mean score

pip install aiagEvery method in aiag Library on GitHub