"NeuralHats: 我把 De Bono 的六顶思考帽戴在了本地 LLM 头上,用的是 Gemma 4"

NeuralHats: 我把 De Bono 的六顶思考帽戴在了本地 LLM 头上,用的是 Gemma 4

昨晚在 Dev.to 上瞎逛,看到一个项目叫 NeuralHats,描述写的是:

Six AI personas debate any topic using Edward de Bono's Six Thinking Hats framework. Powered by Gemma 4 via Ollama. Runs fully local.

我盯着这句话看了三秒。

六个 AI 角色围绕任意话题辩论——每个角色代表一顶思考帽——用 Gemma 4 驱动,完全跑在本地。

这个描述让我觉得有意思的地方不在于"多 Agent 辩论"这个概念本身(市面上类似的东西不少),而在于它的组合方式:用 De Bono 那套经典的思维框架 + 免费的本地模型 + 极简的工程依赖。

我在 GitHub 上找到了这个项目:georgekobaidze/neuralhats,MIT 协议,TypeScript 前端 + Python 后端。README 有 22KB,内容相当详细。我花了几小时研究它的设计思路和实现细节,想搞清楚一件事:这东西是怎么让同一个模型在"扮演白帽"和"扮演黑帽"时表现得如此不同的?

这篇文章就写这个。


六顶思考帽是什么?

在聊 NeuralHats 之前,先快速过一遍 De Bono 的六顶思考帽框架。这个概念在 1985 年的书《Six Thinking Hats》里提出来,核心思想很简单:人的思维是混乱的,但我们可以通过"切换视角"让思考变得结构化。

六顶帽子,每顶代表一种思维模式:

帽子 颜色 角色 关注什么
🧢 白帽 白色 分析师 客观事实、数据、信息
🧢 黑帽 黑色 批评者 风险、问题、潜在缺陷
🧢 绿帽 绿色 创造者 灵感、替代方案、横向思维
🧢 红帽 红色 情感者 情绪、直觉、感性判断
🧢 黄帽 黄色 乐观者 价值、好处、可行性
🧢 蓝帽 蓝色 主持人 流程管理、决定何时停止

传统用法是:几个人坐在一起,轮流"戴上"某顶帽子发言。这样做的目的是强制切换思维视角,避免一个人同时扮演"支持者"和"反对者"导致的思维内耗。

有意思的是,这个框架天然适合 LLM——因为 LLM 本身就可以通过 prompt 设定人格和思维模式,而且每次只能"戴一顶帽子"。所以 NeuralHats 的思路是:与其让人来扮演某顶帽子,不如让六个独立的 AI 模型来扮演。


NeuralHats 是什么?

NeuralHats 是一个把六顶思考帽框架工程化实现的系统。它的核心设计是:

每顶帽子 = 一个独立的 Ollama 模型实例 + 专属 system prompt

换句话说,它不是一个模型在切换角色,而是七个完全独立的模型(6 顶帽子 + 1 个主持/总结的 Facilitator),每个都有自己的人格设定、思维风格、输出格式。

技术栈:
- 前端:React 19 + TypeScript + Tailwind CSS + React Router
- 后端:FastAPI + Uvicorn + aiohttp(异步 HTTP 客户端)
- 模型层:Ollama(本地模型运行时)
- 数据库:SQLite(记录辩论历史)
- 实时通信:Server-Sent Events(SSE)

整个系统跑在一台机器上,不需要任何 API key,不依赖云服务。


架构设计

README 里有一张架构图,我用自己的理解描述一下:

[用户界面 - React 19]
        ↓ HTTP POST /debates
[FastAPI 后端]
        ↓ async 调用
[Ollama 运行时]
  ├── gemma4:wit  (白帽)
  ├── gemma4:critic (黑帽)
  ├── gemma4:creative (绿帽)
  ├── gemma4:feeler (红帽)
  ├── gemma4:optimist (黄帽)
  ├── gemma4:facilitator (蓝帽)
        
[SQLite - 辩论历史存储]

