@expert-council/pi-package

extensionmaintained

Expert Council: cost-aware multi-model expert orchestration for Pi — assemble a council of scout, oracle, implementation-worker, and reviewer experts and delegate bounded tasks

by — · v0.8.9 · published 6d ago

$ pi install npm:@expert-council/pi-package
downloads/mo
0
stars
0
last push
6d ago
open issues
0

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

No downloads in the last 12 weeks.

README

Expert Council

English | 简体中文

Expert Council 是一个面向 Pi 与 Codex 等 MCP 宿主的本地、多模型、成本感知专家编排系统。它会发现 Pi 当前注册的 LLM API 与 Coding Plan 中连接的模型,将运行时元数据与用户定义的计费策略、能力画像和本地可靠性数据结合,动态组建一个精简的语义专家团队,并通过 Pi 执行有明确边界的任务,最后向主代理返回紧凑的结构化结果。

主要优势:

优势说明
省钱灵活运用所订阅的 Plan 和 LLM API,根据任务难度自动调配最合适的模型
快速可同时并发多个最合适的模型进行工作,快速完成仓库探索与上下文压缩
更安全为不同专家分配不同的只读/可写权限,可写专家在 Git worktree 写入后经主代理审查后并入主分支
上下文节省主代理不再需要包含过多工具调用产生的冗长上下文,只接收专家返回的摘要化处理结果

Expert Council在首次运行时会调用网络聚合搜索模型能力评价刻画当前可用的模型能力画像; 在实践中发现,理论上最强的模型不一定是最合适的执行者。一个工具调用稳定、Shell 行为可靠、边际成本较低的模型,可能比更强但执行不稳定的模型拥有更高的实际任务价值。推荐配置执行能力强、成本较低的模型作为主代理,当遇到复杂问题时 Expert Council 可派遣强思考能力模型进行审查或 Plan。

当前状态

