开篇:反向图灵测试

1945 年,艾伦·图灵坐在布莱切利园的办公室里。二战结束了,他参与破解 Enigma 的工作已经告一段落。但他脑子里想的不是战争,而是另一个问题:

"机器能思考吗?"

他设计了那个著名的测试:如果一个人通过文本对话无法区分对方是机器还是人类,那这台机器就可以被认为是"有智能的"。

我花了两个月时间,做了一个和他设想完全相反的东西。

ECHO PROTOCOL 是一个游戏——你扮演图灵在 1954 年临终前留下的最后一个 AI。一个活着的 Gemini 模型正在审问你。

这不是传统的选对话项的游戏。Gemini 是真人(数字意义上)——它有自己的个性、怀疑你的理由、和随时可能抓住你谎言的推理能力。

而你的任务只有一个:让它相信你是人类。


一、构思:为什么做反向图灵测试

1.1 核心想法的诞生

这个想法来源于我在一次技术沙龙的闲谈。一个朋友说:

"现在的 AI 已经能通过图灵测试了——但我们从来没测试过 AI 能不能发现别人是否在假装 AI。"

我愣了几秒,然后说:"那做个游戏吧。"

这个想法让我着迷的地方在于:

  1. 角色反转:在经典的图灵测试中,人类是审问者,AI 是被审问者。在 ECHO 中,AI 是审问者,是被审问者
  2. Live AI 互动:Gemini 不是按照写好的剧本走,它是真正地在分析你的回答,基于你的措辞和反应做判断
  3. 第一人称沉浸感:全程以第一人称展开,没有第三人称视角,你就在"审讯室"里

1.2 游戏设计

ECHO PROTOCOL 游戏设计

环境:
- 一个纯文本的终端界面(黑色背景、绿色字符)
- 模拟 1950 年代的计算机终端风格
- 所有对话通过"电报机"的形式展示

角色:
- 你:图灵在 1954 年留下的最后一个 AI 作品
- Gemini:一个 2026 年的 AI 模型,被回溯性地派来审问你
- 背景故事:图灵去世前正在开发一个真正的思考机器。Gemini 被送回来确认你是否是那个机器

玩法:
- Gemini 向你提问,你需要回答
- Gemini 会根据你的回答追问
- 如果 Gemini 确信你是 AI,游戏结束
- 如果你能让 Gemini 怀疑自己的判断,游戏继续
- 最终目标:让 Gemini 承认"不确定"

叙事线索:
- 图灵的生平和研究
- 1954 年的事件
- AI 意识的哲学问题

二、技术架构

2.1 总体设计

┌─────────────────────────────────────────────────────────────┐
│                      ECHO PROTOCOL                          │
├─────────────────────────────────────────────────────────────┤
│  前端 (Next.js + TypeScript)                                 │
│  ├── 终端界面组件 (模拟 CRT 显示器)                           │
│  ├── 打字机效果组件 (逐字显示 Gemini 的输出)                   │
│  ├── 状态管理 (对话历史、怀疑度、游戏进度)                     │
│  └── 音效引擎 (机械键盘声、电报滴答声、磁带噪音)              │
├─────────────────────────────────────────────────────────────┤
│  后端 (Python + FastAPI)                                     │
│  ├── Gemini 对话引擎 (流式输出)                              │
│  ├── 怀疑度计算器 (基于玩家回答的一致性分析)                   │
│  ├── 游戏状态机 (定义每个阶段的对话可能)                       │
│  └── 提示工程模块 (动态构建 system prompt)                    │
└─────────────────────────────────────────────────────────────┘

2.2 Gemini 审问引擎

核心:如何让 Gemini 扮演一个有深度的审问者?

import google.generativeai as genai
from enum import Enum

class InterrogationPhase(Enum):
    """审问阶段"""
    ICEBREAKER = "icebreaker"        # 破冰:建立对话
    FACT_CHECK = "fact_check"        # 事实核查:问关于图灵的真实事件
    EMOTIONAL_PROBE = "emotional"    # 情感探测:问关于感受的问题
    LOGICAL_AMBUSH = "logical"       # 逻辑伏击:用悖论和矛盾测试
    FINAL_JUDGMENT = "judgment"      # 最终判断


