A coordination primitive for AI agents to converge on ranked options through gossip-based probabilistic consensus.
Snowveil implements a protocol based on this paper Snowveil: A Framework for Decentralised Preference Discovery (Kotsialou). Multiple independent agents submit ranked preferences and converge on a global ranking via probabilistic peer sampling — no central coordinator required.
The library serves both in-process use cases (CrewAI, single-host LangGraph) via InMemoryTransport and cross-process / cross-organisational use cases via WebSocketTransport with Ed25519-signed messages.
pip install snowveil # not yet on PyPI; v0.1.0 release pendingFor development:
pip install -e ".[dev]"Pick the mode that matches your deployment. All three converge on the same consensus given the same submitted profile.
The default path for CrewAI, single-host LangGraph, tests, and demos. Zero network deps.
from snowveil import Snowveil, Config
sv = Snowveil(candidates=["search", "ask_user", "draft_reply", "escalate"],
config=Config(seed=0))
sv.submit("researcher", ["search", "ask_user", "draft_reply", "escalate"])
sv.submit("writer", ["draft_reply", "search", "ask_user", "escalate"])
sv.submit("qa", ["search", "draft_reply", "ask_user", "escalate"])
sv.submit("manager", ["search", "ask_user", "draft_reply", "escalate"])
sv.run()
sv.get_consensus()[0] # → "search"Use WebSocketCluster to collapse the Ed25519 + server lifecycle + transport boilerplate into a single async context manager. Each side runs the same code with its own LocalAgent and the peer's verify key:
from snowveil import Config
from snowveil.transport.cluster import WebSocketCluster, RemoteAgent
from snowveil.transport.identity import Identity
async with WebSocketCluster.serve(
candidates=["A", "B"],
local_agent=("alice", ["A", "B"]),
listen="ws://localhost:9001",
peers=[RemoteAgent("bob", "ws://localhost:9002", bob_verify_key)],
identity=Identity.generate(),
config=Config(seed=0),
) as cluster:
await cluster.run()
cluster.consensus # → ["A", "B"]Runnable two-terminal demo: examples/two_process_websocket.py. For asymmetric setups (multiple voters per node, shared transport across crews) drop down to WebSocketServer + WebSocketTransport directly.
Status:
Process.consensualis currently in an upstream CrewAI PR; until it merges, this snippet requires a CrewAI build that ships theconsensualprocess andconsensus=field. Layers 1 (snowveil_consensus) and 2 (crewai_ranker) work on stock upstream CrewAI today.
Once CrewAI's Process.consensual is available, swap the manager LLM for SnowveilConsensus:
from crewai import Crew, Process
from snowveil.integrations.crewai import SnowveilConsensus
crew = Crew(
agents=[researcher, writer, qa],
tasks=[task],
process=Process.consensual,
consensus=SnowveilConsensus(),
)
crew.kickoff()For frameworks without a ConsensusEngine hook, the lower-level primitive snowveil_consensus(rankers, options, question) works against any callable that returns LLM text. See examples/integration_crewai.py.
CrewAI's default MajorityVoteConsensus and Snowveil's SnowveilConsensus solve the same problem at different fidelities. Use majority vote when speed matters and the decision is uncontested; reach for Snowveil when any of the following hold:
- Plurality picks a divisive winner. Concrete example: 5 voters, 3 options, profile
[A>C>B, A>C>B, B>C>A, B>C>A, C>A>B]. Plurality first-place votes areA=2, B=2, C=1, so plurality picks A or B (a coin flip). But everyone ranks C ≥ 2nd, and Borda totals areA=5, B=4, C=6. Snowveil's CHB rule picks C — the candidate the whole electorate finds acceptable, not the one with the slim divisive plurality. - Coalition resistance is needed. Snowveil's Ω(n) coalition lower bound (paper §4.6, validated empirically by
tests/test_t9_coalition_resistance.py) means a √n-sized coordinated group cannot reliably flip the outcome. Majority vote flips the moment a coalition crosses 50 %. - The full ranking matters, not just the top pick. Majority vote returns one winner;
sv.get_consensus()returns the full social ranking[w_1, w_2, ..., w_m]via sequential elimination, useful for top-k routing, fallback chains, or auditable tie-breaking. - Inputs are noisy or partially informed. Probabilistic peer sampling smooths individual-voter mistakes — voters update toward the local consensus before locking. With high-variance LLM voters, this routinely produces more stable winners across re-runs than majority vote.
- Decisions cross organisational trust boundaries. Snowveil's
WebSocketTransportships with Ed25519 signing and replay protection. Two crews from different organisations can converge without either acting as a central tally authority.
If your decision is unanimous or near-unanimous and runs entirely inside one process, majority vote is faster and simpler — use it. The cost difference is one extra sv.run() (milliseconds at typical crew sizes), so if any of the above apply, the upgrade is cheap.
The defaults in Config() reproduce the paper baseline used in the paper's reference notebooks. Use them unchanged for the well-validated regime; deviate only with awareness of the trade-offs described in the notebook.
| Parameter | Default | Meaning | When to change |
|---|---|---|---|
k |
10 | Peer sample size per round | Lower for very small electorates (k ≤ n − 1 is required); higher for adversarial robustness |
gamma |
10 | Max rounds in the Voter Update Process | Higher for more conservative locking; lower for faster convergence |
tau_max |
6 | Early-lock threshold (out of gamma) |
Roughly floor(gamma/2) + 1 |
tau_min |
3 | Minimum-support threshold | Roughly ceil(gamma/4) |
quorum |
0.67 | Global termination threshold Q ∈ (0.5, 1] |
Higher for stricter consensus; closer to 0.5 weakens uniqueness |
alpha |
0.1 | CHB popularity filter (plurality threshold) | Higher to reject narrow-plurality winners |
beta |
0.8 | CHB consensus filter (Borda threshold) | Lower to admit more eligible candidates |
lambda_ |
0.5 | CHB hybrid weight (consensus vs plurality) | 0.0 = pure consensus; 1.0 = pure plurality |
seed |
None |
RNG seed for reproducibility | Set for deterministic runs / parity tests |
Electorate sizing. The protocol's statistical guarantees were validated for n ≥ 100 voters in the paper. The baseline k=10 requires at least 11 voters. For tiny crews (CrewAI with 3–8 agents), set k to n − 1 and accept that the convergence guarantees weaken — the protocol is not designed for n < 20.
Marginal-parameter behaviour. At k = 5 or γ = 5 (well below baseline), the library's per-voter model exposes individual-voter variance that the paper's mass-action notebook simulator smooths over — so you may see 90–98% accuracy. This is correct behaviour, not a bug.
- v1 ships only WebSocket as a network transport. gRPC, libp2p, MQTT are out of scope and can be added against the same
Transportprotocol later. - Static peer list only — no dynamic peer discovery, PKI, or sybil resistance. Sufficient for federated and cross-org cases where participants are known a priori.
- Each
run()starts from a clean state vector; no durable resume. - Multi-stage distributed mode (
m > 2candidates with the engine distributing across processes for every stage) works in tests but needs more validation on real networks. Single-stage demos are the recommended path for v0.1.0.
- Project brief — what Snowveil is and why
- Architecture — modules, data model, execution flow, transport design
Paper: https://arxiv.org/abs/2512.18444.
MIT — see LICENSE.