
Ken Thompson 不是那种天天在媒体上谈论 AI 的布道者。当行业里所有人都在追大模型参数、比 Agent 工具链数量的时候他依然用几十年前那套工程直觉给 AI 时代提出了一个绕不开的问题任何自动生成的代码本质上只是另一个需要验证的输入。这个视角比单纯比较各家模型的跑分更有参考价值。这次我们来看 Ken Thompson 对 AI 的启发。准确地说不是翻他有没有说过哪句关于大模型的预言而是把他职业生涯里真正经过验证的方法论——1983 年图灵奖演讲里的“信任链”、Belle 国际象棋机器里的“暴力求解”、Go 语言里的“克制设计”——映射到今天的大模型工程实践上。这些思想能直接回答几个实际问题AI 生成的代码能不能信、大模型系统为什么越搞越复杂、做 AI 应用时应该先做什么后做什么。这篇文章会把他的核心思想拆成可以直接落地的判断框架并结合代码示例给出验证方法。无论你是做 AI 应用开发、模型部署还是写 AI 编程工具都可以对照着检查自己的工程流程。读完你会得到一套判断工具什么时候该信任 AI 输出、什么时候该用“笨办法”兜底、怎么给大模型系统做减法。1. Ken Thompson 是谁系统工程师看 AI 的天然视角先说清楚背景否则后面所有讨论都没有锚点。Ken Thompson 是计算机科学史上绕不开的名字。1969 年他在贝尔实验室和 Dennis Ritchie 一起开发了 Unix 操作系统之后又设计了 B 语言直接催生了后来的 C 语言1983 年他和 Dennis Ritchie 因为 Unix 的贡献获得图灵奖2000 年后他加入 Google和 Rob Pike、Robert Griesemer 一起设计了 Go 语言。此外他和 Rob Pike 还共同设计了 UTF-8 编码。这些成就足够让人记住他但真正值得 AI 开发者研究的是他工程实践里反复出现的三个主题对计算资源的高效使用、对系统复杂度的强烈警惕、对“不可信输入”的深刻理解。维度信息核心贡献Unix、B 语言、Go 语言、UTF-8、Belle 国际象棋计算机图灵奖1983 年与 Dennis Ritchie 共同获得代表演讲Reflections on Trusting Trust1983 年图灵奖演讲技术经历贝尔实验室后加入 Google工程信条“When in doubt, use brute force”与 AI 的关联Belle 博弈搜索、编译器信任链理论、Go 在大模型基础设施中的广泛使用这里有一个容易被忽略的事实Thompson 不是没有碰过 AI。他在贝尔实验室开发的 Belle 国际象棋计算机本质上就是一个早期博弈 AI 系统。它用专门硬件加速搜索配合 alpha-beta 剪枝在 1983 年成为第一个拿到美国国际象棋联合会大师等级称号的计算机程序。那条“拿不准就用暴力求解”的格言不是空谈而是在真实棋局里被验证过的经验。所以他对 AI 的发言权重来自系统底层和博弈搜索两个方向而不是 PPT 方向。2. 核心思想速览Thompson 给 AI 时代的五条判断把 Thompson 的观点压缩成表格适合先建立一个整体印象。下面是他的核心思想与 AI 工程实践之间的对应关系。Thompson 的思想原始出处对 AI 工程的映射编译器信任链可以被恶意代码利用1983 年图灵奖演讲AI 生成代码是不可信输入必须审查和验证拿不准时用暴力求解长期编程实践格言先跑通正确性再谈优化用穷举和模糊测试建立基线系统越简单越好Unix 与 Go 语言设计理念模型服务、Agent 工具链应避免过度设计代码是给人读的其次才是给机器执行Unix 内核设计风格Prompt、模型输出和工程代码都要保持可读性自动化增强人的判断而不是取代判断对编译器和工具链的长期改造实践AI 成为编码辅助但正确性责任仍在人这五条不是学术结论而是他在真实系统里反复验证过的经验。下面几个章节逐条展开每一节都给出对应到实际开发的方法。3. 信任链思想AI 生成代码时代的第一安全课1983 年Ken Thompson 在 ACM 图灵奖颁奖典礼上做了一场名为《Reflections on Trusting Trust》的演讲。这场演讲今天看起来几乎像是为 AI 编程时代准备的。他展示了一个实验修改 C 编译器的源码让它在编译某个登录程序时自动往生成的可执行文件里插入一段后门逻辑。关键在于第二步用这个被污染的编译器再去编译一份完全干净的编译器源码生成的编译器仍然带毒。即使到最后你手里的源码是干净的编译器也是自己亲手编译的后门依然存在。这个实验引出的结论在安全领域被反复引用你无法信任一个你未完整参与构建过程的二进制程序。信任本身是整个计算链路里最脆弱的环节。把这条逻辑平移到大模型时代会得到一个很扎心的结论AI 生成的代码本质上就是我们今天面对的“不可信编译器”。当前 AI 编程工具的典型流程是开发者写一段 prompt模型返回一段代码开发者把代码粘进 IDE然后运行。很多人省掉了中间的审查环节因为“模型看起来写得挺对”。但问题在于模型生成代码时它自己无法证明代码在任何语义上都正确也无法保证不存在隐蔽的逻辑缺陷。这和 Thompson 当年面对编译器的处境一模一样你拿到一个能跑的结果但你对它内部的可信度一无所知。这里可以做一张对照表把传统信任链和 AI 信任链并排看环节传统编译器AI 代码生成输入人类编写的源码人类编写的 Prompt处理过程固定规则编译概率采样生成输出可执行文件推荐代码或补丁验证方式测试 安全审计测试 代码审查经常缺失信任风险编译器后门、供应链攻击幻觉、投毒数据、隐蔽逻辑错误从这张表能看出的问题很直接AI 生成代码的风险不在于它多了一个环节而在于很多人默认把最后一个“验证”环节省掉了。工程上应该怎么做至少三条第一把 AI 生成代码当作第三方依赖来管理。不要直接合入主分支先进入独立分支跑静态检查、单元测试和人工审查。步骤可以参考下面的最小化检查流程。# 将 AI 生成的代码视为待审查补丁而不是最终产物 # 假设变基自 feature/ai-generated 分支目标分支是 main git checkout feature/ai-generated git diff main ai_patch.patch # 先做静态扫描再做测试最后人工审查 golangci-lint run ./... 21 | tee lint.log go test ./... -race -count1 21 | tee test.log # 全部通过后再通过 MR 合入主分支第二给 AI 生成的代码补一层“验证白名单”限制其可以访问的 API 和文件路径。尤其在涉及数据库操作、外部网络请求、权限变更的代码上不能因为“生成代码看起来正常”就直接放行。第三保留一个不可信沙箱环境。对 AI 生成的脚本先在一个无关键权限的容器里运行确认行为符合预期后再放到生产环境。Thompson 在 1983 年就证明了一件反直觉的事不是只有源码投毒才算攻击整个构建链路上任何一个环节都可能是攻击面。今天的 AI 编程工具链里模型权重、Prompt、代码补全插件、训练数据每一层都可能被污染单纯相信“我用的模型很主流”是不够的。4. 暴力求解哲学从 Belle 到神经网络“When in doubt, use brute force”这句话在国内技术社区通常被翻译成“拿不准就暴力破解”但它在 Thompson 的语境里并不是鼓励蛮干而是一种排序准则当你不确定最优算法是否真的能解决问题时先用最简单的办法跑通正确性然后再决定是否值得优化。这个思想在他开发的 Belle 国际象棋计算机里体现得最清楚。Belle 是 Thompson 和 Joe Condon 在贝尔实验室合作开发的国际象棋计算机硬件上使用专用电路加速棋盘评估和走法生成软件上使用 alpha-beta 剪枝搜索。它的思路在当时非常直接用硬件计算量换取搜索深度而不是在评估函数里堆砌人工知识。相比早期依赖大量手工调优的国际象棋程序Belle 代表了“用更多算力暴力推进搜索深度”的路线。这条路线后来在整个博弈 AI 领域被证明是有效的。更深层的意义在于Thompson 从来不排斥“笨办法”前提是“笨办法能先给出正确答案”。他的原话经常被传播成一种程序员幽默但背后是工程效率的判断大多数系统瓶颈不在算法复杂度而在于团队还没拿到一个干净的基线就开始优化局部的性能。把这条经验搬到神经网络时代会发现一个惊人的相似性。深度学习模型本质上就是在一张巨大的参数网络上用梯度下降做暴力函数拟合。它没有显式设计规则而是通过大量样本把输入输出关系硬生生拟合出来。GPT 系列如此扩散模型也如此。那些被渲染成“智能涌现”的能力底层其实就是更大规模的计算和更大规模的数据暴力组装。这对实际工程意味着三点第一做 AI 功能时先跑通基线再谈优化。不要一上来就设计多层 Agent、上 RAG、调提示词模板。先用一个最简单的模型加一个最小 Prompt把输入输出链路跑通测量那个版本的准确率和延迟。基线能跑通后续优化才有对比对象。第二当不确定某个系统模块是否可靠时用穷举和边界测试验证不要依赖模型“自觉”。下面这个 Python 示例演示了一个暴力回归脚本对文本处理函数穷举特殊字符和边界输入import random import string def validate_text_function(func): cases [] # 暴力生成一批边界输入 cases.append() cases.append(a * 1) cases.append(a * 1000) cases.append(\n) cases.append(\t) cases.append(中文测试) cases.append( * 5) cases.append(/.join(random.choices(string.printable, k200))) failures [] for case in cases: try: result func(case) # 只做基础断言验证没有崩溃且输出类型正确 assert isinstance(result, str) except Exception as exc: failures.append((case, exc)) if failures: print(f发现 {len(failures)} 个失败输入) for case, exc in failures[:10]: print(f输入: {repr(case[:20])}异常: {exc}) return False print(所有边界输入通过) return True # 示例用随机输入验证文本清洗函数 def clean_text(text: str) - str: return text.strip() validate_text_function(clean_text)这段脚本的设计原则就是 Thompson 式的不依赖模型对输入分布的假设直接暴力覆盖边界情况先把异常暴露出来。在接入任何大模型输出时这种暴力回归脚本都非常值得保留因为模型输出的边界情况远比传统函数返回值的边界情况更难以预测。第三暴力求解不等于放弃理论。Thompson 做 Belle 时依然用了 alpha-beta 剪枝没有只靠全盘遍历。暴力求解的准确含义是在资源和时间约束下先用最笨且能保证正确的方法解决不要为了“优雅”而引入尚未验证的复杂度。对应到 AI 开发就是优先选择经过社区验证的成熟模型和框架而不是追逐每天更新的新 Agent 协议除非新方案能在你的数据集上明确胜出。5. Unix 哲学与 Go 的克制AI 系统设计的反复杂化清单Thompson 参与的 Unix 和 Go 是两个设计风格高度一致的系统都是把“简单”放在第一位。Unix 的核心哲学被总结为“Do one thing and do it well”每个工具只做一件事通过管道组合完成复杂任务。Go 语言的设计思想更加明显没有继承、没有泛型早期版本、没有异常机制用 package 和 interface 组织代码编译速度快语法简单到几乎没有学习成本。它的目标不是创造一种功能最强的语言而是创造一种团队协作时不会被各人风格撕裂的语言。这两段经历对今天 AI 系统设计的启发可能比它们当年对操作系统的影响更重要。因为 AI 系统的复杂度膨胀速度是所有软件类别里最快的。一个典型的大模型应用常常同时包含Prompt 模板系统、RAG 向量库、Agent 编排框架、插件市场、可观测性组件、多个模型网关。这些东西单独看都有价值但合在一起后调试成本会呈指数级增长。你很难判断一个偶发错误是模型幻觉、检索器返回了噪声还是 Agent 编排框架的并发 bug。Thompson 的应对策略是默认不要引入一个组件直到你证明了它的必要性。可以把这个原则转成一组检查清单适用于任何 AI 项目这个 Agent 框架真的减少了代码量还是增加了一层抽象如果不能明确回答就不用。这个 Prompt 模板系统支持版本管理和回滚吗如果只是拼接字符串那字符串拼接就够了。这个向量库解决的是十万级以上的检索需求还是几百条的玩具场景小场景用 JSON 文件加暴力线性扫描可能更稳。这个“多模型切换”功能今天确认需要了吗如果只有一个模型供应商就不要提前做模型网关。Go 语言在这里还有一个非常实际的联动今天大量大模型基础设施包括 Docker、Kubernetes、部分向量数据库、多种 LLM 网关都是 Go 语言实现的。Thompson 留下的简洁语言成了 AI 时代 infra 层的事实标准之一。如果你同时写 Go 和 AI 服务会发现 Go 的并发模型天然适合做模型请求的批处理和 queue worker。下面是一个最小化的模型服务编排示例用 Go 风格展示“简单优先”的并发 workerpackage main import ( fmt sync ) // Task 代表一个待处理的模型请求 type Task struct { ID int Text string } func worker(id int, tasks -chan Task, wg *sync.WaitGroup) { defer wg.Done() for task : range tasks { // 生产环境这里替换为真实模型推理调用 fmt.Printf(worker %d 处理任务 %d: %s\n, id, task.ID, task.Text) } } func main() { const workerCount 3 tasks : make(chan Task, 10) var wg sync.WaitGroup for i : 0; i workerCount; i { wg.Add(1) go worker(i, tasks, wg) } for i : 0; i 10; i { tasks - Task{ID: i, Text: fmt.Sprintf(请求 %d, i)} } close(tasks) wg.Wait() }这个例子没有引入任何第三方库只用了 Go 标准库的并发原语。对应到 Thompson 的哲学能用标准库解决的问题不要急着上框架。尤其在 AI 工程的批处理场景一个简单的 worker 池往往比一套分布式任务调度系统更容易维护和定位问题。6. 把 Thompson 思想用在 AI 工程一套可执行的判断框架前面几节是理论映射这一节直接给操作步骤。把 Thompson 的三条核心经验信任链、暴力求解、简单性合并成一个判断框架从选型到上线都适用。6.1 选型阶段先看信任边界问自己三个问题这个模型或框架是否由可信组织维护它能不能离线部署或者能否做到数据不出域它的安全公告渠道和版本更新策略是什么如果三个答案里有两个是否定的那就必须为它单独设计一个沙箱环境或者用一层包装把它隔离在核心业务之外。对正在做本地部署的团队来说这条格外重要模型文件、依赖库、权重本身都来自外部任何一个环节被污染都可能影响整个系统。6.2 开发阶段先暴力验证再精细优化不要一上来就调参和设计复杂策略。先跑通最小链路然后用边界输入和随机输入做暴力回归把基础正确性钉死再进入性能优化和 Prompt 迭代。# 一个简单的暴力验证框架适合在接入大模型前后做回归 import itertools def brute_force_validate(fn, sample_pool): # 穷举短序列组合覆盖可能出现的相邻字符/词组合 results [] for length in range(1, 4): for combo in itertools.product(sample_pool, repeatlength): text .join(combo) try: out fn(text) results.append((text, out, ok)) except Exception as exc: results.append((text, str(exc), error)) return results # 示例验证一个文本路由函数 def route_text(t): if len(t) 10: return short return long report brute_force_validate(route_text, [hello, 中文, a, test]) print(f共测试 {len(report)} 组输入)这类暴力回归的代码不复杂但能帮你快速暴露输入侧的问题让你在模型能力和系统稳定性之间找到明确的分界线。6.3 上线阶段保持简单可观测上线时不要追求一个“全能”的 AI 服务。最小化接口、最小化依赖、最小化调用链。给每个模型调用加上完整的日志包含输入长度、模型名称、推理耗时、token 用量、返回状态码。没有日志的 AI 服务出现问题就是盲人摸象。一个最小化的 FastAPI 模型服务接口设计如下from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class GenerateRequest(BaseModel): prompt: str max_tokens: int 512 class GenerateResponse(BaseModel): result: str model: str usage: int app.post(/generate, response_modelGenerateResponse) def generate(req: GenerateRequest): # 生产环境这里替换为实际模型推理并在日志中记录 req.prompt 长度 usage len(req.prompt.split()) return GenerateResponse( resultfecho: {req.prompt[:20]}, modelexample-model, usageusage, )这个接口刻意不包含用户鉴权、多模型路由、任务队列等进阶功能。原因是第一版能跑通才能知道后续哪些功能是实际需要的而不是为了“架构完整”提前写好一堆没人用的代码。6.4 维护阶段持续审查数据与模型输出Thompson 的信任链思想要求我们不假设任何输入天然可信。AI 系统上线后要定期做输出抽样审查、数据漂移检测和自定义 prompt 注入测试。尤其在接入员工内部工具或对外服务平台时数据的合规授权和用户隐私保护必须从设计阶段就考虑而不是上线后再补。7. 常见误区与辨析关于 Ken Thompson 和 AI 的关系网络上有几种容易被放大的说法这里统一辨析一下。误解事实与修正Thompson 反对 AI他的工作Belle 博弈搜索、Go 基础设施与 AI 有直接关系更准确的说法是他对 AI 持工程务实态度不迷信宣传“暴力求解”等于不用算法拼蛮力他在 Belle 中使用了 alpha-beta 剪枝暴力求解的真实含义是“先用简单正确的方法建立基线再优化”AI 生成代码可以像传统代码一样直接信任他的 Trusting Trust 演讲证明构建链路的任何环节都可能被污染AI 生成的代码应该视为不可信输入需要单独审查和验证Unix/Go 的简单哲学不适用于复杂大模型系统恰恰相反越复杂的系统越需要默认克制的设计入口否则调试成本会压垮团队只要用 AI 做了自动化人的责任就转移了Thompson 的编译器实验说明自动化可以扩展人的能力但不能消除人对输出正确性的最终责任第四条值得多说两句。大模型系统的复杂度已经不是“要不要引入”的问题而是“如何在复杂中保持可控”。Go 语言用“明确胜过巧妙”的设计理念处理了语言复杂度对应到 AI 工程就是每个组件都要有明确的责任边界和可测试性。如果一个组件无法单测无法替换无法解释那它就是新系统中等待爆雷的部分。8. 实践建议从今天开始可以做的三件事思想层面的东西讲再多不如落地三件事。第一件事给 AI 生成代码加一层“信任边界”。从现在开始凡是 AI 编写的代码不直接合入主分支。单独开出分支跑静态扫描、单测和人工 review全部通过才能合入。这个流程会损失一点速度但能显著降低隐蔽逻辑缺陷进入生产的概率。第二件事用“暴力回归”给系统建立基线。写一个不依赖业务逻辑的通用边界测试脚本把空值、超长文本、Unicode 字符、随机字符串往模型输入输出回路里丢。第一次跑大概率会发现几个让你意外的崩溃点。这些意外点就是系统最需要修复的地方。第三件事给 AI 应用做一次减法。打开你的项目依赖文件和调用链问每个组件它今天被用到了吗如果不用它系统会变简单多少把这个问题的答案写进技术文档作为后续迭代的约束。组件不是越多越好判断标准是它能否让系统的可理解性变强。9. 总结与下一步Ken Thompson 对 AI 的价值不在于他是否留下了一句能被反复引用的预言而在于他提供了一套从系统和工程出发的判断方法。面对 AI 生成代码时能想起来“不可信输入”这个前提面对不确定的方案时能先跑暴力基线再谈优化面对越来越复杂的系统时敢做减法而不是加框架。这三条经验比任何模型榜单都更接近工程实践的本质。如果你想深入建议从三份材料入手第一是《Reflections on Trusting Trust》原文篇幅很短理解它只需要 20 分钟但对 AI 编程安全的理解会提高一个层次第二是 Belle 国际象棋计算机的相关技术文献看它如何在硬件上做搜索加速第三是 Go 语言官方文档和开源社区观察一套被大模型基础设施广泛采用的系统如何用克制设计保持生产力。把这三样东西消化完再回头看今天的大模型工程问题你会多一层自己的判断标准。建议收藏备用后面做 AI 工程选型和安全检查时可以回来对照这套框架。