frontier-council 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- frontier_council-0.1.0/PKG-INFO +150 -0
- frontier_council-0.1.0/README.md +130 -0
- frontier_council-0.1.0/SKILL.md +87 -0
- frontier_council-0.1.0/council_history.jsonl +21 -0
- frontier_council-0.1.0/frontier_council/__init__.py +19 -0
- frontier_council-0.1.0/frontier_council/cli.py +214 -0
- frontier_council-0.1.0/frontier_council/council.py +830 -0
- frontier_council-0.1.0/pyproject.toml +37 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frontier-council
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multi-model deliberation for important decisions. 5 frontier LLMs debate, then a judge synthesizes consensus.
|
|
5
|
+
Project-URL: Homepage, https://github.com/terry-li-hm/skills
|
|
6
|
+
Author-email: Terry Li <terry.li.hm@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Keywords: ai,council,debate,deliberation,frontier,llm,multi-model,openrouter
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: httpx>=0.25.0
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Frontier Council
|
|
22
|
+
|
|
23
|
+
Multi-model deliberation for important decisions. 5 frontier LLMs debate a question, then a judge synthesizes consensus.
|
|
24
|
+
|
|
25
|
+
Inspired by [Andrej Karpathy's LLM Council](https://github.com/karpathy/llm-council), with added blind phase (anti-anchoring), explicit engagement requirements, devil's advocate role, and social calibration mode.
|
|
26
|
+
|
|
27
|
+
## Models
|
|
28
|
+
|
|
29
|
+
- Claude (claude-opus-4.5)
|
|
30
|
+
- GPT (gpt-5.2-pro)
|
|
31
|
+
- Gemini (gemini-3-pro-preview)
|
|
32
|
+
- Grok (grok-4)
|
|
33
|
+
- Kimi (kimi-k2.5)
|
|
34
|
+
- Judge: Claude Opus 4.5
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install frontier-council
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or with uv:
|
|
43
|
+
```bash
|
|
44
|
+
uv tool install frontier-council
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Setup
|
|
48
|
+
|
|
49
|
+
Set your OpenRouter API key:
|
|
50
|
+
```bash
|
|
51
|
+
export OPENROUTER_API_KEY=sk-or-v1-...
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Optional fallback keys (for flaky models):
|
|
55
|
+
```bash
|
|
56
|
+
export GOOGLE_API_KEY=AIza... # Gemini fallback
|
|
57
|
+
export MOONSHOT_API_KEY=sk-... # Kimi fallback
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Basic question
|
|
64
|
+
frontier-council "Should we use microservices or monolith?"
|
|
65
|
+
|
|
66
|
+
# With social calibration (for interview/networking questions)
|
|
67
|
+
frontier-council "What questions should I ask in the interview?" --social
|
|
68
|
+
|
|
69
|
+
# With persona context
|
|
70
|
+
frontier-council "Should I take the job?" --persona "builder who hates process work"
|
|
71
|
+
|
|
72
|
+
# Multiple rounds
|
|
73
|
+
frontier-council "Architecture decision" --rounds 3
|
|
74
|
+
|
|
75
|
+
# Save transcript
|
|
76
|
+
frontier-council "Career question" --output transcript.md
|
|
77
|
+
|
|
78
|
+
# Share via GitHub Gist
|
|
79
|
+
frontier-council "Important decision" --share
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Options
|
|
83
|
+
|
|
84
|
+
| Flag | Description |
|
|
85
|
+
|------|-------------|
|
|
86
|
+
| `--rounds N` | Number of deliberation rounds (default: 2, exits early on consensus) |
|
|
87
|
+
| `--output FILE` | Save transcript to file |
|
|
88
|
+
| `--named` | Let models see real names during deliberation (may increase bias) |
|
|
89
|
+
| `--no-blind` | Skip blind first-pass (faster, but first speaker anchors others) |
|
|
90
|
+
| `--context TEXT` | Context hint for judge (e.g., "architecture decision") |
|
|
91
|
+
| `--share` | Upload transcript to secret GitHub Gist |
|
|
92
|
+
| `--social` | Enable social calibration mode (auto-detected for interview/networking) |
|
|
93
|
+
| `--persona TEXT` | Context about the person asking |
|
|
94
|
+
| `--advocate N` | Which speaker (1-5) should be devil's advocate (default: random) |
|
|
95
|
+
| `--quiet` | Suppress progress output |
|
|
96
|
+
|
|
97
|
+
## How It Works
|
|
98
|
+
|
|
99
|
+
**Blind First-Pass (Anti-Anchoring):**
|
|
100
|
+
1. All models generate short "claim sketches" independently and in parallel
|
|
101
|
+
2. This prevents the "first speaker lottery" where whoever speaks first anchors the debate
|
|
102
|
+
3. Each model commits to an initial position before seeing any other responses
|
|
103
|
+
|
|
104
|
+
**Deliberation Protocol:**
|
|
105
|
+
1. All models see everyone's blind claims, then deliberate
|
|
106
|
+
2. Each model MUST explicitly AGREE, DISAGREE, or BUILD ON previous speakers by name
|
|
107
|
+
3. After each round, the system checks for consensus (4/5 agreement triggers early exit)
|
|
108
|
+
4. Judge synthesizes the full deliberation
|
|
109
|
+
|
|
110
|
+
**Anonymous Deliberation:**
|
|
111
|
+
- Models see each other as "Speaker 1", "Speaker 2", etc. during deliberation
|
|
112
|
+
- Prevents models from playing favorites based on vendor reputation
|
|
113
|
+
- Output transcript shows real model names for readability
|
|
114
|
+
|
|
115
|
+
## When to Use
|
|
116
|
+
|
|
117
|
+
Use the council when:
|
|
118
|
+
- Making an important decision that benefits from diverse perspectives
|
|
119
|
+
- You want models to actually debate, not just answer in parallel
|
|
120
|
+
- You need a synthesized recommendation, not raw comparison
|
|
121
|
+
- Exploring trade-offs where different viewpoints matter
|
|
122
|
+
|
|
123
|
+
Skip the council when:
|
|
124
|
+
- You're just thinking out loud (exploratory discussions)
|
|
125
|
+
- The answer depends on personal preference more than objective trade-offs
|
|
126
|
+
- Speed matters (council takes 60-90 seconds)
|
|
127
|
+
|
|
128
|
+
## Python API
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from frontier_council import run_council, COUNCIL
|
|
132
|
+
import os
|
|
133
|
+
|
|
134
|
+
api_key = os.environ["OPENROUTER_API_KEY"]
|
|
135
|
+
|
|
136
|
+
transcript, failed_models = run_council(
|
|
137
|
+
question="Should we use microservices or monolith?",
|
|
138
|
+
council_config=COUNCIL,
|
|
139
|
+
api_key=api_key,
|
|
140
|
+
rounds=2,
|
|
141
|
+
verbose=True,
|
|
142
|
+
social_mode=False,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
print(transcript)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Frontier Council
|
|
2
|
+
|
|
3
|
+
Multi-model deliberation for important decisions. 5 frontier LLMs debate a question, then a judge synthesizes consensus.
|
|
4
|
+
|
|
5
|
+
Inspired by [Andrej Karpathy's LLM Council](https://github.com/karpathy/llm-council), with added blind phase (anti-anchoring), explicit engagement requirements, devil's advocate role, and social calibration mode.
|
|
6
|
+
|
|
7
|
+
## Models
|
|
8
|
+
|
|
9
|
+
- Claude (claude-opus-4.5)
|
|
10
|
+
- GPT (gpt-5.2-pro)
|
|
11
|
+
- Gemini (gemini-3-pro-preview)
|
|
12
|
+
- Grok (grok-4)
|
|
13
|
+
- Kimi (kimi-k2.5)
|
|
14
|
+
- Judge: Claude Opus 4.5
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install frontier-council
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or with uv:
|
|
23
|
+
```bash
|
|
24
|
+
uv tool install frontier-council
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
Set your OpenRouter API key:
|
|
30
|
+
```bash
|
|
31
|
+
export OPENROUTER_API_KEY=sk-or-v1-...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Optional fallback keys (for flaky models):
|
|
35
|
+
```bash
|
|
36
|
+
export GOOGLE_API_KEY=AIza... # Gemini fallback
|
|
37
|
+
export MOONSHOT_API_KEY=sk-... # Kimi fallback
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Basic question
|
|
44
|
+
frontier-council "Should we use microservices or monolith?"
|
|
45
|
+
|
|
46
|
+
# With social calibration (for interview/networking questions)
|
|
47
|
+
frontier-council "What questions should I ask in the interview?" --social
|
|
48
|
+
|
|
49
|
+
# With persona context
|
|
50
|
+
frontier-council "Should I take the job?" --persona "builder who hates process work"
|
|
51
|
+
|
|
52
|
+
# Multiple rounds
|
|
53
|
+
frontier-council "Architecture decision" --rounds 3
|
|
54
|
+
|
|
55
|
+
# Save transcript
|
|
56
|
+
frontier-council "Career question" --output transcript.md
|
|
57
|
+
|
|
58
|
+
# Share via GitHub Gist
|
|
59
|
+
frontier-council "Important decision" --share
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Options
|
|
63
|
+
|
|
64
|
+
| Flag | Description |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| `--rounds N` | Number of deliberation rounds (default: 2, exits early on consensus) |
|
|
67
|
+
| `--output FILE` | Save transcript to file |
|
|
68
|
+
| `--named` | Let models see real names during deliberation (may increase bias) |
|
|
69
|
+
| `--no-blind` | Skip blind first-pass (faster, but first speaker anchors others) |
|
|
70
|
+
| `--context TEXT` | Context hint for judge (e.g., "architecture decision") |
|
|
71
|
+
| `--share` | Upload transcript to secret GitHub Gist |
|
|
72
|
+
| `--social` | Enable social calibration mode (auto-detected for interview/networking) |
|
|
73
|
+
| `--persona TEXT` | Context about the person asking |
|
|
74
|
+
| `--advocate N` | Which speaker (1-5) should be devil's advocate (default: random) |
|
|
75
|
+
| `--quiet` | Suppress progress output |
|
|
76
|
+
|
|
77
|
+
## How It Works
|
|
78
|
+
|
|
79
|
+
**Blind First-Pass (Anti-Anchoring):**
|
|
80
|
+
1. All models generate short "claim sketches" independently and in parallel
|
|
81
|
+
2. This prevents the "first speaker lottery" where whoever speaks first anchors the debate
|
|
82
|
+
3. Each model commits to an initial position before seeing any other responses
|
|
83
|
+
|
|
84
|
+
**Deliberation Protocol:**
|
|
85
|
+
1. All models see everyone's blind claims, then deliberate
|
|
86
|
+
2. Each model MUST explicitly AGREE, DISAGREE, or BUILD ON previous speakers by name
|
|
87
|
+
3. After each round, the system checks for consensus (4/5 agreement triggers early exit)
|
|
88
|
+
4. Judge synthesizes the full deliberation
|
|
89
|
+
|
|
90
|
+
**Anonymous Deliberation:**
|
|
91
|
+
- Models see each other as "Speaker 1", "Speaker 2", etc. during deliberation
|
|
92
|
+
- Prevents models from playing favorites based on vendor reputation
|
|
93
|
+
- Output transcript shows real model names for readability
|
|
94
|
+
|
|
95
|
+
## When to Use
|
|
96
|
+
|
|
97
|
+
Use the council when:
|
|
98
|
+
- Making an important decision that benefits from diverse perspectives
|
|
99
|
+
- You want models to actually debate, not just answer in parallel
|
|
100
|
+
- You need a synthesized recommendation, not raw comparison
|
|
101
|
+
- Exploring trade-offs where different viewpoints matter
|
|
102
|
+
|
|
103
|
+
Skip the council when:
|
|
104
|
+
- You're just thinking out loud (exploratory discussions)
|
|
105
|
+
- The answer depends on personal preference more than objective trade-offs
|
|
106
|
+
- Speed matters (council takes 60-90 seconds)
|
|
107
|
+
|
|
108
|
+
## Python API
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from frontier_council import run_council, COUNCIL
|
|
112
|
+
import os
|
|
113
|
+
|
|
114
|
+
api_key = os.environ["OPENROUTER_API_KEY"]
|
|
115
|
+
|
|
116
|
+
transcript, failed_models = run_council(
|
|
117
|
+
question="Should we use microservices or monolith?",
|
|
118
|
+
council_config=COUNCIL,
|
|
119
|
+
api_key=api_key,
|
|
120
|
+
rounds=2,
|
|
121
|
+
verbose=True,
|
|
122
|
+
social_mode=False,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
print(transcript)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: llm-council
|
|
3
|
+
description: LLM Council with 5 frontier models (Opus 4.5, GPT-5.2, Gemini 3 Pro, Grok 4, Kimi K2.5). Models deliberate on a question, each seeing previous responses, then a judge synthesizes consensus. Use for important decisions needing diverse AI perspectives.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# LLM Council
|
|
7
|
+
|
|
8
|
+
5 frontier models deliberate on a question. Unlike `/ask-llms` which shows parallel responses, this creates an actual debate where models see and respond to previous speakers, followed by a judge synthesizing the consensus.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- Important decisions that benefit from diverse perspectives
|
|
13
|
+
- You want models to actually debate, not just answer in parallel
|
|
14
|
+
- You need a synthesized recommendation, not raw comparison
|
|
15
|
+
- Exploring trade-offs where different viewpoints matter
|
|
16
|
+
|
|
17
|
+
## When NOT to Use
|
|
18
|
+
|
|
19
|
+
- **Thinking out loud** — exploratory discussions where you're still forming the question
|
|
20
|
+
- **Claude has good context** — if we've been discussing the topic, direct conversation is faster
|
|
21
|
+
- **Personal preference** — council excels at objective trade-offs, not "what would I enjoy"
|
|
22
|
+
- **Already converged** — if discussion reached a conclusion, council just validates
|
|
23
|
+
- **Speed matters** — takes 60-90s and costs several dollars
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export OPENROUTER_API_KEY=sk-or-v1-... # Required
|
|
29
|
+
export GOOGLE_API_KEY=AIza... # Optional: Gemini fallback
|
|
30
|
+
export MOONSHOT_API_KEY=sk-... # Optional: Kimi fallback
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Instructions
|
|
34
|
+
|
|
35
|
+
### Step 1: Get the Question
|
|
36
|
+
|
|
37
|
+
Ask the user what question they want the council to deliberate, or use the question they provided.
|
|
38
|
+
|
|
39
|
+
### Step 2: Run the Council
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
llm-council "Should we use microservices or a monolith for this project?"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Common options:**
|
|
46
|
+
```bash
|
|
47
|
+
llm-council "question" --social # Interview/networking questions
|
|
48
|
+
llm-council "question" --persona "context" # Add personal context
|
|
49
|
+
llm-council "question" --rounds 3 # More deliberation
|
|
50
|
+
llm-council "question" --output file.md # Save transcript
|
|
51
|
+
llm-council "question" --share # Upload to secret Gist
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Step 3: Review and Critique
|
|
55
|
+
|
|
56
|
+
Present the judge's synthesis, then critique it:
|
|
57
|
+
- Did the council overcorrect on any point?
|
|
58
|
+
- Did they miss obvious scenarios?
|
|
59
|
+
- Does the advice fit the user's specific context?
|
|
60
|
+
- Any groupthink where everyone agreed too fast?
|
|
61
|
+
|
|
62
|
+
## Prompting Tips
|
|
63
|
+
|
|
64
|
+
**For social/conversational contexts** (interview questions, networking, outreach):
|
|
65
|
+
|
|
66
|
+
LLMs over-optimize for thoroughness. Add constraints like:
|
|
67
|
+
- "Make it feel like a natural conversation"
|
|
68
|
+
- "Something you'd actually ask over coffee"
|
|
69
|
+
- "Simple and human, not structured and comprehensive"
|
|
70
|
+
|
|
71
|
+
**Match context depth to question type:**
|
|
72
|
+
- Strategic decisions: provide rich context (full background, constraints, history)
|
|
73
|
+
- Social questions: minimal context + clear tone constraints
|
|
74
|
+
|
|
75
|
+
**For architecture/design questions:**
|
|
76
|
+
|
|
77
|
+
Provide scale and constraints upfront to avoid premature optimization advice:
|
|
78
|
+
- "This is a single-user system" (avoids multi-user concerns)
|
|
79
|
+
- "We have 500 notes, not 50,000" (avoids scaling infrastructure)
|
|
80
|
+
- "Manual processes are acceptable" (avoids automation overkill)
|
|
81
|
+
|
|
82
|
+
Without these constraints, council tends to suggest infrastructure for problems that don't exist yet.
|
|
83
|
+
|
|
84
|
+
## See Also
|
|
85
|
+
|
|
86
|
+
- Full documentation: `cat /Users/terry/skills/llm-council/README.md`
|
|
87
|
+
- Python API available: `from llm_council import run_council`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{"timestamp": "2026-01-20T20:40:06.696079", "question": "What is 2+2?", "context": null, "rounds": 1, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
2
|
+
{"timestamp": "2026-01-20T21:19:53.101354", "question": "Terry has a persistent Claude Code setup (tmux, Obsidian vault, browser automation, background tasks). He's job hunting as a senior exec (Head of Data Science) being counselled out of a Hong Kong bank", "gist": null, "context": "AI autonomy for job hunting", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
3
|
+
{"timestamp": "2026-01-20T21:21:25.406704", "question": "Terry is job hunting after being counselled out of his Head of Data Science role at a bank in Hong Kong. He's using Claude Code as his AI assistant. Based on Ethan Mollick's insight that AI agents mak", "gist": null, "context": "job hunting strategy for senior data science leader", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
4
|
+
{"timestamp": "2026-01-21T14:49:02.706670", "question": "Should Terry reach out to Kelvin Chan (AGM, Head of Customer Intelligence at BOCHK) and if so, how direct should he be about job opportunities?\n\n**Context about Terry:**\n- Currently AGM & Head of Data", "gist": null, "context": "career networking decision under time pressure", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
5
|
+
{"timestamp": "2026-01-21T15:12:10.947338", "question": "Review this LinkedIn message from Terry to Kelvin Chan. Is it good to send as-is, or does it need refinement?\n\n**The message:**\n'Hi Kelvin, great connecting at the HKMA GenAI Sandbox graduation \u2014 cong", "gist": null, "context": "LinkedIn outreach message review", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
6
|
+
{"timestamp": "2026-01-22T07:52:14.589415", "question": "Terry is about to have his 3rd round interview at Capco (consulting firm) for a Principal Consultant, AI Solution Lead role. The interviewer is Marco Chiu, a Principal Consultant in Data - so a peer i", "gist": null, "context": "peer interview strategy", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
7
|
+
{"timestamp": "2026-01-22T08:05:15.654039", "question": "Terry has a peer interview at Capco with Marco Chiu (Principal Consultant, Data). He's been there 10+ years. Terry wants 1 question to ask at the end.\n\nConstraint: Make it feel like a natural conversa", "gist": null, "context": "peer interview question", "rounds": 1, "blind": false, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
8
|
+
{"timestamp": "2026-01-22T11:47:56.997936", "question": "**Question:** Why is my job application-to-interview conversion rate low, and what should I change?\n\n**Context:**\n\nI'm Terry Li, AGM & Head of Data Science at China CITIC Bank International (Hong Kong", "gist": null, "context": "career strategy for senior data science leader in Hong Kong banking", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
9
|
+
{"timestamp": "2026-01-22T12:31:37.102976", "question": "Context: Terry Li is AGM & Head of Data Science at China CITIC Bank International (small Chinese bank HK subsidiary). He's being counselled out despite strong performance (Very Good ratings, Best Fint", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
10
|
+
{"timestamp": "2026-01-22T13:45:32.943472", "question": "Final check on this LinkedIn reply before sending.\n\nContext: Terry (AGM at smaller bank CITIC, job hunting) is replying to Kelvin Chan (AGM, Head of Customer Intelligence at BOCHK \u2014 bigger tier-1 bank", "gist": null, "context": "networking reply", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
11
|
+
{"timestamp": "2026-01-22T14:16:55.519265", "question": "Given the commoditization of the tech stack (Python + Docker + DB covers most needs) and the rise of AI coding tools, what is the realistic competitive positioning for a senior technical person in Hon", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
12
|
+
{"timestamp": "2026-01-23T15:20:19.552247", "question": "Terry is negotiating a Principal Consultant, AI Solution Lead role at Capco Hong Kong. He needs advice on compensation negotiation strategy.\n\n**Terry's situation:**\n- Currently AGM & Head of Data Scie", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
13
|
+
{"timestamp": "2026-01-23T15:54:31.430621", "question": "Terry is considering accepting a Principal Consultant role at Capco Hong Kong as a 1-2 year 'bridge' strategy before returning to in-house banking. He wants the council to stress-test this plan.\n\n**Th", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
14
|
+
{"timestamp": "2026-01-25T09:12:10.579975", "question": "Should a bank's customer-facing FAQ chatbot use LLM-generated answers (with guardrails) or stick with verbatim pre-approved answers?\n\n## Context\n\nTerry is building a POC chatbot for CITIC Bank Hong Ko", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
15
|
+
{"timestamp": "2026-01-25T19:38:26.635861", "question": "How are AI coding agents (like Claude Code, Cursor, Copilot) affecting the landscape of software development? Consider impacts on: build vs buy economics, vendor relationships, team structures, skill ", "gist": null, "context": "business strategy discussion between banking executives", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
16
|
+
{"timestamp": "2026-01-26T14:12:01.575180", "question": "Gavin Maxwell (Capco recruiter) just messaged Terry on WhatsApp at 1:54 PM today:\n\n'Hi Terry, is your notice period negotiable?'\n'Or will you need to complete two months'\n\nContext:\n- Terry is at offer", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
17
|
+
{"timestamp": "2026-01-27T06:28:25.073503", "question": "Terry is job hunting in Hong Kong (Head of Data Science level, ~HKD 1.78M total comp). He's at offer stage with Capco (consulting firm) \u2014 final round passed Jan 23, recruiter Gavin confirmed Bertie ap", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
18
|
+
{"timestamp": "2026-01-27T12:03:37.851741", "question": "Terry is at offer stage with Capco (consulting firm) for Principal Consultant, AI Solution Lead role in Hong Kong.\n\n**Timeline:**\n- Jan 16: 2nd round interview (Subashini, Regional Lead Analytics & AI", "gist": null, "context": "job negotiation strategy", "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
19
|
+
{"timestamp": "2026-01-27T19:40:23.765214", "question": "Terry is evaluating a Capco (consulting firm) offer in Hong Kong. He's concerned about the non-compete clause:\n\n**The clause (15.1):** 6 months post-employment, cannot work for:\n- Named competitors: A", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
20
|
+
{"timestamp": "2026-01-30T16:06:22.366395", "question": "I'm leaving CNCBI (being counselled out, have Capco offer). Should I share my personal FAQ chatbot demo with Derek (Head of Direct Banking, worked together on agent-assist chatbot, potential Hang Seng", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
21
|
+
{"timestamp": "2026-01-31T08:10:37.823456", "question": "We've been building a system where:\n\n1. **Skills** = crystallized procedures (how to do things) - stored as SKILL.md files, invoked by Claude\n2. **Vault notes** = content libraries (what to know/say) ", "gist": null, "context": null, "rounds": 2, "blind": true, "models": ["Claude", "GPT", "Gemini", "Grok", "Kimi"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Frontier Council - Multi-model deliberation for important decisions."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
|
|
5
|
+
from .council import (
|
|
6
|
+
run_council,
|
|
7
|
+
run_blind_phase_parallel,
|
|
8
|
+
detect_social_context,
|
|
9
|
+
COUNCIL,
|
|
10
|
+
JUDGE_MODEL,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"run_council",
|
|
15
|
+
"run_blind_phase_parallel",
|
|
16
|
+
"detect_social_context",
|
|
17
|
+
"COUNCIL",
|
|
18
|
+
"JUDGE_MODEL",
|
|
19
|
+
]
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""CLI entry point for frontier-council."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import random
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from .council import (
|
|
13
|
+
COUNCIL,
|
|
14
|
+
detect_social_context,
|
|
15
|
+
run_council,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
description="LLM Council - Multi-model deliberation for important decisions",
|
|
22
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
23
|
+
epilog="""
|
|
24
|
+
Examples:
|
|
25
|
+
frontier-council "Should we use microservices or monolith?"
|
|
26
|
+
frontier-council "What questions should I ask?" --social
|
|
27
|
+
frontier-council "Career decision" --persona "builder who hates process work"
|
|
28
|
+
frontier-council "Architecture choice" --rounds 3 --output transcript.md
|
|
29
|
+
""",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument("question", help="The question for the council to deliberate")
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--rounds",
|
|
34
|
+
type=int,
|
|
35
|
+
default=2,
|
|
36
|
+
help="Number of deliberation rounds (default: 2, exits early on consensus)",
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"--quiet",
|
|
40
|
+
action="store_true",
|
|
41
|
+
help="Suppress progress output",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--output", "-o",
|
|
45
|
+
help="Save transcript to file",
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--named",
|
|
49
|
+
action="store_true",
|
|
50
|
+
help="Show real model names instead of anonymous Speaker 1, 2, etc.",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--no-blind",
|
|
54
|
+
action="store_true",
|
|
55
|
+
help="Skip blind first-pass (faster, but more anchoring bias)",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--context", "-c",
|
|
59
|
+
help="Context hint for the judge (e.g., 'architecture decision', 'ethics question')",
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"--share",
|
|
63
|
+
action="store_true",
|
|
64
|
+
help="Upload transcript to secret GitHub Gist and print URL",
|
|
65
|
+
)
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"--social",
|
|
68
|
+
action="store_true",
|
|
69
|
+
help="Enable social calibration mode (for interview questions, outreach, networking)",
|
|
70
|
+
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--persona", "-p",
|
|
73
|
+
help="Context about the person asking (e.g., 'builder who hates process work')",
|
|
74
|
+
)
|
|
75
|
+
parser.add_argument(
|
|
76
|
+
"--advocate",
|
|
77
|
+
type=int,
|
|
78
|
+
choices=[1, 2, 3, 4, 5],
|
|
79
|
+
help="Which speaker (1-5) should be devil's advocate (default: random)",
|
|
80
|
+
)
|
|
81
|
+
args = parser.parse_args()
|
|
82
|
+
|
|
83
|
+
# Auto-detect social context if not explicitly set
|
|
84
|
+
social_mode = args.social or detect_social_context(args.question)
|
|
85
|
+
if social_mode and not args.social and not args.quiet:
|
|
86
|
+
print("(Auto-detected social context - enabling social calibration mode)")
|
|
87
|
+
print()
|
|
88
|
+
|
|
89
|
+
# Get API keys
|
|
90
|
+
api_key = os.environ.get("OPENROUTER_API_KEY")
|
|
91
|
+
if not api_key:
|
|
92
|
+
print("Error: OPENROUTER_API_KEY environment variable not set", file=sys.stderr)
|
|
93
|
+
sys.exit(1)
|
|
94
|
+
|
|
95
|
+
google_api_key = os.environ.get("GOOGLE_API_KEY")
|
|
96
|
+
moonshot_api_key = os.environ.get("MOONSHOT_API_KEY")
|
|
97
|
+
|
|
98
|
+
use_blind = not args.no_blind
|
|
99
|
+
|
|
100
|
+
if not args.quiet:
|
|
101
|
+
mode_parts = []
|
|
102
|
+
mode_parts.append("named" if args.named else "anonymous")
|
|
103
|
+
mode_parts.append("blind first-pass" if use_blind else "no blind phase")
|
|
104
|
+
if social_mode:
|
|
105
|
+
mode_parts.append("social calibration")
|
|
106
|
+
print(f"Running LLM Council ({', '.join(mode_parts)})...")
|
|
107
|
+
fallbacks = []
|
|
108
|
+
if google_api_key:
|
|
109
|
+
fallbacks.append("Gemini→AI Studio")
|
|
110
|
+
if moonshot_api_key:
|
|
111
|
+
fallbacks.append("Kimi→Moonshot")
|
|
112
|
+
if fallbacks:
|
|
113
|
+
print(f"(Fallbacks enabled: {', '.join(fallbacks)})")
|
|
114
|
+
print()
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
advocate_idx = (args.advocate - 1) if args.advocate else random.randint(0, len(COUNCIL) - 1)
|
|
118
|
+
|
|
119
|
+
if not args.quiet and args.persona:
|
|
120
|
+
print(f"(Persona context: {args.persona})")
|
|
121
|
+
print()
|
|
122
|
+
if not args.quiet:
|
|
123
|
+
advocate_name = COUNCIL[advocate_idx][0]
|
|
124
|
+
print(f"(Devil's advocate: {advocate_name})")
|
|
125
|
+
print()
|
|
126
|
+
|
|
127
|
+
transcript, failed_models = run_council(
|
|
128
|
+
question=args.question,
|
|
129
|
+
council_config=COUNCIL,
|
|
130
|
+
api_key=api_key,
|
|
131
|
+
google_api_key=google_api_key,
|
|
132
|
+
moonshot_api_key=moonshot_api_key,
|
|
133
|
+
rounds=args.rounds,
|
|
134
|
+
verbose=not args.quiet,
|
|
135
|
+
anonymous=not args.named,
|
|
136
|
+
blind=use_blind,
|
|
137
|
+
context=args.context,
|
|
138
|
+
social_mode=social_mode,
|
|
139
|
+
persona=args.persona,
|
|
140
|
+
advocate_idx=advocate_idx,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Print failure summary
|
|
144
|
+
if failed_models and not args.quiet:
|
|
145
|
+
print()
|
|
146
|
+
print("=" * 60)
|
|
147
|
+
print("⚠️ MODEL FAILURES")
|
|
148
|
+
print("=" * 60)
|
|
149
|
+
for failure in failed_models:
|
|
150
|
+
print(f" • {failure}")
|
|
151
|
+
working_count = len(COUNCIL) - len(set(f.split(":")[0].split(" (")[0] for f in failed_models))
|
|
152
|
+
print(f"\nCouncil ran with {working_count}/{len(COUNCIL)} models")
|
|
153
|
+
print("=" * 60)
|
|
154
|
+
print()
|
|
155
|
+
|
|
156
|
+
# Save transcript
|
|
157
|
+
if args.output:
|
|
158
|
+
Path(args.output).write_text(transcript)
|
|
159
|
+
if not args.quiet:
|
|
160
|
+
print(f"Transcript saved to: {args.output}")
|
|
161
|
+
|
|
162
|
+
# Share via gist
|
|
163
|
+
gist_url = None
|
|
164
|
+
if args.share:
|
|
165
|
+
try:
|
|
166
|
+
import tempfile
|
|
167
|
+
with tempfile.NamedTemporaryFile(
|
|
168
|
+
mode='w', suffix='.md', prefix='council-', delete=False
|
|
169
|
+
) as f:
|
|
170
|
+
f.write(f"# LLM Council Deliberation\n\n")
|
|
171
|
+
f.write(f"**Question:** {args.question}\n\n")
|
|
172
|
+
if args.context:
|
|
173
|
+
f.write(f"**Context:** {args.context}\n\n")
|
|
174
|
+
f.write(f"**Date:** {datetime.now().strftime('%Y-%m-%d %H:%M')}\n\n---\n\n")
|
|
175
|
+
f.write(transcript)
|
|
176
|
+
temp_path = f.name
|
|
177
|
+
|
|
178
|
+
result = subprocess.run(
|
|
179
|
+
["gh", "gist", "create", temp_path, "--desc", f"LLM Council: {args.question[:50]}"],
|
|
180
|
+
capture_output=True, text=True
|
|
181
|
+
)
|
|
182
|
+
os.unlink(temp_path)
|
|
183
|
+
|
|
184
|
+
if result.returncode == 0:
|
|
185
|
+
gist_url = result.stdout.strip()
|
|
186
|
+
print(f"\n🔗 Shared: {gist_url}")
|
|
187
|
+
else:
|
|
188
|
+
print(f"Gist creation failed: {result.stderr}", file=sys.stderr)
|
|
189
|
+
except FileNotFoundError:
|
|
190
|
+
print("Error: 'gh' CLI not found. Install with: brew install gh", file=sys.stderr)
|
|
191
|
+
|
|
192
|
+
# Log to history
|
|
193
|
+
history_file = Path(__file__).parent.parent / "council_history.jsonl"
|
|
194
|
+
log_entry = {
|
|
195
|
+
"timestamp": datetime.now().isoformat(),
|
|
196
|
+
"question": args.question[:200],
|
|
197
|
+
"gist": gist_url,
|
|
198
|
+
"context": args.context,
|
|
199
|
+
"rounds": args.rounds,
|
|
200
|
+
"blind": use_blind,
|
|
201
|
+
"models": [name for name, _, _ in COUNCIL],
|
|
202
|
+
}
|
|
203
|
+
with open(history_file, "a") as f:
|
|
204
|
+
f.write(json.dumps(log_entry) + "\n")
|
|
205
|
+
|
|
206
|
+
except Exception as e:
|
|
207
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
208
|
+
import traceback
|
|
209
|
+
traceback.print_exc()
|
|
210
|
+
sys.exit(1)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
main()
|