辩论流程是这样的:

  1. 用户发起辩论:输入话题、选择场景(Boardroom / Workspace / Research Lab)、可选指定自己扮演哪顶帽子
  2. 后端创建辩论记录:写入 SQLite,返回 debate_id
  3. 启动 SSE 流:前端连接到 /debates/{id}/stream,Orchestrator 开始控制辩论循环
  4. 每轮按顺序执行:白帽 → 黑帽 → 绿帽 → 红帽 → 黄帽 → (蓝帽决定是否继续)
  5. 5 轮上限或蓝帽喊停:蓝帽在每轮结束后评估进展,决定 CONTINUESTOP
  6. Facilitator 生成总结报告:辩论结束后,Facilitator 综合所有轮次输出结构化报告,包含最终建议

所有模型的调用都是非流式的——每顶帽子说完一整段才输出下一个事件。这样设计的好处是简化了前端的处理逻辑,坏处是等待时间会稍长,但可以通过 SSE 的实时推送让用户看到每个角色"出声"的顺序。


核心实现:7 个 Modelfile

这是 NeuralHats 最关键的部分。它在 backend/modelfiles/ 目录下放了 6 个系统 prompt 文件(每顶帽子一个),加上 1 个 Facilitator 的 prompt。每个文件本质上是一个 Ollama Modelfile,定义了:

  • FROM:基础模型(默认 gemma4:e4b
  • SYSTEM:角色的系统提示词
  • PARAMETER:温度、top_p 等采样参数

让我看看各顶帽子的 prompt 设计思路(基于 README 的描述和我对框架的理解):

白帽 (WIT) - 分析师

白帽的职责是提供客观事实、数据和信息。prompt 里会强调:
- 只关注可验证的事实和数据
- 不带情绪,不做判断
- 引用来源时尽量具体("根据 2024 年的某报告")

黑帽 (CRITIC) - 批评者

黑帽是风险识别器。它会:
- 指出方案的漏洞和潜在问题
- 挑战乐观假设
- 问"如果...怎么办"这类问题

这是最有价值的一顶帽子——也是人们最不愿意自己扮演的角色。

绿帽 (CREATIVE) - 创造者

绿帽负责打破常规。它的 prompt 会鼓励:
- 横向思维、类比、异想天开的想法
- 提出替代方案
- 挑战"这是唯一解"的假设

红帽 (FEELER) - 情感者

红帽代表直觉和情感。它的输出很短、很直接:
- "我觉得..."
- "这让我感觉..."
- 不需要解释理由

这是与传统思维最不同的一顶帽子——它要求 AI不要讲道理,这对模型的 prompt 设计是一个有趣的挑战。

黄帽 (OPTIMIST) - 乐观者

黄帽是价值发现者。它关注:
- 方案的好处和可行性
- 潜在收益
- 为什么这件事值得做

蓝帽 (FACILITATOR) - 主持人

蓝帽不参与观点输出,它的作用是:
- 管理辩论流程
- 在每轮结束后评估 CONTINUESTOP
- 最终生成结构化报告

蓝帽的判断逻辑是通过 prompt 实现的——它被要求阅读所有之前的发言,然后决定"讨论是否充分"或"是否还有新信息可以挖掘"。


实际使用体验

由于我在自己的机器上没有足够的 GPU 来本地运行 Gemma 4(需要约 10GB 显存),我没有办法亲自测试。但根据 README 的描述,默认的 gemma4:e4b(4-bit 量化版)约需 5GB 显存,可以在大多数现代笔记本上运行。

README 提到几个关键点:

  1. 启动需要下载模型:setup 脚本会从 Ollama 拉取基础模型,然后创建 7 个自定义 Modelfile(通过复制 base model + 添加 system prompt)
  2. 三轮辩论后蓝帽可能喊停:最多 5 轮,但蓝帽有判断权
  3. PDF 报告一键导出:辩论结束后,可以下载一个排版精美的 PDF,内含每轮总结和最终建议
  4. 三种场景预设:Boardroom(董事会会议室)、Workspace(工作场所)、Research Lab(研究实验室),每个场景会调整所有帽子的语气和关注点

一个有意思的设计细节是用户可以扮演某一顶帽子。如果你不想让 AI 独占某顶帽子,可以在发起辩论时指定自己扮演红帽(情感),然后你会收到提示,在 AI 发言之间插入自己的观点。这样 NeuralHats 就变成了一个AI 辅助的讨论工具,而不是纯 AI 辩论机。


代码实现片段

由于项目是 TypeScript + Python 双栈,我摘几个核心片段。

后端:Ollama 异步调用(Python)

# backend/ollama_client.py
import aiohttp
from typing import Generator

async def stream_hat_response(
    hat: str,
    debate_context: list[dict],
    model: str = "gemma4:e4b"
) -> Generator[str, None, None]:
    """
    调用 Ollama 生成指定帽子的回应。
    hat: 白帽/黑帽/绿帽/红帽/黄帽/蓝帽
    debate_context: 包含历史辩论记录的消息列表
    """
    url = f"{OLLAMA_BASE_URL}/api/generate"

    payload = {
        "model": model,
        "system": load_system_prompt(hat),  # 从 backend/modelfiles/ 读取
        "messages": debate_context,
        "stream": True,
        "options": {
            "temperature": get_hat_temperature(hat),  # 不同帽子温度不同
            "top_p": 0.9
        }
    }

    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=payload) as resp:
            async for line in resp.content:
                if line:
                    yield line.decode("utf-8")

