From 168b786e087bfa1fdb9bc3fd2cd8f789b5309d6c Mon Sep 17 00:00:00 2001 From: che cheng Date: Mon, 1 Jun 2026 15:27:52 +0800 Subject: [PATCH] chore(marketplace): extract parallel-ai-agents to standalone public repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved plugins/parallel-ai-agents/ to its own repo at https://github.com/PsychQuant/parallel-ai-agents for independent versioning, mirroring the che-axiom-systems / che-transport-mcp extractions. The standalone repo is a self-marketplace (plugin lives under plugins/parallel-ai-agents/, root holds its own marketplace.json). Marketplace.json entry removed here — users who installed via this marketplace should reinstall from the new source: /plugin marketplace add PsychQuant/parallel-ai-agents /plugin install parallel-ai-agents@parallel-ai-agents History preserved in the new repo via git filter-repo --path. --- .claude-plugin/marketplace.json | 10 - .../.claude-plugin/plugin.json | 8 - plugins/parallel-ai-agents/CHANGELOG.md | 55 -- plugins/parallel-ai-agents/CLAUDE.md | 74 -- plugins/parallel-ai-agents/bin/codex-call | 386 ---------- .../skills/ensemble-academic-review/skill.md | 714 ------------------ .../skills/ensemble-code-review/SKILL.md | 322 -------- .../skills/ensemble-lecture-review/SKILL.md | 209 ----- 8 files changed, 1778 deletions(-) delete mode 100644 plugins/parallel-ai-agents/.claude-plugin/plugin.json delete mode 100644 plugins/parallel-ai-agents/CHANGELOG.md delete mode 100644 plugins/parallel-ai-agents/CLAUDE.md delete mode 100755 plugins/parallel-ai-agents/bin/codex-call delete mode 100644 plugins/parallel-ai-agents/skills/ensemble-academic-review/skill.md delete mode 100644 plugins/parallel-ai-agents/skills/ensemble-code-review/SKILL.md delete mode 100644 plugins/parallel-ai-agents/skills/ensemble-lecture-review/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c0cbcbe..a79edb2 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -339,16 +339,6 @@ "source": "./plugins/cli-tools", "category": "development" }, - { - "name": "parallel-ai-agents", - "version": "2.5.1", - "description": "平行派發任務給多個 AI agent(Claude + Codex),獨立執行後交叉比對結果。Codex 改走直接 HTTP wrapper(bin/codex-call,Swift script)取代 codex exec subprocess,解決 hang 問題且避開 Python 版本飄移", - "author": { - "name": "Che Cheng" - }, - "source": "./plugins/parallel-ai-agents", - "category": "development" - }, { "name": "macdoc", "version": "1.1.0", diff --git a/plugins/parallel-ai-agents/.claude-plugin/plugin.json b/plugins/parallel-ai-agents/.claude-plugin/plugin.json deleted file mode 100644 index bf3cac4..0000000 --- a/plugins/parallel-ai-agents/.claude-plugin/plugin.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "parallel-ai-agents", - "description": "平行派發任務給多個 AI agent(Claude + Codex),獨立執行後交叉比對結果。Codex 改走直接 HTTP wrapper(bin/codex-call,Swift script)取代 codex exec subprocess,解決 hang 問題且避開 Python 版本飄移", - "version": "2.5.1", - "author": { - "name": "Che Cheng" - } -} diff --git a/plugins/parallel-ai-agents/CHANGELOG.md b/plugins/parallel-ai-agents/CHANGELOG.md deleted file mode 100644 index effaaad..0000000 --- a/plugins/parallel-ai-agents/CHANGELOG.md +++ /dev/null @@ -1,55 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -> ⚠ This file was bootstrapped by `changelog-tools:changelog-init` from the -> `plugin.json` description field. Section categorization is best-effort — -> review and refine `Added` / `Changed` / `Fixed` etc. as needed. - -## [Unreleased] - -## [2.3.0] - 2026-05-07 - -### Added -- **`--auto-iterate` mode for `/ensemble-academic-review` (#34)**: round → fix → round 自治收斂迴圈,內部沿用 mix N 的 alternating independent/hybrid pattern,但加上每輪結束的: - - **Verdict parsing**: Codex prompt 強制要求 `PERMANENT_CONVERGENCE | CONVERGED | NEEDS_ITER_N` 結構化 tag,skill 用 regex 解析,不靠語意判斷 - - **HIGH-only fix application**: 從 `review-round-{N}.md` 解 HIGH-severity findings 自動套到 working tree;ambiguous fix skip + log to `skipped_fixes.log` - - **Auto-commit per round**: `iter-{N}: apply HIGH fixes from ensemble round {N}`,user 可隨時 `git revert iter-{N}` - - **Rotate-focus heuristic**: 連續 K=3 同 focus CONVERGED 才 switch (focus pool: method-section / proofs / typography / cross-references / boundary-cases) - - **Stop conditions**: 達 `--converge-on` (default `PERMANENT_CONVERGENCE`) 或 `--max-rounds` (default 12, max 30) -- **8 cumulative methodological lessons** in SKILL.md tail — 來自實戰 23-round campaign (`PsychQuantHsu/psychophysic_representations_manuscript/docs/rounds/INDEX.md`),作為 rare-audited section / hypothesis-inheritance / verdict-tier 等坑的 reference - -### Notes -- Self-contained Bash while + state machine,**不**依賴 ralph-loop 的 Stop-hook 機制 -- 與 ralph-loop 同時跑時 skill 偵測並警告(雙 Stop-hook 衝突風險) -- Spec-only PR — agent 讀 SKILL.md 後在 user 顯式傳 `--auto-iterate` 才觸發,既有 mode 行為不變 - -## [2.2.0] - 2026-05-03 - -### Added -- **`number-verifier` reviewer**: 5th ensemble reviewer that checks every - number in a doc against ground-truth artifacts (`.rds`, `.npz`, `.csv`, - R/Python scripts). Catches hallucinated numbers that other reviewers - miss. Verified by ASSG3 review pipeline (Canadian GDP ARIMA + Australian - yields VAR/VECM) where it caught wrong y_T, drift omission, Ljung-Box - fitdf errors, and ARIMA(1,1,1) reference p-value mistakes across 4 rounds. -- `--no-numeric` flag to disable number-verifier (pure theoretical papers) -- `--no-references` flag to disable reference-verifier (technical notes) -- Auto-detect: number-verifier enables when `analysis/`, `*.rds`, `*.ipynb`, - `*.Rmd`, or `data/*.csv` are present near the doc -- Hybrid mode: `prior_number_issues` watch list passed to number-verifier - in subsequent rounds (analogous to `prior_ref_issues`) - -### Changed -- Reviewer count: 4 → 5 Claude teammates + Codex -- Tool-call rule: "5 calls in one message" → "N+1 calls (N ∈ {3,4,5})" -- Ironclad rules: HIGH-priority bucket now includes hallucinated numbers - alongside hallucinated references - -## [2.1.1] - (date unknown — please fill in) - -### Changed -- 平行派發任務給多個 AI agent(Claude + Codex),獨立執行後交叉比對結果 diff --git a/plugins/parallel-ai-agents/CLAUDE.md b/plugins/parallel-ai-agents/CLAUDE.md deleted file mode 100644 index bd610bd..0000000 --- a/plugins/parallel-ai-agents/CLAUDE.md +++ /dev/null @@ -1,74 +0,0 @@ -# parallel-ai-agents — CLAUDE.md - -## Purpose - -平行派發任務給多個 AI agent,獨立執行後交叉比對結果。使用 Claude orchestrated teams + Codex 實現跨模型、跨角色的盲驗。 - -## Skills - -| Skill | 用途 | 架構 | -|-------|------|------| -| `/parallel-ai-agents:ensemble-review` | 審閱文件/程式碼,交叉比對產出共識/盲點報告 | 4 Claude teammates (team) + 1 Codex | - -## 審閱架構 - -``` -ensemble-review -├── Claude Team(4 teammates,orchestrated) -│ ├── architecture — 設計、API、依賴 -│ ├── correctness — 邏輯、bug、edge case -│ ├── security — 攻擊者視角 -│ └── devils-advocate — 反駁前 3 人 -└── Codex(gpt-5.5,跨模型盲驗) -``` - -## 依賴 - -- Claude Code orchestrated teams(TeamCreate、SendMessage) -- **Codex OAuth token**(`~/.codex/auth.json`)— 由 codex CLI 在首次登入時建立。本 plugin 自帶 wrapper `bin/codex-call` 直接讀這個檔案、走 OAuth refresh + HTTP 直連 `chatgpt.com/backend-api`,不再 spawn `codex exec` subprocess(避免 stdin/stdout pipe 互鎖造成的 hang) -- Swift toolchain(Xcode CLT 內建;用 `#!/usr/bin/env swift` shebang,第一次跑會 compile cache) - -## bin/codex-call - -Swift script wrapper,取代原本的 `codex exec --full-auto`。設計目的: - -| 問題 | `codex exec` | `codex-call` (Swift) | -|------|-------------|---------------------| -| Subprocess hang | 偶發 | ✗ 純 URLSession,無 subprocess | -| Hard timeout | 不可靠 | ✓ URLSession + DispatchSemaphore wait timeout | -| OAuth refresh | CLI 自動 | 自帶 refresh + flock 防 race | -| 計費 | ChatGPT 訂閱 | ChatGPT 訂閱(同一條 OAuth)| -| service_tier=fast | CLI 接受(內部翻譯成 priority)| **接受 `fast`/`priority`/`flex`**,內部翻譯與 codex CLI 一致 | -| Cold start | ~50ms (subprocess) | ~1.5s(swift compile + cache)| -| 依賴 | `codex` CLI 安裝 | macOS 內建 swift(Xcode CLT)| - -範例: - -```bash -codex-call \ - --output result.md \ - --model gpt-5.5 \ - --effort xhigh \ - --max-time 600 \ - --instructions "你是嚴謹 reviewer。" \ - --prompt-file prompt.txt -``` - -或 stdin: - -```bash -echo "..." | codex-call --output out.md --model gpt-5.5 --effort xhigh -``` - -Wrapper 在 plugin 安裝時自動加入 PATH(透過 `bin/`),所以直接呼叫名字即可,不需要絕對路徑。 - -### 為什麼 Swift script 不是 Python - -Python 在 macOS 上版本飄移:`/usr/bin/python3` 是 stub(要 Xcode CLT 才有真 binary);版本可能是 3.9 / 3.10 / 3.11 / 3.13,新語法(如 `dict | None`)需 3.10+ 不一定可用。Swift script 用 Xcode CLT 內建的 swift 5+,shebang 直接跑,無版本兼容問題。 - -不走 Swift binary(che-mcps notarize 模式)的理由:這 wrapper 不需 TCC 權限(只發 HTTPS),開新 repo + notarize 流程過度工程化。Swift script 的 1-2s cold start 對 ensemble 場景(5-15s LLM response 為主)是可接受的雜訊。 - -## Development - -- 測試:`claude --plugin-dir ./plugins/parallel-ai-agents` -- 更新:`/plugin-tools:plugin-update parallel-ai-agents` diff --git a/plugins/parallel-ai-agents/bin/codex-call b/plugins/parallel-ai-agents/bin/codex-call deleted file mode 100755 index 4a8231e..0000000 --- a/plugins/parallel-ai-agents/bin/codex-call +++ /dev/null @@ -1,386 +0,0 @@ -#!/usr/bin/env swift - -// codex-call — direct HTTP wrapper for chatgpt.com/backend-api -// -// Replaces `codex exec --full-auto -o output "prompt"` with a clean HTTP call -// that bypasses the codex CLI subprocess (which can hang on stdin/stdout pipes). -// -// Usage: -// codex-call --output FILE [--model gpt-5.5] [--effort xhigh] -// [--service-tier ""] [--max-time 600] -// [--instructions TEXT] [--prompt-file FILE | PROMPT] -// -// Reads OAuth token from ~/.codex/auth.json (codex CLI's token store). -// Auto-refreshes access_token if within 5 min of expiry. Refresh uses a file -// lock to prevent concurrent races during parallel ensemble runs. - -import Foundation -#if canImport(Darwin) -import Darwin -#endif - -// MARK: - Configuration - -let HOME_DIR = FileManager.default.homeDirectoryForCurrentUser -let AUTH_FILE = HOME_DIR.appendingPathComponent(".codex/auth.json").path -let LOCK_FILE = HOME_DIR.appendingPathComponent(".codex/.token-refresh.lock").path -let TOKEN_URL = URL(string: "https://auth.openai.com/oauth/token")! -let CODEX_URL = URL(string: "https://chatgpt.com/backend-api/codex/responses")! -let CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann" -let REFRESH_THRESHOLD_SEC: Int = 300 - -// MARK: - Helpers - -func die(_ msg: String, code: Int32 = 1) -> Never { - FileHandle.standardError.write(Data("error: \(msg)\n".utf8)) - exit(code) -} - -func log(_ msg: String) { - FileHandle.standardError.write(Data("[codex-call] \(msg)\n".utf8)) -} - -// MARK: - JWT exp - -func jwtExp(_ token: String) -> Int { - let parts = token.split(separator: ".") - guard parts.count >= 2 else { return 0 } - var payload = String(parts[1]) - .replacingOccurrences(of: "-", with: "+") - .replacingOccurrences(of: "_", with: "/") - while payload.count % 4 != 0 { payload += "=" } - guard let data = Data(base64Encoded: payload), - let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], - let exp = json["exp"] as? Int - else { return 0 } - return exp -} - -// MARK: - Auth file - -func loadAuthRaw() throws -> [String: Any] { - let data = try Data(contentsOf: URL(fileURLWithPath: AUTH_FILE)) - guard let obj = try JSONSerialization.jsonObject(with: data) as? [String: Any] - else { throw NSError(domain: "codex-call", code: 1, - userInfo: [NSLocalizedDescriptionKey: "auth.json is not an object"]) } - return obj -} - -func saveAuthRaw(_ auth: [String: Any]) throws { - let data = try JSONSerialization.data(withJSONObject: auth, options: [.prettyPrinted, .sortedKeys]) - let tmp = AUTH_FILE + ".tmp" - try data.write(to: URL(fileURLWithPath: tmp), options: .atomic) - _ = chmod(tmp, 0o600) - if rename(tmp, AUTH_FILE) != 0 { - throw NSError(domain: "codex-call", code: 2, - userInfo: [NSLocalizedDescriptionKey: "rename failed: \(String(cString: strerror(errno)))"]) - } -} - -// MARK: - OAuth refresh - -func httpFormPost(url: URL, fields: [String: String], timeout: TimeInterval = 30) throws -> Data { - var req = URLRequest(url: url, timeoutInterval: timeout) - req.httpMethod = "POST" - req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") - let body = fields.map { (k, v) in - let ek = k.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? k - let ev = v.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? v - return "\(ek)=\(ev)" - }.joined(separator: "&") - req.httpBody = body.data(using: .utf8) - - let sem = DispatchSemaphore(value: 0) - var result: Data? - var error: Error? - var status: Int = 0 - URLSession.shared.dataTask(with: req) { data, resp, err in - status = (resp as? HTTPURLResponse)?.statusCode ?? 0 - result = data - error = err - sem.signal() - }.resume() - sem.wait() - - if let err = error { throw err } - guard status == 200, let data = result else { - let body = result.flatMap { String(data: $0, encoding: .utf8) } ?? "(no body)" - throw NSError(domain: "codex-call", code: status, - userInfo: [NSLocalizedDescriptionKey: "HTTP \(status): \(body.prefix(500))"]) - } - return data -} - -func refreshIfNeeded(_ auth: inout [String: Any]) throws { - guard var tokens = auth["tokens"] as? [String: Any], - let access = tokens["access_token"] as? String, - let refresh = tokens["refresh_token"] as? String - else { throw NSError(domain: "codex-call", code: 3, - userInfo: [NSLocalizedDescriptionKey: "auth.json missing tokens.access_token/refresh_token"]) } - - let now = Int(Date().timeIntervalSince1970) - if jwtExp(access) - now > REFRESH_THRESHOLD_SEC { return } - - // file lock - try FileManager.default.createDirectory( - at: URL(fileURLWithPath: LOCK_FILE).deletingLastPathComponent(), - withIntermediateDirectories: true) - let fd = open(LOCK_FILE, O_CREAT | O_WRONLY, 0o600) - if fd < 0 { - throw NSError(domain: "codex-call", code: 4, - userInfo: [NSLocalizedDescriptionKey: "open lock failed: \(String(cString: strerror(errno)))"]) - } - defer { close(fd) } - if flock(fd, LOCK_EX) != 0 { - throw NSError(domain: "codex-call", code: 5, - userInfo: [NSLocalizedDescriptionKey: "flock failed: \(String(cString: strerror(errno)))"]) - } - defer { _ = flock(fd, LOCK_UN) } - - // Re-read after acquiring lock — another process may have refreshed - auth = try loadAuthRaw() - if let t = auth["tokens"] as? [String: Any], - let a = t["access_token"] as? String, - jwtExp(a) - now > REFRESH_THRESHOLD_SEC { - return - } - tokens = auth["tokens"] as? [String: Any] ?? tokens - - let respData = try httpFormPost(url: TOKEN_URL, fields: [ - "grant_type": "refresh_token", - "refresh_token": refresh, - "client_id": CLIENT_ID, - ]) - guard let json = try JSONSerialization.jsonObject(with: respData) as? [String: Any], - let newAccess = json["access_token"] as? String - else { throw NSError(domain: "codex-call", code: 6, - userInfo: [NSLocalizedDescriptionKey: "refresh response missing access_token"]) } - - tokens["access_token"] = newAccess - if let newRefresh = json["refresh_token"] as? String { tokens["refresh_token"] = newRefresh } - if let newId = json["id_token"] as? String { tokens["id_token"] = newId } - auth["tokens"] = tokens - - let fmt = ISO8601DateFormatter() - fmt.formatOptions = [.withInternetDateTime, .withFractionalSeconds] - auth["last_refresh"] = fmt.string(from: Date()) - - try saveAuthRaw(auth) - log("token refreshed") -} - -// MARK: - SSE streaming - -final class StreamCollector: NSObject, URLSessionDataDelegate { - var accumulated = "" - var buffer = "" - var statusCode: Int = 0 - var streamError: Error? - let done = DispatchSemaphore(value: 0) - var firstBytes: Data? - - func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, - didReceive response: URLResponse, - completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { - statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0 - completionHandler(.allow) - } - - func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { - if statusCode != 200 { - // Buffer error body for reporting - firstBytes = (firstBytes ?? Data()) + data - return - } - guard let chunk = String(data: data, encoding: .utf8) else { return } - buffer += chunk - while let range = buffer.range(of: "\n\n") { - let event = String(buffer[buffer.startIndex.. String? in - guard line.hasPrefix("data:") else { return nil } - return String(line.dropFirst(5)).trimmingCharacters(in: .whitespaces.subtracting(.newlines)) - } - let payload = dataLines.joined() - if payload.isEmpty || payload == "[DONE]" { return } - guard let data = payload.data(using: .utf8), - let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], - let type = json["type"] as? String - else { return } - - switch type { - case "response.output_text.delta": - if let delta = json["delta"] as? String { accumulated += delta } - case "error", "response.failed": - let msg = (json["message"] as? String) - ?? ((json["response"] as? [String: Any])?["error"] as? [String: Any])?["message"] as? String - ?? "Codex error" - streamError = NSError(domain: "codex-call", code: -1, - userInfo: [NSLocalizedDescriptionKey: msg]) - default: - break - } - } -} - -func streamCodex(prompt: String, outputFile: String, model: String, effort: String, - serviceTier: String, maxTime: Int, instructions: String) throws { - var auth = try loadAuthRaw() - try refreshIfNeeded(&auth) - guard let tokens = auth["tokens"] as? [String: Any], - let access = tokens["access_token"] as? String - else { throw NSError(domain: "codex-call", code: 7, - userInfo: [NSLocalizedDescriptionKey: "post-refresh: tokens missing"]) } - let accountId = (tokens["account_id"] as? String) ?? "" - - var body: [String: Any] = [ - "model": model, - "store": false, - "stream": true, - "instructions": instructions, - "input": [["role": "user", "content": [["type": "input_text", "text": prompt]]]], - "text": ["verbosity": "medium"], - "include": ["reasoning.encrypted_content"], - "tool_choice": "auto", - "parallel_tool_calls": true, - "reasoning": ["effort": effort, "summary": "auto"], - ] - // Translate legacy/friendly names to backend wire values. - // Mirrors codex-rs ServiceTier::request_value(): Fast→priority, Flex→flex. - // Backend rejects "fast" with HTTP 400; codex CLI does this translation internally. - let wireTier: String = { - switch serviceTier.lowercased() { - case "fast", "priority": return "priority" - case "flex": return "flex" - case "": return "" - default: return serviceTier // pass through unknown values - } - }() - if !wireTier.isEmpty { body["service_tier"] = wireTier } - - var req = URLRequest(url: CODEX_URL, timeoutInterval: TimeInterval(maxTime)) - req.httpMethod = "POST" - req.setValue("Bearer \(access)", forHTTPHeaderField: "Authorization") - req.setValue("application/json", forHTTPHeaderField: "Content-Type") - req.setValue("responses=experimental", forHTTPHeaderField: "OpenAI-Beta") - req.setValue("codex_cli_rs", forHTTPHeaderField: "originator") - req.setValue(accountId, forHTTPHeaderField: "chatgpt-account-id") - req.setValue("text/event-stream", forHTTPHeaderField: "Accept") - req.httpBody = try JSONSerialization.data(withJSONObject: body) - - let collector = StreamCollector() - let config = URLSessionConfiguration.default - config.timeoutIntervalForRequest = TimeInterval(maxTime) - config.timeoutIntervalForResource = TimeInterval(maxTime) - let session = URLSession(configuration: config, delegate: collector, delegateQueue: nil) - - session.dataTask(with: req).resume() - let waitResult = collector.done.wait(timeout: .now() + .seconds(maxTime + 5)) - session.invalidateAndCancel() - - if waitResult == .timedOut { - throw NSError(domain: "codex-call", code: 408, - userInfo: [NSLocalizedDescriptionKey: "Hard timeout after \(maxTime)s"]) - } - if let err = collector.streamError { throw err } - - try collector.accumulated.write(toFile: outputFile, atomically: true, encoding: .utf8) - log("wrote \(collector.accumulated.count) chars to \(outputFile)") -} - -// MARK: - Argument parsing - -struct Args { - var output: String? - var model: String = "gpt-5.5" - var effort: String = "xhigh" - var serviceTier: String = "" - var maxTime: Int = 600 - var instructions: String = "You are a careful, rigorous reviewer. Respond in the user's language." - var promptFile: String? - var prompt: String? -} - -func parseArgs() -> Args { - var a = Args() - var args = Array(CommandLine.arguments.dropFirst()) - while !args.isEmpty { - let head = args.removeFirst() - func next() -> String { - guard !args.isEmpty else { die("missing value for \(head)") } - return args.removeFirst() - } - switch head { - case "--output", "-o": a.output = next() - case "--model": a.model = next() - case "--effort": a.effort = next() - case "--service-tier": a.serviceTier = next() - case "--max-time": a.maxTime = Int(next()) ?? a.maxTime - case "--instructions": a.instructions = next() - case "--prompt-file": a.promptFile = next() - case "--help", "-h": - print(""" - codex-call — direct HTTP wrapper for chatgpt.com/backend-api - - Usage: - codex-call --output FILE [--model gpt-5.5] [--effort xhigh] - [--service-tier ""] [--max-time 600] - [--instructions TEXT] - [--prompt-file FILE | PROMPT] - - If no PROMPT and no --prompt-file is given, reads prompt from stdin. - - Auth: reads ~/.codex/auth.json (auto-refreshes if expired). - """) - exit(0) - default: - if a.prompt == nil { a.prompt = head } - else { die("unexpected positional argument: \(head)") } - } - } - return a -} - -// MARK: - Main - -let a = parseArgs() -guard let output = a.output else { die("--output is required") } - -let prompt: String -if let pf = a.promptFile { - do { prompt = try String(contentsOfFile: pf, encoding: .utf8) } - catch { die("cannot read prompt file: \(error.localizedDescription)") } -} else if let p = a.prompt { - prompt = p -} else { - let stdinData = FileHandle.standardInput.readDataToEndOfFile() - prompt = String(data: stdinData, encoding: .utf8) ?? "" -} -if prompt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - die("empty prompt") -} - -do { - try streamCodex(prompt: prompt, outputFile: output, model: a.model, effort: a.effort, - serviceTier: a.serviceTier, maxTime: a.maxTime, instructions: a.instructions) -} catch { - die(error.localizedDescription) -} diff --git a/plugins/parallel-ai-agents/skills/ensemble-academic-review/skill.md b/plugins/parallel-ai-agents/skills/ensemble-academic-review/skill.md deleted file mode 100644 index c7fb66b..0000000 --- a/plugins/parallel-ai-agents/skills/ensemble-academic-review/skill.md +++ /dev/null @@ -1,714 +0,0 @@ ---- -name: ensemble-academic-review -description: | - 學術論文 ensemble 審閱:methodology、writing、reference verification、number-verification、devils-advocate。 - 用 che-zotero-mcp 驗證文獻真實性(抓幻覺文獻)、用 R/Python 重跑 ground-truth 抓幻覺數字,用 perspective-writer 審查寫作風格。 - 三種模式:independent(獨立單輪)、hybrid(DA 看前輪的單輪)、mix N(交替 N 輪)。 - Use when: 碩博論文、期刊投稿、學術報告、計量/實證作業需要嚴格審閱。 -argument-hint: "FILE [--mode independent|hybrid|mix] [--rounds N] [--prior summary.md] [--focus 'topic'] [--no-numeric|--no-references]" -allowed-tools: - - Read - - Write - - Edit - - Bash - - Grep - - Glob - - Agent - - TeamCreate - - SendMessage - - TaskCreate - - TaskUpdate - - TaskList - - TaskGet - - AskUserQuestion - - Skill - - mcp__plugin_che-zotero-mcp_zotero__zotero_search - - mcp__plugin_che-zotero-mcp_zotero__zotero_search_by_doi - - mcp__plugin_che-zotero-mcp_zotero__academic_search - - mcp__plugin_che-zotero-mcp_zotero__academic_lookup_doi - - mcp__plugin_che-zotero-mcp_zotero__academic_get_references - - mcp__plugin_che-zotero-mcp_zotero__academic_get_citations ---- - -# /ensemble-academic-review — 學術論文 Ensemble 審閱 - -5 個 Claude teammates(學術審閱角色)+ 1 個 Codex(gpt-5.5)各自獨立審閱,合成比較表找共識和盲點。 - -> **原理同 Ensemble OCR**:不同角色的錯誤模式不重疊。5 個 Claude 以不同學術審閱角度審閱且互相挑戰,Codex 提供跨模型盲驗。 - -## 五個 reviewer 角色 - -| 角色 | 任務 | 預設啟用 | -|------|------|---------| -| **methodology** | 研究設計、統計方法、推論邏輯 | ✅ 永遠 | -| **writing** | 論述結構、學術語氣、APA、文法 | ✅ 永遠 | -| **reference-verifier** | 用 zotero-mcp 逐一驗證引用文獻,抓幻覺文獻 | ✅ doc 內偵測到 References / Bibliography 區塊;可 `--no-references` 關閉 | -| **number-verifier** | 用 R/Python 重跑原始計算 artifact,逐位驗證 doc 中每個數字 vs ground truth,抓幻覺數字 | ✅ 偵測到 `analysis/`、`data/`、`notebooks/`、`.ipynb`、`.Rmd` 或 doc 路徑旁有 `*.rds` / `*.csv`;可 `--no-numeric` 關閉 | -| **devils-advocate** | 對抗性驗證;hybrid 輪看所有前輪結果,挑戰其他 reviewer 的 PASS/LOW 判決 | ✅ 永遠 | - -**何時可關掉 reference-verifier**:純技術筆記、計量作業、無學術引用之文件 → `--no-references` -**何時可關掉 number-verifier**:純理論論文、無實證計算結果之文件 → `--no-numeric` - -兩者皆關 = 退化為 3 reviewer + Codex(保留核心 4 角色 + Codex)。 - -## 四種模式 - -| 模式 | 說明 | 適用情境 | -|------|------|---------| -| **independent**(預設) | 所有審閱者從零開始,不知道前輪結果 | 第一輪審閱、或想要完全獨立的第二意見 | -| **hybrid** | 3 reviewer + Codex 獨立審閱,**只有 devil's advocate 看得到前輪結果** | 在前一輪基礎上挖更深,同時避免 anchoring bias | -| **mix N** | 自動交替 independent → hybrid → independent → hybrid... 共 N 輪 | 最完整的審閱,每輪都是完整 ensemble,用 tasks 追蹤進度 | -| **`--auto-iterate`**(v2.3.0+,#34) | round → fix → round 自治收斂迴圈,直到 Codex 標 `PERMANENT_CONVERGENCE` 或 `--max-rounds` 到上限 | 反覆審閱 + 修正直至 reviewer 全綠;適用稿件最終 polish | - -### mix N 的運作邏輯 - -``` -mix 4 thesis.md -│ -├── Round 1: independent — 全新獨立審閱 -│ → 產出 review-round-1.md -│ -├── Round 2: hybrid — DA 看到 Round 1 結果,找盲點 -│ → 產出 review-round-2.md(標記 🆕 新發現) -│ -├── Round 3: independent — 再一次全新獨立(不看 Round 1-2) -│ → 產出 review-round-3.md -│ -├── Round 4: hybrid — DA 看到 Round 1-3 所有結果,找盲點 -│ → 產出 review-round-4.md(標記 🆕 新發現) -│ -└── Final: 合併所有輪次 → review-summary.md - - 跨輪共識(多輪都指出) - - 每輪獨有的新發現 - - 收斂趨勢(第 N 輪新發現數量遞減 = 審閱飽和) -``` - -**為什麼交替?** -- independent 輪:不受前輪汙染,能從完全不同的角度發現新問題,也能驗證/修正前輪結論 -- hybrid 輪:devil's advocate 帶著所有前輪知識,專門挖前面所有輪次都沒發現的盲點 -- 交替進行:每一輪都有獨立發現的機會,也有針對性深挖的機會 - -### hybrid 模式的資訊分配 - -| 角色 | 看到什麼 | 為什麼 | -|------|---------|--------| -| methodology | 什麼都不看 | 避免 anchoring,獨立發現新問題 | -| writing | 什麼都不看 | 同上 | -| Codex | 什麼都不看 | 跨模型盲驗必須獨立 | -| reference-verifier | 前輪的可疑文獻 watch list | 重點檢查 + 獨立全面查核 | -| number-verifier | 什麼都不看(每輪都要從 ground-truth 重跑)| 防止前輪錯數抄來抄去 | -| **devil's advocate** | **所有前輪的完整結果** | 專攻盲點、升/降級前輪判斷 | - -## 審閱架構 - -``` -/ensemble-academic-review FILE [--mode independent|hybrid|mix] [--rounds N] [--no-numeric|--no-references] [--auto-iterate [--max-rounds N] [--converge-on VERDICT]] -│ -├── Claude Team(最多 5 teammates) -│ ├── methodology — 研究設計、統計方法(永遠獨立) -│ ├── writing — 論述結構、學術語氣、APA(永遠獨立) -│ ├── reference-verifier — 逐一查文獻(hybrid 時收到 watch list)— 可關 -│ ├── number-verifier — 逐一驗證數字 vs 原始計算 artifact(永遠獨立)— 可關 -│ └── devils-advocate — 反駁(hybrid 時看得到所有前輪結果) -│ -└── Codex(gpt-5.5,永遠獨立) - -→ 每輪產出獨立的 review-round-{N}.md -→ mix 模式最後合併所有輪次 → review-summary.md -``` - -## 執行流程 - -### Phase 0: 解析輸入 - -``` -Arguments: - FILE — 要審閱的學術論文檔案(.md, .tex, .docx, .pdf, .ipynb, .Rmd) - --mode — independent(預設)、hybrid、mix - --rounds — mix 模式的輪數(預設 2) - --prior — hybrid 模式的前輪摘要檔案(mix 模式自動管理) - --focus — 審閱重點(可選) - --no-numeric — 關閉 number-verifier(純理論論文、無實證計算) - --no-references — 關閉 reference-verifier(純技術筆記、無學術引用) - --auto-iterate — v2.3.0+,啟動自治 round → fix → round 收斂迴圈(見下方 § auto-iterate 模式) - --max-rounds N — `--auto-iterate` 的上限,default 12,clamp [1, 30] - --converge-on VERDICT — `--auto-iterate` 的停止條件,default `PERMANENT_CONVERGENCE`, - 其他可選: `CONVERGED` (寬鬆 — 任一輪 Codex 給就停) - -如果沒有 FILE,問使用者。 -如果 --mode hybrid 但沒有 --prior,自動搜尋同目錄下的 review-round-*.md。 -如果是 .docx,用 che-word-mcp 的 get_document_text 讀取。 -如果是 .pdf,用 macdoc convert --to md 轉換後讀取。 - -#### Auto-detect reviewer 啟用 - -掃 FILE 同層及上層 1-2 級目錄: -- 找到 `analysis/*.rds`、`data/*.csv`、`*.ipynb`、`output/*.npz`、`scripts/*.{R,py}` → number-verifier ON -- doc 內含 `\bibliography{}`、`References` / `参考文献` 標題下 5+ 條引用 → reference-verifier ON -- 兩者都沒:fallback 到 methodology+writing+devils-advocate(3 reviewer + Codex) - -使用者可用 `--no-numeric` / `--no-references` 強制關閉。 -``` - -### Phase 0.5: Mix 模式的 Task 建立(僅 mix 模式) - -用 TaskCreate 建立所有輪次的 tasks,作為進度追蹤: - -``` -TaskCreate: "Round 1/N: independent review" -TaskCreate: "Round 2/N: hybrid review" -TaskCreate: "Round 3/N: independent review" -... -TaskCreate: "Final: merge all rounds" -``` - -每完成一輪就 TaskUpdate 為 completed,然後開始下一輪。 -**每一輪都是完整的 Phase 1-4 流程,不可偷工減料。** - -### Phase 1: 讀取文件 + 準備 context - -1. 讀取論文全文 -2. 提取所有引用文獻(References / Bibliography 區塊) -3. 準備 context 字串,包含:檔案路徑、全文內容、文獻列表、ground-truth artifact 清單、focus 指示 -4. (hybrid 模式)讀取前輪結果,提取: - - `prior_ref_issues` — 可疑/幻覺文獻清單(給 reference-verifier) - - `prior_number_issues` — 可疑/幻覺數字清單(給 number-verifier) - - `prior_full_report` — 所有前輪的完整結果(只給 devil's advocate) - -### Phase 2: 平行啟動 Claude Team + Codex - -**CRITICAL: 所有 tool calls(TeamCreate + Codex Bash)必須在同一個 message 送出。不可分步驟。** - -**啟用的 reviewer 數依設定變動**:預設 5(含 number+reference verifier),加 `--no-numeric` 變 4,再加 `--no-references` 變 3。Codex 永遠跑。一個 message 內 = (啟用 reviewer 數) Agent + 1 Bash。 - -**CRITICAL: Teammates 必須用 `subagent_type: "general-purpose"`。不可用 `Explore`。** - -#### 2a. Claude Team(4 reviewers) - -用 TeamCreate 建立 team,然後在**同一個 message** 啟動 4 個 Agent + 1 個 Codex Bash(共 5 個 tool calls): - -``` -TeamCreate: - name: "academic-review-{timestamp}-round{N}" - description: "Academic review round {N} for {FILE}" -``` - -**Agent 1: methodology** -``` -Agent: - name: "methodology" - subagent_type: "general-purpose" - team_name: "academic-review-{timestamp}-round{N}" - prompt: | - 你是 Methodology Reviewer,專門審閱學術研究方法。 - 審閱論文:{FILE} - {context} - - 你的任務: - 1. 研究設計是否合理(實驗設計、對照組、隨機化) - 2. 統計方法是否正確(假設檢定、效果量、信賴區間) - 3. 樣本量是否足夠(power analysis) - 4. 推論邏輯是否成立(因果 vs 相關、過度推論) - 5. 研究限制是否充分討論 - 6. 分析流程是否可重現 - - {focus_instruction} - - 用 Read 工具讀取論文相關段落確認。 - 用中文逐點列出問題和建議。每個問題標注嚴重性(HIGH/MEDIUM/LOW)。 - 最後給整體評價(一段話)。 -``` - -**Agent 2: writing** -``` -Agent: - name: "writing" - subagent_type: "general-purpose" - team_name: "academic-review-{timestamp}-round{N}" - prompt: | - 你是 Writing Quality Reviewer,專門審閱學術寫作品質。 - 審閱論文:{FILE} - {context} - - 你的任務: - 1. 論述邏輯 — 各章節之間的銜接是否流暢 - 2. 段落結構 — 每段是否有明確的 topic sentence 和 supporting evidence - 3. 學術語氣 — 是否適當使用 hedging language,避免過度武斷 - 4. APA 格式 — 引用格式、標題層級、圖表標註是否符合規範 - 5. 文法與用詞 — 英文文法錯誤、用詞精確度、一致性 - 6. Abstract 品質 — 是否完整涵蓋 background、method、results、conclusion - - 你可以使用 Skill tool 呼叫 perspective-writer 來分析特定段落的寫作風格。 - - {focus_instruction} - - 用中文逐點列出問題和建議。每個問題標注嚴重性(HIGH/MEDIUM/LOW)。 - 引用具體段落或句子作為例證。 - 最後給整體評價(一段話)。 -``` - -**Agent 3: reference-verifier** -``` -Agent: - name: "reference-verifier" - subagent_type: "general-purpose" - team_name: "academic-review-{timestamp}-round{N}" - prompt: | - 你是 Reference Verifier,專門驗證學術文獻的真實性。 - 審閱論文:{FILE} - {context} - - 你的核心任務:**偵測幻覺文獻**(hallucinated references)。 - - {hybrid_mode_ref_verifier_instruction} - - 步驟: - 1. 從論文中提取所有引用文獻(作者、年份、標題、期刊) - 2. 對每一筆文獻,使用 che-zotero-mcp 工具驗證: - - 用 `academic_search` 搜尋標題或作者+年份 - - 如果有 DOI,用 `academic_lookup_doi` 驗證 - - 用 `zotero_search` 檢查是否已在 Zotero 資料庫中 - 3. 分類每筆文獻: - - ✅ 已驗證(找到匹配的真實文獻) - - ⚠️ 存疑(部分匹配,可能是資訊不完整) - - ❌ 疑似幻覺(完全找不到,或作者/標題/年份不匹配) - 4. 檢查 in-text citation 與 reference list 是否一致(有沒有引了但沒列、或列了但沒引) - - 輸出格式: - ``` - ## 文獻驗證結果 - - ### 已驗證 ✅ - 1. Author (Year). Title. — DOI: xxx ✅ - - ### 存疑 ⚠️ - 1. Author (Year). Title. — 原因:找到類似文獻但年份不同 - - ### 疑似幻覺 ❌ - 1. Author (Year). Title. — 原因:完全查無此文獻 - - ### 引用一致性 - - 引了但沒列在 references:... - - 列在 references 但文中未引用:... - ``` - - 每筆文獻都要查。不可跳過。 - 用中文輸出結果。 -``` - -**Agent 4: number-verifier**(如未 `--no-numeric`) -``` -Agent: - name: "number-verifier" - subagent_type: "general-purpose" - team_name: "academic-review-{timestamp}-round{N}" - prompt: | - 你是 Number Verifier,專門驗證學術/實證文件中每一個數字 vs ground truth artifact。 - 審閱論文:{FILE} - {context} - - 你的核心任務:**偵測幻覺數字**(hallucinated numbers)— tex/docx/md 中與原始計算不符的數值。 - - {hybrid_mode_number_verifier_instruction} - - 步驟: - 1. 識別 ground-truth 來源: - - R 專案:`analysis/*.rds`、`*.RData`、`*.R` 腳本 - - Python 專案:`*.npz`、`*.csv`、`*.ipynb`、`*.py` - - Excel/csv 原始資料:`*.xlsx`、`*.csv` - 2. 從 doc 中提取每個數值(test stat、coef、p-value、AIC/BIC、forecast、平均、sd、t-stat、F、χ²、CI 等) - 3. 對每個數字,找對應 ground-truth: - - 直接讀 .rds:`Rscript -e 'x <- readRDS("..."); print(x$path$to$value)'` - - 直接讀 .npz:`python -c 'import numpy as np; print(np.load("...")["k"])'` - - 重跑腳本:`Rscript analysis/q1.R` 或 `python scripts/main.py` - - 從原始資料重算:必要時自己跑 ADF / ARIMA / VAR / OLS - 4. 分類每個數字: - - ✅ 已驗證(與 ground truth 完全相符) - - ⚠️ rounding(最後一位 ±1 可接受) - - ❌ 幻覺數字(差量超過 rounding 容差) - 5. 跨檔一致性:若有多版本(EN tex、ZH tex、DOCX),每個數字三檔一致嗎? - 6. 內部一致性:tex 表格中的 forecast 是否真的等於 last_obs + cumulative diff? - - 輸出格式: - ``` - ## 數字驗證結果 - - ### ✅ 已驗證 - - ARIMA(1,1,0) coef 0.5564 — matches q1c.rds$fit$coef ✅ - - ### ⚠️ rounding 偏差(last digit ±1) - - log s_q[1] = -0.04723 vs rds -0.04724 (rounding) ⚠️ - - ### ❌ 幻覺數字(必修) - - tex line 52 寫 y_T=890569 — 實際 RGDPCAN.xlsx 末值 836072 ❌ - - tex line 200 寫 1-step σ̂=0.0207 — 實際 fc_se=0.0181(標籤錯)❌ - - ### 三檔一致性(EN tex / ZH tex / DOCX) - - 13 個 spot-check 全綠 ✅ - - 或:EN/ZH 一致但 DOCX 不同步 ❌ - ``` - - 每個出現在 tex 中的數字都要查。不可只抽樣。 - HIGH count = 幻覺數字總數。MEDIUM = rounding-only。 - 用中文輸出結果,最後給一段 verdict。 -``` - -**Agent 5: devils-advocate** -``` -Agent: - name: "devils-advocate" - subagent_type: "general-purpose" - team_name: "academic-review-{timestamp}-round{N}" - prompt: | - 你是 Devil's Advocate,學術審閱的對抗性驗證者。 - 審閱論文:{FILE} - {context} - - 你的任務:等其他 3 個 reviewer(methodology、writing、reference-verifier)完成後, - 用 SendMessage 詢問他們的結論,然後**試著反駁每一個「通過」或「LOW」的判斷**。 - - {hybrid_mode_devils_advocate_instruction} - - 步驟: - 1. 先用 Read 工具讀取論文,形成自己的理解 - 2. 用 SendMessage 分別問 methodology、writing、reference-verifier 他們的 findings - 3. 對每個「通過」的判斷,找理由說它其實有問題 - 4. 對每個「LOW」的判斷,論證為什麼應該是 MEDIUM 或 HIGH - 5. 特別挑戰: - - methodology 說統計方法 OK → 找 alternative interpretation - - writing 說邏輯清晰 → 找隱含的邏輯跳躍 - - reference-verifier 說文獻 OK → 質疑文獻的相關性和時效性 - 6. 如果你找不到反駁的理由,才承認確實通過 - - 這是對抗性驗證 — 你的存在是為了防止群體盲點。 - 用中文輸出你的反駁結果。 -``` - -#### Mode-specific prompt injections - -以下變數在 independent 輪為空字串,在 hybrid 輪注入內容: - -**`{hybrid_mode_ref_verifier_instruction}`** — 給 reference-verifier: -``` -(hybrid 輪時注入) -前幾輪審閱標記了以下可疑文獻,請特別留意: -{prior_ref_issues} -但你的核心任務仍然是逐一查核所有文獻,不要只看這份清單。 -前輪的判斷可能有誤,你需要獨立驗證。 -``` - -**`{hybrid_mode_number_verifier_instruction}`** — 給 number-verifier: -``` -(hybrid 輪時注入) -注意:本作業已跑過至少一輪驗證;前輪 number-verifier 標記過的可疑數字: -{prior_number_issues} -但你的核心任務仍是逐一從 ground truth 重跑驗證所有數字,**不要只看這份清單**。 -前輪可能漏掉新出現或剛修進去的數字(修正常引入新錯)。 -``` - -**`{hybrid_mode_devils_advocate_instruction}`** — 給 devil's advocate: -``` -(hybrid 輪時注入) -## 所有前輪審閱結果 - -以下是前面所有輪次的 ensemble 審閱結果: -{prior_full_report} - -你的額外任務(除了反駁本輪 reviewer 的判斷之外): -1. **挑戰前輪「通過」的判斷** — 前輪認為 OK 或只給 LOW 的項目,是否有被低估的問題? -2. **找出所有前輪的盲點** — 有什麼問題是前面所有輪次都沒想到的? -3. **驗證前輪的結論** — 前輪的 HIGH 判斷是否真的那麼嚴重?有沒有過度反應的? -4. **不要重複已知問題** — 前輪已經充分討論的問題不需要重新論述,除非你有新的反駁角度 - -在輸出中,明確區分: -- 「前輪已知 + 本輪確認」的問題 -- 「前輪已知但需要升級/降級」的問題 -- 「前輪完全未發現」的新問題 🆕 -``` - -#### 2b. Codex(背景執行 — 直接 HTTP,繞過 codex CLI subprocess) - -```bash -codex-call \ - --output "{output_file}" \ - --model gpt-5.5 \ - --effort xhigh \ - --service-tier fast \ - --max-time 900 \ - --instructions "你是嚴謹的學術論文審閱者,從 methodology、writing、reference 三個角度審閱。用中文輸出。" << 'EOF' -{codex_prompt} -EOF -``` - -> **為什麼不用 `codex exec`**:subprocess 偶爾會 hang(stdin/stdout pipe 互鎖、tty 問題),等 10 分鐘 timeout 才能繼續。`codex-call` 是 plugin 自帶 wrapper(`bin/codex-call`,Swift script,安裝時自動加入 PATH),直接 HTTP POST 到 `chatgpt.com/backend-api/codex/responses`,仍走你的 ChatGPT 訂閱 OAuth — 但 `--max-time` 是硬性保證,不會 hang。 -> -> **論文審閱用 max-time 900s**(15 分鐘),因為輸入長、reasoning 比 code review 重。Fast mode:傳 `--service-tier fast`,wrapper 內部翻譯成 `priority`(backend 拒絕字面 "fast",但接受 codex CLI 內部翻譯後的 `priority`)。 -> -> **OAuth token**:wrapper 自動讀 `~/.codex/auth.json`(codex CLI 的同一份),到期前 5 分鐘自動 refresh,用 file lock 避免 ensemble 平行 race。 - -Codex prompt 應包含: -- 論文全文(或摘要 + 關鍵段落,視長度而定) -- 要求從 methodology、writing、reference 三個角度審閱 -- 用中文回答 -- **不提及 Claude team 的存在**(確保獨立性) - -### Phase 3: 收集結果 - -1. 等待 4 個 Claude teammates 完成(透過自動訊息通知) -2. 等待 Codex 完成(輪詢 status) -3. 如果 Codex 失敗或超時(>10 分鐘),跳過,標注「Codex 不可用」 - -### Phase 4: 合併去重 + 寫入本輪結果 - -由主 session 的 Claude 讀取所有結果,產出本輪比較表: - -1. **去重**:相同問題 → 合併,標註來源 -2. **severity 以最高為準** -3. **Devil's Advocate 的反駁如果成立** → 升級 severity -4. **幻覺文獻特別標示** — reference-verifier 的 ❌ 結果優先級最高 - -#### hybrid 輪額外步驟 - -5. **與前輪交叉比對**:將本輪發現分為三類: - - **前輪已知 + 本輪確認**:增加可信度 - - **前輪已知但本輪修正**:前輪判斷不夠精確 - - **本輪新發現 🆕**:前輪所有審閱者都未發現的問題 - -#### 寫入本輪結果 - -將本輪結果寫入 `review-round-{N}.md`(與論文同目錄),然後: -- independent / hybrid 單輪模式:這就是最終輸出 -- mix 模式:TaskUpdate 標記本輪完成,繼續下一輪 - -### Phase 5: Mix 模式的輪次循環 - -``` -for round in 1..N: - if round is odd: - run Phase 1-4 as independent - else: - run Phase 1-4 as hybrid (prior = all previous rounds) - - TaskUpdate: "Round {round}/{N}" → completed - Write review-round-{round}.md - -TaskUpdate: "Final: merge all rounds" → in_progress -``` - -### Phase 5b: `--auto-iterate` 模式的自治收斂迴圈(v2.3.0+, #34) - -`--auto-iterate` 是 `mix N` 的擴展,加上「每輪結束讀 Codex verdict + 自動 apply HIGH-severity fix + 自動 commit per round」的 round → fix → round 收斂迴圈。 - -#### 啟動條件 - -- User 傳 `--auto-iterate` flag -- 不可與 `--mode independent` 或 `--mode hybrid` 同時使用 (auto-iterate 自含 mode 邏輯,內部沿用 mix 的 alternating independent/hybrid pattern) - -#### 主迴圈 - -``` -N=1 -verdict=NEEDS_ITER_1 -focus_history=[] - -while N <= max_rounds: - # 1. Run round (alternating: odd=independent, even=hybrid) - mode = 'independent' if N is odd else 'hybrid' - run Phase 1-4 with mode → review-round-{N}.md - - # 2. Parse Codex verdict - verdict = extract_verdict(codex_output) - # Look for PERMANENT_CONVERGENCE, CONVERGED, etc. - - # 3. Halt check - if verdict == converge_on: - break - - # 4. Apply HIGH-severity fixes - high_findings = parse_findings(review-round-{N}.md, severity='HIGH') - apply_fixes(high_findings) → working tree modified - - # 5. Auto-commit checkpoint - git add -A - git commit -m "iter-{N}: apply HIGH fixes from ensemble round {N}" - - # 6. Rotate focus heuristic (after K=3 same-focus CONVERGED) - focus_history.append((current_focus, verdict)) - if last_3_verdicts_all_CONVERGED_with_same_focus(focus_history): - current_focus = next_focus_in_pool() - # focus pool: method-section, proofs, typography, cross-references, boundary-cases - - N += 1 - -if N > max_rounds: - log("Halted at max_rounds without reaching {converge_on}") -``` - -#### Verdict 解析 protocol - -Codex prompt 結尾必須含明確 instruction: - -``` -At the very end of your review, output exactly one structured verdict tag: - - NEEDS_ITER_{N} — issues remain, iterate again - CONVERGED — review converged within current focus - PERMANENT_CONVERGENCE — converged across all foci, paper-level done - -Choose conservatively. If unsure, NEEDS_ITER_{N}. -``` - -Skill 用 regex `([A-Z_0-9]+)` 從 Codex output 抓 tag。Robust 對 phrasing 變異;regex 容易 miss 「basically converged」近義詞,因此採結構化 tag。 - -#### Apply-fixes protocol - -從 `review-round-{N}.md` 解出 HIGH-severity findings(MEDIUM/LOW 累積到最後一輪一次處理): - -``` -For each finding with severity=HIGH: - parse 「file:line — issue description — suggested fix」 - Apply via Edit tool;若 fix 模糊或 suggestion 帶推測,skip + log to {SESSION}/skipped_fixes.log -``` - -跳過 ambiguous fix 而非硬套,避免 reviewer suggestion 自身錯誤被放大。 - -#### Backup / rollback - -每輪結束 auto-commit `iter-{N}: apply HIGH fixes from ensemble round {N}`。User 可隨時: - -```bash -git log --oneline | grep 'iter-' # 列所有迭代 checkpoint -git revert iter-{N} # 回退某輪 -git reset --hard iter-{N-1} # hard reset 到前一輪 -``` - -#### Rotate-focus heuristic - -如連續 K=3 輪同一 focus 都 CONVERGED 但未 PERMANENT_CONVERGENCE → 自動 switch focus 避免 local optimum。Focus pool: - -| Focus | 描述 | -|-------|------| -| `method-section` | 統計方法、研究設計 | -| `proofs` | 數學證明、推導 | -| `typography` | 排版、引用、bibliography | -| `cross-references` | 交叉引用、ToC、bookmarks | -| `boundary-cases` | edge case、退化情境 | - -Default 起始 focus = (none) — 不設,讓 reviewer 自主探;同一 focus 重複 CONVERGED 才強制 rotate。 - -#### 與 ralph-loop 的差別 - -`--auto-iterate` 是 **self-contained Bash while + state machine**,不依賴 ralph-loop 的 Stop-hook re-feed。Mode boundary 明確,可隨時中止 (`Ctrl+C` 在 round 之間生效);ralph-loop 把整個 session 鎖進迴圈,容易意外干擾其他 skill。 - -若 user 同時跑 `ralph-loop` + `--auto-iterate`,skill 偵測 ralph-loop active 會印警告。 - -#### Default 上限與 cost 警示 - -- `--max-rounds` default 12,max 30 -- 每輪約 5 reviewer + Codex ≈ 6 LLM call;30 rounds × 6 ≈ 180 call -- Skill prompt 會提示預估 token cost 並要求 confirm 才開跑 - -### Phase 6: 最終合併(mix 模式) - -讀取所有 `review-round-*.md`,產出最終的 `review-summary.md`: - -```markdown -## Ensemble Academic Review: {FILE} -## Mode: mix {N} rounds - -### 審閱歷程 -| 輪次 | 模式 | 新發現數 | 累計問題數 | -|------|------|---------|-----------| -| Round 1 | independent | 15 | 15 | -| Round 2 | hybrid | 8 🆕 | 23 | -| Round 3 | independent | 4 | 27 | -| Round 4 | hybrid | 1 🆕 | 28 | - -### 收斂判斷 -新發現數逐輪遞減(15 → 8 → 4 → 1),審閱已趨近飽和。 - -### 跨輪共識(多輪都獨立指出) -| # | 問題 | 出現輪次 | 嚴重性 | -|---|------|---------|--------| -| 1 | ... | R1, R2, R3 | HIGH | - -### 僅單輪發現 -| # | 問題 | 首次出現 | 嚴重性 | 後續輪次確認? | -|---|------|---------|--------|--------------| -| 1 | ... | R2 🆕 | HIGH | R3 確認 | -| 2 | ... | R4 🆕 | MEDIUM | 未再驗證 | - -### 文獻驗證(取最完整的一輪) -... - -### 建議修改優先順序 -... -``` - -### Phase 7: 詢問下一步 - -``` -審閱完成({mode}, {N} 輪)。 -新發現趨勢:{round1_new} → {round2_new} → ... → {roundN_new} -{收斂判斷} - -要怎麼做? -1. 修正幻覺文獻和 HIGH 問題 -2. 只看不改(純審閱) -3. 針對特定問題深入討論 -4. 用 /perspective-writer 改寫特定段落 -5. 再跑一輪(如果新發現數未收斂) -``` - -## 鐵律 - -### 所有模式共用 - -- **所有 tool calls 在同一個 message 送出**(N Agent + 1 Bash codex;N ∈ {3,4,5} 視啟用設定)。不可分步驟。 -- **Codex 看不到 Claude Team 的討論**。完全獨立的盲驗。 -- **Codex 的審稿結果原封不動呈現**,不要修改或摘要。 -- **reference-verifier 必須逐一查每筆文獻**。不可跳過或抽樣。 -- **number-verifier 必須對每個出現在 doc 中的數字查 ground truth**。不可只抽樣。 -- **幻覺文獻、幻覺數字皆最高優先級**。任何 ❌ 結果都是 HIGH severity。 -- **共識問題 > 單方問題**:多個來源都指出的問題最需要修。 -- **衝突不自動裁決**:呈現給使用者判斷。 -- **Devil's Advocate 是必要的**。防止群體盲點。 - -### hybrid 輪專屬 - -- **methodology、writing、Codex 絕對不看前輪結果**。防止 anchoring bias。 -- **只有 devil's advocate 拿到所有前輪完整結果**。 -- **reference-verifier 與 number-verifier 只拿到 watch list**,不是完整判斷。仍須獨立全面查核(從 ground truth 重跑)。 -- **合併時必須標記 🆕 新發現**。 - -### mix 模式專屬 - -- **每一輪都是完整的 Phase 1-4**。不可偷工減料、跳過 Codex、或減少 reviewer。 -- **用 TaskCreate/TaskUpdate 追蹤每輪進度**。讓使用者看到即時狀態。 -- **每輪結果獨立寫入 `review-round-{N}.md`**。不可覆蓋前輪。 -- **最終合併必須包含「收斂判斷」**:新發現數是否遞減。如果最後一輪仍有大量新發現,建議使用者再跑一輪。 -- **前輪結論可以被後輪推翻**。independent 輪的獨立審閱者如果得出不同結論,標記衝突供使用者判斷。 - -### `--auto-iterate` 模式專屬(v2.3.0+) - -- **不可在 `--mode independent|hybrid` 上同時開**;mode 自含 alternating logic -- **每輪 auto-commit `iter-{N}` checkpoint**,user 可隨時 git revert 回退 -- **HIGH-only fix application**;MEDIUM/LOW 累積到最後一輪一次處理(降低 round-to-round 雜訊) -- **Ambiguous fix 一律 skip + log to `skipped_fixes.log`**,寧少做不錯做 -- **`...` tag 必填**;Codex prompt 結尾強制要求,parsing 用結構化 regex 不靠語意 -- **rotate-focus 在 K=3 同 focus CONVERGED 才觸發**;新 focus 從 pool 順序輪換 -- **`max_rounds` 上限 30**;達上限 halt 並報告未達 verdict 條件 -- **同時跑 ralph-loop + `--auto-iterate` 時 skill 警告**(雙 Stop-hook 衝突風險) - -## 8 個 cumulative methodological lessons (v2.3.0+, from real 23-round campaign) - -來源:`PsychQuantHsu/psychophysic_representations_manuscript/docs/rounds/INDEX.md`。可作 `--auto-iterate` 模式 review 中的「常踩坑」清單,Codex prompt 可選擇性 inject 部分 lessons 加強 detection。 - -1. **Light-weight spot-check 容易 false-positive CONVERGED** — 至少跑滿 3 reviewer + Codex 才能 trust verdict -2. **Theorem counter shared across `\newtheorem{lemma}[theorem]` = printing off-by-one** — 必查 LaTeX counter 結構 -3. **Hypothesis silently inherited across theorems** — 在某 theorem 加的 condition 可能影響後續 theorem,需明示「not inherited by Theorem N」 -4. **Stress-test on rare-audited sections (§Notation, §Discussion) 抓得到 11+ NEW HIGH** — 建議 rotate-focus 時主動進這些區 -5. **Codex `gpt-5.5 xhigh` 比 4-Claude consensus 嚴格** — 4-Claude 全 PASS 不等於 Codex 也 PASS -6. **PDF Token warnings (hyperref) 是真 bug 不是 cosmetic** — silent 不 fix 會在 ToC/bookmarks 失敗 -7. **degenerate counter-example 必查** — 「u≡0」「g(z)=z²-1」這類 counter 應在 case-taxonomy 之內排除 -8. **CONVERGED ≠ PERMANENT_CONVERGENCE** — 前者是 scope-limited 的「目前 focus 沒抓到新東西」,後者是 cross-focus 全綠;只接受後者作 halt verdict 是 default 設計 diff --git a/plugins/parallel-ai-agents/skills/ensemble-code-review/SKILL.md b/plugins/parallel-ai-agents/skills/ensemble-code-review/SKILL.md deleted file mode 100644 index 6708241..0000000 --- a/plugins/parallel-ai-agents/skills/ensemble-code-review/SKILL.md +++ /dev/null @@ -1,322 +0,0 @@ ---- -name: ensemble-code-review -description: | - Claude + Codex 雙 AI 獨立審閱程式碼,交叉比對找共識和盲點。 - 4 Claude teammates(architecture, correctness, security, devils-advocate)+ Codex GPT-5.5 獨立審一遍,最後合成比較表。 - Use when: 程式碼、技術文件、設計文件發布前需要嚴格審閱。 -argument-hint: "FILE_OR_DIR [--focus 'review focus'] e.g. 'src/auth/', 'packages/ocr-swift/ --focus API正確性'" -allowed-tools: - - Read - - Write - - Edit - - Bash - - Grep - - Glob - - Agent - - TeamCreate - - SendMessage - - TaskCreate - - TaskUpdate - - TaskList - - TaskGet - - AskUserQuestion ---- - -# /ensemble-review — Orchestrated Team + Codex 交叉審閱 - -4 個 Claude teammates(orchestrated team)+ 1 個 Codex(gpt-5.5)各自獨立審閱,合成比較表找出共識和盲點。 - -> **原理同 Ensemble OCR**:不同模型、不同角色的錯誤模式不重疊。4 個 Claude 以不同專業角度審閱且互相挑戰,Codex 提供跨模型盲驗。 - -## 審閱架構 - -``` -/ensemble-review FILE_OR_DIR -│ -├── Claude Team(4 teammates,互相挑戰) -│ ├── architecture — 設計模式、API 用法、依賴關係、全局合理性 -│ ├── correctness — 邏輯正確性、bug、edge case、型別安全 -│ ├── security — injection、secrets、權限、輸入驗證(攻擊者視角) -│ └── devils-advocate — 讀前 3 人結論,反駁「通過」判斷 -│ -└── Codex(gpt-5.5,完全獨立 process,跨模型盲驗) - -→ 5 份 findings 合併去重 → 比較表 -``` - -**為什麼 5 個?** -- 4 個 Claude teammates 在同一個 team 裡**互相挑戰**(不是各自獨立報告) -- Devil's Advocate 的工作是**試著證明其他 3 個的通過判斷是錯的** -- Codex 是完全不同的模型家族(gpt-5.5),提供**跨模型盲驗** - -## 執行流程 - -### Phase 0: 解析輸入 - -``` -Arguments: - FILE_OR_DIR — 要審閱的檔案或目錄路徑 - --focus — 審閱重點(可選,如「API正確性」「技術準確性」「安全性」) - -如果沒有 FILE_OR_DIR,問使用者。 -如果 FILE_OR_DIR 是目錄,讀取所有原始碼檔案作為審閱範圍。 -``` - -### Phase 1: 讀取文件 + 準備 context - -1. 讀取目標文件(如果是目錄,列出所有檔案路徑和內容摘要) -2. 自動判斷審閱類型: - -| 檔案類型 | 審閱重點 | -|---------|---------| -| `.md` blog/文章 | 技術準確性、邏輯一致性、聲明可驗證性 | -| `.md` 設計文件 | 架構合理性、邊界情況、可行性、遺漏 | -| `.swift` / `.py` / `.ts` 程式碼 | bug、安全漏洞、效能、API 用法、edge case | -| 目錄(整個 package) | 架構、死碼、API 一致性、依賴管理 | - -3. 準備 context 字串,包含:檔案路徑、內容、focus 指示 - -### Phase 2: 平行啟動 Claude Team + Codex - -**CRITICAL: 所有 tool calls(TeamCreate + Codex Bash)必須在同一個 message 送出。不可分步驟。** - -**CRITICAL: Teammates 必須用 `subagent_type: "general-purpose"`。不可用 `Explore`(Explore 不會主動 SendMessage 回報結果,會直接 idle)。** - -#### 2a. Claude Team(4 reviewers) - -用 TeamCreate 建立 team,然後用 Agent 啟動 4 個 teammates: - -``` -TeamCreate: - name: "ensemble-review-{timestamp}" - description: "Ensemble review for {FILE_OR_DIR}" -``` - -然後在**同一個 message** 啟動 4 個 Agent + 1 個 Codex Bash(共 5 個 tool calls): - -**Agent 1: architecture** -``` -Agent: - name: "architecture" - subagent_type: "general-purpose" - team_name: "ensemble-review-{timestamp}" - subagent_type: "general-purpose" - prompt: | - 你是 Architecture Reviewer。 - 審閱範圍:{FILE_OR_DIR} - {context} - - 你的任務: - 1. 設計模式是否正確(protocol 使用、抽象層級) - 2. API 用法是否符合上游框架的推薦方式 - 3. 依賴關係是否合理(有沒有多餘或缺少的) - 4. 檔案組織是否清晰 - 5. 有沒有死碼或重複實作 - - {focus_instruction} - - 用 Read/Grep/Glob 工具實際去看相關檔案確認。 - 用中文逐點列出問題和建議。每個問題標注嚴重性(HIGH/MEDIUM/LOW)。 - 最後給整體評價(一段話)。 -``` - -**Agent 2: correctness** -``` -Agent: - name: "correctness" - subagent_type: "general-purpose" - team_name: "ensemble-review-{timestamp}" - subagent_type: "general-purpose" - prompt: | - 你是 Correctness Reviewer。 - 審閱範圍:{FILE_OR_DIR} - {context} - - 你的任務: - 1. 邏輯正確性 — 有沒有 bug - 2. Edge cases — null、empty、boundary values - 3. 型別安全 — 隱式轉換、optional handling - 4. 控制流程 — if/else 覆蓋、switch fall-through - 5. 錯誤處理 — 有沒有漏接的 error - - {focus_instruction} - - 用 Read 工具查看完整函數上下文。 - 用中文逐點列出問題和建議。每個問題標注嚴重性(HIGH/MEDIUM/LOW)。 - 最後給整體評價(一段話)。 -``` - -**Agent 3: security** -``` -Agent: - name: "security" - subagent_type: "general-purpose" - team_name: "ensemble-review-{timestamp}" - subagent_type: "general-purpose" - prompt: | - 你是 Security Reviewer,以攻擊者視角審閱。 - 審閱範圍:{FILE_OR_DIR} - {context} - - 你的任務: - 1. Injection 風險(SQL、command、path traversal) - 2. Hardcoded secrets(API keys、passwords、tokens) - 3. 權限檢查(有沒有繞過的可能) - 4. 輸入驗證(external data 是否被信任) - 5. 敏感資訊洩漏(error message、log) - - {focus_instruction} - - 用 Grep 搜尋可疑模式(hardcoded strings、eval、exec 等)。 - 用中文逐點列出問題和建議。每個問題標注嚴重性(HIGH/MEDIUM/LOW)。 - 最後給整體評價(一段話)。 -``` - -**Agent 4: devils-advocate** -``` -Agent: - name: "devils-advocate" - subagent_type: "general-purpose" - team_name: "ensemble-review-{timestamp}" - subagent_type: "general-purpose" - prompt: | - 你是 Devil's Advocate。 - 審閱範圍:{FILE_OR_DIR} - {context} - - 你的任務:等其他 3 個 reviewer(architecture、correctness、security)完成後, - 用 SendMessage 詢問他們的結論,然後**試著反駁每一個「通過」或「LOW」的判斷**。 - - 步驟: - 1. 用 SendMessage 分別問 architecture、correctness、security 他們的 findings - 2. 對每個「通過」的判斷,找理由說它其實有問題 - 3. 對每個「LOW」的判斷,論證為什麼應該是 MEDIUM 或 HIGH - 4. 如果你找不到反駁的理由,才承認確實通過 - - 這是對抗性驗證 — 你的存在是為了防止群體盲點。 - 用中文輸出你的反駁結果。 -``` - -#### 2b. Codex(背景執行 — 直接 HTTP,繞過 codex CLI subprocess) - -```bash -codex-call \ - --output "{output_file}" \ - --model gpt-5.5 \ - --effort xhigh \ - --service-tier fast \ - --max-time 600 \ - --instructions "你是嚴謹的程式碼審閱者,用中文輸出。" << 'EOF' -{codex_prompt} -EOF -``` - -> **為什麼不用 `codex exec`**:subprocess 偶爾會 hang(stdin/stdout pipe 互鎖、tty 問題),等 10 分鐘 timeout 才能繼續。`codex-call` 是 plugin 自帶 wrapper(`bin/codex-call`,Swift script,安裝時自動加入 PATH),直接 HTTP POST 到 `chatgpt.com/backend-api/codex/responses`,仍走你的 ChatGPT 訂閱 OAuth — 但 `--max-time` 是硬性保證,不會 hang。 -> -> **Fast mode**:傳 `--service-tier fast`,wrapper 內部會翻譯成 backend 接受的 `priority`(codex CLI 內部也是這樣翻譯)。Fast = 1.5× 速度、2.5× credit(gpt-5.5);ensemble 場景值得,因為 user 在等。 -> -> **OAuth token**:wrapper 自動讀 `~/.codex/auth.json`(codex CLI 的同一份),到期前 5 分鐘自動 refresh,用 file lock 避免 ensemble 平行 race。 - -Codex prompt 應包含: -- 審閱範圍和 focus -- 要求逐點分析,標注嚴重性 -- 用中文回答 -- **不提及 Claude team 的存在**(確保獨立性) - -### Phase 3: 收集結果 - -1. 等待 4 個 Claude teammates 完成(透過自動訊息通知) -2. 等待 Codex 完成(輪詢 status) -3. 如果 Codex 失敗或超時(>10 分鐘),跳過,標注「Codex 不可用」 - -`codex-call` 完成後輸出會寫入 `--output` 指定的檔案,直接用 Read 讀取即可。 - -### Phase 4: 合併去重 + 交叉比對 - -由主 session 的 Claude 讀取所有結果,產出比較表: - -1. **去重**:相同檔案 + 相似描述 → 合併,標註來源 `[team:architecture+codex]` -2. **severity 以最高為準**:如果 correctness 說 MEDIUM 但 codex 說 HIGH → HIGH -3. **Devil's Advocate 的反駁如果成立** → 升級 severity - -輸出格式: - -```markdown -## Ensemble Review: {FILE_OR_DIR} - -### 審閱者 -- **Claude Team**: architecture, correctness, security, devils-advocate(orchestrated) -- **Codex GPT-5.5**: 獨立盲驗 - -### 共識(≥2 個來源都指出) -| # | 問題 | 嚴重性 | 來源 | 說明 | -|---|------|--------|------|------| -| 1 | ... | HIGH | team:arch+correct+codex | ... | - -### 僅 Claude Team 指出 -| # | 問題 | 嚴重性 | 來源 | 說明 | -|---|------|--------|------|------| -| 1 | ... | ... | team:security | ... | - -### 僅 Codex 指出 -| # | 問題 | 嚴重性 | 說明 | -|---|------|--------|------| -| 1 | ... | ... | ... | - -### Devil's Advocate 反駁結果 -| # | 原始判斷 | 反駁 | 成立? | -|---|---------|------|--------| -| 1 | correctness: LOW | 「其實是 MEDIUM 因為...」 | ✅ 升級 | -| 2 | security: 通過 | 「未能反駁」 | ❌ 維持 | - -### 衝突(來源間意見矛盾) -| # | 議題 | Claude Team | Codex | 建議 | -|---|------|------------|-------|------| -| 1 | ... | ... | ... | 交由使用者判斷 | - -### Summary -- 共識問題: N 個(最需要修) -- 僅 Claude Team: M 個 -- 僅 Codex: K 個 -- Devil's Advocate 升級: L 個 -- 衝突: J 個 - -### 建議修改優先順序 -1. {highest priority fix} -2. ... -``` - -### Phase 5: 詢問下一步 - -``` -審閱完成。要怎麼做? -1. 根據共識問題修改文件 -2. 只看不改(純審閱) -3. 針對特定問題深入討論 -``` - -## Codex CLI 參考 - -```bash -# companion script 路徑(優先 marketplace,fallback cache) -CODEX_SCRIPT="$HOME/.claude/plugins/marketplaces/openai-codex/plugins/codex/scripts/codex-companion.mjs" - -# 啟動 task -node "$CODEX_SCRIPT" task --effort high "prompt" - -# 查狀態 -node "$CODEX_SCRIPT" status --all - -# 取結果 -node "$CODEX_SCRIPT" result $JOB_ID -``` - -## 鐵律 - -- **5 個 tool calls 在同一個 message 送出**(4 Agent + 1 Bash codex)。不可分步驟。 -- **Codex 看不到 Claude Team 的討論**。它是完全獨立的盲驗。 -- **Codex 的審稿結果原封不動呈現**,不要修改或摘要。 -- **交叉比對由主 session 的 Claude 做**,因為主 session 有完整 context。 -- **共識問題 > 單方問題**:多個來源都指出的問題最需要修。 -- **衝突不自動裁決**:呈現給使用者判斷。 -- **Devil's Advocate 是必要的**。防止 3 個 reviewer 的群體盲點。 diff --git a/plugins/parallel-ai-agents/skills/ensemble-lecture-review/SKILL.md b/plugins/parallel-ai-agents/skills/ensemble-lecture-review/SKILL.md deleted file mode 100644 index 31dbafb..0000000 --- a/plugins/parallel-ai-agents/skills/ensemble-lecture-review/SKILL.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -name: ensemble-lecture-review -description: | - 教學講義 Ensemble 審閱:4 個 Claude teammates(教學審閱角色)各自獨立審閱講義品質。 - 當用戶提到「review 講義」「審閱講義」「講義品質」「lecture review」時使用。 -argument-hint: "[講義 HTML 路徑] [--srt 對應逐字稿路徑]" ---- - -# /ensemble-lecture-review — 教學講義 Ensemble 審閱 - -4 個 Claude teammates 各自獨立審閱講義,合成比較表找共識和盲點。 - -> **與 ensemble-academic-review 的差異**:角色從「學術論文審閱」改為「教學材料審閱」。不查文獻真偽,改查逐字稿覆蓋率。 - -## 審閱架構 - -``` -/ensemble-lecture-review lecture.html [--srt transcript.srt] -│ -├── Claude Team(4 teammates) -│ ├── content-accuracy — 內容正確性(統計、理論、公式) -│ ├── student-readability — 學生可讀性(白話程度、邏輯銜接) -│ ├── completeness — 完整性(對照逐字稿找遺漏) -│ └── devils-advocate — 反駁前三個的判斷 -│ -└── 比較表 + 修改建議 -``` - -## 執行流程 - -### Phase 0: 解析輸入 - -``` -Arguments: - FILE — 講義 HTML 檔案路徑 - --srt — 對應的 SRT 逐字稿(可選,completeness reviewer 會用) - -如果沒有 FILE,掃描 handout 目錄列出所有講義讓用戶選。 -如果沒有 --srt,嘗試從 lectures/ 目錄自動匹配。 -``` - -### Phase 1: 讀取文件 + 準備 context - -1. 讀取講義 HTML 全文 -2. 如果有 SRT,讀取逐字稿全文 -3. 讀取 teaching.json(了解學生背景) -4. 準備 context 字串 - -### Phase 2: 平行啟動 4 個 Claude Teammates - -**CRITICAL: 所有 4 個 Agent tool calls 必須在同一個 message 送出。** - -**CRITICAL: Teammates 必須用 `subagent_type: "general-purpose"`。** - -用 TeamCreate 建立 team,然後在同一個 message 啟動 4 個 Agent: - -#### Agent 1: content-accuracy - -``` -你是 Content Accuracy Reviewer,專門檢查教學講義的知識正確性。 -審閱講義:{FILE} -{context} - -學生背景:{student_info} - -你的任務: -1. **統計概念**:定義是否正確(p-value、power、effect size、confidence interval) -2. **公式**:數學符號有沒有寫錯(KaTeX 語法是否正確) -3. **理論解釋**:心理學理論的描述是否準確(Higgins, Regulatory Focus/Fit) -4. **因果推論**:有沒有把相關說成因果、或過度推論 -5. **術語一致性**:同一個概念在不同地方是否用同一個名稱 -6. **範例正確性**:舉的例子是否恰當地支持概念 - -用 Read 工具讀取講義確認。 -逐點列出問題,每個標注嚴重性(HIGH/MEDIUM/LOW)。 -引用具體的段落或句子。 -``` - -#### Agent 2: student-readability - -``` -你是 Student Readability Reviewer,從學生的角度審閱講義的易懂程度。 -審閱講義:{FILE} -{context} - -學生背景:{student_info}(注意:學生零程式基礎,概念理解可能表面化) - -你的任務: -1. **白話程度**:有沒有用了專業術語但沒解釋的地方 -2. **邏輯銜接**:段落之間的跳躍是否太大(學生能不能跟上) -3. **具體例子**:抽象概念有沒有搭配具體例子 -4. **視覺輔助**:表格、圖表是否幫助理解(還是增加混淆) -5. **篇幅平衡**:重要概念是否得到足夠篇幅(vs 次要內容佔太多) -6. **結構導航**:學生能不能快速找到想看的段落(標題是否清楚) -7. **前後呼應**:「重點整理」是否真的涵蓋了最重要的內容 - -用 Read 工具讀取講義。 -站在學生的角度思考:「如果我是零基礎的學生,讀到這裡我會卡住嗎?」 -逐點列出問題,每個標注嚴重性(HIGH/MEDIUM/LOW)。 -``` - -#### Agent 3: completeness - -``` -你是 Completeness Reviewer,檢查講義是否完整覆蓋了上課教的內容。 -審閱講義:{FILE} -{context} - -{srt_instruction} - -你的任務: -1. **逐字稿覆蓋率**:逐字稿裡有教但講義沒寫到的重點(最重要) -2. **結構完整性**: - - 有沒有「重點整理」section - - 有沒有「課後作業」section - - h2/h3 層級是否正確(沒有孤立的 h3) - - h2 之間有沒有 --- 分隔 -3. **KaTeX/Mermaid**:有數學符號的地方有沒有加 KaTeX CDN?有路徑圖的地方有沒有用 Mermaid 或 ASCII art? -4. **連結有效性**:href 指向的檔案是否存在 -5. **缺少的教學元素**: - - 有沒有該有 blockquote 提醒但沒有的地方 - - 有沒有該用表格對比但只用文字描述的地方 - -如果有 SRT,分段讀(每次 200 行),逐段對照講義找遺漏。 -沒有 SRT 就只做結構完整性檢查。 -逐點列出問題,每個標注嚴重性(HIGH/MEDIUM/LOW)。 -``` - -SRT instruction(有 SRT 時注入): -``` -逐字稿路徑:{SRT_PATH} -請用 Read 工具分段讀取逐字稿(每次 200 行),逐段對照講義。 -如果逐字稿裡有教學重點但講義沒寫到,標記為 HIGH。 -``` - -#### Agent 4: devils-advocate - -``` -你是 Devil's Advocate,教學講義審閱的對抗性驗證者。 -審閱講義:{FILE} -{context} - -你的任務:等其他 3 個 reviewer 完成後,用 SendMessage 詢問他們的結論, -然後試著反駁每一個「通過」或「LOW」的判斷。 - -步驟: -1. 先用 Read 工具讀取講義,形成自己的理解 -2. 用 SendMessage 分別問 content-accuracy、student-readability、completeness 他們的 findings -3. 對每個「通過」的判斷,找理由說它其實有問題: - - content-accuracy 說概念正確 → 找邊界情況或過度簡化 - - student-readability 說易懂 → 找可能讓特定背景學生困惑的地方 - - completeness 說完整 → 找隱含的教學目標是否達成 -4. 特別挑戰: - - 「重點整理」是否真的是重點,還是只是把小標題抄了一遍 - - 表格是否真的幫助理解,還是增加認知負擔 - - 課後作業是否可執行,學生知不知道具體要做什麼 -5. 如果找不到反駁的理由,才承認確實通過 - -用中文輸出反駁結果。 -``` - -### Phase 3: 收集結果 - -等待 4 個 teammates 完成(透過自動訊息通知)。 - -### Phase 4: 合併去重 - -由主 session 的 Claude 讀取所有結果,產出比較表: - -```markdown -## Ensemble Lecture Review: {FILE} - -### 比較表 -| # | 問題 | 嚴重性 | 來源 | 位置 | -|---|------|--------|------|------| -| 1 | Power 的定義缺少直覺解釋 | HIGH | content-accuracy, student-readability | 統計概念 section | -| 2 | h3 「OpenClaw」沒有 h2 父層 | HIGH | completeness | AI 工具比較 section | -| 3 | ... | ... | ... | ... | - -### 共識問題(多個 reviewer 都指出) -... - -### Devil's Advocate 升級的問題 -... - -### 統計 -- content-accuracy: N 個問題(H: x, M: y, L: z) -- student-readability: N 個問題 -- completeness: N 個問題 -- devils-advocate 升級: N 個 -``` - -### Phase 5: 詢問下一步 - -``` -審閱完成。要怎麼做? -1. 修正 HIGH 問題 -2. 只看不改 -3. 針對特定問題深入討論 -4. 用 /teaching-toolkit:lecture-enrich 充實講義 -``` - -## 鐵律 - -- **4 個 tool calls 在同一個 message 送出**。不可分步驟。 -- **completeness reviewer 必須讀逐字稿**(如果有提供)。不可跳過。 -- **共識問題 > 單方問題**:多個 reviewer 都指出的問題最需要修。 -- **Devil's Advocate 是必要的**。防止群體盲點。 -- **考慮學生背景**:所有 reviewer 都要知道學生的程度和目標。