当前版本(0.8.9)已包含:

  • waiting_on_host:为“专家在等宿主作答”单立的告警码。未被回应的 request_decision 不再被报成运行停滞, 提示语也改为让宿主去回应而不是去中止。同时,宿主主动发起的 aborted 不再计入模型可靠性, 上下文窗口溢出会被正确分类而不是读成推理能力差。
  • 升级路径现在遵守操作者的路由策略;Pi 的开发依赖升到 0.86.1。
  • 专家对话记录(显式开启):security.observability.contentStream 提供五档(none、assistant、assistant+tool-tail、transcript、transcript+args)并可按角色覆盖,另有每事件/每文件/每目录的字节上限。默认 none;只有运营者配置或环境变量能放宽——模型、委派参数、专家输出都无权开启。
  • security.observability.recordReasoning:对“绝不记录思维链”规则的显式例外,默认关闭。推理在每条记录上都有标记,渲染为 thinks 而非 says,且开启时 expert_inspect 会主动声明。
  • panel 转录版式:expert-council watch 与观察窗口按 Pi 风格渲染——叙述正文不加逐行前缀、工具输出为带底色的块并在策略允许时显示 $ command 头、中日韩宽度对齐;一套统一的控制字符策略使任何渲染字段都无法操纵终端。
  • 可选自动开窗口(security.observability.autoOpenWindow):每次委派一个窗口,复用同一个流文件与 watch,只有你按键才关闭。
  • 两份 README 给出 council-config.json 与 route-policy.json 的完整标准写法,并由测试用真实 validator 解析、把“全默认”文档与代码实际默认值逐项比对。
  • expert-council --version:在任何 council、运行时或配置被触碰之前就能回答,取自发布出去的 manifest。
  • 可用性标记会报告它自己的类别与存活时长,两分钟限流不再被描述成一次宕机。
  • 交互式专家:运行中专家可在重大、难回退或方向含糊处暂停,通过 request_decision 向主代理给出 2-4 个推荐选项(含可选自由文本);宿主用 expert_respond 回答,专家在同一会话继续。非终止——与 report_and_stop 区分。
  • 动态工具权限:预设角色工具是种子而非上限。专家用 request_tool 申请缺失工具;宿主授予 once(用一次后自动撤销)/persistent(本会话)/reject。security.toolGrants 提供运维侧持久按角色授予。只读执行永不升级为变更/shell 工具(隔离保证)。
  • 交互经正确性通道发现:未决交互以 pendingInteraction 出现在 expert_status(view:"running") 与 expert_result(includeProgress);原生 Pi 包还会在交互一打开时用 expert-council-interaction 通知唤醒宿主;无法接收推送的无头宿主(Codex/MCP)继续轮询。每执行有界(默认 3 轮 + 等待超时)。
  • 跨语言环境:provisionWorkspace 不再只支持 Node。驱动注册表从各工具链已有的全局下载缓存物化 node/pnpm/yarn/bun、python(uv/poetry/宿主 venv)、rust、go、jvm/maven、dotnet、ruby、php、elixir。环境即代码后端(flake.nix、.devcontainer/)被检测并交还委托而非重造。新增 security.workspaceProvisioning.strategy(auto/drivers/as-code/in-place)与 runtimeEnv(isolated/host-env);并发 worktree 绝不共享重编译 target 目录。
  • 进度可观测开关:security.observability.expertWindow 决定能看到专家实时工作的多少——off(默认,不环境播报)、events(running 视图浮现轻量进度),或 interactive:此外写入一份可让运维者在另一个终端跟随的实时事件流,用 expert-council watch --exec ID --follow 查看,完全不占用主代理上下文。security.observability.streamToHost 可关流,redactToolArgs(默认 true)决定工具调用是否只按名称记录。三者都不约束 pendingInteraction 正确性通道,也不约束宿主显式索取的 expert_result(includeProgress) 快照。
  • 挣扎检测:security.guardrails 统计运行时实际观测到的工具失败与预算消耗,以 attention 呈现在运行视图并为主代理发一条原生通知,每次执行最多 steer 专家两次,且从不中止。maxTotalWallMs 为一次委派的全部尝试设总时长上限;executionMetadata.attemptHistory 说明每次尝试做了什么。
  • 与宿主无关的 Core:配置校验、模型归一化、按模型/供应商计费倍率、画像分层、角色评分、任务分类、动态团队规模、重试/升级和遥测聚合。
  • 基于 Pi 当前 ModelRuntime 与 createAgentSession API 的执行运行时。
  • 每个专家会话的硬工具白名单和已安装 Skill 过滤。
  • 写入型专家的独立 Git worktree 隔离,及按仓库锁文件的自动依赖供给(npm/pnpm/bun/yarn,另有 uv/poetry/cargo 等驱动):在工具链支持的场合抑制安装期脚本执行、白名单化子进程环境、按执行复用工作树;无法抑制的驱动会明确声明并把残余暴露作为 limitation 交给主 Agent;Python 生态自动暴露宿主 .venv 解释器绝对路径。
  • 运行时验证门:供给完成后自动运行仓库 typecheck 与测试,失败把成功结果降级为 partial 并触发修正重试,全程零主代理开销。
  • 专家主动停止工具 report_and_stop:专家判定任务无法完成(缺工具/缺环境/权限拒绝)时立即提交结构化报告(阻塞原因、发现、风险、建议下一步)并以 partial 结果终止,委派循环不重试不升级,变更工作树保留待检。
  • 专家与主会话同生命周期(security.expertLifetime,默认 host-bound):主会话退出或被替换时自动中止所有运行中的专家,孤儿专家不再烧额度;detached 可恢复旧行为。
  • 失败结果保留产物:可写执行下,超时、会话错误、抛错失败的结果都附带 filesChanged 与最后助手输出,长任务超时不再返回空结果。只读执行则正确地不上报任何 filesChanged。 来自只读角色的 partial 结果现在是直接交付、而不是重跑一遍(缺陷 #32):要不要重试本该由主 Agent 判断,Council 不再替它把整份预算花第二次,而是带着一条 risk 说明交付这份不完整的答复。可写角色的 partial 照旧升级。调用 report_and_stop 主动停止的专家不受此规则影响:它照旧直接终止、不再重试,因为那是“任务被阻塞”而不是“任务不完整”。
  • 持久化写侧钳制:超长数组与文本在唯一持久化入口截断,啰嗦专家再也不能写出不可重载的 state 文件。
  • expert_availability_reset:按 */供应商/provider/id 即时清除误标或已恢复的可用性标记,无需改文件或重启。
  • expert_verify:插件侧在保留 worktree 或受校验工作区运行有界命令并返回真实退出码与输出尾部;tests[] 现携带退出码、计数、耗时与输出尾部。
  • expert_status 视图:默认的有界 summary(运行中含剩余预算、近期完成、供应商并发槽)、running、以及旧全量 full,大幅降低观测上下文开销。
  • 完成通知可靠性:Pi 扩展在 idle/streaming 竞态时短暂重试完成消息,不再静默丢弃。
  • timeoutMs 与 reasoningLevel 均为必填派遣参数:主代理必须按任务与模型显式选择;批量派遣时每一项自带这一对参数,仅单次委派从顶层读取;组合名单可按角色/模型钉定思考档位并覆盖派遣参数。
  • 专家 fail-fast 纪律:发现工具或环境不可能完成任务时立即以结构化 missing_context/permission_error 终止并实时回传,委派循环不做无谓重试。
  • 持久化理事会组合:council-compositions.json 命名名单(角色可配多模型与思考档位)、首问组合菜单、会话绑定、model 钉定与同角色并发派遣。
  • 供应商限额与并发:route-policy.json 按供应商设置日/周加权 token 上限(usage-ledger.json 按 costMultiplier 记账)与并发上限,超限候选自动排除并给出原因。
  • 运行时可用性标记分级:模型失效(24 小时)、配额周期耗尽(6 小时、全 plan 传播)、瞬时限流 TPM/RPM(2 分钟) 三档 TTL,自动过期重试,不再把限流误当配额耗尽拉黑。
  • council-config.json 运营者配置默认在数据目录发现(无需环境变量),expert_inspect 返回路径、生效的供给模式与进度可观测设置,供主代理代为编辑。
  • 支持 JSON 输出的 CLI。
  • 包含 13 个异步语义工具、事件驱动完成等待、验收反馈闭环及显式 worktree 清理能力的 MCP Server。
  • 原生 Pi Package。
  • 提供商会话错误透传:403 AccessDenied 等上游拒绝不再被吞掉,会以真实诊断和正确失败类型返回主代理。
  • 跨进程共享模型评估:多实例并行时,可用性标记无需重启即可互相可见。
  • 自包含的 Codex 插件,内置共享 Skill、stdio MCP Server 与经过验证的 Pi SDK 运行时,通过 Codex 宿主自有的 codex/sandbox-state-meta 能力发现工作区(无需 hook 或全局 SDK 解析)。
  • 不会消耗模型额度的确定性自动化测试。

架构

Codex 或 Pi 主代理
        |
        | 语义工具 / 共享 Skill
        v
 Expert Council Core
 - 资源与模型归一化
 - 计费与能力画像
 - 确定性路由
 - 角色与团队规模
 - 失败处理
 - 遥测聚合
        |
        v
     Pi Runtime
 - 可调用模型发现
 - 工具硬白名单
 - 已安装 Skill 过滤
 - 有边界的专家会话
 - 工作区隔离
     /     |      \
   CLI  Pi Package  MCP Server
                        |
                   Codex 插件

TypeScript project references 保证依赖只能按以下方向流动:

core <- pi-runtime <- cli
                   <- mcp-server <- codex-integration
                   <- pi-package

Core 不导入 Pi、Codex、MCP transport、文件系统、Shell 或进程 API。CLI、MCP Server 和宿主分发层使用的是同一套服务与路由逻辑。

快速开始

环境要求:

  • Node.js 22.19 或更高版本。
  • npm 11 或兼容版本。
  • 已安装并配置至少一个可用模型的 Pi。
  • 当写入型专家需要 worktree 隔离时,Git 仓库必须至少有一个提交。

安装 Pi Package

从 npm 安装(推荐):

pi install npm:@expert-council/pi-package
pi list
pi --verbose

pi list 应显示 npm:@expert-council/pi-package 及其解析后的目录;新启动的 verbose Pi 会话应加载 dist/extension.js、expert-council Skill 和 12 个语义工具。后续升级:

pi update npm:@expert-council/pi-package

安装 Codex 插件(可选)

若要由 Codex 担任主代理,可直接从 Git Marketplace 安装固定版本的预构建插件,无需克隆仓库或在本地构建:

codex plugin marketplace add Labiey/expert-council-router --ref v0.8.9 --json
codex plugin add expert-council@expert-council-router --json

安装后请完全重启 Codex Desktop。插件自带经过测试的 Pi SDK 运行时,运行时不依赖全局安装的 pi 包;但仍需至少安装并配置过一次 Pi(或手动把提供商凭据放入 ~/.pi),账号与模型目录才可用。Windows CLI 定位、验证、升级和卸载步骤见 Codex 插件。

从源码构建(开发)

npm install
npm run build
npm test

只发现模型,不调用任何模型:

node packages/cli/dist/bin.js models --json
node packages/cli/dist/bin.js inspect --json

只构建专家团队,不执行专家:

node packages/cli/dist/bin.js build "修复设备热插拔竞态问题" --max-experts 4 --json

只有在你确定需要实际调用 Pi 模型时才执行委派:

node packages/cli/dist/bin.js delegate architecture-oracle "分析并发调用路径" \
  --workspace /path/to/repo --timeout-ms 600000 --reasoning-level high --json

零配置模式会使用保守的能力默认值,将无法确认的计费类型标记为 unknown,并拒绝未隔离的写入操作。它不会猜测某个 API 是免费的,也不会根据模型名称臆测其能力强弱。

模型发现

PiExpertRuntime 调用 Pi 的 ModelRuntime.getAvailable(),而不是使用硬编码模型列表。仅仅出现在模型注册表或用户画像中的模型不会自动进入路由;只有 Pi 报告当前可调用的模型才会被使用。

运行时会归一化以下信息:

  • Provider 与模型 ID;
  • 显示名称;
  • 推理支持以及 Pi 暴露的推理等级映射;
  • 上下文窗口和最大输出;
  • 输入模态;
  • 已发布的 API 价格字段;
  • 安全的兼容性元数据。

运行时会优先使用 PI_CODING_AGENT_MODULE 指定的目录,其次解析本地已安装的兼容 Pi SDK,最后才尝试全局 npm Pi 安装。指定的目录加载失败会直接报错,不会静默替换成另一个 Pi 版本。若全部失败,会返回可操作的诊断信息,而不是伪造模型列表。

具体加载哪一份需要弄清楚,因为它不一定是宿主正在跑的那一份。@earendil-works/pi-coding-agent 是 peer 依赖,所以安装 @expert-council/mcp-server 或 CLI 时,npm 会把当时已发布的 Pi 拉进该安装自己的 node_modules,而自动探测最先找到的就是这份本地副本——结果是一个钉死 Pi 版本的智能体运行时,而不是与宿主对齐的运行时。在 local-path 的 Pi 扩展里,同一规则读的是仓库的 node_modules。若你希望专家完全运行在你正在对话的那个 Pi 上,就把 PI_CODING_AGENT_MODULE 指向宿主的包目录。

Pi 在每个会话内只构建一次这份清单,且提供商目录可能保留失效的模型名称,因此 listAvailableModels() 可能包含一个上游已无法服务的模型。在真实调用失败之前,路由会把它视为可调用;这正是带失效模型证据的失败会在持久化模型评估中标记该模型不可用的原因(见下文路由一节)。标记会在 24 小时后过期,恢复的模型会自动重新被尝试。

配置

通过 EXPERT_COUNCIL_CONFIG 环境变量,或 CLI 的 --config PATH 参数指定配置文件。可以从 config/examples/balanced.example.json 开始。

画像优先级为:

内置保守默认值
  < 用户配置或可选预设
  < 当前任务运行时覆盖

客观运行时元数据单独合并。本地结果数据只有在积累至少 3 个样本后才会影响路由,而且调整幅度受 routing.localLearningMaxAdjustment 限制。显式用户配置始终拥有更高权威。

计费策略

支持以下计费类型:

subscription  metered  quota  free  unknown

边际成本以单一数值 costMultiplier 表示,因为公开 Token 单价无法表达订阅计划、固定额度、本地推理和促销额度。Pi Runtime 适配器会把运行时明确报告的订阅或具名 Token Plan 目录识别为 subscription;否则,Pi 模型目录存在非零单价的 Provider 识别为 metered,没有可靠证据的保持 unknown。expert_inspect 会返回推断来源,显式用户配置始终具有最高优先级。同一按量 Provider 内的模型仍会通过 routing.apiPriceWeight(默认 0.35)比较具体单价;全零价格表按“未提供”处理,不会猜测为免费。

按模型的计费条目。 订阅 token plan 带有周期配额(常见为周限额)且各模型消耗倍率不同,单一 provider 级权重无法表达真实边际成本。为特定模型增加 provider/id 键即可覆盖 provider 默认值——路由先查显式 billingProfile,再查模型级条目,最后才是 provider 级。同样的键也可用于 model-assessment.json 的 billing 段与用户配置:

{
  "billing": {
    "providers": {
      "subscription-provider": {
        "billingType": "subscription",
        "costMultiplier": 0.1
      },
      "subscription-provider/qwen3.8-max": {
        "billingType": "subscription",
        "costMultiplier": 2.0
      },
      "scarce-provider": {
        "billingType": "quota",
        "costMultiplier": 5.0
      }
    }
  }
}

计费倍率

costMultiplier 是用于成本评分与 Provider 额度计账的相对 Token 消耗权重,省略时默认为 1.0,取值范围为 0.01–100。数值越低,在成本权重较高的角色中越经济;成本效率得分为 10 / (1 + costMultiplier)(在计费类型加成之前)。参考值:按量 flash 级 ≈1.0,按量旗舰全尺寸 ≈5.0,token plan flash 级 ≈0.1,token plan 旗舰 ≈2.0。

已移除的档位词汇仍会被接受并确定性映射,旧配置继续可用:

旧 marginalCostClasscostMultiplier
very-low0.1
low0.5
normal1.0
high3.0
scarce5.0

旧的 usagePreference 字段会被忽略并丢弃,不再影响路由。

Provider 限额与并发

route-policy.json 可在模型 allow/deny 策略旁携带可选的 providers 映射——allow/deny 语法见路由策略文件,本节不再重复:

{
  "version": 1,
  "providers": {
    "qwen-token-plan-cn": { "maxConcurrency": 2, "dailyTokenCap": 5000000, "weeklyTokenCap": 40000000 },
    "zai": { "maxConcurrency": 0 }
  }
}
  • maxConcurrency(整数 ≥ 0)限制该 Provider 同时运行的执行数;0 或省略表示不限制。
  • dailyTokenCap 与 weeklyTokenCap(整数 > 0)限制每个 UTC 自然日与每个 ISO 周(周一为起点,UTC)的加权 Token 消耗。默认值为每日 20,000,000、每周 150,000,000。
  • 计账是加权的:每次尝试消耗该模型 (inputTokens + outputTokens) × costMultiplier;不计算缓存读写 Token。
  • 触顶会在持久化评估中标记该 Provider 的所有模型,直到下一个 UTC 重置边界——每日触顶为下一个 UTC 零点,每周触顶为下周一 00:00 UTC——路由因此停止在已耗尽的 Provider 上浪费尝试,并在重置后自动重试。
  • 在途计数使用分配给该 Provider 的运行中执行;达到 maxConcurrency 的 Provider 在新候选中被排除,直到其中一个完成。
  • 未接入 usage ledger 时,额度与并发限制均禁用,Core 不进行任何 I/O。

expert_inspect 会为每个 Provider 返回 providerLimits,包含 maxConcurrency、两项额度、加权 usedToday/usedWeek、remainingDaily/remainingWeekly 与 inFlight。

config/examples/ 提供以下示例:

  • balanced.example.json:适用于零配置的保守策略。
  • subscription-heavy.example.json:优先消耗订阅资源,保护稀缺额度。
  • metered-quality.example.json:区分经济型与高质量按量 API。
  • qwen-glm.example.json:明确标记为假设性用户偏好的示例,不代表客观评测结论。

运营者配置(council-config.json)

council-config.json 是运营者配置文件。它可选且默认在共享数据目录(与 model-assessment.json、route-policy.json 同层)自动发现——不需要任何环境变量。解析顺序:

  1. configPath 选项或 EXPERT_COUNCIL_CONFIG 环境变量——最高优先,且文件必须存在;
  2. <数据目录>/council-config.json——存在则读取,不存在则静默跳过;
  3. 否则全部走内置默认(workspaceProvisioning.mode = "none")。

文件接受完整的运营者 schema:security、billing、profiles、routing。典型示例:

完整的标准写法如下,每个键都停在它的内置默认值上。所有键都可以省略——文件里可以只写一行,没写的部分就按下面的值兜底:

{
  "billing": {
    "providers": {}
  },
  "profiles": {
    "models": {}
  },
  "routing": {
    "maxExperts": 4,
    "minimumWorkerToolReliability": 4,
    "localLearningMaxAdjustment": 1,
    "apiPriceWeight": 0.35,
    "roleWeights": {},
    "diversity": {
      "repeatedModelPenalty": 0.35,
      "reviewerSameProviderPenalty": 0.25,
      "reviewerSameFamilyPenalty": 0.5
    },
    "taskClassification": {
      "tinyMaxWords": 8,
      "tinyMaxCjkChars": 18,
      "complexMinWords": 35,
      "complexMinCjkChars": 60,
      "complexSignalThreshold": 2
    }
  },
  "retry": {
    "maxAttempts": 1,
    "maxEscalations": 0,
    "correctedRetriesPerModel": 0
  },
  "security": {
    "workspaceStrategy": "auto",
    "allowInPlaceMutations": false,
    "allowedWorkspaceRoots": [],
    "trustedSkills": [],
    "worktreeRetentionMs": 86400000,
    "toolGrants": {},
    "expertLifetime": "host-bound",
    "observability": {
      "expertWindow": "off",
      "streamToHost": true,
      "redactToolArgs": true,
      "autoOpenWindow": false,
      "recordReasoning": false,
      "contentStream": "none",
      "contentByRole": {},
      "contentWindowLines": 10,
      "contentWindowChars": 120,
      "contentEventBytes": 65536,
      "contentFileBytes": 10485760,
      "contentTotalBytes": 209715200
    },
    "guardrails": {
      "warnHost": true,
      "nudgeExpert": true,
      "consecutiveToolFailures": 3,
      "minCallsForRatio": 8,
      "failureRatio": 0.5,
      "budgetFractions": [0.6, 0.85]
    },
    "workspaceProvisioning": {
      "mode": "none",
      "strategy": "auto",
      "runtimeEnv": "isolated",
      "timeoutMs": 600000,
      "maxConcurrent": 1,
      "scrubEnv": true,
      "removalTimeoutMs": 300000
    }
  }
}

provider 与 model 的键名必须和 expert_inspect 报告的完全一致,即 "provider" 与 "provider/id"。下面是大多数人真正会改的两段记录(acme-* 只是虚构示例,不是推荐值):

{
  "billing": {
    "providers": {
      "acme-plan": { "billingType": "subscription", "costMultiplier": 0.1 },
      "acme-metered": { "billingType": "metered", "costMultiplier": 1 },
      "acme-host": { "billingType": "quota", "costMultiplier": 5, "disabled": false }
    }
  },
  "profiles": {
    "models": {
      "acme-plan/acme-flash": {
        "reasoning": 7,
        "planning": 7,
        "architecture": 6.5,
        "coding": 7,
        "debugging": 7,
        "review": 6.5,
        "longContext": 7.5,
        "toolReliability": 7,
        "bashReliability": 7,
        "autonomousExecution": 7,
        "speed": 8.5,
        "preferredReasoningByRole": { "scout": "low", "architecture-oracle": "high" },
        "incompatibleRoles": [],
        "billingProfile": "acme-plan",
        "disabled": false
      },
      "acme-metered/acme-pro": {
        "coding": 9,
        "debugging": 9,
        "toolReliability": 9,
        "bashReliability": 9,
        "autonomousExecution": 9
      }
    }
  }
}

字段说明:

  • security.workspaceProvisioning.mode——"none"(默认,不安装)、"auto"(按锁文件探测生态自动安装)或 "custom"(原样运行 command)。
  • security.workspaceProvisioning.verifyCommand——单条命令的扁平 argv 数组,供给后的变更型专家完工时以它替代默认的「先 typecheck 后 test」。
  • security.workspaceProvisioning.scrubEnv——为 true(默认)时,供给与验证子进程只收到白名单环境;~/.npmrc 的 registry 令牌仍可能到达子进程(已记录的残余风险)。
  • security.worktreeRetentionMs——供给后的工作树保留多久供审查(默认 24h)。有下限:比「最长合法单次尝试 + 十分钟」更年轻的树永不回收,因为那可能属于仍在工作的专家;设得更小会被抬到下限,而不是被忽略。回收前会先把未提交改动写成补丁——见 SECURITY.md。回收是尽力而为,且有一个具体地方你需要知道:删除做到一半失败时,可能留下一个 Git 已不再登记的目录,此后任何按年龄回收都碰不到它;这类残留会被点名并给出大小,而不是瞒着你删。
  • billing / profiles / routing——与 model-assessment.json 计费条目、模型能力画像、角色权重相同的 schema。

主代理编辑契约:expert_inspect 在 operatorConfig 下返回文件位置与生效的供给模式。当用户要求调整供给行为时,主代理直接编辑该文件,并告知用户需要重启宿主会话才能生效。JSON 格式错误或非法枚举会在启动时硬报错(运营者笔误绝不会静默关闭安全设置)。

工作树供给与验证门

变更型工作树从已提交的 HEAD 创建,天然不含未追踪的本地产物。运行时可在专家开工前用仓库自身提交的锁文件为其安装依赖;该功能默认关闭:

{
  "security": {
    "workspaceProvisioning": {
      "mode": "auto",
      "timeoutMs": 600000,
      "maxConcurrent": 1,
      "scrubEnv": true,
      "removalTimeoutMs": 300000
    }
  }
}

该配置保存在共享数据目录(与 model-assessment.json 同一层)的 council-config.json 中。文件可选且默认自动发现——不需要任何环境变量:存在则读取,不存在则全部走默认。expert_inspect 会在 operatorConfig 下返回其路径,主代理可按用户需求代为编辑(修改在宿主会话重启后生效)。显式的 configPath 选项或 EXPERT_COUNCIL_CONFIG 环境变量仍优先,且必须指向已存在的文件。

  • mode 取 none(默认,永不预置)、auto(检测仓库提交的锁文件并安装)或 custom(把 command 原样作为 argv 数组运行)。
  • auto 运行 pnpm install --frozen-lockfile --prefer-offline、npm ci --prefer-offline --no-audit --no-fund 或 bun install --frozen-lockfile,三者都附加 --ignore-scripts。yarn 是刻意的例外:Berry 已把该 flag 从 CLI 移除,加上它只会让安装报错而非加固,因此改由子进程环境提供 YARN_ENABLE_SCRIPTS=false 与 YARN_IGNORE_SCRIPTS=true,一次覆盖两代 yarn。注册表还会用各工具链自己的全局下载缓存物化 python(uv sync、poetry install,或宿主 .venv 解释器)、rust、go、jvm/maven、dotnet、ruby、php、elixir;这些驱动并不都有等价开关,所以每个驱动各自声明能否抑制构建脚本——用了抑制不了的驱动时,向主 Agent 上报一条 limitation,而不是假装没有这回事。声明了 environment-as-code 后端(flake.nix、.devcontainer/)的仓库会被检测并交还给它,从不另起炉灶重新实现。
  • 只有变更型工作树会被预置;只读角色在主工作区运行,永不预置。
  • 子进程环境经过白名单 scrub(PATH、HOME、USERPROFILE、APPDATA、LOCALAPPDATA、TEMP、TMP、SYSTEMROOT、SYSTEMDRIVE、COMSPEC、PROGRAMFILES、PROGRAMDATA、GIT_*、npm_config_registry、npm_config_cache);API token 与云凭据不会被转发。驱动自带的抑制设置项在 scrub 之后合并,因此既不会被白名单擦掉,父进程里的同名值也无法反向覆盖议会的意图。
  • 预置为 ready 时,若配置了 verifyCommand 就运行它,否则先 npm run typecheck 再 npm test。验证门失败会把本已成功的专家结果降级为 partial(failureType: "test_failure")。 供给完成后验证门自动运行仓库的 typecheck 与测试命令;门失败会把本已成功的结果降级为 partial(failureType: "test_failure"),从而触发既有修正重试路径。

可观测性与专家窗口

security.observability 决定能看到专家实时工作的多少。它与 pendingInteraction 通道有意分开:后者是正确性面,永远上报。

{
  "security": {
    "observability": {
      "expertWindow": "interactive",
      "streamToHost": true,
      "redactToolArgs": true
    }
  }
}
expertWindow得到什么
off(默认)无环境播报:running 视图只有已用/剩余预算。
events在此基础上,expert_status running 视图多出有界的实时进度块(messageCount 与最后一条助手输出),需 streamToHost 为 true。
interactive在此之上再写入一份事件流,运维者可在另一个终端跟随,且不花费主代理任何上下文。

事件流位于 <数据目录>/observability/<executionId>.jsonl,一行一个 JSON 对象:started、tool_started、tool_finished、assistant_text、interaction_opened、interaction_answered,最后恰好一个 stopped / completed / failed。跟随方式:

expert-council watch --exec exec_abc123 --follow
  • --follow 一定会退出:遇到终止事件、超过 --timeout-ms(默认 300000)、或文件消失。它按字节偏移量追读,绝不输出未写完的半行。--json 输出原始对象。
  • 专家叙述被压成每事件一行的有界文本;磁盘上不会有工具输出,也不会有思考链。
  • redactToolArgs(默认 true)只记录工具名;设为 false 会额外写入有界的入参摘要,内容是专家传了什么——包括文件路径与完整命令行。仅在本地文件里留下这些值也可接受的场合才关闭。
  • 事件流文件 7 天后自动清理。
  • 事件流由运行专家的那个进程写入。同一数据目录下的 watch 才能看到它;换一个无关仓库去跟是看不到的。
  • expert_inspect 会报 runtimeCapabilities.eventStream,主代理由此能判断所请求的档位是否真可用;写入能力缺失时 interactive 降级为 events,并在 warnings 里说明。
  • watch 在 Council 写出委派级终止标记(delegation_final)时关闭,并把收尾报成 delegation finished,而不是拿第一次尝试的终止事件当结局。之所以要区分,是因为重试或升级后的委派每次尝试都会写一个终止事件:见到第一个就停的观察者会在失败那一刻关窗,永远看不到真正完成任务的那次尝试。旧运行时的流、或进程已死的流,改为在足够长的静默之后才关闭(默认 15 秒,可用 --quiet-ms 调整)——沉默不等于死亡:专家在工具调用之间会思考数秒,一次构建更能安静几分钟。这条兜底只在已经看到终止事件之后生效;从未出现过终止事件的流改由 --timeout-ms 兜底,因为刚启动的专家在模型思考时本来就可以什么都不写。--timeout-ms 始终给等待设上限。
  • 每个事件都带上产生它的那次尝试序号,重试后的尝试渲染成 [role model #2],于是升级本身在窗口里就看得见。事件渲染被强制压成一行:一条事件吐出第二行会让运维者的 tail 与流失步。

自动打开观察窗口

默认什么都不弹。security.observability.autoOpenWindow 是一个显式的 opt-in:开启后,每次委派会拥有 一个自己的终端,跑的正是上面那条命令——和你亲手开一个窗口看到的完全一样,因此主 Agent 依然不为此花任何上下文。

{
  "security": {
    "observability": {
      "expertWindow": "interactive",
      "autoOpenWindow": true
    }
  }
}
  • 一次委派一个窗口,而不是一次尝试一个。 重试或升级沿用同一个 execution id 与同一个流文件,否则会闪出 第二个窗口只给你看半截工作。委派写到最终标记时,该窗口记录被释放。
  • 不限制同时开启的窗口数。 三个专家在跑就是三个窗口——数量是操作员的事,不是议会的事。
  • 专家结束不会关掉窗口。 跟随器在 delegation_final 停止滚动,然后等你按键,你可以慢慢往回翻看整段 过程,再由你决定它何时消失。
  • 窗口自身的超时跟随它所观察的那次尝试(该尝试的 timeoutMs 再乘 1.25,用于覆盖前后的路由、 准备与收尾;下限 15 分钟,上限为跟随者的 6 小时),所以观察者不会在被观察的委派仍在运行时先过期。 上限原本是一小时,于是一次可以合法跑上数小时的单尝试,其正确预算根本无法表达。
  • 窗口只读。 决策请求与工具授权仍然回到宿主,因此能回答专家的地点始终只有一个。
  • 只有这个配置项能开窗。 任何工具参数、专家输出或任务文本都触发不了它,任务内容也不会进入命令行: 参数只有 execution id、角色名与若干数值选项。

开窗是运行时唯一一次代替操作员启动本机进程:它启动的是操作系统的终端宿主(装有 Windows Terminal 时用 它,否则退回经典控制台),运行的是本项目自带的 CLI 跟随器。若找不到终端宿主或 CLI,或平台不是 Windows, 那就什么都不弹、委派完全不受影响,并且 expert_inspect / expert_status 会给出一条说明回退原因的 limitation。要把观察者指向特定的 CLI 构建,设 EXPERT_COUNCIL_CLI;要指定终端宿主,设 EXPERT_COUNCIL_TERMINAL。

手动路径对所有人继续有效:在另一个终端跑 expert-council watch --exec <execution-id> --follow;这也是 回看一次已完成委派的方式——流文件在 7 天清理前一直在盘上。

观察者记录什么(内容挡位)

默认情况下,事件流只记录名字与计数:跑了哪个工具、成功没有、用了几次;它不记录工具返回了 什么。这是一次刻意的暴露面选择,而 security.observability.contentStream 是你主动放宽它的地方 ——五个递增挡位,让你自己挑风险,而不是被一个复选框替你决定:

挡位落盘内容为什么选它
none(默认)名字、计数、结局标记除了进程内信息,什么都不出去
assistantassistant 文本,随生成随记想看叙述思路,但不想把文件内容也存下来
assistant+tool-tail上面这些,外加每个工具结果的尾部只看得到失败那一行,不必存整份文件
transcript上面这些,外加完整工具结果(带可见接缝)事后复现专家当时看到的东西
transcript+args上面这些,外加完整工具入参连 shell 命令与路径都有——调试价值最高,也是唯一可能记下密钥的一挡
{
  "security": {
    "observability": {
      "expertWindow": "interactive",
      "autoOpenWindow": true,
      "contentStream": "transcript",
      "contentByRole": { "debugger": "transcript", "reviewer": "assistant" },
      "contentWindowLines": 10,
      "contentWindowChars": 120,
      "contentEventBytes": 65536,
      "contentFileBytes": 10485760,
      "contentTotalBytes": 209715200
    }
  }
}
  • 挡位解析顺序:内置 none < contentStream < contentByRole[角色] < EXPERT_COUNCIL_CONTENT。 只有配置与环境变量能开启:任何工具参数、专家输出、任务文本都开不了它——这是隐私开关,不是性能开关。 contentByRole 里的角色名会拿真实角色表校验,所以拼错会报出一条列出合法值的错误,而不是得到一个 永远不生效的哑挡。
  • 除非你明确要求,否则不记录:security.observability.recordReasoning: true(或单进程用 EXPERT_COUNCIL_REASONING=1),并且还要一个会记 assistant 文本的挡位。开启后推理走同样的游标与 上限,每条都带 reasoning: true,呈现时是 thinks 而不是 says,运行时还会在能力限制里点名这个开关。 默认关,因为推理是一条消息里体量最大、也最私有的内容,而且很多提供商给的是摘要而不是真实轨迹。
  • 默认情况下事件流里没有任何推理。 只有 type: "text" 的内容才可能进入事件流——这是项目红线,不是 一个会漂移的默认值:开关没开而思维链落了盘,那条专门的测试就变红。
  • 窗口显示的内容比存下来的少。 工具块流式阶段一次最多 3 行;结束记录显示载荷的最后 contentWindowLines 行(默认 10)。叙述在落盘前先合并:真实模型吐出的碎片小到不值得单独记一条, 所以要等它结束一行或一句、且已攒下约 80 字节才记;一段不间断的长文本到 600 字节上限时释放, 切点回退到最近的空白而不是切进单词;消息结束时把还攥着的余量冲刷出去。真机实测:合并前 2.2 KB 文本要 129 条记录,合并后 4.2 KB 只要 28 条(平均每条 150 字符),且一个字符都不丢。被藏起的行数与 被省略的字节数都会明确说出来而不是悄悄丢掉,而且标题会给出这条记录在事件流文件里的行号, 所以看全文只差一次跳转:
sed -n '137p' ~/AppData/Local/ExpertCouncil/observability/exec_abc123.jsonl | jq -r .text
  • 三道上限,且只管内容:contentEventBytes 限单个载荷(更大的存成"头 + 尾",中间缺口以字节数写明)、 contentFileBytes 限单次委派的流(默认 10 MB)、contentTotalBytes 限整个目录(默认 200 MB, 按最旧优先淘汰)。撞上上限时丢的是内容,并写一条 stream_truncated 通知;它不会丢掉委派如何结束, 也永远不会让委派失败。正在写入的流永远拥有最新的修改时间,所以目录淘汰不可能删掉你窗口正跟着的文件。
  • 文件仍然 7 天后清理;开启记录也不会给主 Agent 增加任何东西:专家返回的仍是那份紧凑的结构化结果。

观察窗口的版式

观察窗口按编码 Agent 自己那种记录读法渲染:叙述就是正常段落文本(每行不加署名), 每次工具调用是一个带底色的块,标题写明哪个工具、被要求跑什么。

expert-council watch --exec exec_abc123                        # 终端里是面板,被管道接走就是单行
expert-council watch --exec exec_abc123 --style plain          # 每个事件一行,与从前一致
expert-council watch --exec exec_abc123 --style panel --columns 100 --no-color
  • --style auto(默认)只在终端里选 panel,其他一律 plain,所以把流重定向到文件时, 字节与面板出现之前完全一致。--json 既不渲染也不上色。
  • 块与块之间也要隔开,不只是块与散文之间。 两条灰带相接会被读成同一个块,所以现在相邻块之间 会留一行空行,而且标题比自己的正文浅一档底色,即使行相接也看得到接缝。正在运行的工具重绘时 故意不拆——那本来就是一个块在长。
  • 观察窗口用的是显式版式。 启动器在命令行里直接传 --style panel --color,不让跟随器去猜有没有 终端:它开出来的窗口继承的是被忽略的 stdio 句柄,在那里"猜"会报成管道,否则操作员会在真终端里看到 plain 版式。--style auto 仍是给"人坐在自己终端前"用的正确默认。
  • 要有块,挡位得真的记工具输出。 none 与 assistant 没有可放进块的内容,于是每次调用仍是一行 暗淡的工具名——这正是那两挡的用途:看见活动,但不记内容。从 assistant+tool-tail 起,块里才有结果。
  • $ <命令> 这个标题需要入参可见:redactToolArgs: false 给一条有界的单行摘要,只有 transcript+args 存完整文本。开着脱敏时标题就只有 bash——这是隐私选择,不是渲染能力不足。
  • 颜色属于视图,不属于数据。 ANSI 只在终端里发,且 NO_COLOR 或 TERM=dumb 时一律不发; --color / --no-color 可强制。两种情况下流文件都仍是纯 JSON。把块补到右边缘时,东亚文字按 每字符两格计算,所以中文叙述行不会让底色短半格。

挣扎检测(护栏)

security.guardrails 决定 Council 是否会察觉「专家卡住」而不是「只是忙」。检测在设计上不阻塞、不中止:误报只浪费一次查看,自动中止会毁掉好成果。

选项默认作用
warnHosttrue统计挣扎信号,并以 attention 出现在 expert_status({ view: "running" });每条警告另发一次原生 Pi 通知(expert-council-guardrail)。关闭后不再有警告,计数仍会记录。
nudgeExperttrue用一条有界指令 steer 专家本身:不要重复同样失败的调用;要么说明换什么做法,要么 report_and_stop,要么 request_decision。每次执行最多 2 次,且仅当运行时会话支持 steer。
consecutiveToolFailures3连续失败多少次触发 consecutive_tool_failures。
minCallsForRatio / failureRatio8 / 0.5观测到至少 8 次调用后,失败比例达到 50% 触发 failure_ratio_high。
budgetFractions[0.6, 0.85]在本次尝试 timeoutMs 的这些分位发 budget_fraction 警告;只有最高一档才同时 nudge。
maxTotalWallMs未设一次委派全部尝试的总时长上限。未设即保持旧行为;设置后重试循环会提前停止并给出原因,而不会无声地花掉 retry.maxAttempts × timeoutMs。
tool_silence(不可配置)-当一次尝试用掉 40% 的 timeoutMs 却一次工具调用都没有时,发一条 attention 告知。失败计数器看不见这种情况——正在思考的专家不会报工具错误——而单看耗时又分不清「想得深」和「卡住了」。它只告警:不 nudge、不中止。
waiting_on_host(不可配置)-当一次尝试用掉 40% 的 timeoutMs,而专家此刻正卡在一个宿主尚未回答的交互上,发一条 attention 说明实况,并把可行动作指向 expert_respond。等答案的专家按设计不会产生被计数的工具调用,所以把它报成 tool_silence 等于把一个健康的运行说成停滞——而真正有用的动作是作答,不是中止。它只告警:不 nudge、不中止。

每条警告也会以 attention 事件写入事件流,运维者在 expert-council watch 里能看到。

这些数字来自运行时自己观测到的工具事件,不是专家自述。委派结果的 executionMetadata 会带 toolCalls、toolErrors、attention,以及 attemptHistory(每次尝试一条有界记录:模型、状态、失败类型、耗时、摘要前 300 字符),因此主代理无需翻状态文件就能看懂「三次尝试为什么花了一小时」。

供应商故障不能当作模型证据。传输类失败(Connection error.、fetch failed、502/503/504、gateway timeout、overloaded)现在归为 provider_error 而非 unknown,并且带该失败类型的结果会被排除在喂给路由的可靠性聚合之外——一次断流不会让一个能干的模型看起来不可靠。宿主主动发起的 aborted 同理被排除:是 Main Agent 中止了执行,模型根本没机会得出结论,把它记成失败等于为一个它没有做出的决定扣分。两种记录都仍然保留并可在 expert_status 中看到;只是不产生学习信号。

上下文窗口溢出故意不在上面那个排除集合里。各家提供商的相关文案(Prompt too long、exceeds the context window、maximum context length、too many tokens)归为 missing_context,并且照常计入学习信号——一个模型装不下你交给它的活儿,正是路由应该学到的事。但它同样不被当成故障:不记录可用性标记,该模型在小任务上仍然可选。

能力画像

模型可以在以下维度获得 0 到 10 分的用户评分:

reasoning planning architecture coding debugging review longContext
toolReliability bashReliability autonomousExecution speed

模型键必须使用 models --json 返回的精确 provider/model。新发现或没有本地画像的模型会获得保守默认值;不可用模型的残留配置只会产生警告,不会导致路由崩溃。

主代理能力审查与首次组建偏好

用户不需要逐个模型维护使用优先级。主代理决定当前任务值得组建委员会后,如果这是新对话中的第一次组建且既未指定 composition 也未指定 costPolicy,expert_build 会返回一份组合菜单,主代理应原样呈现给用户选择:

1. 已保存的理事会组合(最多 3 个,按 council-compositions.json 中的顺序)
2. 自动组建(auto)——再选择一次成本偏好:价格优先(economy)/综合平衡(balanced)/速度优先(speed)

选择已保存组合即绑定到当前会话(池内路由,route-policy deny 恒胜);选择 auto 则进入成本偏好流程并解除旧绑定。Pi Package 把成本偏好记录在当前 Pi Session 的隐藏扩展状态中;本对话后续委员会自动复用,除非用户主动改变。economy 会增强成本权重,speed 会增强经过审查的速度维度,balanced 使用正常的角色权重。旧的 quality API 值保留兼容,但不会作为默认提问选项。组合的创建与修改直接编辑 council-compositions.json(路径见「委员会编成」一节),主代理可代为操作。

  • 已绑定名册下的推理等级优先级是:名册条目自带的档位 > 宿主在派遣参数里传的 reasoningLevel > 路由依据操作者画像派生的档位(后者已按模型支持的等级筛过)。所以名册钉住的档位会盖过宿主显式传入的值—— 而 reasoningLevel 是必填派遣参数,于是宿主可能要求一档而实际跑到另一档。 expert_inspect 会把当前生效的绑定显示为 compositions.sessionBinding;只用 costPolicy 建一次委员会即可解除。

模型能力由主代理审查,而不是要求用户手工排序。expert_inspect 会返回强制评估门禁:若尚无审查、审查已超过 30 天、可调用模型发生变化,或用户明确要求重新审查,主代理必须使用宿主已经具备的联网工具研究门禁列出的每个可调用模型;完成前 expert_build 不会组建委员会。主代理提交一份完整 modelAssessment,其中 ISO 时间必须读取宿主真实时钟,来源应合并为 1–12 个 URL,并包含 0–10 能力维度及可验证 Provider 访问/计费方式。未来时间戳会单独报告,修正时间时无需重新联网研究。若检查结果表明已保存评估仍为 current,宿主调用 expert_build 时应省略 modelAssessment;不完整、过期或未来时间的替代表不能覆盖当前有效快照。评估在当前用户数据目录只保存一份,只要可调用模型清单仍兼容,新对话和其他工作区都直接复用;普通计划和执行状态保存只保留全局评估,不会让持有旧内存快照的服务实例覆盖它,只有显式提交并成功通过门禁的新评估才会全局替换旧评分。显式用户计费配置始终高于主代理判断,不能确认的计费方式保持 unknown。

推荐交叉验证而不是信任单榜:Artificial Analysis Data API 可提供 Coding、Agentic、价格、吞吐与延迟数据,LiveBench 提供 Coding 与 Agentic Coding,Arena 反映人类偏好,Provider 官方资料用于核对版本、上下文、工具与访问方式。OpenRouter Rankings 主要反映实际使用量,只作为采用度信号,不能单独证明模型质量。门禁要求审查而宿主没有联网工具时,主代理必须说明限制并停止组建,不能静默使用未经审查的默认值;Expert Council 不会自动安装插件、Skill 或第三方可执行包。

角色权重

每个语义角色都有归一化默认权重。Implementation Worker 更重视工具可靠性、编码、自治执行与 Shell 可靠性;Architecture Oracle 更重视架构、规划、长上下文与审查。

{
  "routing": {
    "roleWeights": {
      "implementation-worker": {
        "toolReliability": 0.4,
        "coding": 0.3,
        "costEfficiency": 0.1
      }
    }
  }
}

权重会自动重新归一化。costPolicy: economy 会提高成本因素的影响,speed 会提高速度并降低成本因素的影响,旧的 quality 会降低成本因素;它们都不会绕过安全或兼容性硬约束。

语义角色与团队规模

角色由任务语义定义,不与任何模型名称绑定:

角色默认权限目的
Planner只读任务拆解、依赖与风险
Scout只读仓库探索与上下文压缩
Architecture Oracle只读困难跨文件推理与第二意见
Implementation Worker可写有边界的代码修改和聚焦测试
Debugger可写复现、定位、修复和验证
Reviewer只读回归、边界条件和设计审查
Verifier只读检查已报告的测试、Diff 与验收标准

微小任务只使用一个 Worker;普通任务使用 Worker 与 Verifier;复杂功能使用 Planner、Worker、Reviewer 与 Verifier;复杂调试使用 Scout、Debugger、Oracle 与 Verifier。maxExperts 会限制团队规模,主代理永远不会再被复制成一个多余的 lead 专家。

只读专家在主工作区运行,因此从不报告 filesChanged:它们没得变更工具,所以那里的 git 脏文件属于主代理;把它们归给专家等于伪造作者身份。对这类运行调用 expert_cleanup 会答 not-required,因为并不存在 worktree。确实需要改代码时,请改派 implementation-worker 或 debugger,并审阅它独立的 worktree。 可写执行若读不出 diff 则是另一种情况,并且会被如实标出:executionMetadata.filesChangedError 外加一条 risk。因为 git status 失败得来的空列表会被读成“专家什么都没改”,照这个列表做集成的宿主就会把真实成果丢成没有成果(缺陷 #28)。

任务分类和全部评分运算都是确定性的。宿主可以在委派前检查已选模型、备选模型、分数和简明理由。组建 Council 时还会应用可配置的多样性惩罚;Reviewer 会在经济合理时优先选择与先前成员不同的 Provider 和推断模型家族,但角色适配度与硬约束仍然优先。

路由过程

发现当前可调用候选模型
  -> 应用硬约束
  -> 合并能力与画像层
  -> 计算角色适配度和有效成本
  -> 保守应用本地结果调整
  -> 使用稳定规则排序
  -> 返回选择、备选项和理由

硬约束会排除不可用或已禁用模型、不兼容角色、工具可靠性不足、上下文不足、运行时不支持写入、日常任务中的 escalation-only 资源,以及带有活跃运行时可用性标记的模型。

Pi 会在会话内缓存模型清单,提供商目录也可能保留失效的模型名称,否则 Council 可能围绕一个上游已无法服务的模型组建。当一次委派尝试以 provider_error 失败且带失效模型证据(例如 model_not_found、未知或已停产的模型、以及运行时自身的预检可用性检查)时,服务会通过一次原子 read-modify-write 把 modelAvailability 标记写入持久化的共享模型评估(EXPERT_COUNCIL_DATA_DIR,Windows 上即 %LOCALAPPDATA%/ExpertCouncil/model-assessment.json),且不会回退其他正在运行的 Pi/Codex 实例写入的更新快照。受影响的 expert_result 会在 executionMetadata.unavailableModels 和 risks 中点名该模型,expert_inspect 会警告活跃标记,后续 expert_build、委派和升级会以硬约束拒绝被标记的模型。标记是保守的本地证据:24 小时后自动过期,在提交全新审计时保留,并且显式的 modelOverrides["provider/model"].overrideUnavailableMarker: true 可以重新启用某个模型。标记并不只属于"模型已死":瞬态 TPM/RPM 限流会记录 rate-limited 标记(MODEL_RATE_LIMIT_MARKER_TTL_MS,2 分钟),并连带同提供商的兄弟模型——限流是账户级条件;提供商传输不可达记录 transport-unstable(5 分钟),且只标记失败的那一条路由;套餐或余额耗尽记录 quota-exhausted(6 小时);而套餐级访问被拒(例如 403 AccessDenied.Unpurchased)属于失效模型证据,记录 unavailable(24 小时)。这些瞬态种类一律不计入模型自身的可靠性记录——那是模型的数据,上述是供应商的数据。若尚无已保存的评估,标记无法持久化,但失败仍会报告给主代理并记入本地遥测。

推理等级是可选且与模型相关的。只有当 Pi 明确暴露选定模型支持某个等级时,角色偏好才会生效;否则 Pi 会保留或钳制到模型支持的默认值。

Skill 与最小权限

平台无关的主代理指导唯一源文件是 shared/skills/expert-council/SKILL.md。构建过程会把它与 shared/skills/expert-council/hosts/ 下的小型宿主适配层合成,生成不同的 Pi 与 Codex SKILL.md,而不复制公共工作流。Pi 产物只说明完成后的 steer/followUp 行为,完全不暴露 expert_wait;Codex 产物才说明有限时的 expert_wait 流程。共享角色提示位于 packages/core/src/roles/prompts/,作为包资源复制,而不是为不同宿主重复编写。

只读角色永远不会获得 edit、write、bash 或 powershell,即使调用者试图把它们加入工具列表。Pi 会话使用真实的 tools allowlist,因此它比仅靠 Prompt 约束更强。在提供专用的无副作用命令运行器之前,需要 Shell 执行测试的任务应交给隔离 worktree 中的写入型角色。

系统只会激活已经安装并启用、且当前角色需要的 Pi Skill。用户级 Skill 默认可信;项目级和临时 Skill 默认排除,只有名称精确列入 security.trustedSkills 才能启用。每个专家资源加载器都会禁用扩展、Prompt 模板、主题和项目上下文文件;无法强制这些策略的 Pi SDK 版本会被拒绝。Expert Council 不会下载或安装任何 Skill 或可执行扩展。

专家提示要求:修改前先阅读、验证路径、优先局部编辑、失败后诊断再换方法、使用有限时非交互命令、检查执行结果、禁止递归委派,并返回紧凑 JSON,而不是私人思维过程。

失败处理

失败类型会被归一化为:

tool_call_error reasoning_failure test_failure timeout provider_error
missing_context permission_error aborted unknown

一次委派只运行一次尝试。 自动升级——同模型重试、切换模型、失败后放大超时——已按实测结果移除。 在一个真实任务上,一次 25 分钟的 worker 尝试超时后升级了两次:63 分钟、74 万 token 反复做同一份分析, 把第一次尝试已经写入共享 worktree 的成果 reset 掉,最后仍然返回失败。把一件本来就难的任务重做一遍 不会让它变容易,只会把它变成两遍。

所以失败会带着它的类型、逐尝试历史、以及任何 discardedChanges 补丁回到你手上,由你决定。 expert_escalate 仍然保留给「下一次尝试会带来新信息」的情形——它是宿主决定,不是后台行为。 可用性标记仍然会生效:失败过的模型或 provider 在标记有效期内不会被后续委派再次选中, 这才是「下一次委派更安全」的依据。

retry.maxAttempts、retry.maxEscalations、retry.correctedRetriesPerModel 仍然被接受,好让既有配置继续加载; 但要求多于一次尝试会得到一条明确警告,而不是被静默忽略。security.guardrails.maxTotalWallMs 同样被接受 但不再被查询:只有一次尝试时,已经没有需要拦下的东西。

系统不存在无限循环,也不会按策略盲目重复同一种失败操作。

结构化结果与上下文效率

专家结果包含状态、角色、模型、摘要、修改文件、测试、发现、风险、下一步建议、失败类型、Pi 可提供的近似用量,以及有限的执行元数据。系统优先使用专家返回的结构化失败类型,并确定性识别测试、Provider、工具和上下文失败;无法解析的非 JSON 输出会标记为 reasoning_failure。系统不会请求或保存私有思维过程,也不会把整份源码复制回主代理上下文。

工作区安全

系统不会因为 Codex 自身处于沙箱就假设外部 Pi 进程同样安全。Pi Runtime 使用独立边界:

  1. 规范化请求工作区路径。
  2. 要求路径位于允许的根目录内。
  3. 从仓库当前 HEAD 在系统临时目录内当前用户专属的私有目录中创建 detached worktree。
  4. 在该 worktree 中为 Worker 提供写入工具。
  5. 返回 worktree 路径和修改文件列表。
  6. 由 Codex 或 Pi 主代理检查、整合并最终验收。
  7. 整合或拒绝结果后调用 expert_cleanup。一次调用会删除该 execution ID 因重试或升级创建的全部 worktree,并返回所有已删除路径。无人认领的 worktree 会在 security.worktreeRetentionMs 后尽力自动清理,默认保留 24 小时,并同步 prune Git 元数据;清不掉的残留会告知你,不会静默删除。

非 Git 工作区默认拒绝写入。若确实需要原地修改,必须显式配置:

{
  "security": {
    "workspaceStrategy": "bounded-in-place",
    "allowInPlaceMutations": true,
    "allowedWorkspaceRoots": ["/absolute/path/to/project"]
  }
}

启用前请阅读 SECURITY.md。

遥测与本地学习

默认用户数据根目录为:Windows %LOCALAPPDATA%\ExpertCouncil,Linux $XDG_STATE_HOME/expert-council 或 ~/.local/state/expert-council,macOS ~/Library/Application Support/ExpertCouncil。共享的 telemetry.jsonl 保存不透明执行结果,使实际可靠性能够跨对话和工作区复用;同一 execution 的反馈会覆盖早期样本,不会重复计数。共享的 model-assessment.json 保存最新的显式能力与计费评分,以及运行时学习到的模型可用性标记。可用 EXPERT_COUNCIL_DATA_DIR、EXPERT_COUNCIL_TELEMETRY、EXPERT_COUNCIL_MODEL_ASSESSMENT 和 EXPERT_COUNCIL_STATE 覆盖位置。

它不会记录 Prompt、源码内容、凭据、API Key、Secret 或思维过程。聚合指标包括按角色成功率、首轮成功率、工具错误率、重试率、验证通过率和平均尝试次数。Expert Council 没有远程分析端点。

计划、执行状态和已完成结构化结果仍按工作区隔离,保存在 workspaces/<工作区哈希>/state.json。进程重启后仍可查询;重启时仍在运行的任务会被关闭为明确的中断失败。旧版项目内 .expert-council 和 %USERPROFILE%\.expert-council 目录不会被自动删除。

CLI

CLI 与 MCP、Pi Package 使用完全相同的 Core 和 Pi Runtime:

expert-council models
expert-council inspect
expert-council compositions
expert-council build <task>
expert-council delegate <role> <task>
expert-council feedback <execution-id> --verification passed|failed
expert-council status
expert-council abort <execution-id>
expert-council reset <scope>
expert-council verify (--exec <id> | --workspace <path>) --command JSON_ARRAY
expert-council respond <execution-id> --kind decision|tool_approval
expert-council cleanup <execution-id>
expert-council watch --exec <execution-id> [--follow]
expert-council --version

常用参数:

  • --json:机器可读输出。
  • --cwd:项目工作区。
  • --config:用户策略文件。
  • --telemetry:自定义本地遥测路径。
  • --state:自定义持久化计划、执行和结果状态路径。
  • --timeout-ms:专家执行超时。
  • watch 额外接受 --dir(事件流目录)、--interval-ms(轮询间隔,默认 1000)与 --timeout-ms(最长跟随时间,默认 300000)、--quiet-ms(没有终止标记的流需静默多久才放弃跟随,默认 15000);它需要 security.observability.expertWindow: "interactive" 正在产生事件流。
  • watch 还接受 --max-lines N(每条正文块显示几行,1-50,默认 10)与 --max-chars N(每行截断宽度,20-400,默认 120);自动打开的窗口会从配置继承 contentWindowLines 与 contentWindowChars,所以你把数字调大不会被跟随器自己的默认值悄悄覆盖。

MCP Server

MCP 表面刻意保持为 13 个语义工具:

  • expert_inspect
  • expert_build
  • expert_delegate
  • expert_wait
  • expert_result
  • expert_abort
  • expert_feedback
  • expert_cleanup
  • expert_escalate
  • expert_status
  • expert_availability_reset
  • expert_verify
  • expert_respond

expert_respond 用于回答运行中专家的未决 pendingInteraction(request_decision 的抉择或 request_tool 的授权),使其会话继续;无法接收推送的无头宿主通过 expert_status(view: "running")或带 includeProgress 的 expert_result 轮询发现未决交互。

expert_inspect 和 expert_build 默认返回面向宿主的紧凑视图。只有确实需要准确模型元数据、备选项、评分、工具或 Skill 时才传入 detail: "full"。

委员会编成(Council compositions)

已保存的委员会名单保存在共享状态目录中与 route-policy.json 同级的 council-compositions.json——不新增任何工具。默认位置:Windows %LOCALAPPDATA%\ExpertCouncil\council-compositions.json,macOS ~/Library/Application Support/ExpertCouncil/council-compositions.json,Linux $XDG_STATE_HOME/expert-council/council-compositions.json(或 ~/.local/state/expert-council/council-compositions.json);可用 EXPERT_COUNCIL_COMPOSITIONS 覆盖。

{
  "compositions": [
    {
      "name": "daily-cheap",
      "roles": {
        "scout": ["qwen-token-plan-cn/deepseek-v4-flash"],
        "implementation-worker": ["zai/glm-5.3-flash", "deepseek/deepseek-v4-flash"]
      }
    }
  ],
  "sessions": { "01a066b2-a166-7e67-983d-2bbec848c223": "daily-cheap" }
}
  • compositions 是有序列表(即菜单优先级),最多 32 个唯一名称(≤80 字符)。roles 以七个语义角色为键;每个值是 provider/id 模型键列表(每角色 ≤16 个)。角色缺失或列表为空时该角色自动路由。
  • sessions 把会话键(宿主对话 ID)映射到编成名称。条目 30 天后过期;工具写入的绑定带 updatedAt 时间戳,手写的字符串条目则保留。
  • 首次构建既未传 composition 也未传 costPolicy 时,expert_build 返回 compositionMenu:最多三个已保存名单加一个 auto 选项。把选中的名称作为 composition 传回;auto 选项即成本策略流程(economy/balanced/speed)。
  • 显式 composition 构建成功后会把该名称绑定到会话;成功传入 costPolicy 会解除绑定。菜单响应不绑定任何内容。
  • 某角色的候选池是编成列表与路由策略过滤、Provider 限额/并发排除的交集。路由策略的 deny 恒胜于候选池,池被完全排除的角色会报告为无法编成。
  • expert_delegate 的单项参数与 assignments[] 均接受可选 model(provider/id)。对同一角色的多个任务分别固定不同的池内模型即可并发派遣多个专家;固定模型不在该角色池、已发现清单或路由策略内时会返回结构化错误。

路由策略文件

模型黑白名单保存在共享状态目录中与 model-assessment.json 同级的 route-policy.json——不新增任何工具。文件包含所有会话共同遵守的 system 条目,以及按宿主会话键组织的 sessions 条目(Pi 会话 ID 在 resume 后保持不变;MCP stdio 会话使用稳定的 "default" 键)。会话只能收紧系统策略:deny 取并集、allow 取交集、deny 恒胜。条目为 provider/id 或裸 provider(整个供应商)。expert_inspect 会返回本会话的 sessionKey、当前 effective 策略与文件 sourcePath,宿主(或你)可以直接编辑该文件;改动在下次专家调用即生效,超过 30 天的会话条目自动清理,损坏文件会带警告忽略。

{
  "version": 1,
  "system": {
    "allow": [],
    "deny": ["acme-host", "acme-metered/acme-pro"],
    "updatedAt": "2026-09-18T12:00:00+08:00",
    "note": "free text, never sent to an expert"
  },
  "sessions": {
    "4f0c…": {
      "allow": ["acme-plan/acme-flash"],
      "deny": ["acme-plan/acme-heavy"],
      "updatedAt": "2026-09-18T12:00:00+08:00",
      "note": "narrows the system policy for this conversation only"
    },
    "default": { "deny": ["acme-metered"] }
  },
  "providers": {
    "acme-plan": { "maxConcurrency": 1, "dailyTokenCap": 12000000, "weeklyTokenCap": 60000000 },
    "acme-metered": { "maxConcurrency": 2 }
  }
}
  • version 必须是字面量 1;其他值会让整份文档被拒绝。
  • system 对所有会话生效。sessions 以宿主会话 id 为键——Pi 的 session id 在 resume 后仍然有效,MCP stdio 会话共用稳定的 "default" 键。
  • 会话只能收窄系统策略:deny 取并集,allow 取交集,deny 永远优先。allow 是排他清单——只要写了它,未列出的模型就不可路由。
  • 条目形如 provider/id,或只写 provider 表示整个 provider。每个清单最多 32 条、每条最多 200 字符;控制字符会被剥掉,不符合该形状的内容会被丢弃而不是被信任。
  • providers 放的是花费与并发上限,不是黑白名单:maxConcurrency(每个 provider 0-8,0 表示停放)、dailyTokenCap、weeklyTokenCap,都是正整数。上限依据用量账本执行;撞顶的 provider 在本窗口内被跳过,而不是让整次委派失败。
  • note、updatedAt、workspace 只是留存的元数据。特别是 workspace:它会被保存并原样往返,但目前没有任何路由代码读它——不要依赖它来限定策略范围。
  • 删除这个文件不是无害操作:没有 deny 清单后,此前被排除的 provider 会重新可路由,这可能把工作从包月计划悄悄挪到按量计费的 API 上。
  • expert_inspect 会报告本会话的 sessionKey、生效后的 effective 策略以及文件的 sourcePath,所以宿主(或你)可以直接编辑它;改动在下一次调用生效,过期的会话条目 30 天后清理,文件损坏时会被忽略并给出警告。

一次委派多个专家

expert_delegate 会启动后台任务并立即返回 execution ID,原有单任务参数保持兼容。timeoutMs 是每项任务的必填参数(1000–21600000 ms)——缺省即报错;请按任务难度设置。超时的尝试会自动把重试预算放大 1.5×,且当专家发现以现有工具无法完成任务时会以结构化 missing_context/permission_error 提前终止。存在两个以上相互独立的任务时,应在继续其他主代理工作前一次发配整个批次:

{
  "assignments": [
    { "role": "scout", "task": "定位相关文件", "taskDescription": "仓库映射", "timeoutMs": 300000 },
    { "role": "reviewer", "task": "审查边界设计", "taskDescription": "边界审查", "timeoutMs": 600000 }
  ]
}

assignments 必须是实际 JSON 数组,不能是包含 JSON 文本的字符串。原生 Pi 适配器对部分模型偶发的字符串化数组提供有界兼容解析,但正常调用仍应直接生成数组。

可选的 taskDescription 是供宿主识别任务的简短标签,不属于专家实际任务内容。派发后主代理应继续所有可独立完成的工作;无其他有用工作时,调用一次 expert_wait,传入最多 8 个 execution ID、通常使用 mode: "all"(任一早期结果即可推进时使用 "any"),并按预计剩余难度设置 timeoutMs。等待由执行 Promise 的完成事件驱动而不是轮询;阻断当前 MCP 调用属于预期行为,等待期间不会继续消耗主模型 Token。

等待后台任务

{
  "executionIds": ["exec_a", "exec_b"],
  "mode": "all",
  "timeoutMs": 900000
}

expert_wait 只返回完成状态和任务 ID,随后使用 expert_result 获取正式反馈,并在主代理验收后调用 expert_feedback。expert_wait.timeoutMs 只限制本次等待,不会延长各专家自己的执行期限。所有可能阻断的 Expert Council、Bash、PowerShell 或其他 MCP 调用仍必须按操作难度附带显式的有限超时;其余同步 Expert Council 操作受独立的 30 秒 Server 内部上限保护,只有一处刻意的例外:expert_verify 的上限取该默认值与 60 秒中的较大者,因为把专家的声称变成观察到的退出码,通常意味着要跑一次构建或测试。expert_status 会返回有界的逐次尝试历史。原生 Pi Package 使用主动完成通知,因此不暴露 expert_wait。

直接运行 stdio 服务

直接启动 stdio Server:

node packages/mcp-server/dist/bin.js

环境变量

支持以下环境变量:

  • EXPERT_COUNCIL_WORKSPACE:默认允许工作区。
  • EXPERT_COUNCIL_CONFIG:用户 JSON 配置。
  • EXPERT_COUNCIL_TELEMETRY:本地遥测 JSONL 路径。
  • EXPERT_COUNCIL_STATE:持久化计划、执行和结果状态路径。
  • EXPERT_COUNCIL_WORKTREES:专家变更 worktree 的父目录(仍保留按用户隔离的私有子目录);适用于短路径盘或更快的磁盘。
  • EXPERT_COUNCIL_COMPOSITIONS:已保存委员会编成的路径。
  • EXPERT_COUNCIL_DATA_DIR:全部每用户数据的根目录——评估、路由策略、账本、遥测、观察流。
  • EXPERT_COUNCIL_CONTENT:仅对本进程生效的内容挡位,覆盖 contentStream 与 contentByRole。
  • EXPERT_COUNCIL_MODEL_ASSESSMENT:持久化共享模型评估的路径。
  • EXPERT_COUNCIL_ROUTE_POLICY:持久化每会话黑白名单策略的路径。
  • EXPERT_COUNCIL_USAGE_LEDGER:provider 上限背后持久化的用量账本。
  • EXPERT_COUNCIL_MCP_TIMEOUT_MS:同步 MCP 操作的有限超时,默认 30000 毫秒。
  • PI_CODING_AGENT_MODULE:显式指定的 Pi 包目录,在自动解析之前生效。若已设置却无法加载,则以失败告终并说明原因,而不是静默回退到另一个 Pi 版本——你指定的版本优于恰好被提升到了附近的那一个。

环境变量覆盖和 CLI 路径参数属于“受信任的操作者输入”。其中 PI_CODING_AGENT_MODULE 会加载可执行代码,配置、工作区、遥测和状态路径会选择本地文件;不要从不受信任仓库、任务文本或模型输出中接受这些值。

原生 Pi Package

日常使用推荐通过 npm 安装(见快速开始);本节面向源码开发与本地候选版验证。

在仓库根目录构建并安装本地候选版。即使在 Windows 上,只要命令可能经过 Pi 的 Bash 兼容 Shell,也应使用正斜杠;未正确引用的 .\packages\pi-package 会在到达 Pi 前丢失反斜杠。

npm run build
pi install "./packages/pi-package"
pi list
pi --verbose

pi list 应显示配置中的 source 及解析后的绝对 Package 目录。新启动的 verbose Pi 会话应显示 dist/extension.js、expert-council Skill,以及不含 expert_wait 的 12 个语义工具。已经运行的 Pi 进程不会热加载重新构建或已移除的 Package。

或仅在当前运行中临时加载:

pi --verbose -e "./packages/pi-package"

无费用装载检查只需让 Pi 调用 expert_inspect。真实编排检查应在新对话中组建一个只读委员会,一次性批量派发两个相互独立的只读任务,确认 expert_delegate 立即返回 execution ID,再用 expert_result 和 expert_feedback 验收每个完成结果。若测试写入型专家,还应确认一次 expert_cleanup 会在 workspaces 中报告该 execution 的全部重试 worktree,且随后 git worktree list 只剩主工作区。

移除持久安装前,先退出所有已经加载该 Package 的 Pi 进程,然后仍在仓库根目录执行:

pi remove "./packages/pi-package"
pi list

如果当前目录已经变化,请改用解析后的绝对路径。PowerShell 示例:

$ecPiPackage = (Resolve-Path "./packages/pi-package").Path
pi remove "$ecPiPackage"
pi list

如果通过 Pi 的 Bash 兼容 Shell 执行移除,请使用 pi list 第二行显示的正斜杠绝对路径,例如 pi remove "C:/path/to/ExpertCouncil/packages/pi-package"。不要直接复制 pi list 中缩进显示的相对 source,除非命令也从相同的 settings 目录上下文解析。

Pi 会通过当前包清单中的 pi.extensions 与 pi.skills 加载 dist/extension.js 和同步后的 expert-council Skill。扩展注册 12 个语义工具;由于原生 Pi 已提供完成 steer/followUp,因此省略 MCP 专用的 expert_wait。它不包含另一套路由实现。

Pi 委派是非阻断式的;一个调用最多可在返回前启动 8 个相互独立的后台任务。专家完成后,扩展发送精简 JSON:必含已完成的 executionId,仅在调用时提供过 taskDescription 才包含该描述,绝不直接携带 feedback。主 Agent 工作中时通知使用 steer;主 Agent 空闲时使用带 triggerTurn 的 followUp 立即唤醒。随后由主 Agent 调用 expert_result 获取结构化反馈。主 Agent 应先发完当前已准备好的整个批次再结束回合,之后不要轮询或静默等待消耗 token。

Codex 插件

Codex 插件让 Codex 成为主代理:它内置共享的 expert-council Skill 与 stdio MCP Server,专家通过 Pi 执行。插件不包含任何 hook——服务器优先使用 MCP roots,其次从 Codex 宿主自有的 codex/sandbox-state-meta 能力解析当前任务工作区,最后回退到 EXPERT_COUNCIL_WORKSPACE 覆盖项,并且拒绝把插件安装目录当作工作区。

构建产物根目录:

packages/codex-integration/plugin/expert-council/
  .codex-plugin/plugin.json
  .mcp.json
  skills/expert-council/SKILL.md
  dist/server.mjs
  dist/roles/*.md
  THIRD_PARTY_NOTICES.md

安装

当前版本已包含预构建 MCP Server 及经过验证的 Pi SDK 运行时,Codex 可以直接把本仓库作为固定版本的 Git Marketplace 安装。运行时需要 Node.js 22.19 或更高版本,以及已经配置好的 Pi 账户/模型目录;无需克隆仓库、执行 npm install,也不再依赖从全局 npm 目录解析 @earendil-works/pi-coding-agent。

codex plugin marketplace add Labiey/expert-council-router --ref v0.8.9 --json
codex plugin marketplace list --json
codex plugin list --marketplace expert-council-router --available --json
codex plugin add expert-council@expert-council-router --json
codex plugin list --json

每个发布版本只需执行一次 marketplace add。如果已经用旧版本或本地路径注册了同名 Marketplace,请先移除旧来源,或按下方升级流程操作。plugin list --json 应显示 expert-council 已从 expert-council-router 安装。

Windows 版 Codex Desktop 内置 CLI,但它可能不在 PATH 中。可以在 PowerShell 定位正在运行的 Desktop CLI,再执行同样的远程安装命令:

$ecCodex = (Get-Command codex.exe -ErrorAction SilentlyContinue).Source
if (-not $ecCodex) {
    $ecCodex = Get-Process codex -ErrorAction SilentlyContinue |
        Where-Object Path |
        Select-Object -First 1 -ExpandProperty Path
}
if (-not $ecCodex) {
    $ecCodex = Get-ChildItem (Join-Path $env:LOCALAPPDATA "OpenAI/Codex/bin") `
        -Filter codex.exe -File -Recurse -ErrorAction SilentlyContinue |
        Sort-Object LastWriteTime -Descending |
        Select-Object -First 1 -ExpandProperty FullName
}
if (-not $ecCodex) { throw "未找到 Codex Desktop CLI。" }

& $ecCodex plugin marketplace add Labiey/expert-council-router --ref v0.8.9 --json
& $ecCodex plugin marketplace list --json
& $ecCodex plugin list --marketplace expert-council-router --available --json
& $ecCodex plugin add "expert-council@expert-council-router" --json
& $ecCodex plugin list --json

完全退出 Codex Desktop,等待其后端进程结束,再重新打开并新建任务。部分 Desktop 版本仅新建任务并不能可靠触发 MCP 重载。

若要开发插件,可克隆仓库、执行 npm ci && npm run build,再把仓库根目录的绝对路径传给 codex plugin marketplace add。普通使用建议安装固定版本的远程 Release。

加载成功时会同时出现 expert-council Skill 和全部 13 个 expert_* MCP 工具;expert_inspect 必须返回真实资源清单,而不是 “No compatible Pi SDK is installed” 诊断。如果只有 Skill 而没有工具,或检查仍出现该诊断,请先确认 Marketplace 固定到 v0.8.7 或更高版本,再重启或重装插件;不要手动启动 dist/server.mjs 或手写 JSON-RPC。

在新的 Codex 任务中输入以下提示以验证安装:

使用 Expert Council 检查当前可用的 Pi 模型、Provider、计费分类和模型评估状态。只返回紧凑摘要,不组建委员会,也不派遣专家。

任务应调用 expert_inspect,不应要求信任 hook、手工启动 MCP Server,也不应把插件缓存目录当作项目工作区。

升级

使用 --ref 固定的 Marketplace 会有意停留在该发布版本。升级时请移除已安装插件与旧 Marketplace,然后添加新标签并重新安装:

codex plugin remove expert-council@expert-council-router --json
codex plugin marketplace remove expert-council-router --json
codex plugin marketplace add Labiey/expert-council-router --ref vX.Y.Z --json
codex plugin add expert-council@expert-council-router --json

请把 vX.Y.Z 替换为目标版本。如果明确希望跟随默认分支,可以在首次添加时省略 --ref,以后执行 codex plugin marketplace upgrade expert-council-router --json;普通使用仍建议固定标签。重装后请完全重启 Codex Desktop,并在新任务中测试。不要同时安装多个都声明 expert_council MCP Server 的副本。

行为要点

  • 内置 .mcp.json 将宿主工具调用上限提升到 3660 秒,让单次有边界的 expert_wait 可以阻塞到完成;Skill 仍要求为每个操作设定明确时限,而不是把该上限当作默认预算。
  • 对话中第一次组建委员会时,主代理会与你确定唯一的成本策略(economy、balanced 或 speed);在此之前 expert_build 与 expert_delegate 的响应都会携带询问提醒。
  • 可写专家在受信任工作区下的独立 Git worktree 内修改;变更返回给主代理审查,永不自动合并。

卸载

移除插件及其 Marketplace 注册:

codex plugin remove expert-council@expert-council-router --json
codex plugin marketplace remove expert-council-router --json
codex plugin list --json
codex plugin marketplace list --json

如果 PowerShell 找不到 codex,先定位 Codex Desktop 自带的 CLI(Codex Desktop 运行时可从进程取路径,否则回退到安装目录):

$ecCodex = Get-Process codex -ErrorAction SilentlyContinue |
    Where-Object Path |
    Select-Object -First 1 -ExpandProperty Path

if (-not $ecCodex) {
    $ecCodex = Get-ChildItem (Join-Path $env:LOCALAPPDATA "OpenAI\Codex") `
        -Filter codex.exe -File -Recurse -ErrorAction SilentlyContinue |
        Sort-Object LastWriteTime -Descending |
        Select-Object -First 1 -ExpandProperty FullName
}

if (-not $ecCodex) {
    throw "未找到 Codex Desktop 自带的 codex.exe"
}

然后通过定位到的 CLI 卸载:

& $ecCodex plugin remove "expert-council@expert-council-router" --json
& $ecCodex plugin marketplace remove "expert-council-router" --json

& $ecCodex plugin list --json
& $ecCodex plugin marketplace list --json

如果重新打开后仍显示,可在 Codex 关闭状态下安全清理旧 Expert Council 缓存(仅删除 %USERPROFILE%\.codex\plugins\cache\expert-council-*):

$ecCacheRoot = [IO.Path]::GetFullPath(
    (Join-Path $env:USERPROFILE ".codex\plugins\cache")
)
$ecCachePrefix = $ecCacheRoot.TrimEnd("\") + "\"

$ecTargets = Get-ChildItem -LiteralPath $ecCacheRoot `
    -Directory -ErrorAction SilentlyContinue |
    Where-Object Name -Like "expert-council-*"

foreach ($ecTarget in $ecTargets) {
    $ecResolved = [IO.Path]::GetFullPath($ecTarget.FullName)

    if (
        $ecResolved.StartsWith(
            $ecCachePrefix,
            [StringComparison]::OrdinalIgnoreCase
        ) -and
        (Split-Path $ecResolved -Leaf) -like "expert-council-*"
    ) {
        Write-Host "删除缓存: $ecResolved"
        Remove-Item -LiteralPath $ecResolved -Recurse -Force
    }
}

之后请完全退出 Codex Desktop,再开始新任务。

测试

npm test
npm run typecheck
npm run build
npm run pack:check
npm run validate

npm run validate 会先构建,确保全新克隆在测试前已经生成 workspace 包入口。测试覆盖模型归一化、公开价格与真实策略计费、Worker 可靠性、Oracle 评分、Reviewer 多样性、硬约束、未知和缺失模型、团队规模、重试和逐次诊断、结构化失败分类、升级、重试上限、角色权限、紧凑宿主输出、配置校验、遥测隐私/反馈/用量聚合、Core 宿主独立性、模拟 Pi 发现与执行、CLI JSON、MCP Schema、真实 Pi 0.85.1 扩展加载/包装及异步批量通知、Pi 扩展注册和真实 Git worktree 隔离。

普通测试只使用 Mock Runtime,绝不会调用付费模型。真实只读 Pi 执行必须同时指定模型并明确确认成本:

$env:EXPERT_COUNCIL_LIVE_MODEL = "provider/model"
$env:EXPERT_COUNCIL_LIVE_CONFIRM = "YES"
npm run smoke:live:pi

普通验证流程永远不会执行该脚本。

发布

先运行 npm run validate,检查每个 npm pack --dry-run 文件列表,再按依赖顺序发布:

@expert-council/core
@expert-council/pi-runtime
@expert-council/cli
@expert-council/mcp-server
@expert-council/pi-package

已知限制

  • 护栏计时依赖宿主进程确实被调度:进程被挂起或长期抢不到 CPU 时,budget_fraction 警告可能晚于它本应预警的超时点。

  • nudge 需要运行时会话支持 steer。Pi 会话支持;对不支持的运行时,Council 只通知宿主而不去干扰专家。

  • 工具失败计数只记录运行时能观测到的部分,它不是审计面;工具「卡住」而非报错时,在该次尝试超时之前不会计入失败。

  • Pi API 变化较快。当前版本已在本机 0.85.1 SDK(package.json 所钉版本)上验证;运行时会检查 SDK、模型运行时、资源加载器和 Session 必需方法,并在不兼容时明确列出缺失合约。

  • Pi 没有统一的真实计费类型 API。运行时订阅信号和具名 Token Plan 优先;否则非零目录价格按按量计费处理,没有可靠证据的 Provider 保持 unknown,直到评估或显式用户配置确认。

  • Expert Council 不会根据模型名称推断主观编码质量,也不会自动下载基准预设。

  • Detached worktree 从已提交的 HEAD 开始,不会复制主工作区未提交改动。这是刻意的隔离设计;运行时会检测脏源工作区,并在委派前通过运行时限制和 mutation 委员会警告提示该偏差。

  • Worktree 修改只返回给主代理审查,不会自动合并或应用;验收或拒绝后应调用 expert_cleanup,否则将在保留期结束后自动清理。

  • 非 Git 工作区的写入需要显式原地修改授权。

  • 正在进行的模型调用不会在 Server 重启后续跑;持久化状态会把它关闭为明确的中断失败,同时保留计划和已完成结果。

  • 交互式专家窗口是尽力而为的可观测性,不是审计日志:事件文件有上限、异步写入、I/O 故障时可能丢事件,且 7 天后会被清理。它们只能从“运行专家那个宿主进程”所在的数据目录里跟随,换一个检出目录或不同的 EXPERT_COUNCIL_DATA_DIR 什么都看不到。

  • Codex 自身的沙箱不会自动包含外部 Pi Runtime,因此 Expert Council 使用单独的允许根目录和 worktree 边界。

  • Expert Council 不包含任意第三方包自动安装、递归专家树、图形界面、远程控制平面或远程遥测。

许可证

MIT,详见 LICENSE。