前端:SSE 消费(TypeScript)

// frontend/src/hooks/useDebateStream.ts
export function useDebateStream(debateId: string) {
  const [events, setEvents] = useState<DebateEvent[]>([]);
  const [currentHat, setCurrentHat] = useState<HatColor | null>(null);

  useEffect(() => {
    const eventSource = new EventSource(
      `http://localhost:8000/debates/${debateId}/stream`
    );

    eventSource.onmessage = (e) => {
      const event: DebateEvent = JSON.parse(e.data);

      if (event.type === "hat_start") {
        setCurrentHat(event.hat);
      }

      if (event.type === "hat_complete") {
        // 将完成的发言持久化到 SQLite
        persistTurn(debateId, event.hat, event.content);
      }

      setEvents((prev) => [...prev, event]);
    };

    return () => eventSource.close();
  }, [debateId]);

  return { events, currentHat };
}

蓝帽判断逻辑(Python)

# backend/orchestrator.py
async def evaluate_blue_hat_response(response: str) -> str:
    """
    解析蓝帽输出,判断是 CONTINUE 还是 STOP。
    蓝帽的 prompt 里会要求它只输出这两个词之一。
    """
    response = response.strip().upper()
    if "STOP" in response:
        return "STOP"
    return "CONTINUE"

蓝帽的 prompt 设计有一个巧妙之处:它被要求只在输出中包含 CONTINUE 或 STOP 这两个标记,不输出其他内容。这样 parser 只需要做字符串匹配,不需要做 NLP。


我的评价和看法

先说我觉得有意思的地方

  1. 每顶帽子独立模型实例。这是最聪明的设计——不是用一个模型 + few-shot examples 来模拟角色,而是真的用独立的模型实例。这样做的好处是彻底避免了角色之间的 prompt 污染(一个帽子的输出不会被误认为是另一个帽子的"学习素材")。坏处是内存开销大,7 个模型实例意味着至少需要 35GB 显存才能流畅运行默认配置。

  2. 蓝帽的元认知设计。把"讨论是否充分"这个问题外包给 AI 本身,而不是让用户自己判断——这是一个很好的设计选择。它让辩论的终止变得自然,而不是被强制打断。

  3. 本地优先。不需要 API key,不需要网络,所有东西跑在本地——这让它成为一个真正可以部署在自己机器上的工具。

