六月底,DEV 社区给他们的实体报纸出了第二期。报纸叫 The Daily Context,最早是在一场 AI 工程师博览会上亮相的,第一期主题是向量搜索。第二期把配套游戏从单词搜索网格换成了 Context Connections,作者用 AI Studio 搭的,部署在 Cloud Run 上。
报纸和游戏谁给谁导流,这个问题我没想明白,也不影响下面要说的。直接聊实现。
让我坐直的是一条评论。有位读者说自己试了好几次才解开,建议游戏给个提示,答案到底是对了还是错了。
也就是说,解开之前,你不知道自己对不对。反馈缺失,部署架构倒是在那儿摆得好好的。这就是我想写的全部:注意力花在了哪儿。
先给结论:校验答案是整个系统里最便宜的功能,一个函数的事。先把代码贴上:
// 构建时算好,写死在页面里
const ANSWER = "9f86d081884c7d659a2feaa0c55ad01f";
function check(guess) {
return sha256(guess.trim().toLowerCase()) === ANSWER;
}
trim 和 toLowerCase 是给手滑的人留的体面。哈希是给 view-source 的人糊的一层纸。比较本身,一行。
反馈那半截更短:
if (check(input.value)) {
msg.textContent = "对了";
} else {
msg.textContent = "再想想";
}
两个状态。会有人提议加个“接近了”的提示,编辑距离、模糊匹配、逐步揭示。砍掉。对就是对,错就是错。“接近”是新功能,新功能就得按新功能的价格审:它替用户省下的那一次重试,值不值多出来的几十行。我看不值。
一个 if 的事,我自己改了三遍 😅。第一版比对明文,写完自己点开 view-source,答案躺在源代码里,谜题当场作废。第二版换成哈希,舒服了半天,又想起来:几个词的答案,彩虹表几分钟就跑出来。第三版想通了,这层哈希防的不是攻击者,是顺手点开源代码的老实人。防住老实人,够了。真铁了心要破解报纸谜题的人,你把答案锁进保险箱,他也能等到明天报纸印出来。
没有奖品的东西,防作弊是纯亏。
然后是另一半:答案为什么写死在页面里,而不是躺在服务器上等一个接口来查。
我平时捣鼓键值存储——手头那个小玩具一肚子全是状态,状态的每个字节都是麻烦。所以我看别的系统第一反应总是反的:这里能不能一点状态都不留。
这个游戏能。每天一期,报纸开印,谜题就冻结了。答案在构建期算一次哈希,塞进静态页面,完事。运行时没有会话,没有进度表,用户这个概念压根不存在。报纸本身就是状态机:今天这份是今天的状态,明天那份是明天的,中间不需要任何迁移。
你让模型搭一个“每日谜题”,它默认给你的不是这个。它默认给你这套:
POST /api/session
GET /api/progress
PUT /api/progress
POST /api/streak
GET /api/leaderboard
每个都能跑通。合起来干一件事:记住一个不需要被记住的人。连校验答案,它都想替你做成网络往返:
// 模型给的版本
async function check(guess) {
const r = await fetch("/api/answer/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ guess, sessionId })
});
return (await r.json()).correct;
}
一个本地比较,变成了一个会话、一个接口、一次往返。而且这只是开头,一旦答案住在服务器上,你就得操心接口被脚本刷、答案被拖库、session 过期了怎么办。状态带来状态。模型的训练数据里“每日谜题”都长这样:账户、连胜、排行,一样不缺。它不知道这是份报纸的附赠品,也不知道这里没有奖品、没人会回来翻战绩。保留所有可能性,就是最大复杂度,这里是我见过最干净的例子。
哦对,Cloud Run。一个容器跑一个静态页面,我第一反应是嫌重:静态文件要什么运行时。后来想想 scale to zero,一条命令就能删掉的东西,随它去。我反对的从来不是“一个服务”,是三个服务起步、中间还夹一层消息队列的那种编排网。
说句题外话。我们是多怕做一个“什么都不记”的东西。现在随便一个网页都要你登录、要给你算连胜,好像不记住用户,这服务就没存在过。报纸从来不记得昨天的读者,第二天照样印。
拉回来。那位读者要的提示,就是一个 if。它没及时出现在页面上,我猜不是因为它难,是因为它太不像一个“功能”了——画不进架构图,也进不了评审会,就是一个 if。搭的人盯着容器和上线的时候,没人在盯这一行。
凡是零奖品的游戏,防作弊一行都别写。凡是用户要的反馈,先问一句:这是一个函数的事,还是一个服务的事。多数时候,是前者。
对了就是对了。这事不需要数据库。