Council and Voting
TnsAI.Coordination provides two complementary systems for group decision-making: `CouncilExecutor` for multi-model deliberation with peer review, and `ConsensusBuilder` / `GroupDecisionFramework` for agent voting.
CouncilExecutor
The CouncilExecutor lets you get the best answer from multiple LLMs by having them independently respond, anonymously peer-review each other, and then synthesize a top-rated answer. It is a 3-stage deliberation pipeline inspired by Karpathy's llm-council. Multiple LLM models independently answer a question, peer-review each other's responses (anonymized), and a chairman synthesizes the best answer.
Three Stages
The deliberation process follows three stages. Each stage builds on the previous one to produce a high-quality final answer.
- Individual Responses -- All members answer the question independently in parallel.
- Peer Review -- Each member evaluates all anonymized responses, providing analysis and a ranking.
- Chairman Synthesis -- The chairman receives all responses, reviews, and aggregate rankings, then produces a final synthesized answer.
Usage
Create a council with at least two LLM members and a chairman, then call deliberate() with your question. The result includes the final synthesized answer, individual responses, peer reviews, and an aggregate ranking of models.
CouncilExecutor council = CouncilExecutor.builder()
.members(List.of(gpt4Client, claudeClient, geminiClient, grokClient))
.chairman(geminiClient)
.anonymize(true) // default: true
.timeout(Duration.ofMinutes(5))
.build();
CouncilResult result = council.deliberate("How should we architect the auth system?");
// Final synthesized answer
System.out.println(result.finalAnswer());
// Aggregate ranking of models
result.topRanked().ifPresent(r ->
System.out.println("Top: " + r.llmName() + " (avg rank: " + r.averageRank() + ")"));
// Access each stage
List<CouncilResponse> responses = result.individualResponses(); // Stage 1
List<CouncilReview> reviews = result.peerReviews(); // Stage 2
CouncilSynthesis synthesis = result.synthesis(); // Stage 3
List<RankedMember> ranking = result.aggregateRanking();Builder Configuration
These are the parameters you can set when building a council.
| Parameter | Default | Description |
|---|---|---|
members | (required) | At least 2 LLMClient instances |
chairman | (required) | LLMClient that produces final synthesis |
anonymize | true | Whether to hide model identities during peer review |
timeout | 5 minutes | Per-stage timeout |
Anonymization
Anonymization prevents bias during peer review by hiding which LLM produced each response. When anonymize is true, model identities are replaced with labels (Model A, Model B, ...) during peer review. The chairman receives both labels and real names.
CouncilResult
The CouncilResult contains all outputs from the three deliberation stages, plus an aggregate ranking of which model performed best.
| Field | Type | Description |
|---|---|---|
individualResponses() | List<CouncilResponse> | Stage 1 responses (llmName, response, durationMs) |
peerReviews() | List<CouncilReview> | Stage 2 reviews (reviewerName, evaluation, parsedRanking) |
synthesis() | CouncilSynthesis | Stage 3 chairman output (chairmanModel, synthesis) |
aggregateRanking() | List<RankedMember> | Models ranked by average position across reviews |
finalAnswer() | String | Shortcut to synthesis().synthesis() |
topRanked() | Optional<RankedMember> | The highest-ranked model |
RankedMember
A RankedMember records how well a specific LLM performed across all peer reviews. Lower averageRank is better (1.0 means consistently ranked first).
public record RankedMember(String llmName, double averageRank, int rankingsCount)Lower averageRank is better (1.0 = consistently ranked first).
VotingMethod
TnsAI provides five voting methods for group consensus, ranging from simple majority to weighted voting. Choose the method that matches how much agreement you need.
| Method | Description | Threshold |
|---|---|---|
MAJORITY | Simple majority (\> 50%) | 50% |
SUPERMAJORITY | Configurable threshold (default 2/3) | 66.7% |
UNANIMOUS | All voters must agree | 100% |
RANKED_CHOICE | Instant-runoff with ranked preferences | Majority after elimination |
WEIGHTED | Votes weighted by voter weight | 50% of total weight |
ConsensusBuilder
The ConsensusBuilder is the simplest way to run a vote among agents. You specify the question, the options, the voters, and the voting method, then call .vote() to get the result. It is a fluent builder that orchestrates a voting session among agents. Collects votes in parallel, applies the chosen voting method, and returns results.
VotingResult result = ConsensusBuilder.create()
.topic("Which database to use?")
.options("PostgreSQL", "MySQL", "MongoDB")
.among(List.of(dbExpert, backendDev, infraAgent))
.method(VotingMethod.MAJORITY)
.timeout(Duration.ofSeconds(30))
.quorum(2)
.vote();
System.out.println("Winner: " + result.winner());
System.out.println("Total votes: " + result.totalVotes());
System.out.println("Quorum met: " + result.quorumMet());Configuration
These builder methods let you configure every aspect of the voting session.
| Method | Default | Description |
|---|---|---|
.topic(String) | (required) | The question to vote on |
.options(String...) | (required) | Available choices |
.among(List<Agent>) | (required) | Voting agents |
.method(VotingMethod) | MAJORITY | Voting algorithm |
.timeout(Duration) | 30 seconds | Maximum wait for votes |
.quorum(int) | 1 | Minimum votes required |
.supermajorityThreshold(double) | 0.667 | Threshold for SUPERMAJORITY |
.listener(VotingListener) | none | Event callbacks |
How Voting Works
Here is what happens internally when you call .vote():
- A prompt is built with the topic and options, sent to all agents in parallel.
- Each agent's response is parsed using case-insensitive matching against the option list.
- Votes are tallied using the selected method (
MajorityVotingorRankedChoiceVoting). - The
VotingResultincludes the winner, vote counts, and quorum status.
VotingListener
Attach a VotingListener to get real-time callbacks as votes come in, when quorum is reached, and when the final result is ready.
ConsensusBuilder.create()
.topic("Deploy now?")
.options("Yes", "No", "Defer")
.among(agents)
.listener(new VotingListener() {
@Override
public void onVoteReceived(Vote vote) {
System.out.println(vote.voterId() + " voted: " + vote.choice());
}
@Override
public void onQuorumReached(int voteCount, int quorum) {
System.out.println("Quorum reached: " + voteCount + "/" + quorum);
}
@Override
public void onComplete(VotingResult result) {
System.out.println("Winner: " + result.winner());
}
})
.vote();GroupDecisionFramework
The GroupDecisionFramework is a more structured alternative to ConsensusBuilder, designed for use within an AgentGroup. It follows a formal propose-vote-converge lifecycle and records a decision trace for auditability. It provides a structured 3-phase protocol for group-level decision-making within an AgentGroup. Provides propose-vote-converge semantics with decision tracing.
GroupDecisionFramework framework = new DefaultGroupDecisionFramework(traceStore);
// Full pipeline in one call
DecisionProposal proposal = DecisionProposal.of(
"Which database?",
List.of("PostgreSQL", "MySQL", "MongoDB")
);
DecisionResult result = framework.makeDecision(group, proposal);Three Phases
The framework splits every group decision into three distinct phases:
- Propose -- Create a
DecisionProposalwith a topic, options, and voting method. - Vote -- Collect votes from all group members.
- Converge -- Tally votes, determine outcome, save trace, broadcast result. Falls back to leader decision if no consensus and a leader exists.
// Step-by-step usage for more control
DecisionProposal proposal = framework.propose(
"Which database?",
List.of("PostgreSQL", "MySQL", "MongoDB"),
VotingMethod.RANKED_CHOICE
);
List<Vote> votes = framework.vote(group, proposal);
DecisionResult result = framework.converge(group, votes, proposal);DecisionTraceStore
Every decision is recorded in a trace store so you can review what was proposed, who voted for what, and what the outcome was. All decisions are persisted to a DecisionTraceStore for auditability. The default implementation InMemoryDecisionTraceStore keeps traces in memory.
Advanced Patterns
Advanced coordination patterns in TnsAI.Coordination for production multi-agent systems.
Judge Agent Pattern
TnsAI.Coordination provides a judge agent pattern for evaluating and selecting the best output from multiple agents. A judge applies a policy to score candidate outputs and pick a winner. Package: `com.tnsai.coordination.judge`.