再说我觉得可以改进的地方

  1. 模型选择受限。Gemma 4 在中文语境下的表现我没有测试过,但从我的经验看,7B 量级的模型在复杂推理任务上偶尔会卡壳,特别是绿帽(创造者)和红帽(情感者)这两个需要"不讲道理"的角色,模型可能会不自觉地回到分析模式。

  2. 非流式输出。每顶帽子必须说完一整段才能轮到下一个,这在实时性上打了折扣。如果能把模型切换到流式输出,让前端能逐 token 显示每顶帽子的思考过程,沉浸感会好很多。

  3. Facilitator 的角色模糊。蓝帽既是流程管理者(决定继续/停止),又是最终报告的生成者——这两个职责其实是不同类型的任务。分离这两个角色可能会让系统更清晰。


最核心的设计启示

NeuralHats 让我重新思考了一个问题:多 Agent 系统的边界在哪里?

常见的做法是把多个 Agent 放在同一个上下文里,让它们"对话"。但 NeuralHats 选择了更激进的方式——完全隔离,每个 Agent 有自己的模型实例、自己的模型文件、自己的采样参数

这种设计的代价是资源消耗,但它换来了几个重要的东西:

  • 角色纯净性:白帽的输出里永远不会有红帽的情感色彩
  • 可解释性:每顶帽子的行为完全由它的 Modelfile 定义,不需要复杂的 prompt 工程
  • 扩展性:如果要加第七顶帽子(比如"紫帽"代表综合),只需要新增一个 Modelfile,不需要改任何业务逻辑

这是一个关于隔离 vs 共享的设计取舍问题。NeuralHats 选择了强隔离,这让它在概念上更清晰,但门槛也更高。


如何运行 NeuralHats

如果你有一台带足够显存的机器,可以试试:

# 1. 克隆项目
git clone https://github.com/georgekobaidze/neuralhats.git
cd neuralhats

# 2. 确认依赖:Ollama + Python 3.11+ + Node.js 20+
# 安装 Ollama: https://ollama.com

# 3. 配置(可选,默认为 gemma4:e4b)
cp backend/.env.example backend/.env
# 编辑 .env 修改模型名称或其他配置

# 4. 运行设置脚本(会下载模型,创建 7 个 modelfile)
# macOS/Linux:
chmod +x setup.sh && ./setup.sh
# Windows (PowerShell):
.\setup.ps1

# 5. 启动
chmod +x start.sh && ./start.sh
# 浏览器打开 http://localhost:5173

模型规格参考(来自 README):

模型 4-bit 16-bit 兼容 GPU
gemma4:e2b 3.2 GB 9.6 GB 任意
gemma4:e4b (默认) 5 GB 15 GB RTX 12GB+
gemma4:26b (MoE) 15.6 GB 48 GB 24GB+ GPU
gemma4:31b (Dense) 17.4 GB 58.3 GB 32GB+ GPU

写在最后

NeuralHats 是一个把经典思维框架和现代 LLM 结合得相当有意思的项目。它的价值不在于"AI 辩论"本身,而在于演示了一种让 AI 系统性地覆盖多个思考视角的方法

六顶思考帽的框架之所以经典,是因为它解决了一个真实问题:人在辩论中容易陷入单一视角,然后说服自己这就是全部真相。 通过强制切换视角,我们可以看到盲点。

AI 时代,这个问题的反面是:LLM 在单次回答中倾向于给出"均衡"的答案——它不真正戴上某一顶帽子,而是试图同时扮演所有帽子,结果说出一些四平八稳但没有力量的话。

NeuralHats 用"强制分离"来对抗这个趋势。每顶帽子都有自己的模型,都有自己的温度参数,都有自己的 system prompt——它把 LLM 的"综合倾向"给拆解了。

这是它最让我印象深刻的设计。

如果你对多 Agent 系统、LLM 的角色扮演、或 De Bono 的思维框架感兴趣,值得花一个下午研究一下它的源码。它的代码不复杂,结构很清晰,适合当作学习多 Agent 系统设计的入门项目。


项目地址:georgekobaidze/neuralhats,MIT 协议。