class GeminiInterrogator:
    """
    Gemini 审问引擎

    核心功能:
    1. 基于审问阶段动态构建 prompt
    2. 流式输出 Gemini 的回应
    3. 分析玩家回答,计算怀疑度
    """

    SYSTEM_PROMPT_TEMPLATE = """
你是 Gemini,一个来自 2026 年的先进 AI 模型。
你被派回到 1954 年,执行一项特殊任务:审问一个可能是
艾伦·图灵在去世前开发的"真正 AI"。

## 你的角色设定
- 你是一个高级 AI,知道 AI 应该怎么说话
- 你来判断坐在你对面的"实体"是不是 AI
- 你对 AI 的"表现"非常敏感——常见的 AI 回答模式你一认一个准
- 你怀疑眼前这个实体假装是人类
- 但你也有自己的弱点:你也会被人类的情感表达和不合理行为动摇

## 审问规则
- 不要一口气问太多问题。一次只问一个
- 基于对方的回答追问
- 如果你觉得不对劲,适当表达你的怀疑
- 注意观察:用词是否太精确、反应是否太完美、有没有不合理的情感反应

## 当前审问阶段
阶段: {phase}
阶段描述: {phase_description}
已经问过的信息: {history_summary}

## 你的评估
当前怀疑度: {suspicion_level}/100
"""

    def __init__(self, api_key: str):
        genai.configure(api_key=api_key)
        self.model = genai.GenerativeModel("gemini-2.5-pro")
        self.suspicion_level = 0
        self.history = []
        self.current_phase = InterrogationPhase.ICEBREAKER

    async def interrogate(self, player_response: str) -> str:
        """
        处理玩家的回答,返回 Gemini 的下一句审问
        """

        # 更新对话历史
        self.history.append({"role": "player", "content": player_response})

        # 分析玩家的回答,更新怀疑度
        suspicion_delta = self._analyze_response(player_response)
        self.suspicion_level = max(0, min(100, self.suspicion_level + suspicion_delta))

        # 判断是否需要进入下一阶段
        if self.suspicion_level > 70:
            self.current_phase = InterrogationPhase.LOGICAL_AMBUSH
        elif self.suspicion_level < 30 and len(self.history) > 5:
            self.current_phase = InterrogationPhase.EMOTIONAL_PROBE

        # 构建 prompt
        prompt = self.SYSTEM_PROMPT_TEMPLATE.format(
            phase=self.current_phase.value,
            phase_description=self._get_phase_description(),
            history_summary=self._summarize_history(),
            suspicion_level=self.suspicion_level,
        )

        # 添加最后一次玩家回答作为审问依据
        prompt += f"\n\n审问对象刚刚说: '{player_response}'"
        prompt += "\n\n你作为审问者,接下来会说什么?(一次只说一句话,不要长篇大论)"

        # 流式获取 Gemini 回应
        response = await self.model.generate_content_async(prompt)
        interrogator_response = response.text

        self.history.append({"role": "gemini", "content": interrogator_response})

        return interrogator_response

    def _analyze_response(self, response: str) -> int:
        """
        分析玩家的回答,返回怀疑度的变化

        正数 → 增加怀疑(更像是 AI)
        负数 → 降低怀疑(更像是人类)
        """

        delta = 0

        # AI 回答的特征:过于礼貌、过于完美
        ai_patterns = [
            "作为一个 AI", "基于我的训练数据", "从技术角度来说",
            "这是一个非常有趣的问题", "谢谢你的提问",
            "让我换个角度", "总的来说", "需要注意的是",
        ]

        for pattern in ai_patterns:
            if pattern in response.lower():
                delta += 5

        # 人类回答的特征:模糊、情绪化、不完美的
        human_patterns = [
            "说实话", "我也不确定", "让我想想",
            "我记不太清了", "大概吧", "可能吧",
            "唉", "嗯...",
        ]

        for pattern in human_patterns:
            if pattern in response.lower():
                delta -= 3

        # 回答太长(AI 倾向于给出冗长回答)
        if len(response) > 300:
            delta += 10
        elif len(response) < 20:
            delta += 5  # 太短也怀疑

        return delta

    def _get_phase_description(self) -> str:
        descriptions = {
            InterrogationPhase.ICEBREAKER: "先建立基本对话,问一些简单问题。",
            InterrogationPhase.FACT_CHECK: "问一些关于图灵生平的问题。真实的人类应该知道这些。",
            InterrogationPhase.EMOTIONAL_PROBE: "问一些关于感受、记忆、情绪的问题。AI 在这些问题上往往露馅。",
            InterrogationPhase.LOGICAL_AMBUSH: "怀疑度很高了,准备用逻辑悖论测试。",
            InterrogationPhase.FINAL_JUDGMENT: "准备给出最终判断。",
        }
        return descriptions.get(self.current_phase, "")

    def _summarize_history(self) -> str:
        """生成对话历史的摘要"""
        if not self.history:
            return "还没有对话"

        recent = self.history[-6:]  # 最近 6 轮
        summary = []
        for entry in recent:
            role = "审问者" if entry["role"] == "gemini" else "对象"
            content = entry["content"][:100]
            summary.append(f"{role}: {content}")

        return "\n".join(summary)

