Fix info leak in web game + add handover notes

This commit is contained in:
karylab_agents
2026-08-04 12:49:17 +08:00
parent 6711736bd0
commit f8b75abc15
29 changed files with 2590 additions and 283 deletions
+96
View File
@@ -0,0 +1,96 @@
"""Python 引擎冒煙測試(不連網、不呼叫真實 LLM)。
以「模擬 LLM」取代 API 呼叫,跑完整場騙子酒館遊戲,驗證
game.py / player.py / game_record.py 主流程可正常完成並產生記錄。
使用方式:
.venv\\Scripts\\python.exe scripts\\smoke_test_python.py
(若主控台遇到簡體中文編碼錯誤,請先設定 $env:PYTHONIOENCODING="utf-8"
"""
import os
import sys
import random
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, REPO_ROOT)
from game import Game
class MockChat:
"""附著在某位玩家身上的假 LLM:依提示內容回傳格式正確的 JSON。
注意:chat() 統一回傳 (content, reasoning_content) 二元素 tuple。
"""
def __init__(self, player):
self.player = player
self.decision_count = 0
def chat(self, messages, model=None):
prompt = messages[0]["content"] if messages else ""
if "played_cards" in prompt:
return self._play_reply(), ""
if "was_challenged" in prompt:
return self._challenge_reply(), ""
return "mock opinion: \u8a72\u73a9\u5bb6\u76ee\u524d\u7121\u660e\u986f\u50be\u5411\u3002", ""
def _play_reply(self):
"""\u7d66\u4e86\u81ea\u5df1\u7684\u624b\u724c\uff1a\u540c\u9ede\u6578 >=2 \u51fa\u6700\u591a 3 \u5f35\uff0c\u5426\u5247\u51fa 1 \u5f35\u3002"""
hand = sorted(self.player.hand)
cards = [hand[0]]
for c in hand[1:3]:
if c == cards[0]:
cards.append(c)
cards_json = ",".join('"%s"' % c for c in cards)
return ('{"played_cards": [%s], "behavior": "\u7a69\u5065", '
'"play_reason": "mock heuristic"}' % cards_json)
def _challenge_reply(self):
"""\u6bcf 4 \u6b21\u8cea\u7591\u6c7a\u7b56\u5c31\u8cea\u7591\u4e00\u6b21\uff0c\u8986\u84cb\u6210\u529f/\u5931\u6557\u5169\u689d\u8def\u5f91\u3002"""
self.decision_count += 1
do_challenge = (self.decision_count % 4 == 0)
return ('{"was_challenged": %s, "challenge_reason": "mock trigger"}' %
("true" if do_challenge else "false"))
def main():
random.seed(20260803)
player_configs = [
{"name": "DeepSeek", "model": "mock"},
{"name": "GPT", "model": "mock"},
{"name": "Claude", "model": "mock"},
{"name": "Gemini", "model": "mock"},
]
game = Game(player_configs)
for p in game.players:
p.llm_client = MockChat(p)
max_plays = 400
for step in range(max_plays):
if game.game_over:
break
game.play_round()
if game.game_over:
print("[OK] \u904a\u6232\u7d50\u675f\uff0c\u4e3b\u6d41\u7a0b\u53ef\u6b63\u5e38\u5b8c\u6210\u3002")
else:
print(f"[FAIL] \u8dd1\u4e86 {max_plays} \u6b65\u4ecd\u672a\u7d50\u675f\uff08\u53ef\u80fd\u89c4\u5247\u6709\u6b7b\u7d50\uff09\u3002 rounds={game.round_count}")
return 1
rec = game.game_record
rounds = len(rec.rounds)
alive = [p.name for p in game.players if p.alive]
print(f"[OK] \u7372\u52dd\u8005={rec.winner} | \u5b58\u6d3b\u73a9\u5bb6={alive}")
print(f"[OK] \u8a18\u9304\u56de\u5408\u6578={rounds}")
import glob
files = glob.glob(os.path.join(REPO_ROOT, "game_records", "*.json"))
print(f"[OK] game_records \u7522\u51fa {len(files)} \u500b JSON \u8a18\u9304\u6a94")
if files:
newest = max(files, key=os.path.getmtime)
print(f" \u6700\u65b0\u8a18\u9304: {os.path.basename(newest)}")
return 0
if __name__ == "__main__":
sys.exit(main())