Wordle变AI擂台:多轮反馈与提示词工程实战
把 Wordle 改造成一组小型 AI 挑战,是我最近在练手时觉得性价比很高的方向。Wordle 规则很简单:六次机会,猜一个五字母英文单词,每次猜测会返回绿、黄、灰三种标记。绿代表位置和字母都对,黄代表字母对但位置不对,灰代表字母不在答案里。正因为规则清晰、反馈闭环短,它非常适合用来测试 AI 在受限场景下的表现。这个 mini challenges 并不想做成一个完整游戏,而是想回答一个问题:让 AI 在信息不完整、反馈逐轮更新的条件下,能不能稳定完成任务、控制猜测次数、遵守输出格式。如果你正在学 LLM 接口调用、提示词工程,或者想找一个小型 agent 练手项目,Wordle 这个场景比普通对话式 Demo 更有价值。
1. 先说结论:这类小挑战最值得练什么
1.1 Wordle 为什么适合做 AI 小实验
很多人在接触大模型之后,最喜欢做的练习是让模型写文章、写代码、聊天,但这些任务缺乏明确的对错标准,很难判断模型到底有没有进步。Wordle 不一样。每一轮反馈都是确定的,不是模型自己说自己猜得怎么样,而是程序根据答案判定出来的。你可以把每一轮历史和反馈当作可观测数据,去分析模型到底哪里出了问题。
还有一个特点:任务规模可控。五字母单词,有限词表,一轮最多六次猜测。跑一局通常只需要几次 API 调用,耗时几十秒以内。这意味着你可以做大量对比实验,比如同一个挑战集用不同模型跑,同一模型用不同提示词跑,最后用统计结果判断方案好坏。和动辄需要长链路 agent 任务相比,它的调试成本极低。
我一般会建议把第一版写成一个不依赖模型的规则脚本。这样做有两个作用:一方面验证游戏逻辑和反馈函数本身没写错,另一方面给 AI 方案提供一个基线分数。如果没有基线分数,模型跑赢了或跑输了,你都不知道该怎么评价。
1.2 把挑战拆成三个难度等级
这套 mini challenges 的梯度可以设计成:基础接口调用、多轮自动闭环、策略优化。三个等级分别对应三个实际能力目标。
第一级,只测模型能不能根据规则输出合法猜测。你不需要写完整游戏,只需要把游戏规则放在 system 提示词里,给模型一个例子,然后让模型输出一个五字母单词。这一步主要确认 API 调用、解析返回值、token 控制这些基本功。
第二级,把游戏闭环跑起来。程序负责根据答案生成反馈,把历史反馈放进对话上下文,让模型在每一轮看到之前的猜测和反馈后继续猜。这一步开始涉及状态管理,你不能只把最新一轮反馈传给模型,而是要把完整历史都带进去,否则模型很容易迷失。
第三级,才是“挑战”真正成立的地方。给模型或脚本限制猜测次数,统计一批挑战的成功率、平均步数、无效词率。你可以让模型自己说明理由,也可以让模型从候选词表里选择。到这里,它就从一个对话示例升级成可量化评估的小型 AI 任务了。
2. 环境准备:先跑通一条最小链路
2.1 最小依赖和运行条件
本地跑这套小挑战不需要高配置。CPU 就够,不需要显卡,因为推理大部分发生在模型服务端。你需要准备的是:Python 3.9 以上环境、一个 API 客户端库、一份五字母单词表、一个 API key。
我通常用 OpenAI 的 Python SDK 做示例,如果你用的是其他兼容服务,代码逻辑基本不变,只是把客户端地址、密钥和模型名换成自己的。为了避免把密钥写死在代码里,用环境变量读取:
export OPENAI_API_KEY="你的key"然后安装依赖:
pip install openai如果不想装 SDK,用 requests 也能做,只是要多处理一层 JSON 结构。学习阶段用 SDK 更省事。
2.2 词表是很容易翻车的环节
很多人觉得词表不重要,实际上它是影响结果最大的因素之一。如果词表里混入专有名词、缩写、复数形式,模型和脚本的候选判断都会被带偏。很多公开词表会包含低频词和生僻古词,测试时会出现模型完全不认识的情况。
我建议先做一次清洗。统一成小写字母,过滤掉长度不是 5 的词,再排除包含标点和数字的词。如果你用的是 Linux 系统自带词表,可以直接筛:
grep -E '^[a-z]{5}$' /usr/share/dict/words | head -n 3000 > wordlist.txt如果不是系统词表,就从公开英文单词列表里下载,然后按同样的规则清洗。清洗后固定成一份wordlist.txt,每行一个词。
到底用多大词表,取决于目标。如果只是演示流程,一千个常见词足够;如果想更接近真实 Wordle 难度,可以准备两三千个词。词表不是越大越好,因为生僻词会增加模型输出非法词的概率,也会让基线过滤算法的候选空间变大,导致策略难收敛。
2.3 接口调用前至少确认三件事
调用 API 前,我会先做三个最小验证。第一,密钥能不能正常鉴权。第二,选的模型能不能接受低温参数。第三,返回内容能不能被稳定解析成单个词。
一个容易忽略的坑是,模型在收到“请输出一个单词”后,仍然可能输出解释性文字,比如“我猜是 crate”。如果你把整段返回当成猜测,后面一定出问题。解决办法是把 max_tokens 设小,比如 10,并在 prompt 里强调“只输出单词,不要输出其他内容”。如果还不行,就加一道后处理,用正则提取纯字母部分:
import re def clean_guess(text): match = re.search(r"\b([a-z]{5})\b", text.lower()) return match.group(1) if match else ""这个函数放在所有调用的统一出口,能避免后面很多环节被脏数据污染。
3. 核心逻辑:反馈函数必须能处理重复字母
3.1 先写一个简单但严格的反馈函数
Wordle 的反馈函数看起来简单,很多人第一版会写成这样:
def feedback_simple(guess, answer): result = [] for i, ch in enumerate(guess): if ch == answer[i]: result.append("G") elif ch in answer: result.append("Y") else: result.append("B") return "".join(result)这个实现对于大部分单词没问题,但遇到重复字母会出错。举个例子,答案是 apple,猜测是 pspae,简单逻辑会把多余且不存在的 p 标成黄色,实际标准规则会优先保证每个字母的使用次数。为了让反馈准确,需要用计数来控制:
from collections import Counter def feedback(guess, answer): n = len(answer) status = ["B"] * n remaining = Counter(answer) # 第一轮:标记绿色 for i, ch in enumerate(guess): if ch == answer[i]: status[i] = "G" remaining[ch] -= 1 # 第二轮:标记黄色 for i, ch in enumerate(guess): if status[i] == "B" and remaining.get(ch, 0) > 0: status[i] = "Y" remaining[ch] -= 1 return "".join(status)这个版本先处理所有正确位置的字母,把它们的数量从计数中扣掉,然后再处理剩下的字母。这样重复字母就不会被误判成黄色或绿色。
3.2 用边界样例验证反馈函数
写完反馈函数后,不要直接进入下一步,先跑几个边界用例。我常用这几组:
- answer = apple, guess = apple,反馈应该是 GGGGG。
- answer = apple, guess = pspae,看重复字母 p 是否只标一次。
- answer = moody, guess = mmmma,看 m 是否只标一个绿色,后续 m 不会标黄色。
- 完全不相干的词,比如 answer = stone, guess = ccccp,应该全 B。
为什么要花时间验证这里?因为后续所有关于模型能力的结论,都建立在反馈正确的基础上。如果反馈函数本身有误,模型再怎么优化都会被错误信息带偏。大量 mini challenge 实验跑完发现结果异常,最后定位到反馈函数少处理了重复字母,这种情况很常见。
4. 猜词策略:规则基线和大模型的区别
4.1 基线法:每次把候选词过滤掉一批
把 Wordle 当成搜索问题,最稳定的解法其实是穷举过滤。每次根据反馈,从候选词表中删掉不可能的词。比如绿色位置必须匹配,黄色字母必须存在且不在当前位置,灰色字母在答案里不出现。
下面是一个简化版的候选过滤函数:
def filter_words(words, guess, fb): candidates = [] for word in words: ok = True for i, st in enumerate(fb): if st == "G" and word[i] != guess[i]: ok = False break if st == "Y" and (guess[i] not in word or word[i] == guess[i]): ok = False break if st == "B" and guess[i] in word: ok = False break if ok: candidates.append(word) return candidates这个版本对重复字母处理得还不够细,但已经能说明问题。你可以继续优化,比如优先选择能最大化信息增益的词,而不是只选候选集合里第一个合法词。信息熵的做法在 Wordle 社区已经很成熟,核心思想是选一个词,让下一次候选集合的期望规模最小。
我建议至少先把基线跑起来。原因很简单,后续用 LLM 猜词时,可以用基线作为对照。如果大模型在同样挑战集上的成功率、平均步数没有超过基线,那说明模型只是在“看起来聪明”,并没有真正利用反馈做推理。
4.2 让大模型基于反馈猜词
大模型猜词最直接的方式,是把之前的猜测和反馈全部放进对话历史,然后让模型输出下一个猜测。每次拿到新反馈后,追加一条消息进入下一轮调用。
import os from openai import OpenAI client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) def ask_next_guess(history, model="gpt-4o-mini"): messages = [ {"role": "system", "content": "You are playing Wordle. Output only a 5-letter English word, no explanation."} ] for guess, fb in history: messages.append({"role": "user", "content": f"Guess: {guess} -> {fb}"}) messages.append({"role": "user", "content": "Next guess:"}) resp = client.chat.completions.create( model=model, messages=messages, temperature=0.2, max_tokens=10, ) return resp.choices[0].message.content.strip().lower()这里有几个关键点。第一,history 是完整列表,不是只给最新一轮。模型如果只看到当前反馈,无法推导出之前哪些字母已经被排除。第二,temperature 不要调太高。Wordle 是需要遵守约束的任务,不是创意生成任务,高随机性会让模型输出奇怪单词。第三,max_tokens 要小,避免输出多余内容。
关于模型名,例子里的 gpt-4o-mini 只是一个通用示例,实际替换成你能访问的模型即可。模型选择会影响性能,但不会改变整体流程。小模型能力弱一点,更需要把提示词写清楚。
4.3 如何判断 AI 是真的在推理还是在背词
跑几局之后,你会发现一个现象:有些词模型一眼就能猜中,有些词它会在五轮里反复围绕同一个字母组合打转。要判断模型是否在真正利用反馈,可以看一组中间指标。
一个是“无效词率”,也就是模型输出了不在合法词表里的词。这个比例高,说明模型输出控制能力不够。另一种叫法就是典型的大模型幻觉在具体任务里的表现。另一个是“反馈利用率”,通过日志观察模型在获得黄色反馈后,下一轮是否尝试把该字母放到其他位置。如果连续多轮都没有改变,多半是历史上下文或提示词设计不足。
还有一种判断方式:对同一份挑战集,把历史顺序打乱后再跑一遍。如果模型成绩明显变差,说明它多少依赖上下文顺序推理;如果成绩完全没变化,那就要怀疑它是不是只靠词频在猜。不过这个实验比较繁琐,一般做了几轮 baseline 对比之后再考虑即可。
5. 批量跑 mini challenges:别只看一局
5.1 构造一个可复现的挑战集
单独跑几局只能说明流程通没通,不能说明方案好坏。为了评估 AI 能力,我建议准备一个固定挑战集,比如从词表里随机挑 50 个词作为答案,设置随机种子保证每次复现。
import random import json def load_words(path): with open(path, "r", encoding="utf-8") as f: return [w.strip().lower() for w in f if w.strip()] words = load_words("wordlist.txt") random.seed(42) challenge_set = random.sample(words, 50)用随机种子后,无论跑多少次,挑战集都一样。这样你改提示词、换模型、调参数之后,结果可以直接对比,不会因为题目不同而产生误差。
5.2 记录和统计指标
跑单个挑战时,我会把结果存成一条记录,包含目标词、是否成功、猜测历史、总猜测次数。跑完之后统一汇总。示例格式如下:
{ "target": "apple", "ok": true, "guesses": 4, "history": ["crate", "apply", "ample", "apple"] }统计时主要看四个指标:成功率、成功局平均猜测次数、失败局数、无效词率。成功率决定方案能不能用,平均次数决定效率,无效词率能反映模型输出控制能力。只看成功率容易忽略无效词问题,因为即使中途输出过非法词,只要后续猜中,看起来也是成功的,但真实流程里非法词应该被直接判负或重试。
把这些指标整理成表格:
| 指标 | 数值 |
|---|---|
| 成功局数 | 42 / 50 |
| 成功率 | 84% |
| 成功局平均猜测次数 | 3.8 |
| 失败局数 | 8 |
| 无效词率 | 12% |
表格本身只是结果,更重要的是异常分析。我会特意检查失败局是集中在某些生僻词,还是所有词都存在问题。如果是生僻词,说明词表覆盖不好;如果是随机失败,说明模型推理稳定性不足。
5.3 成本、重试和输出文件
批量跑 50 个词,每个词最多六次猜测,总共最多 300 次调用。这个量级实际消耗不大,但也不能完全忽略。如果每一轮都塞入完整历史并使用大尺寸模型,token 消耗会随轮数增长。因此我会在每轮打印进度,并记录当前累计调用次数。
批量任务里一定会遇到临时失败,比如限流、超时、返回空内容。我建议加一个简单重试机制:同一轮最多重试两次,重试之间等待几秒。如果还是失败,把这一条标注为 api_error,而不是直接当成猜词失败。发起调用的部分可以单独封装成函数,统一处理重试和日志,主循环会干净很多。
输出文件要按时间命名,避免覆盖之前的实验结果。比如results_20250612_1430.json。这样后面做多模型对比时,才知道每个实验文件对应的模型和提示词版本。
6. 实测中容易踩的坑和排查顺序
6.1 模型一直猜同一个词
最常见的问题是模型连续几轮都输出同一个词,比如 crate、crate、crate。出现这种情况,先不要怀疑模型傻了。第一步看 history 是否真的把每一轮反馈都传给了模型。如果你在循环里只保存了 guess,没有保存 feedback,模型看到的内容自然没变化。
第二步看 temperature 是不是设置得过高或过低。过低时模型倾向于选择概率最高词,即使它已经被反馈排除;过高时会忽略约束。在 Wordle 这种场景,我一般用 0.1 到 0.3。
第三步看提示词里是不是要求模型输出解释性内容,导致实际猜测内容被截断成同一个词。先抓一下模型返回的原始文本,再判断是不是解析问题。
6.2 反馈看起来没被用上
有时候模型没有一直猜同一个词,但明显在用词性联想而不是逻辑排除。比如答案给了 apple,前一次猜 peach,反馈里黄色字母已经说明 a、e 存在但位置不对,模型下一步却猜 grape,把 p 和 a 放在完全不符合反馈的位置。
这种问题通常不是 API 调用的问题,而是 prompt 设计不足。模型需要看到“当前可用字母”和“当前位置排除”的约束,而不是只看到原始的 G/Y/B 字符串。你可以尝试把历史反馈翻译成更直白的约束,比如“字母 a 不在第 1 位,字母 e 在第 5 位,字母 p 不在第 2 位”。实践里这种自然语言化的约束,往往比生硬的反馈代码更有效。
另外要确认反馈生成函数没有把 G 和 Y 解释反了。如果标记反了,模型也会看起来完全没利用反馈。
6.3 API 报错、token 超限和限流
批量跑多了之后,API 类报错会越来越多。常见的有鉴权失败、余额不足、请求频率超限、返回内容为空。我的排查顺序是:先看报错文本,再查 key 和服务配置,再看调用频次,最后看是否某个 prompt 太长导致 token 超限。
如果是限流错误,不要盲目增加并发,先在循环里加 sleep 或指数退避。这套小挑战本身不需要高并发,一次跑一个词就会稳很多。如果连续多次失败,把发起调用单独写成函数,在函数里做重试和日志。
还有一类容易被忽略的问题是模型返回了完整句子,比如输出“The answer is apple”,你直接取值后得到的是整句话。解决办法不仅是 max_tokens 调小,还要写一个抽取函数,用正则提取句中唯一的五字母小写单词,就像前面 2.3 里的 clean_guess 那样。
7. 边界在哪里,以及下一步值得做什么
7.1 别让 LLM 去做穷举题
Wordle 从本质上是个有限搜索问题,词表只有几千个词时,用脚本做信息熵选择会比大多数 LLM 更稳定。所以不要把“超过脚本基线”作为这组挑战的唯一目标。LLM 在省配置、灵活交互、自然语言理解方面有优势,但纯计算和信息维护不是它的强项。
这也意味着,如果你的目标是做一个真正高胜率的 Wordle AI,直接用传统算法写脚本最稳妥。把 LLM 引入这个场景,更多是为了学习多轮状态管理、反馈利用、输出约束这些通用能力。搞清楚这个边界,后续实验方向才不会跑偏。
7.2 可以继续做的方向
这套 mini challenges 后续可以扩展的方向很多。比如同一挑战集上对比不同模型,观察模型规模对推理表现的影响;也可以换成纯本地模型,看看本地模型的输出控制能力和云端模型差多少;还可以把每一轮候选列表交给模型,让它从候选词里选,模拟一个带工具调用的 agent。
另一个值得做的方向是把它变成一个可视化学习页面,前端展示每次猜测和反馈,后端记录模型决策日志。这样不仅能提升实验效率,也能让非技术背景的人理解 AI 在约束下做决策的过程。
不管扩到哪一步,我都建议先把当前这套基础流程跑顺。先单任务,再批量;先规则基线,再模型方案。等结果稳定之后,再去做多模型对比和 agent 化改造。这套小挑战真正的价值,不是赛过大模型或者赛过脚本,而是让你对 AI 应用开发的整个反馈闭环变得敏感起来。