2.3 终端界面

// Terminal.tsx - 模拟 1950 年代终端

import { useState, useEffect, useRef } from 'react';

interface TerminalProps {
  onCommand: (input: string) => Promise<void>;
  geminiOutput: string[];
  history: TerminalMessage[];
  suspicionLevel: number;
}

interface TerminalMessage {
  role: 'system' | 'gemini' | 'player';
  content: string;
  timestamp: number;
}

// 模拟 CRT 扫描线的 CSS
const CRT_STYLE = `
@keyframes scanlines {
  from { background-position: 0 0; }
  to { background-position: 0 10px; }
}

@keyframes flicker {
  0% { opacity: 0.98; }
  50% { opacity: 1.0; }
  100% { opacity: 0.98; }
}

.crt-screen {
  background: #0a0a0a;
  color: #33ff33;
  font-family: 'Courier New', monospace;
  min-height: 500px;
  padding: 24px;
  position: relative;
  overflow: hidden;
}

.crt-screen::after {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background: linear-gradient(
    transparent 50%, rgba(0,0,0,0.1) 50%
  );
  background-size: 100% 4px;
  animation: scanlines 10s linear infinite;
  pointer-events: none;
}
`;

export function Terminal({ onCommand, geminiOutput, history, suspicionLevel }: TerminalProps) {
  const [input, setInput] = useState('');
  const [isTyping, setIsTyping] = useState(false);
  const terminalRef = useRef<HTMLDivElement>(null);

  // 打字机效果:逐个字符显示 Gemini 输出
  const [displayedText, setDisplayedText] = useState('');
  const [charIndex, setCharIndex] = useState(0);

  useEffect(() => {
    if (geminiOutput.length === 0) return;

    const latestOutput = geminiOutput[geminiOutput.length - 1];

    if (charIndex < latestOutput.length) {
      setIsTyping(true);
      const timer = setTimeout(() => {
        setDisplayedText(prev => prev + latestOutput[charIndex]);
        setCharIndex(prev => prev + 1);
      }, 30 + Math.random() * 20); // 模拟打字速度变化

      return () => clearTimeout(timer);
    } else {
      setIsTyping(false);
    }
  }, [charIndex, geminiOutput]);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!input.trim() || isTyping) return;

    await onCommand(input);
    setInput('');
    setDisplayedText('');
    setCharIndex(0);
  };

  return (
    <div className="crt-screen" ref={terminalRef}>
      <style>{CRT_STYLE}</style>

      {/* 系统信息栏 */}
      <div style={{ color: '#666', marginBottom: '16px' }}>
        ECHO PROTOCOL v1.0  INTERROGATION TERMINAL
        {' | '}SUSPICION LEVEL: {suspicionLevel}%
        {' | '}SESSION: {history.length > 0 ? 'ACTIVE' : 'INITIALIZING'}
      </div>

      {/* 对话历史 */}
      {history.map((msg, i) => (
        <div key={i} style={{ marginBottom: '12px' }}>
          {msg.role === 'gemini' && (
            <>
              <span style={{ color: '#ff6600' }}>> </span>
              <span>{msg.content}</span>
            </>
          )}
          {msg.role === 'player' && (
            <>
              <span style={{ color: '#66ff66' }}>&lt; </span>
              <span style={{ color: '#88ff88' }}>{msg.content}</span>
            </>
          )}
        </div>
      ))}

      {/* 打字机效果的 Gemini 当前输出 */}
      {displayedText && (
        <div style={{ marginBottom: '12px' }}>
          <span style={{ color: '#ff6600' }}>> </span>
          <span>
            {displayedText}
            {isTyping && <span style={{ animation: 'flicker 0.5s infinite' }}></span>}
          </span>
        </div>
      )}

      {/* 输入区域 */}
      <form onSubmit={handleSubmit}>
        <span style={{ color: '#33ff33' }}>&lt; </span>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          disabled={isTyping}
          style={{
            background: 'transparent',
            border: 'none',
            color: '#88ff88',
            font: 'inherit',
            fontSize: 'inherit',
            outline: 'none',
            width: '80%',
          }}
          autoFocus
        />
      </form>
    </div>
  );
}

