Files
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
49 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "bzhenbot/bytedance-werewolf", "files": {"shared/api-contract.md": {"1b4a1a22a8b74877191f9618beab0fc89a9252a1": {"t": 1786068494.9432654, "n": 23486, "h": "5ce6e73048d37557", "c": "# shared/api-contract.md\n\n前后端接口共享契约。此文件是前后端协同开发的唯一真相源(Single Source of Truth)。\n\n---\n\n## 一、数据结构\n\n### GameState\n\n完整的对局状态,通过 `GET /api/games/{game_id}` 返回。\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| game_id | string | 对局唯一标识 |\n| phase | string | 当前阶段:night / day / speech / discussion / vote / hunter_shoot / judgement / game_over |\n| subphase | string\\|null | 夜晚子阶段:werewolf_action / seer_action / witch_action / guard_action / night_resolve / null |\n| day | integer | 当前天数(从 1 开始) |\n| round | integer | 当前轮次 |\n| status | string | 对局状态:waiting / running / finished |\n| winner | string\\|null | 获胜方:werewolf / villager / null(未结束) |\n| votes | object | 公开投票流向,格式 `{ voter_player_id: target_player_id }`;弃票记录为 `abstain` |\n| vote_results | object | 公开票数聚合,格式 `{ target_player_id: count }` |\n| abstain_count | integer | 当前弃票数;不计入任何玩家得票 |\n| pending_human_action | object\\|null | 若当前轮到 Human 席位行动,返回待提交动作描述(玩家、阶段、可选动作、合法目标) |\n| players | PlayerState[] | 玩家列表 |\n| recent_events | GameEvent[] | 最近事件列表 |\n| debug | DebugInfo\\|null | 调试信息(仅在 debug 模式下返回) |\n\n### PlayerState\n\n单个玩家状态。\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| player_id | string | 玩家唯一标识 |\n| name | string | 玩家名称 |\n| seat | integer | 座位号 |\n| role | string\\|null | 角色(仅 debug 模式返回,public 模式下为 null) |\n| alive | boolean | 是否存活 |\n| player_type | string | `ai` 或 `human` |\n| is_ai | boolean | 是否为 AI 玩家 |\n| status | string | 状态:active / eliminated / spectating |\n| public_claim | string\\|null | 公开声称的角色(玩家发言中声称的身份) |\n\n**信息隔离规则**:\n- `public` 模式:`role` 字段必须为 `null`\n- `player` 模式:仅当前玩家的 `role` 可见\n- `debug` 模式:所有角色可见\n\n### PublicGameState\n\n普通观众主界面使用的公开状态,通过 `GET /api/games/{game_id}/public-state` 返回。前端游戏桌 UI 应优先消费此结构,而不是 debug/raw 状态。\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| game_id | string | 对局唯一标识 |\n| status | string | 对局状态:running / finished |\n| phase | string | 当前阶段;猎人被投票放逐后可进入 `hunter_shoot` |\n| subphase | string\\|null | 夜晚子阶段 |\n| day | integer | 当前天数 |\n| day_count | integer | 当前天数,兼容旧前端字段 |\n| round | integer | 当前轮次 |\n| speed | integer | 播放倍速,默认 1 |\n| is_paused | boolean | 是否暂停 |\n| players | PublicPlayerState[] | 公开玩家列表,固定 12 人局时包含 12 项 |\n| alive_players | string[] | 存活玩家 ID |\n| eliminated_players | string[] | 已出局玩家 ID |\n| current_speaker | string\\|null | 当前发言玩家 |\n| current_actor | string\\|null | 兼容旧前端字段;若存在 `pending_human_action`,通常指向该 Human 玩家 |\n| discussion_topics | object[] | `discussion` 阶段由首轮发言提炼出的 topic 列表(`speaker/target/topic/intent`) |\n| discussion_participants | object[] | `discussion` 阶段的微回合顺序(challenge/defense/rebuttal),每项含 `player_id/target/topic/intent/discussion_type/turn_id/status` |\n| discussion_max_messages | integer | 自由讨论总消息上限(默认 8) |\n| current_action | string\\|null | 当前公开动作描述 |\n| key_event | string\\|null | 最近关键公开事件 |\n| public_events | GameEvent[] | 公开事件 |\n| speech_log | object[] | 当前发言日的公开发言流 |\n| current_speech_day | integer | 当前发言流聚焦的天数,用于避免主屏显示旧日发言 |\n| votes | object | 公开投票流向,格式 `{ voter_player_id: target_player_id }`;弃票记录为 `abstain` |\n| vote_results | object | 公开票数聚合,格式 `{ target_player_id: count }` |\n| vote_result | object\\|null | 当前投票聚合,空时为 null |\n| abstain_count | integer | 当前弃票数;不计入任何玩家得票 |\n| pending_hunter_shooter | string\\|null | `hunter_shoot` 阶段待开枪猎人;public 仅用于展示阶段动作,不暴露隐藏身份 |\n| pending_human_action | object\\|null | Human 席位待行动描述(`player_id/phase/subphase/allowed_actions/legal_targets/day_count`,discussion 额外含 `discussion_target/discussion_type/discussion_intent/discussion_topic/discussion_turn_id`) |\n| revealed_deaths | object[] | 已公开死亡玩家与公开身份 |\n| game_over | boolean | 游戏是否结束 |\n| winner | string\\|null | 获胜方 |\n| summary | object\\|null | 局后战报,未生成时为 null |\n| debug | null | 普通公开状态永远为 null |\n\n### GameSummary\n\n局后战报对象。当前实现为启发式生成,不依赖 LLM。\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| winner | string\\|null | 获胜方:werewolf / good / null |\n| key_turning_points | string[] | 关键转折点 |\n| mvp | object\\|null | MVP 玩家与理由 |\n| scapegoat | object\\|null | 背锅侠玩家与理由 |\n| best_speech | object\\|null | 最佳发言 |\n| decisive_vote | object\\|null | 关键投票 |\n| role_performance | object[] | 玩家角色表现摘要 |\n| timeline_summary | string | 一句话复盘 |\n| share_text | string | 可复制复盘文本 |\n\n### PublicPlayerState\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| id | string | 玩家 ID |\n| player_id | string | 兼容旧前端字段,等于 id |\n| seat | integer | 座位号 |\n| name | string | 座位名,如 P1 |\n| display_name | string | AI 玩家昵称 |\n| avatar_key | string | 头像键 |\n| title | string | 称号 |\n| personality | string | 人格标签 |\n| speech_style | string | 发言风格 |\n| short_bio | string | 简短人设说明 |\n| player_type | string | `ai` 或 `human` |\n| is_alive | boolean | 是否存活 |\n| alive | boolean | 兼容旧前端字段,等于 is_alive |\n| role_revealed | boolean | 身份是否已公开 |\n| public_role | string\\|null | 公开身份;未公开时为 null |\n| role | string\\|null | 兼容旧前端字段;未公开时为 null |\n| status_tags | string[] | 公开状态标签 |\n| suspicion_level | number\\|null | 观众态可见怀疑度,暂无时为 null |\n| vote_target | string\\|null | 当前公开投票目标 |\n| is_current_speaker | boolean | 是否正在发言 |\n| is_ai | boolean | 是否 AI 玩家 |\n| status | string | active / eliminated |\n| public_claim | string\\|null | 公开跳身份声明,暂无时为 null |\n\n**PublicGameState 信息隔离规则**:\n- 未出局且游戏未结束的玩家:`role` 和 `public_role` 必须为 `null`。\n- 已出局玩家可以公开 `public_role`。\n- 不返回 `true_state`、`wolf_team`、`seer_results`、女巫药品状态、夜间刀口、守卫目标、狼人夜间票型等私密字段。\n- Debug/raw 状态只能通过 debug 视角或 debug 接口显式获取,不作为普通观战主数据源。\n\n### GameEvent\n\n游戏事件。\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| event_id | string | 事件唯一标识 |\n| game_id | string | 所属对局 |\n| turn | integer | 轮次 |\n| day | integer | 天数 |\n| phase | string | 发生阶段 |\n| type | string | 事件类型(见下方枚举) |\n| actor_id | string\\|null | 触发者 ID |\n| visibility | string | 可见性:public / private / debug |\n| payload | object | 事件负载(根据 type 不同而变化) |\n| created_at | string | ISO 时间戳 |\n\n**event type 枚举**:\n\n| type | 说明 |\n|------|------|\n| game_created | 游戏创建 |\n| phase_changed | 阶段变化 |\n| speech | 公开发言 |\n| vote | 投票 |\n| vote_result | 投票结算,包含 `vote_counts`、`abstain_count`、`eliminated`、`result_type` |\n| vote_tie | 平票或无有效票提示 |\n| hunter_shoot | 猎人被投票放逐后开枪或放弃开枪 |\n| night_action | 夜晚行动 |\n| night_end | 夜晚结算完成;public 仅包含死亡结果,debug/god 视角可包含 `night_actions` |\n| day_opening | 白天开场摘要,用于说明昨夜结果与当天讨论起点 |\n| vote_rally | 归票摘要,用于投票前收束当天讨论焦点 |\n| wolf_discussion_message | 狼人夜聊单条意见(仅狼人私有或 debug/god 可见) |\n| death | 玩家死亡 |\n| memory_write | Agent Memory 写入 |\n| fallback | Agent 降级/回退事件 |\n| system | 系统事件 |\n\n**visibility 枚举**:\n\n| visibility | 说明 |\n|------|------|\n| public | 所有观众可见 |\n| private | 仅特定玩家可见 |\n| debug | 仅调试模式可见 |\n\n### DebugInfo\n\n调试信息(仅 debug 视角)。\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| fallback_count | integer | Fallback 事件总数 |\n| memory_write_count | integer | Memory 写入次数 |\n| agent_action_count | integer | Agent 行动次数 |\n| warnings | string[] | 警告信息列表 |\n| last_agent_action | object\\|null | 最近一次 Agent 行动详情 |\n\n---\n\n## 二、REST API 接口\n\n### 1. 创建对局\n\n```\nPOST /api/games\n```\n\n**Request Body**:\n\n```json\n{\n \"player_count\": 12,\n \"mode\": \"ai_only\",\n \"roles\": [\"werewolf\", \"seer\", \"witch\", \"villager\", \"villager\", \"villager\"],\n \"agent_ids\": [\"agent_linzhou_001\", \"agent_xuran_002\"],\n \"human_player_ids\": [\"player_1\"],\n \"role_assignment_mode\": \"random\",\n \"allow_duplicates\": false\n}\n```\n\n**Response** (201):\n\n```json\n{\n \"game_id\": \"game_001\",\n \"phase\": \"night\",\n \"day\": 1,\n \"players\": [],\n \"agent_ids\": [\"agent_linzhou_001\", \"agent_xuran_002\"],\n \"player_types\": {\n \"player_1\": \"human\",\n \"player_2\": \"ai\"\n },\n \"human_player_ids\": [\"player_1\"],\n \"agent_selection_warnings\": []\n}\n```\n\n**说明**:当前仅支持 `player_count: 9` 或 `12`;`mode` 字段预留(目前仅 ai_only 模式);`roles` 字段被接收但当前使用默认角色分配(自定义 roles 功能待实现)。`agent_ids` 为空时由后端从 Agent 库自动补齐;数量不足会补齐并在 `agent_selection_warnings` 说明;数量超出、重复或不存在时返回 400。`human_player_ids` 可选,未传时默认全 AI。旧接口 `/api/games/start` 保留兼容并支持同样请求体。\n\n### 1a. Agent 库\n\n```\nGET /api/agents\nGET /api/agents/{agent_id}\n```\n\n`GET /api/agents` 返回可参赛 Agent 列表:\n\n```json\n{\n \"agents\": [\n {\n \"agent_id\": \"agent_linzhou_001\",\n \"name\": \"林舟\",\n \"avatar\": \"🧭\",\n \"persona\": \"冷静分析位\",\n \"description\": \"重视票型和逻辑。\",\n \"speech_style\": \"先结论后证据\",\n \"strategy_tags\": [\"analysis\"],\n \"default_profile\": {},\n \"enabled\": true\n }\n ]\n}\n```\n\n`seat/player_id/agent_id/role` 关系:`seat` 和 `player_id` 只在本局有效;`agent_id` 是跨局身份,profile 与长期 memory 跟随 `agent_id`;`role` 每局由规则随机或配置分配,不与 `agent_id` 固定绑定。\n\n### 2. 获取对局状态\n\n```\nGET /api/games/{game_id}\n```\n\n**Query Parameters**:\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| view_mode | string | 否 | 视角模式:public(默认)/ player / debug |\n| viewer_id | string | 否 | 当 view_mode=player 时指定玩家 ID |\n\n**Response** (200):返回 `GameState` 对象。\n\n### 2a. 获取公开观战状态\n\n```\nGET /api/games/{game_id}/public-state\n```\n\n**Response** (200):返回 `PublicGameState` 对象。\n\n**说明**:普通前端游戏桌和观众互动 UI 应使用此接口;该接口不接受 `view_mode`,不会返回隐藏身份、夜间私密行动或 debug/raw 状态。\n\n### 3. 推进一步\n\n```\nPOST /api/games/{game_id}/step\n```\n\n推进游戏一个阶段(night → day/speech → vote → hunter_shoot(仅投票放逐猎人时)→ judgement → next night)。\n\n**Response** (200):返回更新后的 `GameState`。\n\n**说明**:已实现。旧接口 `/api/games/{game_id}/advance` 保留兼容,新代码建议使用标准契约接口 `/step`。\n\n### 3a. 暂停对局\n\n```\nPOST /api/games/{game_id}/pause\n```\n\n**Response** (200):返回更新后的 `PublicGameState`,其中 `is_paused: true`。\n\n### 3b. 继续对局\n\n```\nPOST /api/games/{game_id}/resume\n```\n\n**Response** (200):返回更新后的 `PublicGameState`,其中 `is_paused: false`。\n\n### 3c. 设置倍速\n\n```\nPOST /api/games/{game_id}/speed\n```\n\n**Request Body**:\n\n```json\n{ \"speed\": 4 }\n```\n\n**Response** (200):返回更新后的 `PublicGameState`。\n\n**说明**:`speed` 仅支持 `1 / 2 / 4 / 8`。\n\n### 3d. 重新开局\n\n```\nPOST /api/games/{game_id}/restart\n```\n\n**Response** (200):返回新对局的 `PublicGameState`。新对局保留原人数,默认 `speed: 1`、`is_paused: false`。\n\n### 3e. Human 席位提交行动\n\n```\nPOST /api/games/{game_id}/human/action\n```\n\n**Request Body**:\n\n```json\n{\n \"player_id\": \"player_1\",\n \"action_type\": \"vote\",\n \"target_id\": \"player_3\",\n \"message\": \"我先投给 P3\",\n \"discussion_type\": \"challenge\",\n \"intent\": \"question\",\n \"topic\": \"P3 在主发言中未给出明确投票意图\",\n \"discussion_turn_id\": \"day1_topic1_challenge_1\"\n}\n```\n\n**Response** (200):\n\n```json\n{\n \"success\": true,\n \"game_id\": \"game_001\",\n \"phase\": \"vote\",\n \"player_id\": \"player_1\",\n \"action\": \"vote\",\n \"target\": \"player_3\",\n \"pending_human_action\": null\n}\n```\n\n**说明**:仅 `player_type=human` 可调用。后端会按当前阶段校验合法动作:\n- `night`:狼人 `kill/pass`,预言家 `check/pass`,女巫 `heal/poison/pass`,守卫 `guard/pass`\n- `speech`:`speak/pass`\n- `discussion`:`discuss/speak/pass`(推荐 `discuss`,并携带 `intent/topic/discussion_turn_id`)\n- `vote`:`vote/pass`\n- `hunter_shoot`:`shoot/pass`\n\n### 4. 获取事件流\n\n```\nGET /api/games/{game_id}/events\n```\n\n**Query Parameters**:\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| visibility | string | 否 | 过滤可见性:public / private / debug;默认 public |\n| limit | integer | 否 | 返回条数限制,默认 50 |\n\n**Response** (200):\n\n```json\n{\n \"game_id\": \"game_001\",\n \"events\": [ /* GameEvent[] */ ]\n}\n```\n\n**当前状态**:已实现 — 见 `backend/src/api/app.py` 中的 `get_game_events` 路由。\n\n**信息隔离**:未显式传入 `visibility=debug` 时,该接口只返回 public-safe 事件;`memory_write`、`fallback`、`role_assignment`、`night_action` 等审计/私有事件不会进入默认事件流,非 debug 视角会递归移除 `role`、`private_state`、`night_actions` 等敏感 payload 字段。\n\n### 5. 获取 Debug 信息\n\n```\nGET /api/games/{game_id}/debug\n```\n\n**Response** (200):返回 `DebugInfo` 对象。\n\n**当前状态**:已实现 — 见 `backend/src/api/app.py` 中的 `get_game_debug` 路由。\n\n### 6. 获取局后战报\n\n```\nGET /api/games/{game_id}/summary\n```\n\n**Response** (200):\n\n```json\n{\n \"game_id\": \"game_001\",\n \"summary\": {\n \"winner\": \"good\",\n \"key_turning_points\": [\"投票阶段淘汰了 P1 林舟\"],\n \"mvp\": { \"player_id\": \"player_5\", \"name\": \"P5 沈星\", \"reason\": \"...\" },\n \"scapegoat\": null,\n \"best_speech\": null,\n \"decisive_vote\": null,\n \"role_performance\": [],\n \"timeline_summary\": \"本局进行到第 2 天,好人阵营。\",\n \"share_text\": \"AI狼人杀复盘...\"\n }\n}\n```\n\n**说明**:普通 `PublicGameState.summary` 仅在 `game_over: true` 或已经能判定胜方时返回;未结束时为 `null`。\n\n---\n\n## 三、WebSocket 接口\n\n### 连接\n\n```\nws://localhost:8000/ws/games/{game_id}\n```\n\n### 客户端 → 服务端消息\n\n```json\n{ \"action\": \"subscribe\", \"view_mode\": \"public\", \"viewer_id\": null }\n{ \"action\": \"get_state\" }\n```\n\n### 服务端 → 客户端消息\n\n```json\n{\n \"event_type\": \"state_update | game_start | phase_change | night_action | speech | discussion | vote | game_end | initial_state | wolf_discussion\",\n \"game_id\": \"game_001\",\n \"data\": { /* 根据 event_type 不同的负载 */ },\n \"timestamp\": 1716934800.123\n}\n```\n\n常用事件 payload:\n\n| event_type | payload 字段 |\n|------|------|\n| phase_change | `phase`、`subphase`、`previous_subphase`、`message` |\n| discussion | `player_id`、`content`、`target`、`discussion_type`、`intent`、`topic`、`discussion_turn_id` |\n| vote | `voter`、`target`、`is_abstain`、`vote_results`、`abstain_count` |\n| night_end | `eliminated`、`phase`、`day_count`、`message`;debug/god 可额外包含 `night_actions` |\n| system/day_opening | `message`、`event_kind: \"day_opening\"`、`day_count`、`eliminated` |\n| system/vote_rally | `message`、`event_kind: \"vote_rally\"`、`day_count` |\n\n`night_actions` 调试负载字段:\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| kill_target | string\\|null | 狼人最终刀口 |\n| heal_target | string\\|null | 女巫解药目标 |\n| healed_target | string\\|null | 实际被解药救下的刀口,仅存在时返回 |\n| poison_target | string\\|null | 女巫毒药目标 |\n| guard_target | string\\|null | 守卫守护目标 |\n| seer_target | string\\|null | 预言家查验目标 |\n| wolf_votes | object | 狼人夜间刀人投票,仅 debug/god 可见 |\n\n---\n\n## 四、迁移说明\n\n当前后端实际接口与契约定义的差异:\n\n| 契约接口 | 实际接口 | 差异 | 优先级 |\n|------|------|------|------|\n| POST /api/games | POST /api/games | 已实现,路由名一致 | 仅支持 9/12 人,roles/mode 待扩展 |\n| POST /api/games/{id}/step | POST /api/games/{id}/step | 已实现,路由名一致,返回 GameState | - |\n| POST /api/games/{id}/human/action | POST /api/games/{id}/human/action | 已实现;仅 Human 席位可调用 | 最小人机混战闭环 |\n| GET /api/games/{id}/events | GET /api/games/{id}/events | 已实现,路由名一致 | - |\n| GET /api/games/{id}/debug | GET /api/games/{id}/debug | 已实现,路由名一致 | - |\n\n迁移策略:所有契约接口路由已实现。旧接口(`/start`、`/advance`)保留向后兼容。`roles`/`mode` 等高级参数待后续阶段支持。\n\n---\n\n## 五、History API(比赛交付新增)\n\n### `GET /api/history/games`\n\n查询参数:`limit`、`offset`、`status`、`winner`、`include_test_data`、`real_llm_only`。\n\n`status=completed` 会在后端兼容映射为内部完成态 `finished`,用于前端首屏和 History 默认查询最近真实 LLM 完成局。\n\n默认行为:\n- `include_test_data=false`:隐藏测试对局、异常测试局和测试 Agent 对局。\n- `real_llm_only=false`:返回真实/FakeLLM 对局;前端 History 主视图默认传 `real_llm_only=true`,优先展示真实 LLM 对局。\n\n返回:\n\n```json\n{\n \"games\": [\n {\n \"game_id\": \"abc123\",\n \"status\": \"finished\",\n \"player_count\": 12,\n \"winner\": \"good\",\n \"win_reason\": \"所有狼人已被消灭\",\n \"current_day\": 3,\n \"current_phase\": \"game_over\",\n \"started_at\": \"2026-05-26T10:00:00\",\n \"ended_at\": \"2026-05-26T10:05:00\",\n \"event_count\": 120,\n \"config\": {\n \"use_real_llm\": true,\n \"model_name\": \"deepseek-v4-flash\",\n \"agent_ids\": [\"agent_linzhou_001\"]\n }\n }\n ],\n \"limit\": 50,\n \"offset\": 0,\n \"include_test_data\": false,\n \"real_llm_only\": true\n}\n```\n\n### `GET /api/history/games/{game_id}`\n\n返回数据库中的 game metadata、summary,以及当前进程仍保留时的 players 调试信息。响应包含 `game_agent_assignments`,用于记录本局 `seat/player_id/agent_id/display_name` 绑定;仅 `visibility=debug` 时返回 `assigned_role`。\n\n### `GET /api/history/games/{game_id}/events`\n\n查询参数:`visibility=public|player|debug`、`player_id`、`phase`、`event_type`、`limit`、`offset`。\n\n隔离规则:\n\n- `public` 仅返回 `visibility=public` 事件。\n- `player` 返回 public + 该 `player_id` 可见的 player/private 事件。\n- `debug` 返回完整事件。\n\n### `GET /api/history/games/{game_id}/decisions`\n\n查询参数:`visibility=public|player|debug`、`agent_id`、`phase`、`action_source`、`limit`、`offset`。\n\n返回 `agent_decision` 事件,payload 包含 `policy_suggestion`、`llm_raw_output`、`final_action`、`action_source`、`repair_reason`、`fallback_reason`、`fallback_used`、`memory_ids_used`。\n\n信息隔离:默认 `visibility=public`,返回空决策集;只有显式 `visibility=debug` 时返回完整 Agent 决策链。前端 History 页必须随当前视角传入该参数,不能在 public/player 视角主动请求 debug 决策。\n\n## 六、Memory API(比赛交付新增)\n\n### `GET /api/memory/agents`\n\n查询参数:`include_test_data`,默认 `false`。\n\n返回 Agent 记忆统计,并附带“每个 Agent 10 局真实 LLM memory”覆盖度。默认只展示当前启用 Agent Registry(`preset_20`)中的 Agent,并隐藏 `agent_api_test_*`、`agent_memory_test_*` 等测试/历史遗留 Agent;勾选测试数据后才展示数据库里的完整列表。\n\n```json\n{\n \"agents\": [\n {\n \"agent_id\": \"agent_linzhou_001\",\n \"memory_count\": 12,\n \"last_updated_at\": \"2026-06-02T20:00:00\",\n \"self_lesson_count\": 4,\n \"role_lesson_count\": 6,\n \"opponent_style_count\": 2,\n \"real_llm_game_count\": 3,\n \"target_real_llm_games\": 10,\n \"remaining_real_llm_games\": 7,\n \"real_llm_memory_ready\": false\n }\n ],\n \"include_test_data\": false,\n \"real_llm_memory_target_games\": 10,\n \"real_llm_memory_ready_count\": 0,\n \"real_llm_memory_total_agents\": 20\n}\n```\n\n### `GET /api/memory/real-llm-preflight`\n\n只读接口,不触发真实 LLM 调用。用于在执行“每个 Agent 10 局真实 LLM memory”前展示固定计划、当前覆盖缺口和安全确认要求。\n\n固定标准:`preset_20`、12 人局、`target_agent_games=10`、`max_days=10`、`max_workers=1`。在 20 个 Agent / 12 人局下,系统需要计划 `17` 局,才能保证每个 Agent 至少入场 10 局。\n\n```json\n{\n \"configured\": true,\n \"use_real_llm\": true,\n \"agent_pool\": \"preset_20\",\n \"agent_roster_size\": 20,\n \"player_count\": 12,\n \"target_agent_games\": 10,\n \"required_games\": 17,\n \"max_days\": 10,\n \"max_workers\": 1,\n \"schedule_strategy\": \"balanced\",\n \"min_scheduled_agent_games\": 10,\n \"max_scheduled_agent_games\": 11,\n \"agent_memory_target_met_by_schedule\": true,\n \"current_min_real_llm_games\": 0,\n \"current_max_real_llm_games\": 0,\n \"ready_agent_count\": 0,\n \"pending_agent_count\": 20,\n \"total_remaining_agent_games\": 200,\n \"external_api_call_required\": true,\n \"safety_confirmation_required\": true,\n \"no_auto_execute\": true,\n \"command\": \"cd backend && python3 scripts/run_batch_games.py --use-real-llm --confirm-real-llm-cost-and-writes --agent-pool preset_20 --target-agent-games 10 --max-workers 1 --max-days 10\",\n \"llm_status\": {\n \"configured\": true,\n \"mode\": \"real\",\n \"model_name\": \"deepseek-v4-flash\",\n \"total_tokens\": 0,\n \"estimated_cost\": 0\n },\n \"agent_statuses\": [\n {\n \"agent_id\": \"agent_linzhou_001\",\n \"name\": \"林舟\",\n \"current_real_llm_games\": 0,\n \"target_real_llm_games\": 10,\n \"remaining_real_llm_games\": 10,\n \"scheduled_games\": 10,\n \"real_llm_memory_ready\": false\n }\n ]\n}\n```\n\n### `GET /api/memory/agents/{agent_id}`\n\n查询参数:`memory_type`、`role`、`limit`、`offset`。\n\n返回:`memory_id`、`memory_type`、`source_role`、`content`、`confidence`、`importance`、`usage_count`、`last_used_at`、`metadata`。\n\n### `GET /api/memory/agents/{agent_id}/usage`\n\n返回该 Agent 的 memory usage:`memory_id`、`game_id`、`decision_event_id`、`usage_type`、`score`、`created_at`。\n\n## 七、LLM 与 Reports API(真实 LLM 展示新增)\n\n### `GET /api/llm/status`\n\n不触发外部 API 调用,仅返回当前运行时配置和累计统计。\n\n```json\n{\n \"configured\": true,\n \"mode\": \"real\",\n \"model_name\": \"deepseek-v4-flash\",\n \"base_url\": \"https://api.deepseek.com\",\n \"llm_call_count\": 12,\n \"total_tokens\": 34000,\n \"estimated_cost\": 0.0123,\n \"average_latency_ms\": 2500\n}\n```\n\n### `GET /api/reports`\n\n查询参数:`real_llm_only`、`demo_standard_only`、`limit`。\n\n默认 `real_llm_only=true` 且 `demo_standard_only=true`,只返回 `backend/reports/batch_*_metrics.json` 中 `experiment_config.use_real_llm=true` 且符合标准 Demo 证据的报告:`game_count>=3`、`success_count>=3`、`failure_count=0`、`max_days=10`、`max_workers=1`。前端 Reports 页可切换显示 FakeLLM / Offline 报告。\n\n返回中的 `reports` 会按 `generated_at` 倒序;当历史报告只保留紧凑时间戳(如 `20260603_165249`)时,后端会同时返回:\n- `generated_at`:归一化后的 ISO 时间,供前端排序和展示。\n- `generated_at_raw`:原始报告时间戳,供审计或回溯文件名。\n\n返回:\n\n```json\n{\n \"reports\": [\n {\n \"report_id\": \"batch_20260602_200138\",\n \"generated_at\": \"2026-06-02T20:01:38\",\n \"generated_at_raw\": \"20260602_200138\",\n \"use_real_llm\": true,\n \"model_name\": \"deepseek-v4-flash\",\n \"game_count\": 3,\n \"success_count\": 3,\n \"failure_count\": 0,\n \"completed_game_count\": 3,\n \"max_days\": 10,\n \"max_workers\": 1,\n \"target_agent_games\": 0,\n \"min_scheduled_agent_games\": 1,\n \"agent_memory_target_met_by_schedule\": null,\n \"demo_standard\": true,\n \"win_metrics\": { \"avg_days\": 2.3 },\n \"agent_quality\": { \"fallback_rate\": 0.01 },\n \"memory_metrics\": { \"memory_usage_count\": 25 },\n \"governance_metrics\": {\n \"reviewed_games\": 3,\n \"private_info_leak_count\": 0,\n \"info_leak_check_passed\": true,\n \"private_info_leak_examples\": []\n },\n \"real_llm_runtime\": {\n \"use_real_llm\": true,\n \"api_key_configured\": true,\n \"ready_for_real_llm\": true,\n \"model_name\": \"deepseek-v4-flash\",\n \"base_url\": \"https://api.deepseek.com\",\n \"fail_fast_enforced\": true\n },\n \"event_metrics\": { \"discussion_count\": 9 },\n \"cost_metrics\": {\n \"total_prompt_tokens\": 80000,\n \"total_completion_tokens\": 40000,\n \"total_tokens\": 120000,\n \"llm_call_count\": 180,\n \"estimated_cost\": 0.08,\n \"avg_llm_latency\": 2300\n },\n \"total_prompt_tokens\": 80000,\n \"total_completion_tokens\": 40000,\n \"total_tokens\": 120000,\n \"llm_call_count\": 180,\n \"estimated_cost\": 0.08,\n \"avg_llm_latency\": 2300,\n \"fallback_count\": 1,\n \"invalid_action_count\": 0,\n \"memory_usage_count\": 25,\n \"info_leak_check_passed\": true,\n \"has_markdown\": true,\n \"has_leaderboard\": true,\n \"top_agents\": []\n }\n ],\n \"real_llm_only\": true,\n \"demo_standard_only\": true,\n \"real_count\": 1,\n \"real_demo_count\": 1,\n \"offline_count\": 10,\n \"llm_status\": { \"configured\": true, \"mode\": \"real\" },\n \"excellence_status\": {\n \"generated_at\": \"2026-06-04T13:40:00\",\n \"ci\": {\n \"mode\": \"ci\",\n \"score\": 100,\n \"level\": \"outstanding\",\n \"minimum_score\": 90,\n \"passed\": true,\n \"failed_count\": 0,\n \"failed_checks\": [],\n \"summary\": \"通过\",\n \"headline\": \"ci 100.00/90.00 · 0 阻塞\"\n },\n \"acceptance\": {\n \"mode\": \"acceptance\",\n \"score\": 95,\n \"level\": \"outstanding\",\n \"minimum_score\": 95,\n \"passed\": false,\n \"failed_count\": 2,\n \"failed_checks\": [\n {\n \"check_id\": \"real_llm_demo_artifact\",\n \"detail\": \"latest real report runtime attestation missing\"\n },\n {\n \"check_id\": \"real_llm_memory_artifact\",\n \"detail\": \"info_leak_check_passed!=true (3 leaks)\"\n }\n ],\n \"summary\": \"未通过\",\n \"headline\": \"acceptance 95.00/95.00 · 2 项阻塞\"\n }\n }\n}\n```\n\n`governance_metrics.private_info_leak_examples` 在 `private_info_leak_count>0` 时返回最多若干条样本,每条包含:\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| game_id | string | 泄露样本所属对局 |\n| review_path | string | 生成样本的 agent review 文件路径 |\n| player_id | string | 触发样本的玩家 |\n| role | string | 该玩家角色 |\n| day | integer | 发生天数 |\n| phase | string | 发生阶段 |\n| detail | string | 命中的泄露规则或说明 |\n| evidence | string | 对应 public_message 片段 |\n\n前端 Reports 页必须把 `info_leak_check_passed=false` 显示为需复核,并在样本存在时展示样本证据。旧报告可能只有 `private_info_leak_count` 而没有 examples,此时只能展示计数。\n\n`real_llm_runtime` 用于真实 LLM artifact 证据链,不包含密钥值,只表示生成报告时真实 LLM 预检是否通过:\n- `api_key_configured=true` — 进程可读取 `LLM_API_KEY` / `OPENAI_API_KEY` / `DEEPSEEK_API_KEY` 之一。\n- `ready_for_real_llm=true` — `use_real_llm=true` 且真实 LLM fail-fast 预检已通过。\n- `fail_fast_enforced=true` — 缺少 API Key 时会拒绝运行,不允许静默降级为 FakeLLM。\n\n`excellence_status` 是 `/api/reports` 的统一验收摘要来源,供 Live 首页与 Reports 首页共用,禁止前端自行拼装“验收通过”结论:\n- `ci`:代码级门禁摘要,只反映测试、构建、静态规则和文档契约。\n- `acceptance`:答辩证据门禁摘要,会直接复用 `scripts/check_excellence.py --mode acceptance` 的真实规则。\n- `failed_checks[]`:每项至少包含 `check_id` 和 `detail`,前端应直接展示阻塞原因,不得将失败状态包装为 `100/100` 或“已就绪”。\n\n用于扩展“每个 Agent 10 局真实 LLM memory”的固定命令:\n\n```bash\ncd backend && python3 scripts/run_batch_games.py \\\n --use-real-llm \\\n --confirm-real-llm-cost-and-writes \\\n --agent-pool preset_20 \\\n --target-agent-games 10 \\\n --max-workers 1 \\\n --max-days 10\n```\n\n该命令会自动把 `num_games` 从默认值提升到满足 20 个 preset Agent、12 人局、每个 Agent 至少入场 10 局的最小局数,即 17 局。真实 LLM 批量运行会调用外部 API 并产生费用,必须在人工确认后执行。\n"}}, "README.md": {"1b4a1a22a8b74877191f9618beab0fc89a9252a1": {"t": 1786068586.393084, "n": 11705, "h": "1f4a0819f56c0e37", "c": "# 🐺 狼人杀 Multi-Agent Arena\n\n一个基于多智能体协作和对抗的完整狼人杀游戏系统。\n\n---\n\n## 📋 项目简介\n\n本项目是一个完整的 AI 狼人杀游戏系统,具有以下特性:\n\n- **LLM-first Multi-Agent Harness**: 采用统一的 Harness 架构而非硬编码角色 Agent\n- **Observation 信息隔离**: 严格的信息过滤机制,确保不同视角看到不同的游戏状态\n- **PlayerAgent 统一调度**: 所有角色使用相同的智能体框架\n- **ActionParser 结构化行动**: 解析 LLM 输出为有效的游戏动作\n- **RuleEngine 裁决**: 验证动作合法性并执行游戏规则\n- **LLM 日志与复盘**: 完整记录 LLM 输入输出,自动生成复盘报告\n- **前端观战**: 支持三种视角(公众/玩家/调试)实时观看游戏\n\n---\n\n## 🏗️ 核心创新点\n\n### 1. Harness 架构而非硬编码角色 Agent\n\n本项目采用统一的 PlayerAgent 框架,而非为狼人、预言家、村民等每个角色硬编码独立 Agent。这种设计的优势:\n\n- 统一的推理接口:所有角色使用相同的决策框架\n- 可扩展性:易于添加新角色或修改现有角色行为\n- 模块化:各组件解耦,便于独立开发和测试\n- 可观测性:统一的日志和追踪机制\n\n### 2. 信息隔离机制\n\n通过 InformationFilter 实现严格的信息隔离:\n\n- **公众视角 (public)**: 无隐藏信息,适合观众观看\n- **玩家视角 (player)**: 只能看到特定玩家看到的信息\n- **调试视角 (debug)**: 上帝视角,查看所有隐藏信息,包括角色身份\n\n### 3. LLM 完整集成\n\n- 支持 OpenAI、DeepSeek 等多种 LLM\n- FakeLLM 降级模式,无需 API Key 即可演示\n- Token 计数和费用估算\n- LLM 调用历史记录和统计\n\n---\n\n## 📊 系统架构\n\n```mermaid\ngraph TB\n subgraph Frontend[\"前端 (React)\"]\n GH[GameHeader<br/>游戏头部]\n PP[PlayerPanel<br/>玩家面板]\n GET[GameEventTimeline<br/>事件时间线]\n LSP[LLMStatsPanel<br/>LLM统计面板]\n end\n\n subgraph APILayer[\"API 层\"]\n WS[WebSocket Server<br/>多视角推送]\n REST[REST API<br/>游戏控制]\n end\n\n subgraph GameEngine[\"GameEngine 核心\"]\n PM[Phase Manager<br/>阶段管理]\n RM[Role Manager<br/>角色管理]\n IF[Information Filter<br/>信息过滤]\n RE[Rule Engine<br/>规则引擎]\n end\n\n subgraph AgentSystem[\"Agent 系统\"]\n PA[PlayerAgent<br/>统一玩家智能体]\n PB[PromptBuilder<br/>提示词构建]\n AP[ActionParser<br/>动作解析]\n LLM[LLM Integration<br/>真实/Fake LLM]\n end\n\n subgraph LoggingReview[\"日志与复盘\"]\n GL[GameLogger<br/>游戏日志]\n GR[GameReviewer<br/>复盘生成]\n ME[Metrics<br/>指标统计]\n end\n\n Frontend -->|WebSocket/REST| APILayer\n APILayer -->|GameState|GameEngine\n GameEngine -->|Observation|AgentSystem\n AgentSystem -->|Actions|GameEngine\n GameEngine -->|Game Events|LoggingReview\n```\n\n---\n\n## 🚀 快速开始\n\n### 前置要求\n\n- Python 3.9+\n- Node.js 16+\n- npm 或 yarn\n\n### 一键安装 & 启动\n\n```bash\n# 安装所有依赖\nmake install\n\n# 启动后端(终端 1)\nmake backend\n\n# 启动前端(终端 2)\nmake frontend\n```\n\n### 后端启动(手动)\n\n```bash\ncd backend\n\n# 安装依赖\npip install -r requirements.txt\n\n# 复制环境变量配置\ncp .env.example .env\n# 编辑 .env 配置你的 LLM API Key(可选)\n```\n\n### FakeLLM 演示(无需配置)\n\n无需任何 API Key 配置,直接使用模拟的 LLM 响应:\n\n```bash\n# 运行单局游戏\npython3 scripts/run_single_game.py --max-days 3\n```\n\n### 真实 LLM 配置\n\n如需接入真实的 LLM,编辑 `.env` 文件:\n\n```bash\n# 通用配置(推荐)\nLLM_API_KEY=your-api-key\nLLM_BASE_URL=https://api.deepseek.com/v1\nLLM_MODEL=deepseek-chat\n\n# 或使用 OpenAI 配置\nOPENAI_API_KEY=your-openai-key\n\n# 或使用 DeepSeek 配置\nDEEPSEEK_API_KEY=your-deepseek-key\n```\n\n### 运行真实 LLM 游戏\n\n```bash\n# 使用真实 LLM 运行单局游戏\npython3 scripts/run_single_game.py --use-real-llm --max-days 10\n```\n\n### 前端启动\n\n```bash\ncd frontend\n\n# 安装依赖\nnpm install\n\n# 构建生产版本\nnpm run build\n\n# 或使用开发模式\nnpm start\n```\n\n---\n\n## 📚 使用指南\n\n### 单局游戏运行\n\n```bash\ncd backend\n\n# FakeLLM 模式(默认)\npython3 scripts/run_single_game.py --max-days 3\n\n# 真实 LLM 模式\npython3 scripts/run_single_game.py --use-real-llm --max-days 10\n```\n\n### 批量游戏运行\n\n```bash\n# FakeLLM 批量\npython3 scripts/run_batch_games.py --num-games 5 --max-days 5\n\n# 真实 LLM 批量\npython3 scripts/run_batch_games.py --use-real-llm --agent-pool preset_20 --num-games 3 --max-workers 1 --max-days 10\n```\n\n### 复盘报告生成\n\n```bash\n# 为最新游戏生成复盘\npython3 scripts/generate_review.py --latest\n\n# 或指定游戏 ID\npython3 scripts/generate_review.py --game-id <game-id>\n```\n\n复盘报告会生成在 `backend/reports/` 目录下,包含 JSON 和 Markdown 两种格式。\n\n### 测试命令\n\n```bash\n# 运行所有测试\ncd backend\npython3 -m pytest tests/ -v\n```\n\n---\n\n## 📁 项目结构\n\n```\nbytesdance-werewolf/\n├── AGENTS.md # 所有 coding agent 行为规范(硬规则)\n├── CLAUDE.md # Claude Code 工作指南\n├── Makefile # 本地开发快捷命令\n├── shared/\n│ └── api-contract.md # 前后端 API 共享契约(唯一真相源)\n├── .github/\n│ ├── workflows/ci.yml # CI 自动检查(测试+构建)\n│ └── pull_request_template.md\n├── backend/\n│ ├── src/\n│ │ ├── agents/ # 智能体实现\n│ │ │ ├── player_agent.py # 统一玩家智能体\n│ │ │ ├── prompt_builder.py # 提示词构建\n│ │ │ ├── action_parser.py # 动作解析\n│ │ │ └── llm_integration.py # LLM 集成\n│ │ ├── api/ # API 和 WebSocket\n│ │ │ └── app.py # FastAPI 应用\n│ │ ├── communication/ # 通信和信息控制\n│ │ │ ├── information_filter.py # 信息过滤\n│ │ │ └── message_bus.py # 消息总线\n│ │ ├── game/ # 游戏核心\n│ │ │ ├── game_engine.py # 游戏引擎\n│ │ │ ├── phase_manager.py # 阶段管理\n│ │ │ ├── role_manager.py # 角色管理\n│ │ │ └── models.py # 数据模型\n│ │ ├── evaluation/ # 评估和复盘\n│ │ │ ├── reviewer.py # 复盘生成\n│ │ │ └── metrics.py # 指标统计\n│ │ └── logs/ # 日志系统\n│ │ └── game_logger.py # 游戏日志\n│ ├── prompts/ # Prompt 配置\n│ ├── tests/ # 后端测试用例\n│ ├── scripts/ # 实用脚本\n│ ├── requirements.txt\n│ ├── .env.example\n│ └── .gitignore\n├── frontend/\n│ ├── src/\n│ │ ├── components/ # React 组件\n│ │ ├── hooks/ # 自定义 Hooks\n│ │ │ └── useGameState.js\n│ │ └── App.js\n│ ├── package.json\n│ ├── .env.example\n│ └── ...\n├── docs/ # 文档\n├── docker-compose.yml # Docker 部署(可选)\n└── README.md\n```\n\n---\n\n## 🔒 安全说明\n\n### API Key 安全\n\n- **请勿提交 `.env` 文件**: 项目包含 `.gitignore` 规则,防止提交包含敏感信息的配置文件\n- **API Key 不被记录**: LLM 集成代码确保 API Key 不会被记录到日志文件中\n- **环境变量优先级**: 支持通用配置 `LLM_API_KEY`,也支持特定提供商配置\n\n### 信息隔离安全\n\n- 敏感信息如角色身份在不同视角下会被正确隐藏\n- 公众视角和玩家视角不会泄露隐藏信息\n- 调试视角仅供开发和调试使用\n\n---\n\n## ✅ 当前已验证结果\n\n| 项目 | 状态 | 说明 |\n|------|------|------|\n| 后端测试 | ✅ 290 passed, 1 skipped | `cd backend && python3 -m pytest -q` |\n| FakeLLM 单局 | ✅ 完成 | `python3 scripts/run_single_game.py --use-fake-llm --max-days 2` |\n| FakeLLM 批量 | ✅ 完成 | `python3 scripts/run_batch_games.py --num-games 1 --max-workers 1 --max-days 2` |\n| 真实 LLM 批量 | ✅ 通过 | `python3 scripts/run_batch_games.py --use-real-llm --num-games 1 --max-workers 1 --max-days 2`,样本 `482fe406` |\n| 真实 LLM 单局 | ✅ 通过 | `python3 scripts/run_single_game.py --use-real-llm --max-days 2`,样本 `7fa8622e` |\n| LLM Token 统计 | ✅ 完成 | 完整的 Token 和费用统计 |\n| 复盘报告 | ✅ 完成 | JSON + Markdown 格式 |\n| 前端测试/构建 | ✅ 通过 | `npm test -- --watchAll=false`、`npm run build` |\n| 信息隔离 | ✅ 通过 | public History API 不返回 private/debug 事件 |\n\n---\n\n## 🔗 前后端联调\n\n### 快速启动\n\n```bash\n# 方式1:使用 Makefile(需要两个终端)\n# 终端 1\nmake backend\n\n# 终端 2\nmake frontend\n\n# 方式2:手动启动\n# 终端 1\ncd backend && python3 -m src.api.app\n\n# 终端 2\ncd frontend && npm start\n```\n\n### 联调说明\n\n- 后端运行在 `http://localhost:8000`,提供 REST API + WebSocket\n- 前端运行在 `http://localhost:3000`,通过 `REACT_APP_API_BASE_URL` 环境变量连接后端\n- 前端默认连接真实后端(`REACT_APP_USE_MOCK=false`)\n- Mock 模式需显式开启(`REACT_APP_USE_MOCK=true`),UI 中会显示黄色警告条\n\n### 环境变量\n\n**后端**:复制 `backend/.env.example` 为 `backend/.env`\n**前端**:复制 `frontend/.env.example` 为 `frontend/.env`\n\n前端核心变量:\n- `REACT_APP_API_BASE_URL` — 后端地址,默认 `http://localhost:8000`\n- `REACT_APP_USE_MOCK` — Mock 模式开关,默认 `false`\n\n---\n\n## 📋 API 契约\n\n前后端接口契约定义在:**[shared/api-contract.md](shared/api-contract.md)**\n\n此文件是前后端协同开发的**唯一真相源**,定义了:\n- 数据结构(`GameState`、`PlayerState`、`GameEvent`、`DebugInfo`)\n- REST API 接口(创建对局、获取状态、推进阶段、事件流、Debug)\n- WebSocket 通信协议\n- 信息隔离规则\n\n⚠️ **重要**:任何 API 变更必须同步更新此文件。\n\n---\n\n## 🌿 分支与 PR 流程\n\n### 分支规则\n\n- `main` 分支保护,**禁止直接提交**\n- 所有变更通过 feature/fix 分支 + PR 合并\n- 分支命名:`feature/<描述>`、`fix/<描述>`、`cleanup/<描述>`\n\n### PR 流程\n\n1. 创建分支 → 提交代码\n2. 推送并创建 PR(使用 PR 模板)\n3. CI 自动运行(后端测试 + 前端构建)\n4. Code Review 通过后合并\n\n### 提交前检查\n\n```bash\n# 后端测试\nmake test\n\n# 前端构建\nmake build\n```\n\n---\n\n## ✅ 最小验收路径\n\n```text\n1. 启动 backend:make backend(或 cd backend && python3 -m src.api.app)\n2. 启动 frontend:make frontend(或 cd frontend && npm start)\n3. 打开浏览器 http://localhost:3000\n4. 点击 \"开始游戏\" 创建一局游戏\n5. 页面显示玩家列表(右侧面板)\n6. 点击推进阶段按钮\n7. phase/day/round 状态更新\n8. timeline 出现新事件\n9. 切换到 debug 视角查看角色和统计信息\n```\n\n---\n\n## 📖 更多文档\n\n- [演示指南](docs/DEMO_GUIDE.md) - 完整的 5 分钟/10 分钟演示脚本\n- [架构详解](docs/ARCHITECTURE.md) - 系统架构和设计原理\n- [验收检查清单](docs/ACCEPTANCE_CHECKLIST.md) - 项目验收标准\n- [真实 LLM 配置](docs/REAL_LLM_SETUP.md) - 真实 LLM 接入指南\n- [API 文档](docs/API_DOC.md) - REST API 文档\n- [开发指南](docs/DEVELOPMENT.md) - 开发者指南\n\n---\n\n## 🎯 后续扩展方向\n\n### P0 - 近期优化\n\n- Prompt 调优实验\n- 多局真实 LLM 对比分析\n- 前端 UI 体验优化\n\n### P1 - 中期扩展\n\n- Prompt A/B 测试框架\n- 多模型对比(OpenAI vs DeepSeek vs ...)\n- Leaderboard 系统\n\n### P2 - 长期愿景\n\n- 自进化机制(从复盘学习优化策略)\n- 多人对战模式(人类 vs AI)\n- 更多角色扩展(白痴等)\n- WebSocket 实时游戏平台\n\n---\n\n## 📝 许可证\n\n本项目仅供学习和研究使用。\n\n---\n\n## 🤝 致谢\n\n感谢所有为项目做出贡献的开发者!\n\n---\n\n## 比赛交付级持久化闭环\n\n### 数据库\n\n项目默认使用 SQLite 保存对局全过程与 Agent Memory:\n\n- 默认地址:`sqlite:///data/werewolf_arena.db`\n- 默认文件:`data/werewolf_arena.db`\n- 可通过环境变量覆盖:`DATABASE_URL`\n\n初始化数据库:\n\n```bash\ncd backend\npython3 -m src.db.init_db\n```\n\n核心表:\n\n- `games`:每局游戏 metadata、状态、胜方、摘要。\n- `game_events`:发言、投票、夜间行动、Agent 决策、repair/fallback、memory 事件。\n- `agent_memories`:跨局长期记忆,替代仅依赖 JSON 文件的存储方式。\n- `agent_memory_usage`:记录 memory 何时被检索、注入 prompt 或局后更新。\n\n### 运行一局并保存历史\n\n```bash\ncd backend\npython3 scripts/run_single_game.py --use-fake-llm\n```\n\n运行时 `GameLogger` 会同步写入 JSON 日志和 SQLite。数据库写入失败不会中断游戏,但会写入后端 warning 日志。\n\n### 查看历史对局与 Memory\n\n启动后端:\n\n```bash\ncd backend\nuvicorn src.api.app:app --reload\n```\n\n启动前端:\n\n```bash\ncd frontend\nnpm start\n```\n\n前端顶部导航包含:\n\n- `Live Game`:实时观战。\n- `History`:历史对局列表、单局时间线、Agent 决策记录。\n- `Agent Memory`:Agent 长期记忆与 usage timeline。\n- `Reports / Evaluation`:批量评测入口说明。\n\n### Agent 库与开局选择\n\n- 后端 Agent 库定义在 `backend/src/agents/registry/default_agents.json`,运行时由 `backend/src/agents/registry/agent_registry.py` 提供 `GET /api/agents` 与 `GET /api/agents/{agent_id}`。\n- 前端 `Live Game` 开局入口会展示 Agent 名称、persona、描述和策略标签;开始游戏时向 `POST /api/games` 传入 `agent_ids`。\n- `seat` / `player_id` 只代表本局座位与玩家;`agent_id` 代表跨局智能体;`role` 仍由本局规则随机分配;长期 profile 与 memory 始终按 `agent_id` 读取和写入。\n- History detail 的 `game_agent_assignments` 记录每局 `seat/player_id/agent_id/display_name` 绑定;public 视角不返回真实角色,debug 视角才返回 `assigned_role`。\n\n### History API\n\n- `GET /api/agents`\n- `GET /api/agents/{agent_id}`\n- `GET /api/history/games`\n- `GET /api/history/games/{game_id}`\n- `GET /api/history/games/{game_id}/events`\n- `GET /api/history/games/{game_id}/decisions`\n\n`events` 默认 `visibility=public`,不会返回 private/debug 事件;`visibility=debug` 用于答辩展示完整链路。\n\n### 投票与猎人规则\n\n- 投票阶段支持弃票:`target=null`、`target=\"abstain\"`、`pass` 或 `弃票` 都会被规范化为弃票。\n- 弃票写入 `vote` 事件,`payload.is_abstain=true`,并计入 `vote_result.abstain_count`;弃票不计入任何玩家得票。\n- 投票结算支持全部弃票、唯一最高票、平票、弃票与有效票混合;死亡玩家不能投票,不能投给死亡或不存在玩家。\n- 猎人仅在“被投票放逐”后进入 `hunter_shoot` 阶段,可选择射击一名存活玩家或不开枪。\n- 当前项目规则不支持猎人夜间死亡或女巫毒杀后开枪;该限制保留为显式规则边界。\n- 前端 `Live Game` 的投票面板提供“全员弃票”,History 时间线可读展示弃票、投票结算与猎人开枪/不开枪事件。\n\n### Memory API\n\n- `GET /api/memory/agents`\n- `GET /api/memory/agents/{agent_id}`\n- `GET /api/memory/agents/{agent_id}/usage`\n\n旧 JSON memory 可导入数据库:\n\n```bash\ncd backend\npython3 scripts/import_memory_json_to_db.py --memory-dir data/memory\n```\n\n### 批量评测\n\n```bash\ncd backend\npython3 scripts/run_batch_games.py --agent-pool preset_20 --num-games 50 --max-workers 2 --max-days 5 --seed 20260530\n```\n\n批量运行会生成:\n\n- `reports/batch_{timestamp}_metrics.json`\n- `reports/batch_{timestamp}_report.md`\n\n报告包含胜率、平均天数、fallback/repair/action_source 分布、memory usage、数据库记录数、token 统计,以及 `vote_count`、`abstain_count`、`vote_result_count`、`vote_tie_count`、`hunter_shoot_count`。\n\n### 比赛演示流程\n\n1. `cd backend && python3 -m src.db.init_db`\n2. `cd backend && uvicorn src.api.app:app --reload`\n3. `cd frontend && npm start`\n4. 在 `Live Game` 创建并推进对局。\n5. 切换到 `History` 查看时间线与决策链路。\n6. 切换到 `Agent Memory` 查看长期记忆和 prompt 注入记录。\n7. 运行 `python3 scripts/run_batch_games.py --agent-pool preset_20 --num-games 50 --max-workers 2 --max-days 5 --seed 20260530` 生成 20 预设 Agent 训练评测报告。\n\n\n## 自由讨论阶段\n- 白天发言后、投票前新增 `DISCUSSION` 阶段。\n- 讨论事件会写入日志、数据库与 History 时间线。\n- 真实 LLM 与 FakeLLM 都支持该阶段。\n"}}}}