关于 council
council 是通过 orca orchestration 运行分阶段 LLM 评议的 Agent 技能,适合需要多方回答和审计记录的复杂问题。它结合匿名互评、主席综合、确定性转录与版本化 HTML 报告。
主要功能
- 01三阶段评议流程
- 02匿名互评排序
- 03确定性汇总与异议呈现
- 04JSON 和 Markdown 转录
- 05离线版本化 HTML 报告
适用场景
- 01用多个模型审视高风险问题
- 02隐藏作者身份比较回答
- 03留存可审计的排序记录
- 04将评议结果发布为 HTML 报告
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheinmir/ setup --skill council安装后在 Claude Code 中输入/council确认已加载。
详细介绍
Skill: council
🧭 为每个席位选择后端(廉价模型 vs Claude),并行运行多个席位(每个席位一个 worktree——不要在一个文件夹中运行多个 opencode)→ 请参阅 orca-dispatch-reference(唯一事实来源)。
围绕 Andrej Karpathy 的 llm-council
(https://github.com/karpathy/llm-council) 三阶段构建的确定性运行框架——Stage 1「First Opinions」、Stage 2「Review」(匿名同行排名)、Stage 3「Final Response」(主席)——接入 orca 编排。
内容
目的与背景
- 目的: 在 orca 编排上运行 Karpathy 风格的三阶段 LLM 理事会——N 个席位独立作答、匿名交叉评分、主席汇总——并提供确定性 transcript 和强制 HTML report(Stage 4)。
- 触发条件(何时使用): 面对棘手问题时,如果单个模型的答案风险较高,且你希望由评审团提供答案,并留下“谁给什么排名”的审计记录,同时通过匿名处理消除偏袒自己的倾向。若只需要一个快速答案,直接询问一个模型即可。调用方式:「run a council」、「llm-council」、「panel of models」、「blind peer-rank these answers」、「ensemble + chairman」或
/council。 - 非目标: 不用于只需一个答案的快速问题;
council.py不调用模型;不负责为席位选择模型(由适配器harness/council.config.yaml负责);report 不会在 transcript 之外添加新的评判。
心智模型
分工如下:
harness/scripts/council.py负责确定性协议,绝不调用模型:匿名处理(移除作者信息 → 按稳定的 sha256 顺序映射为 A/B/C)、均值排名聚合、呈现异议、锚点保护(由 seed 驱动,按评审者调整展示顺序——不使用Math.random),以及 json+md transcript。- orca 编排 负责模型生成:每个席位的答案、每位评审者的排名、主席的综合结果。它会像处理其他多 Agent 批次一样进行调度(参见
orca-workflow、orchestration)。 harness/council.config.yaml是唯一适配器(verified: false):指定每个席位使用的模型、评审模型和主席。每个值都是# ASSUMPTION (not verified)。引擎不会基于这些值分支处理,只会将它们写入 transcript。编辑此文件即可完成理事会配置。
question → Stage 1 answers.json → Stage 2a blind packet(A/B/C + 展示顺序)→ Stage 2b judges.json → Stage 2c transcript(均值排名、异议、chairman_brief)→ Stage 3 chairman_synthesis → Stage 4 版本化 HTML report。
输入和输出约定
| 字段 | 必需? | 含义 | |
|---|---|---|---|
| 输入 | 问题 | 是 | 所有席位使用相同的问题;--question 用于在 report 中显示问题 |
| 输入 | harness/council.config.yaml | 是 | 席位 / 评审者 / 主席(verified: false) |
| 输入 | roster persona(--case / --profile / --personas) | 否 | 为 Stage-1 提示词添加额外的视角 |
| 输入 | --seed N | 否 | 锚点保护的 seed,覆盖 anchor_seed |
| 输出 | answers.json · judges.json | 是 | [{"id","author","text"}] · [{"judge","ranking":[...]}] |
| 输出 | scratchpad/council-<slug>/council.packet.{json,md} + council.transcript.{json,md} | 是 | 匿名数据包;共识、获胜者、异议、chairman_brief、chairman_synthesis |
| 输出 | llmwiki/html/council/council-report-NNN-seed<seed>.html | 是(必需) | 版本化 report,不覆盖旧文件;latest.html 指向最新版本 |
规则与能力
- RULE-01 (MUST):语言(强制):所有 seat/judge/chairman 都必须使用完整带变音符号的越南语作答;将此句加入每个 Stage-1/Stage-2/Stage-3 提示词。
- RULE-02 (MUST):运行输出始终位于
scratchpad/council-<slug>/下——绝不能直接放在run/或仓库根目录文件夹中。 - RULE-03 (MUST):采用盲测而非排除机制作为保护措施——评审只能看到 A/B/C 标签,且标签顺序按各自的展示顺序排列。
- RULE-04 (MUST):名单始终包含至少 1 对立场相反的成员;使用奇数规模(3/5),避免平均排名出现平局。
- RULE-05 (MUST):始终渲染 Stage-4 HTML;渲染器出错时只发出 WARN,绝不能因此终止
rank命令或transcript.{json,md}。 - RULE-06 (MUST):
council.py绝不调用模型;引擎绝不根据配置值进行分支判断——只将其写入 transcript。 - 能力:通过编排层(seat/judge/chairman)分发模型生成任务;在本地确定性地计算协议;写入 transcript + HTML 文件;报告不使用网络/CDN。
失败边界
council.py selftest未以 0 退出,或orca status --json未显示运行中的 runtime → 阻塞,不运行 council。- Seat 的回答未使用变音符号 → 报告也会缺少变音符号(如实渲染)→ 重申 RULE-01 并重新运行该 seat。
- Judges 缺失 →
rank只会生成盲测数据包,然后停止(部分完成,等待 Stage 2b)。 - HTML 渲染器出错 → 发出 WARN,transcript 仍会生成(Stage 4 部分完成,重新运行
render)。 - 名单缺少立场相反的成员 → 在 stderr 发出警告;大小写或 persona 拼写错误 → 提供最接近的建议,不会直接失败。
操作方式
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出状态 | 失败/后续操作 |
|---|---|---|---|---|---|
| W01 | 确定性 | repo、runtime | 前置条件:council.py selftest、orca status --json、配置中包含 seats/judges/chairman | 满足条件 | 缺失 → 阻塞 |
| W02 | 有副作用 | 问题(+ lens) | Stage 1——为每个 seat 分发一个 worker,汇总为 answers.json | answers.json | seat 出错/未使用变音符号 → 重新运行该 seat |
| W03 | 确定性 | answers.json | Stage 2a——council.py prepare | 盲测数据包 | — |
| W04 | 有副作用 | 数据包 | Stage 2b——按展示顺序向 judge 分发盲测回答,汇总为 judges.json | judges.json | — |
| W05 | 确定性 | answers + judges | Stage 2c——council.py rank(自动写入 Stage-4 HTML) | transcript + 报告 | 渲染出错 → WARN,B02 |
| W06 | 有副作用 | chairman_brief | Stage 3——分发给 chairman,将 chairman_synthesis 粘贴到 transcript 中 | 完整 transcript | — |
| W07 | 确定性 | transcript | Stage 4——运行 council.py render <transcript.json>,生成包含 synthesis 的报告 | 新的版本化报告 | 出错 → WARN,transcript 保持不变 |
各步骤的详细说明(W01–W07 的事实依据):
前置条件
python3 harness/scripts/council.py selftest以 0 退出(引擎运行正常)。orca status --json显示有 runtime 正在运行;已启用编排。- 在
harness/council.config.yaml中设置了 Seats/judges/chairman。
协议(将各阶段映射到 orca 分发任务)
Stage 1——首轮意见(由编排生成)
在 council.config.yaml 中为每个 seat 分发一个 worker,向所有 worker 提出相同的问题。
语言(强制):所有 seat/judge/chairman 都必须使用完整带变音符号的越南语 作答(拼写正确,包含全部声调符号和抑扬符号)。绝对不要使用不带变音符号的越南语 (ASCII)。
council.py会如实渲染输入文本 (ensure_ascii=False,不进行规范化)——如果 seat 的回答缺少变音符号,报告 也会缺少变音符号。将此句加入每个 Stage-1/Stage-2/Stage-3 提示词。
orca orchestration task-create --spec "Answer: <question>" --json
orca orchestration dispatch --task <task_id> --to <seat_handle> --inject --json
orca orchestration check --wait --types worker_done --timeout-ms 300000 --json
将回复汇总到 answers.json 中,格式为 [{"id","author","text"}, ...],其中
author 是 seat ID(真实身份;下一步会将其移除)。
阶段 2a — 盲评数据包(council.py 是确定性的)
python3 harness/scripts/council.py prepare answers.json --config harness/council.config.yaml --out scratchpad/council-<slug>/
<slug> = 本次运行的简短名称(例如 release-gate)。始终放在 scratchpad/
下(按每个仓库的约定加入 gitignore)——绝不要直接放在 run/ 或仓库根目录的文件夹中;这些
位置会留下无人清理的未跟踪杂项(经验教训 170726:过去的会话照搬此示例,
导致仓库根目录出现 6 个多余的 run-council*/ 目录)。写入
scratchpad/council-<slug>/council.packet.{json,md}:将答案重新标记为 A/B/C 并
移除作者信息,同时记录每位评委在锚点防护机制中的展示顺序。
按每位评委对应行的顺序向其展示答案,以消除位置偏差。
阶段 2b — 评审(由编排生成)
按 council.config.yaml,以每位评委的展示顺序向其分发盲评答案。请每位评委
按标签返回排名(最佳在前)。
汇总至 judges.json — [{"judge","ranking":["B","A","C"]}, ...]。
防护机制依靠的是盲评,而非排除:评委可以是席位成员,但它无法 识别自己的答案,因此不能偏袒自己。
阶段 2c — 汇总(council.py 是确定性的)
python3 harness/scripts/council.py rank answers.json --judges judges.json --config harness/council.config.yaml --out scratchpad/council-<slug>/
写入 scratchpad/council-<slug>/council.transcript.{json,md}:平均排名共识、获胜者、
异议表(争议最大的答案)和 chairman_brief。
阶段 3 — 最终回复(由编排生成)
向主席分发 transcript 中的 chairman_brief(共识顺序 +
必须解决的异议要点)。主席的综合意见即为最终答案;将其
粘贴回 transcript 的 chairman_synthesis 字段以供记录,然后运行
council.py render <transcript.json>,重新生成显示综合意见的阶段 4 HTML
(rank 自动写入的报告中综合意见为空,因为 rank 只会根据答案和评委重新构建)。
阶段 4 — HTML 报告(必需,由 council.py 自行渲染,离线)
始终渲染——无需标志,不依赖外部技能。 每次 rank 成功后,
render_report_html(t, personas)(就在 council.py 中)会写入一个带版本号的
.html 文件 — llmwiki/html/council/council-report-NNN-seed<seed>.html
(NNN 递增,不覆盖 → 每次运行留下一份不可变记录)+ latest.html 作为便捷
指针(加入 gitignore,避免 diff 频繁变动)。不使用 CDN(系统字体 + 内联 SVG
favicon/grain)→ 离线可用;不耦合 docs-site-macos。数据仅
来自刚构建的 transcript(t)→ 不会编造新内容,所有席位文本都在本地通过
html.escape 转义(防止 HTML 注入)。整个渲染块由 try/except 包裹:
渲染器出错只会发出 WARN,绝不会导致 rank 命令或 transcript.{json,md} 失败
(Taleb 爆炸半径防护)。委员姓名和视角取自 council.personas.yaml;传入
--question 时显示问题(留空 → 报告会隐藏问题行,而不是打印占位文本)。
页面严格包含以下三个 section,顺序如下:
- 理事会意见。 每个席位一张卡片(边框颜色 =
sha256(author),按 共识排名 + 奖牌排序),答案会将(1)(2)(3)自动格式化为列表。 来源:t["answers"]+t["aggregate"]。 - 无记名投票(盲评投票)。 投票表使用匿名标签 A/B/C:每位评委
占一行,显示按标签排列的排名 + 锚点防护机制的展示顺序。仅在本 section
末尾揭示 A/B/C → 作者的映射。
来源:
t["judge_rankings"]+t["anchor_guard"]。 - 最终仪表板。 KPI(获胜者 / 争议最大项 / 共识度)+ 平均排名
共识表;若
chairman_synthesis已粘贴到 transcript 中,则醒目显示。 (rank 只根据答案和评委重新构建,因此主席填写之前此字段为空—— 此时综合意见区块会自动隐藏。) 来源:t["winner"]/t["most_contested"]/t["aggregate"]。
由于所有指标都来自已确定的 transcript,报告只是展示层—— 不会添加新的评判。同一份 transcript → 相同的 HTML。
分支
| ID | 类型 | 守卫条件 | 行为 | 跳过 / 失败 | 汇合点 |
|---|---|---|---|---|---|
| B01 | user_optional | 想要多样化视角(provider 较少) | 抽取 persona roster(见下方 Persona lenses),将 lens 插入 Stage-1 提示词(动态方式),或填入配置中的 persona: 字段(固定方式);roster 和理由写入 transcript | 未抽取 → seat = 纯模型 | W02 |
| B02 | recovery | HTML 报告出错,或需要显示 chairman_synthesis | 重新运行 council.py render <transcript.json> → 新的版本化文件 | renderer 仍然出错 → WARN,transcript 是事实来源 | 结束 |
| B03 | conditional_required | 运行 rank 时没有 --judges | 仅生成 blind packet,然后停止 | — | W04 |
验证与停止条件
确定项:selftest(12 项检查)证明相同的 answers.json + judges.json + seed → transcript 字节完全一致;报告只是 transcript 的展示层。需要审核:chairman synthesis 的质量。在 W07 之后停止,此时报告已包含 synthesis。
示例
- 正向: 使用
roster --case decision配置 3 个 seat,运行“召开 council:是否应该拆分 monorepo” → 3 个 seat 用带越南语变音符号的越南语作答 →prepare生成 A/B/C → 3 个 judge 进行排名 →rank --out scratchpad/council-monorepo/输出 winner + dissent → chairman 汇总 →render写入包含 synthesis 的council-report-NNN-seed<seed>.html。 - 边界/失败:
council.py selftest退出码非 0 → 在 W01 停止,不 dispatch 任何 seat。rank中 renderer 出错 → 仅 WARN,仍会写入council.transcript.json,之后重新运行render。
参考 — Persona lenses — “伟人”视角(可选,增量式)
默认情况下,每个 seat = 一个 MODEL 作答。Persona 层增加多样化视角:每个 seat 戴上一个
lens(Feynman / Munger / Taleb / Rams 等)——当模型多样性受限(provider 较少)时尤为重要。Engine council.py 不变:persona 只是塞入 Stage-1 提示词的文字。
忘了名字 / 语法? council.py roster --list(或不带参数的 roster)会打印全部 case(按用途)· profile · 18 个 persona——按用途选择 --case,不必记住名字。
记错了也能准确调用: 名称和 id 均可匹配,且不区分大小写(--personas Feynman,Taleb 可用);输入错误 → 提供最接近的建议(--case risks → “你是不是想输入 'risk'?”),不会直接失败。
按 case 抽取 3-5 人(纯代码,可记录日志):
python3 harness/scripts/council.py roster --list # catalog: case/profile/persona
python3 harness/scripts/council.py roster --case risk # 3 ghế, có ≥1 cặp đối-trọng
python3 harness/scripts/council.py roster --case ml-ai --size 5 # 5 ghế
python3 harness/scripts/council.py roster --profile lean # 5 người execution-lean
python3 harness/scripts/council.py roster --personas feynman,taleb,rams --json
- Case 标签:
design · strategy · debug · risk · product · decision · simplify · ml-ai(见harness/council.personas.yaml中的表格)。 - 规则: roster 始终会配置 至少一对对立观点(避免回音室);若缺少 → 在 stderr 发出警告。使用奇数数量(3/5),让 mean-rank 不会出现平局。
- 库:18 个 persona + 13 对对立观点(来源:
github.com/0xNyk/council-of-high-intelligence)。
在 protocol 中使用: 抽取 roster 后,将每个 persona 分配给一个 seat——有两种方式:
- 动态(推荐): orchestrator 获取
roster --json的输出,为每个 seat 将 lens 插入 Stage-1 提示词:“通过 <name> 的视角回答:<lens>。<sig>。” - 固定: 在
harness/council.config.yaml中为每个 seat 填写persona:字段。
Roster 和抽取理由(case、tension 对)会写入 transcript,以便审计(支柱 5)。“哪个 model 填入 seat”仍是已隔离的未知项,记录在 council.config.yaml(verified)中——persona-lens 与此无关。
参考 — council.py 命令
| 命令 | 功能 |
|---|---|
rank <answers.json> --judges <judges.json> | 完整聚合 → transcript.json + .md |
rank <answers.json> --judges <j> | + 自动写入带版本号的 llmwiki/html/council/council-report-NNN-seed<seed>.html(Stage-4,必需) |
rank <answers.json>(不带 judges) | 输出盲测数据包,然后停止 |
prepare <answers.json> | 仅生成盲测数据包(Stage 2a) |
render <transcript.json> | 从已有 transcript 重新渲染 Stage-4 HTML(例如粘贴 chairman_synthesis 后)→ 新的带版本号文件 |
roster --case <tag> / --profile <p> / --personas a,b,c | 抽取 3-5 个 persona-lens(纯查找,至少 1 对立场相反);--size 3|5、--json |
selftest | 一致性测试向量;断言确定性 + 正确性 |
标志:--seed N(锚点防护种子;覆盖配置中的 anchor_seed),
--out DIR(transcript json/md)、--config harness/council.config.yaml、
--question "..."(报告中显示的实际问题;留空 → 隐藏 question 行)。
Stage-4 HTML 始终带版本号并写入 llmwiki/html/council/ — 无需标志。
参考 — 适配器边界(现在构建,之后适配)
- 契约(现在已构建并测试): json schemas(
answers.json、judges.json、transcript)以及council.py中所有确定性操作。 - 隔离区(
verified: false):harness/council.config.yaml中的模型身份。数学运算与它们无关。 - 之后适配(一个文件): 编辑配置中的席位/评委/主席,运行一次真实 council,然后将
verified: true。无需更改引擎。
参考 — 确定性保证
相同的 answers.json + judges.json + 种子 → 每次运行都会生成逐字节相同的 transcript。匿名化取决于 sha256(id),而非输入顺序或作者,因此位置和作者身份都不会泄露。锚点防护使用参数为每位评委单独设定种子,不使用全局 RNG。selftest 可验证以上所有保证(12 项检查)。