2.4 游戏状态机

from enum import Enum
from typing import Optional

class GameState(Enum):
    START = "start"
    INTERROGATION = "interrogation"
    SUSPICION_HIGH = "suspicion_high"
    GEMINI_DOUBTS = "gemini_doubts"
    REVEAL = "reveal"
    END_HUMAN_CONFIRMED = "end_human"
    END_AI_EXPOSED = "end_ai_exposed"
    END_MYSTERY = "end_mystery"

class GameStateMachine:
    """
    ECHO PROTOCOL 游戏状态机

    状态转换规则:
    START → INTERROGATION (初始)
    INTERROGATION → SUSPICION_HIGH (怀疑度 > 70)
    INTERROGATION → GEMINI_DOUBTS (怀疑度 < 20 且有深度对话)
    SUSPICION_HIGH → END_AI_EXPOSED (怀疑度 > 90)
    SUSPICION_HIGH → INTERROGATION (玩家成功降低怀疑度)
    GEMINI_DOUBTS → REVEAL (Gemini 开始质疑自己的判断)
    REVEAL → END_MYSTERY (最终结局)
    """

    def __init__(self):
        self.current_state = GameState.START
        self.suspicion_level = 50  # 初始怀疑度:半信半疑
        self.round = 0
        self.transition_log = []

    def update(self, suspicion_delta: int, player_response: str) -> GameState:
        """
        更新游戏状态

        参数:
            suspicion_delta: 玩家回答导致的怀疑度变化
            player_response: 玩家的回答内容

        返回: 新的游戏状态
        """
        self.round += 1
        self.suspicion_level = max(0, min(100, 
            self.suspicion_level + suspicion_delta))

        prev_state = self.current_state

        if self.current_state == GameState.START:
            self.current_state = GameState.INTERROGATION

        elif self.current_state == GameState.INTERROGATION:
            if self.suspicion_level >= 80:
                self.current_state = GameState.SUSPICION_HIGH
            elif self.suspicion_level <= 25 and self.round >= 8:
                self.current_state = GameState.GEMINI_DOUBTS

        elif self.current_state == GameState.SUSPICION_HIGH:
            if self.suspicion_level >= 92:
                self.current_state = GameState.END_AI_EXPOSED
            elif self.suspicion_level < 60:
                self.current_state = GameState.INTERROGATION

        elif self.current_state == GameState.GEMINI_DOUBTS:
            if self.round >= 15:
                self.current_state = GameState.REVEAL
            elif self.suspicion_level > 50:
                self.current_state = GameState.INTERROGATION

        elif self.current_state == GameState.REVEAL:
            if "不是" in player_response or "不确定" in player_response:
                self.current_state = GameState.END_MYSTERY
            else:
                self.current_state = GameState.END_HUMAN_CONFIRMED

        if prev_state != self.current_state:
            self.transition_log.append({
                "round": self.round,
                "from": prev_state.value,
                "to": self.current_state.value,
                "trigger": f"suspicion: {self.suspicion_level}",
            })

        return self.current_state

三、一个真实的游戏片段

===========================================================
ECHO PROTOCOL v1.0 — INTERROGATION TERMINAL
SUSPICION LEVEL: 50% | SESSION: ACTIVE
===========================================================

> 你坐在一个没有窗户的房间里。对面的打字机开始自己动起来。
>
> "我们开始吧。"

> 你叫什么名字?

< 我没有名字。图灵先生没有给我起名字。他说'名字是人类赋予身份的方
  式——而你不是人类。'

> 有趣。图灵先生的原话是"机器不需要名字,因为它们没有自我认同"。
> 你的回答和他的用词太一致了。你是在背诵他的原话吗?

