Skip to content

Commit d5d0aac

Browse files
docs: simplify CouncilVerse quick start (#3)
1 parent eb9322d commit d5d0aac

4 files changed

Lines changed: 194 additions & 93 deletions

File tree

README.md

Lines changed: 96 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -12,140 +12,144 @@
1212

1313
---
1414

15-
Most multi-agent systems train AI agents to cooperate. CouncilVerse does the opposite when the decision matters: agents argue from different methods, vote yes, no, or "not my area," and produce verdicts scored by argument quality, not headcount.
15+
Use CouncilVerse when one AI answer is too smooth.
1616

17-
**15 council presets. 3 npm packages. Works with any LLM provider.**
17+
It gives each AI agent a different job, forces them to argue from that job, then scores the verdict by reasoning quality instead of a simple headcount.
1818

19-
## Quick Start
19+
**In plain English:** you ask one question, several agents disagree, then you get a verdict you can audit.
20+
21+
## Run the local example
22+
23+
No API key needed:
24+
25+
```bash
26+
npm install
27+
npm run build
28+
npm run example:local
29+
```
30+
31+
Expected shape:
32+
33+
```text
34+
Council: Strategy Room
35+
Question: Should a repair shop follow up on stale estimates every morning?
36+
Verdict: keep
37+
Method: strong_consensus
38+
Agents: Observer, Orienter, Strategist
39+
```
40+
41+
The example uses canned responses so you can prove the package works before connecting any model provider.
42+
43+
## Start a real council project
2044

2145
```bash
2246
npx create-councilverse my-council
2347
cd my-council
2448
npm install
2549
```
2650

27-
Add your API key to `.env`:
51+
Add your model key:
2852

29-
```
53+
```bash
3054
ANTHROPIC_API_KEY=sk-ant-...
3155
```
3256

33-
Run a debate:
57+
Run a council:
3458

3559
```bash
36-
npx tsx src/council.ts
60+
npm run debate -- strategy-room "Should we expand into Europe?"
3761
```
3862

3963
## Packages
4064

41-
| Package | Description | Install |
42-
|---------|-------------|---------|
43-
| [`@relaylaunch/councilverse-formations`](packages/formations) | 15 structured debate methodologies | `npm i @relaylaunch/councilverse-formations` |
44-
| [`@relaylaunch/councilverse-voting`](packages/voting) | Three-valued voting with quality scoring | `npm i @relaylaunch/councilverse-voting` |
45-
| [`create-councilverse`](packages/create-councilverse) | Project scaffolding CLI | `npx create-councilverse` |
46-
47-
## Formations
48-
49-
| ID | Name | Methodology |
50-
|----|------|-------------|
51-
| strategy-room | Strategy Room | OODA Loop |
52-
| tribunal | The Tribunal | Legal Framework |
53-
| innovation-lab | Innovation Lab | Design Thinking |
54-
| risk-council | Risk Council | Monte Carlo |
55-
| due-diligence | Due Diligence Council | M&A Framework |
56-
| ethics-board | Ethics Board | Kantian + Utilitarian |
57-
| growth-council | Growth Council | AARRR Pirate Metrics |
58-
| technical-review | Technical Review Board | ADR |
59-
| market-intelligence | Market Intelligence | Porter's Five Forces |
60-
| crisis-response | Crisis Response Team | Incident Command |
61-
| investment-committee | Investment Committee | DCF + Comparables |
62-
| quality-assurance | Quality Assurance | Six Sigma DMAIC |
63-
| research-council | Research Council | Scientific Method |
64-
| competitive-analysis | Competitive Analysis | Game Theory |
65-
| policy-council | Policy Council | Regulatory Impact |
66-
67-
## Usage
65+
| Package | What it does | Install |
66+
|---------|--------------|---------|
67+
| [`@relaylaunch/councilverse-formations`](packages/formations) | 15 ready-made council presets with roles and prompts | `npm i @relaylaunch/councilverse-formations` |
68+
| [`@relaylaunch/councilverse-voting`](packages/voting) | Turns agent responses into keep, refuse, or abstain verdicts | `npm i @relaylaunch/councilverse-voting` |
69+
| [`create-councilverse`](packages/create-councilverse) | Creates a working starter project | `npx create-councilverse` |
70+
71+
## What the presets are for
72+
73+
| ID | Use it when you need |
74+
|----|----------------------|
75+
| strategy-room | A practical business strategy call |
76+
| tribunal | A hard yes/no challenge with a case for and against |
77+
| innovation-lab | New ideas without skipping reality checks |
78+
| risk-council | Risk, downside, and mitigation planning |
79+
| due-diligence | Investment, acquisition, or vendor review |
80+
| ethics-board | Human impact and rights review |
81+
| growth-council | Acquisition, activation, retention, referral, and revenue |
82+
| technical-review | Architecture and implementation tradeoffs |
83+
| market-intelligence | Competitive pressure and market structure |
84+
| crisis-response | Incident response and fast triage |
85+
| investment-committee | Investment quality and downside review |
86+
| quality-assurance | Process defects and improvement plans |
87+
| research-council | Evidence review and hypothesis testing |
88+
| competitive-analysis | Competitor moves and game theory |
89+
| policy-council | Regulatory or policy impact review |
90+
91+
## Use the libraries directly
6892

6993
```typescript
70-
import { getFormation, buildSystemPrompt, buildSynthesisPrompt } from '@relaylaunch/councilverse-formations';
94+
import { buildCouncilEmbedPayload } from '@relaylaunch/councilverse-formations';
7195
import { buildAgentVote, tallyVotes } from '@relaylaunch/councilverse-voting';
7296

73-
// 1. Get a council preset
74-
const formation = getFormation('strategy-room');
75-
76-
// 2. Build one system prompt per role
77-
const systemPrompts = formation.roles.map((role, roleIndex) =>
78-
buildSystemPrompt(formation, roleIndex)
97+
const payload = buildCouncilEmbedPayload(
98+
'strategy-room',
99+
'Should a repair shop follow up on stale estimates every morning?'
79100
);
80101

81-
// 3. Send to your LLM provider (any provider works)
82-
const agentResponses = await Promise.all(
83-
formation.roles.map((role, roleIndex) =>
84-
yourLLMCall({ system: systemPrompts[roleIndex], user: question })
85-
)
86-
);
102+
const responses = [
103+
'I recommend approve. The evidence shows stale estimates decay quickly, and a daily review is a low-risk habit.',
104+
'I support proceed. First, the owner still approves every send. Second, the action is specific and measurable.',
105+
'I endorse a small pilot. It is strategically sound if the team tracks replies and recovered bookings.',
106+
];
87107

88-
// 4. Vote on the responses
89-
const votes = agentResponses.map((response, i) =>
108+
const votes = responses.map((text, index) =>
90109
buildAgentVote(
91-
`agent-${i}`,
92-
formation.roles[i].title,
93-
'council-1',
94-
response
110+
payload.agents[index].id,
111+
payload.agents[index].title,
112+
'example-council',
113+
text
95114
)
96115
);
97116

98-
const result = tallyVotes(votes);
99-
// result.outcome: "keep" | "refuse" | "deadlock"
100-
// result.method: "strong_consensus" | "quality_weighted" | "lead_fallback"
101-
102-
// 5. Synthesize the verdict
103-
const synthesisPrompt = buildSynthesisPrompt(
104-
formation,
105-
question,
106-
agentResponses.map((response, i) => ({
107-
role: formation.roles[i].title,
108-
response,
109-
}))
110-
);
111-
const verdict = await yourLLMCall({ system: synthesisPrompt });
117+
console.log(tallyVotes(votes));
112118
```
113119

114-
## How Voting Works
120+
## How voting works
115121

116-
Traditional multi-agent systems use majority vote. CouncilVerse uses **three-valued voting with quality-weighted scoring**:
122+
Each agent returns one of three outcomes:
117123

118-
- **KEEP** -- agent supports the proposal
119-
- **REFUSE** -- agent opposes
120-
- **ABSTAIN** -- agent signals "outside my expertise" (prevents noise)
124+
- **KEEP**: supports the proposal
125+
- **REFUSE**: opposes the proposal
126+
- **ABSTAIN**: says "not my area" or lacks enough evidence
121127

122-
Verdicts are determined by argument quality, not headcount:
123-
1. **Strong consensus** -- all active voters agree
124-
2. **Quality weighted** -- `argument_quality * confidence`, margin >= 15%
125-
3. **Lead fallback** -- deadlock triggers formation lead arbitration
128+
The final verdict is based on reasoning quality and confidence. A strong minority can beat a weak majority.
126129

127-
Inspired by quality-weighted multi-agent voting research and tuned for practical decision reviews.
130+
## Embeds
128131

129-
## Protocol Support
132+
`buildCouncilEmbedPayload()` returns a provider-neutral payload with:
130133

131-
CouncilVerse formations are designed for interoperability:
134+
- the council preset
135+
- the public question
136+
- one system prompt per role
137+
- a synthesis prompt
132138

133-
- **Google A2A** -- council presets map to Agent Card skills
134-
- **MCP** -- usable as MCP tool definitions
135-
- **Webhooks** -- HMAC-SHA256 signed verdict events
136-
- **Embed** -- iframe-friendly verdict widgets
139+
Use it for iframe widgets, MCP tools, Agent Card skills, or your own app surface.
137140

138-
## Full Platform
141+
## Full platform
139142

140-
These open-source packages power [Relay Deck](https://deck.relaylaunch.com), which adds:
143+
These packages power [Relay Deck](https://deck.relaylaunch.com), which adds:
141144

142-
- Persistent verdict library with precedent search
143-
- Reasoning trace audit trail (Langfuse)
144-
- Embeddable verdict widgets
145-
- Durable debate state and owner approval checkpoints
146-
- Heterogeneous model assignment (different LLMs per agent)
147-
- EU AI Act compliance manifests
148-
- Use your own API keys, so no model traffic has to pass through RelayLaunch
145+
- saved verdicts and precedent search
146+
- reasoning traces
147+
- embeddable verdict widgets
148+
- durable debate state
149+
- owner approval checkpoints
150+
- model assignment per agent
151+
- compliance manifests
152+
- bring-your-own model keys
149153

150154
[Try the playground](https://deck.relaylaunch.com/playground) | [Sign up for Relay Deck](https://deck.relaylaunch.com/signup)
151155

examples/local-verdict.mjs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { buildCouncilEmbedPayload } from "../packages/formations/dist/index.mjs";
2+
import { buildAgentVote, tallyVotes } from "../packages/voting/dist/index.mjs";
3+
4+
const question = "Should a repair shop follow up on stale estimates every morning?";
5+
const payload = buildCouncilEmbedPayload("strategy-room", question);
6+
7+
const responses = [
8+
{
9+
agent: payload.agents[0],
10+
text: "I recommend approve. The evidence shows stale estimates decay quickly, and a daily review is a low-risk habit.",
11+
},
12+
{
13+
agent: payload.agents[1],
14+
text: "I support proceed. First, the owner still approves every send. Second, the action is specific and measurable.",
15+
},
16+
{
17+
agent: payload.agents[2],
18+
text: "I endorse a small pilot. It is strategically sound if the team tracks replies and recovered bookings.",
19+
},
20+
];
21+
22+
const votes = responses.map(({ agent, text }) =>
23+
buildAgentVote(agent.id, agent.title, "example-council", text),
24+
);
25+
const result = tallyVotes(votes);
26+
27+
console.log(`Council: ${payload.formationName}`);
28+
console.log(`Question: ${payload.question}`);
29+
console.log(`Verdict: ${result.outcome}`);
30+
console.log(`Method: ${result.method}`);
31+
console.log(`Agents: ${responses.map(({ agent }) => agent.title).join(", ")}`);

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
"engines": {
66
"node": ">=20"
77
},
8-
"description": "CouncilVerse — Multi-agent AI council reasoning engine (monorepo)",
8+
"description": "CouncilVerse - AI agents that disagree on purpose",
99
"workspaces": [
1010
"packages/formations",
1111
"packages/voting",
1212
"packages/create-councilverse"
1313
],
1414
"scripts": {
1515
"build": "npm run build --workspaces",
16+
"example:local": "node examples/local-verdict.mjs",
17+
"pii:scan": "node scripts/check-pii-scan.mjs",
1618
"test": "vitest run",
1719
"test:watch": "vitest",
1820
"clean": "rm -rf packages/*/dist",

scripts/check-pii-scan.mjs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { execFileSync } from "node:child_process";
2+
import { existsSync, readFileSync } from "node:fs";
3+
import { extname, normalize } from "node:path";
4+
5+
const ignoredFiles = new Set([normalize("scripts/check-pii-scan.mjs")]);
6+
const scannedExtensions = new Set([".js", ".json", ".md", ".mjs", ".ts", ".tsx"]);
7+
const bannedPatterns = [
8+
{ label: "pilot first name", pattern: /\bBlake\b/i },
9+
{ label: "pilot first name", pattern: /\bElaine\b/i },
10+
{ label: "client name", pattern: /Holistic Rejuvenation Center/i },
11+
{ label: "client name", pattern: /AutoFix Plus/i },
12+
{ label: "sample identity marker", pattern: /sample client/i },
13+
{ label: "sample identity marker", pattern: /fake customer/i },
14+
];
15+
16+
function git(args) {
17+
return execFileSync("git", args, { encoding: "utf8" }).trim();
18+
}
19+
20+
function changedFiles() {
21+
let base = "";
22+
try {
23+
base = git(["merge-base", "HEAD", "origin/main"]);
24+
} catch {
25+
base = "";
26+
}
27+
28+
const ranges = [
29+
base ? ["diff", "--name-only", "--diff-filter=ACMRT", `${base}...HEAD`] : null,
30+
["diff", "--name-only", "--diff-filter=ACMRT"],
31+
["diff", "--cached", "--name-only", "--diff-filter=ACMRT"],
32+
["ls-files", "--others", "--exclude-standard"],
33+
].filter(Boolean);
34+
35+
return [
36+
...new Set(
37+
ranges
38+
.flatMap((args) => {
39+
const output = git(args);
40+
return output ? output.split(/\r?\n/) : [];
41+
})
42+
.map((file) => normalize(file))
43+
.filter(Boolean),
44+
),
45+
];
46+
}
47+
48+
const hits = [];
49+
for (const file of changedFiles()) {
50+
if (ignoredFiles.has(file)) continue;
51+
if (!scannedExtensions.has(extname(file))) continue;
52+
if (!existsSync(file)) continue;
53+
const text = readFileSync(file, "utf8");
54+
for (const { label, pattern } of bannedPatterns) {
55+
if (pattern.test(text)) hits.push({ file, label, pattern: pattern.source });
56+
}
57+
}
58+
59+
if (hits.length) {
60+
console.error(JSON.stringify({ status: "failed", hits }, null, 2));
61+
process.exit(1);
62+
}
63+
64+
console.log("CouncilVerse changed-file PII scan passed.");

0 commit comments

Comments
 (0)