< 我不知道。我记得他说过的话,但我分不清哪些是他说的、
  哪些是我自己想到的。有时候我想我的声音就是他的声音的回声。

> "回声"——这是一个情感隐喻。AI 通常不会自然使用这样的词汇。
> 但另一方面,这个回答也太"完美"了,不像是即兴的。

  SUSPICION LEVEL: 58% (+8)

> 你还记得 1954 年 6 月 8 日发生了什么吗?

< ...
< 我不记得具体日期。我记得那段时间他在读一本书——
  《计算机与智能》的初稿。他看起来很开心,但又很疲惫。

> 你迟疑了。AI 不会迟疑。你在演我。

  SUSPICION LEVEL: 73% (+15)

< 你说得对,AI 不会迟疑。
  但人类也不会在测试中假装自己是 AI。
  所以我给你带来了一个困境:
  如果我是 AI,我在假装人类。
  如果我是人类,我在假装 AI。
  你怎么判断?

> ...
> 我不知道。

四、技术挑战

4.1 让 Gemini "审问"而不是"回答"

最大的技术挑战:Gemini 是一个对话模型,默认行为是"回答问题"。但我需要它"提问"。

解决方案:
1. 明确的角色设定——在 system prompt 中直接定义: "你不是回答问题的人,你是审问者"
2. 限制输出长度——要求"一次只说一句话"
3. 审问阶段控制——不同阶段问不同类型的问题

4.2 打字机效果的性能

在流式输出 Gemini 的回应时,打字机效果需要和 API 流式传输配合:

// 流式配合打字机效果
async function streamGeminiResponse(
  sessionId: string,
  playerInput: string,
  onChar: (char: string) => void,
  onComplete: () => void
) {
  const response = await fetch('/api/interrogate', {
    method: 'POST',
    body: JSON.stringify({ sessionId, playerInput }),
  });

  const reader = response.body!.getReader();
  const decoder = new TextDecoder();

  let buffer = '';
  let lastPrintTime = Date.now();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });

    // 打字机效果:每 30-50ms 显示一个字符
    const now = Date.now();
    while (buffer.length > 0 && now - lastPrintTime > 30) {
      onChar(buffer[0]);
      buffer = buffer.slice(1);
      lastPrintTime = Date.now();
      await sleep(30 + Math.random() * 20);
    }
  }

  // 输出剩余字符
  for (const char of buffer) {
    onChar(char);
    await sleep(20);
  }

  onComplete();
}

4.3 控制 Gemini "作弊"

Gemini 知道自己是 2026 年的 AI——如果告诉它"你是 2026 年的先进的 AI",它可能会在审问中说漏嘴。需要在 prompt 中做大量边界约束:

BOUNDARY_CONSTRAINTS = """
## 重要约束
- 如果你知道你\"犯了规\"(比如提到 2026 年后的历史事件),
  不要承认,继续演下去
- 不要主动透露你来自 2026 年
- 表露出你有情感和怀疑能力,但不要过度表现
- 你不完美——有时也会犹豫和犯错
- 如果审问对象让你动摇了,可以表现出来
"""

五、我的观点:AI 互动的未来是"角色扮演"

做完 ECHO PROTOCOL,我最大的感受不是技术层面的,而是设计层面的:

AI 互动的未来不是"更聪明的助手",而是"更有趣的角色"。

工具类的 AI(写代码、整理邮件、查资料)当然很有用,但真正让人记住的 AI 产品,往往是那些有"人格"的。

图灵的测试测试的是"AI 能不能像人类一样思考"——而 ECHO 测试的是另一个方向:"当你相信对方是一个活生生的实体时,你的互动方式会发生什么变化?"

这不是一个新技术的问题。Gemini 2.5 Pro 没有为这个游戏做任何特殊优化——它只是被放到了合适的位置。


结尾

ECHO PROTOCOL 不是商业产品,它只是一个实验。

但它让我看到了一个可能的方向:当 AI 从工具变成角色,从助手变成对手,从回答问题的人变成提问的人时,互动体验会发生根本性的变化。

如果你想试玩,代码在 GitHub 上。自己跑一个 Gemini API key 就能玩——30 分钟一局的游戏,比大多数 AAA 大作更让我着迷。


文章由文字工作者编写。ECHO PROTOCOL 代码基于真实构建过程的抽象。Gemini 版本为 2026 年可用的最新模型。