从Wordle求解器看AI Agent:信息熵、状态与工具调用
2026/8/27 8:44:28 网站建设 项目流程

不知道你有没有过这种感受:一个看起来简单到不能再小的游戏规则,一旦你试图让程序或 AI 自己“学会”怎么玩,难点会突然冒出一大堆。Wordle 就是这样一个典型例子——五个字母、六次机会、绿黄灰三种反馈,听起来连小学生都能秒懂,但如果你真的动手写一个求解器,很快就会发现:这不是“查字典”这么简单。

我第一次写 Wordle 求解器时,第一版代码只用了一句“把不符合反馈的单词全部过滤掉”,结果在第二批猜测时就经常出现候选词列表直接归零的情况。后来我才意识到,问题出在重复字母、反馈状态的建模,以及最关键的“猜哪个词能带来最多信息”这几个环节上。也正是从那个时候开始,我意识到这一类“小到不能再小的 AI 挑战”,反而是理解 AI Agent 开发、工具调用、状态机和搜索策略的最佳训练场。

这篇文章就用 “Wordle but small AI mini challenges” 这个项目标题展开,带你把一个完整的小型 AI 猜词系统从零写出来。我们会实现三版方案:第一版是纯规则过滤的基准求解器;第二版引入信息熵概念,让 AI 每次猜词都尽可能获得更多信息;第三版把求解器封装成一个可被大模型调用的工具,模拟 Agent 调用工具解决问题的完整链路。读完你不仅能跑通一个 Wordle 求解器,还能把这一套“环境建模 + 工具封装 + Agent 循环”的思路迁移到其他 AI 小项目里。

1. 这篇文章真正要解决的问题

先说判断:Wordle 类 AI 项目,真正的难点从来不是“猜词”,而是“状态建模”和“决策策略”。很多初学者一上来就想着调用大模型直接生成答案,这是典型的思路误区。大模型确实能猜词,但它不会告诉你为什么猜这个词,也不会在连续几轮错误之后自动修正策略——这恰恰是 AI 工程里最需要解决的部分。

“Wordle but small AI mini challenges”这个项目最值得做的原因有三点。

第一,它把复杂的 AI 问题压缩到一个极小的规模里。没有海量数据、没有复杂网络、没有训练环节,核心就是一个单词表加一套反馈规则,但你依然要设计算法、处理边界情况、验证效果。这种“小而完整”的项目特别适合理解 AI 推理的基础逻辑。

第二,它天然适合讲清楚“工具调用”的价值。大模型直接猜词容易产生幻觉,但如果把求解器封装成工具,让大模型每一次决策都调用工具获取精确反馈,结果就会稳定很多。这是当前 AI Agent 开发里最重要的工程模式之一。

第三,它有明确的评价标准。你不需要主观判断 AI 好不好,只需要看它在六次机会内能不能猜中、平均用了几次,以及候选词列表的收敛速度。这种可量化的反馈,对开发调试极其友好。

什么样的读者最应该读这篇文章?如果你正在学 AI Agent 开发,想找一个不用烧钱、不需要大量数据、又足够完整的练手项目;或者你已经在做 AI 编程,但总是对“状态管理”“工具调用”“提示词设计”这三个概念理解得不够落地;又或者你只是对 Wordle 感兴趣,想看看程序怎么玩这个游戏——这篇文章都适合你。

2. 基础概念与核心原理

2.1 Wordle 的规则本质

Wordle 的规则用一句话就能说清:猜一个五字母单词,每次猜测后,系统对每个位置给出绿、黄、灰三种反馈。绿色代表该字母在正确位置,黄色代表该字母存在于答案中但位置不对,灰色代表该字母完全不在答案中。

但这句话里藏着一个特别容易搞错的细节:重复字母的反馈规则。比如答案单词是“crane”,你猜了“cocoa”,第一个 c 是绿色,第二个 c 会是什么颜色?答案是灰色。因为答案里只有一个 c,已经被第一个位置用掉了。这类逻辑不处理好的话,求解器的候选词过滤马上就会出错。

2.2 状态反馈的数据结构

写程序时,我们用一个长度为 5 的列表来表示一次猜测的反馈,每个元素取G(绿色)、Y(黄色)、W(灰色)之一。例如:

# 反馈示例:猜词 "crane",答案是 "slate" # 结果为 ["W", "G", "Y", "W", "Y"] # 解释: # 第 0 位 c 不在答案中 -> W # 第 1 位 r 在答案中且位置正确 -> G # 第 2 位 a 在答案中但位置不对 -> Y # 第 3 位 n 不在答案中 -> W # 第 4 位 e 在答案中但位置不对 -> Y

这个数组就是整个求解器唯一依赖的外部反馈。所有候选词的筛选、评分、信息熵计算,都围绕这个 5 位数组展开。

2.3 几类求解策略的对比

策略核心思想优点缺点
暴力枚举把所有五字母词都猜一遍实现最简单六次机会内基本猜不中
排除过滤每次根据反馈筛掉不可能的词逻辑直观有时候候选词会快速耗尽
词频排序优先猜常见词效果尚可依赖词频数据,且首猜词不一定最优
信息熵选择期望信息量最大的词平均猜测次数少需要额外计算,理解门槛稍高
LLM 直接生成让大模型输出猜测看起来“智能”易幻觉、不可控、不透明

其中“排除过滤”是基础,“信息熵”是优化,“LLM + 工具调用”是向 Agent 工程迁移的关键。下面会把这三层都写出来。

3. 环境准备与前置条件

这个项目对运行环境的要求非常低,核心逻辑只用 Python 标准库就能写完,不需要装任何第三方包。

  • Python 3.9 或更高版本,推荐 3.10+
  • 一个能跑 Python 脚本的命令行环境
  • 如果后面要接大模型,准备一个可用的模型服务 API Key(可选)
  • 一个包含五字母英文单词的词典文件(可以自己收集,下面会给出最小示例)

环境验证很简单,在终端执行:

python --version

只要输出是 3.9 以上,就可以继续。

在开始之前先想清楚:我们不需要一个包含几十万单词的完整词典,甚至不需要真实的 Wordle 官方答案列表。为了演示求解逻辑,用一个 8 到 10 个词的微型词表就够跑通流程。后面要追求更强效果时,再换成覆盖更全的词典即可。这一步会避免很多人“先找全量词典再写代码”的启动困难。

建议的项目结构如下:

wordle-ai-mini/ ├── wordle.py # 核心规则与求解器 ├── agent_demo.py # Agent 调用工具演示 ├── dictionary.txt # 五字母单词表 └── README.md # 项目说明

4. 核心流程拆解

整个 AI 猜词系统按下面这条链路运行,这也是所有 Agent 类项目的通用骨架:

  1. 初始化候选词列表,即“所有可能的答案”。
  2. AI 生成一个猜测词。
  3. 系统根据答案计算反馈(绿黄灰数组)。
  4. AI 根据反馈过滤掉不可能的候选词。
  5. 检查是否猜中,猜中则结束;否则回到第 2 步。

这里要特别标注一个最容易出错的地方:第 2 步“AI 生成猜测词”的颗粒度。如果 AI 直接生成一个单词,每次猜词都是独立决策,前面几轮积累的反馈信息完全靠模型自己“记得”,很容易出现逻辑混乱。更稳妥的做法是让 AI 只做“选择”,也就是从候选词列表里选一个词,而不是凭空生成。

这个区别非常关键,我们可以把它叫做“填空式决策”与“开放式生成”的区别。在 Agent 工程里,前者意味着你给模型一个明确的、受限的动作空间,后者意味着让模型自由发挥。对于状态空间很小的任务,受限动作空间的效果通常远好于开放式生成。

从算法层面看,第 4 步的过滤逻辑应该是这样的:

  • 如果某位置反馈是 G,那么候选词该位置必须等于猜词的该字母。
  • 如果某位置反馈是 Y,那么候选词该位置不能等于猜词的该字母,但候选词中必须至少包含一个该字母。
  • 如果某位置反馈是 W,那么候选词中不能再包含该字母,除非某个 G 或 Y 位置已经消耗了同字母的额度。

最后一条是最容易写错的,一定要结合 2.1 节的重复字母规则去实现。

5. 完整示例与代码实现

5.1 模块一:评估函数

评估函数负责根据“答案”和“猜测”计算出反馈数组。这是整个项目的地基,后续所有逻辑都依赖它的正确性。

# 文件路径:wordle.py from collections import Counter def evaluate_guess(answer: str, guess: str) -> list[str]: """ 根据 Wordle 规则计算反馈。 参数: answer: 真实答案,五字母字符串 guess: 本次猜测,五字母字符串 返回: 长度为 5 的反馈列表,元素为 "G" / "Y" / "W" """ if len(answer) != len(guess): raise ValueError("answer 和 guess 长度必须一致,且应为 5") n = len(answer) result = ["W"] * n # 统计答案中每个字母的剩余次数 remaining = Counter(answer) # 第一遍:标记绿色,并扣除该字母的剩余次数 for i in range(n): if guess[i] == answer[i]: result[i] = "G" remaining[guess[i]] -= 1 # 第二遍:标记黄色或灰色 for i in range(n): if result[i] == "G": continue ch = guess[i] if ch in remaining and remaining[ch] > 0: result[i] = "Y" remaining[ch] -= 1 else: result[i] = "W" return result

这段代码的核心是“先绿后黄”的两遍扫描。如果只做一遍循环,遇到重复字母时很容易给出错误的反馈。先扣除绿色占用的次数,再判断黄色,就能防止把同一个字母额度重复使用。

5.2 模块二:排除法求解器

有了评估函数,接下来实现候选词过滤和基础求解流程。

# 文件路径:wordle.py from typing import Iterable def filter_candidates( candidates: list[str], guess: str, feedback: list[str], ) -> list[str]: """ 根据一次猜测的反馈,过滤候选词列表。 参数: candidates: 当前候选词列表 guess: 本次猜测 feedback: evaluate_guess 返回的反馈数组 返回: 仍有可能成为答案的词列表 """ result = [] for word in candidates: if matches_feedback(word, guess, feedback): result.append(word) return result def matches_feedback(word: str, guess: str, feedback: list[str]) -> bool: """ 核心判断:某个候选词 word 是否与当前反馈保持一致。 判断方式是:假设 word 就是答案,重新计算一次反馈, 如果计算出的反馈和真实反馈完全一致,就保留该词。 """ return evaluate_guess(word, guess) == feedback

这里用了一个非常聪明的 trick:不直接写一堆条件判断,而是“假设某个词就是答案,评估一次反馈,如果和真实反馈一致,就认定它仍然是候选词”。

为什么这个写法更好?因为evaluate_guess本身已经处理好了重复字母逻辑,过滤逻辑不用重复实现第二遍。这种“用评估函数反向验证”的思路,在测试驱动开发里非常实用,也减少了条件遗漏的风险。

接下来是基础求解器:

# 文件路径:wordle.py def solve_by_elimination( dictionary: list[str], answer: str, max_attempts: int = 6, verbose: bool = True, ) -> int | None: """ 使用排除法求解 Wordle。 返回: 猜中时的次数(1 到 max_attempts),如果超次未中返回 None """ candidates = list(dictionary) # 第一猜默认选第一个词,实际项目中可以换成语料里最常见的词 guess = candidates[0] for attempt in range(1, max_attempts + 1): feedback = evaluate_guess(answer, guess) if verbose: print(f"第 {attempt} 次猜测:{guess},反馈:{''.join(feedback)}") if feedback == ["G"] * 5: if verbose: print(f"猜中了!答案就是 {answer}") return attempt candidates = filter_candidates(candidates, guess, feedback) if not candidates: if verbose: print("候选词列表已为空,请检查词典或反馈逻辑!") return None guess = candidates[0] if verbose: print(f"{max_attempts} 次内未猜中,最终候选词:{candidates}") return None

这个排除法能跑通完整流程,但它有个明显短板:每次只是“选候选词里的第一个词”,并没有考虑“哪个词能给下一轮带来最多信息”。在候选词数量很多时,选择哪一个词会显著影响收敛速度,这就是下一小节信息熵出场的原因。

5.3 模块三:信息熵优化版本

信息熵的核心思想是:对于当前候选词列表,如果我选择某个词作为猜测,那么每个可能的反馈结果会把我当前的候选词分成不同数量的子集。反馈结果越“均匀”,说明猜这个词之后的不确定性越小,也就是信息量越大。

我们选择能让“期望信息量”最大的词。计算公式是:

import math def calculate_entropy(candidates: list[str], guess: str) -> float: """ 计算猜测 guess 对当前候选词列表的期望信息熵。 """ # 统计每个反馈模式对应的候选词数量 pattern_count = {} for word in candidates: fb = tuple(evaluate_guess(word, guess)) pattern_count[fb] = pattern_count.get(fb, 0) + 1 total = len(candidates) entropy = 0.0 for count in pattern_count.values(): p = count / total entropy -= p * math.log2(p) return entropy def best_guess_by_entropy(candidates: list[str], full_dictionary: list[str]) -> str: """ 从全量词典中选择一个能带来最大信息熵的猜测词。 注意:猜测词不一定要在候选词列表里,因为好的猜测可能来自 已经确定不可能的单词,它能更快地缩小答案范围。 """ best_word = candidates[0] best_score = -1.0 # 如果候选词很少,只在候选词里选,减少计算量 search_space = candidates if len(candidates) < 100 else full_dictionary for word in search_space: score = calculate_entropy(candidates, word) if score > best_score: best_score = score best_word = word return best_word

信息熵版本的求解器只需要把solve_by_elimination中的guess = candidates[0]改成:

guess = best_guess_by_entropy(candidates, dictionary)

为什么要从全量词典里选词,而不是只从候选词里选?举个例子,如果当前候选词只有两个,且都是“____t”结尾,那么猜测一个包含更多不同字母的词,比如“crane”,可能得到非常分散的反馈,帮助你瞬间锁定答案。这个“crane”很可能已经不在候选词列表里,但它的信息价值很高。

这个细节正是很多 Wordle 求解器效果不佳的原因:他们只看候选词,不利用外部单词来获得更多信号。

5.4 模块四:Agent 工具调用模拟

现在进入第三个层次:把求解能力封装成工具,模拟 Agent 调用工具的完整链路。

在真实的 AI Agent 工程中,大模型通常是“决策者”,工具是“执行者”。决策者根据当前状态决定调用哪个工具,执行者返回精确结果,决策者再根据结果做下一步决定。这个过程可以用一个最小化的循环来演示。

# 文件路径:agent_demo.py """ 模拟 Agent 调用工具解决 Wordle 任务。 这里不依赖真实大模型,而是用一个小型决策逻辑模拟模型行为。 如果你要接入真实大模型,可以把 choose_tool 替换为 LLM 调用。 """ from wordle import ( evaluate_guess, filter_candidates, best_guess_by_entropy, ) # 微型词典,用于演示 DICTIONARY = [ "crane", "slate", "crisp", "train", "plane", "brave", "grape", "dream", ] # 模拟每个工具的描述 TOOLS = { "get_feedback": "根据答案和猜测词返回绿黄灰反馈", "filter_candidates": "根据反馈过滤候选词列表", "select_best_guess": "从候选词中选择信息量最大的猜测词", } def run_agent_demo(answer: str, max_attempts: int = 6) -> int | None: """ 以 Agent 循环的方式求解 Wordle。 每个循环内,Agent 会依次调用工具,更新自己的状态。 """ candidates = list(DICTIONARY) guess = best_guess_by_entropy(candidates, DICTIONARY) print(f"Agent 可用工具:{list(TOOLS.keys())}") print(f"任务开始,真正的答案是:{answer}(仅演示用)") print("-" * 40) for attempt in range(1, max_attempts + 1): print(f"\n[Agent 状态] 当前第 {attempt} 次尝试") print(f"[Agent 决策] 调用 select_best_guess 工具,选择猜测词:{guess}") # 调用工具 1:获取反馈 feedback = evaluate_guess(answer, guess) print(f"[工具返回] get_feedback 执行完毕,反馈:{''.join(feedback)}") if feedback == ["G"] * 5: print(f"[结果] 第 {attempt} 次猜中!答案:{answer}") return attempt # 调用工具 2:过滤候选词 before_count = len(candidates) candidates = filter_candidates(candidates, guess, feedback) after_count = len(candidates) print(f"[工具返回] filter_candidates 执行完毕,候选词数量:{before_count} -> {after_count}") if not candidates: print("[错误] 候选词列表为空,说明词典或反馈逻辑有问题") return None # 调用工具 3:选择下一个猜测 guess = best_guess_by_entropy(candidates, DICTIONARY) print(f"[工具返回] select_best_guess 执行完毕,下一猜测词:{guess}") print(f"[结果] {max_attempts} 次内未猜中") return None if __name__ == "__main__": run_agent_demo(answer="crane")

这段代码里的“Agent”其实是一个固定决策序列:选择猜测、获取反馈、过滤候选、再选择猜测。但它展示了一个非常重要的工程模式——我们把每个能力拆成了独立的工具函数,任何一个环节都可以替换成真实的大模型调用,而不用重写整条链路。

如果在真实的 Agent 项目里接大模型,逻辑会是:Agent 收到“当前候选词有 8 个”这一状态,由大模型决定“调用 select_best_guess”,工具执行完返回一个词,大模型再决定“调用 get_feedback”,如此循环。大模型只负责决策,不负责计算反馈或统计词频,这样能大幅降低幻觉风险,也更容易排查错误。

6. 运行结果与效果验证

写完代码后,先在终端跑一下排除法版本:

python -c "from wordle import solve_by_elimination; solve_by_elimination(['crane', 'slate', 'crisp', 'train', 'plane', 'brave', 'grape', 'dream'], answer='crane')"

预期输出类似:

第 1 次猜测:crane,反馈:GGGGG 猜中了!答案就是 crane

因为是微型词典,且第一个词就是答案,所以一次猜中。为了验证过滤逻辑,可以故意让答案和首猜不同,比如:

python -c "from wordle import solve_by_elimination; solve_by_elimination(['crane', 'slate', 'crisp', 'train', 'plane', 'brave', 'grape', 'dream'], answer='slate')"

这时会看到系统经过多轮猜测逐步缩小范围,最终猜中。如果候选词列表为空,第一个要怀疑的就是evaluate_guess里重复字母的处理。

再跑一下 Agent 演示:

python agent_demo.py

预期会看到 Agent 每轮打印“选择猜测词”“获取反馈”“过滤候选词”的过程,最后输出第几次猜中。这里建议重点观察候选词数量的变化:从第一轮到最后一轮,数量应该逐步下降,直到收敛到 1。如果某轮候选词数量不降反升,说明反馈逻辑或词典清单存在重复单词。

判断成功的标准有三个:

  1. 能在 6 次内猜中答案。
  2. 每轮候选词数量呈下降趋势。
  3. 没有任何一轮反馈与答案状态矛盾。

运行失败时,按以下顺序排查:先单独测evaluate_guess的已知用例,再测filter_candidates,最后才看整个求解循环。不要从头到尾看一遍代码找 bug,那样效率很低。

7. 常见问题与排查思路

问题现象可能原因排查方式解决方案
第一轮候选词就归零词典里有非五字母词或重复单词检查词典数据清洗词典,确保词长均为 5
重复字母的判断错误评估函数没有处理“先绿后黄”evaluate_guess("crane", "cocoa")这类用例测试改成两遍扫描,先扣绿色再判黄色
排除法猜了很多次仍不中每次选第一个候选词,信息量太差打印每轮候选词数量换用信息熵版本best_guess_by_entropy
接入大模型后,AI 一直猜不在词典里的词模型在“生成”而不是“选择”检查提示词是否限制了动作空间提示词要求模型只能从给定候选词列表中选一个作为输出,且必须调用工具
LLM 返回格式不稳定,无法解析模型自由发挥,不遵守 JSON 格式查看原始响应内容使用结构化输出约束;在代码里做格式校验,解析失败时返回重试
API 调用次数过多,消耗 credits 太快Agent 循环里每次决策都调用大模型打印每次工具调用的 token 和费用对简单的状态更新改用本地函数;大模型只负责关键决策

第 4 和第 5 个问题在真实 Agent 开发里尤其常见。很多从算法转向 Agent 开发的人会高估大模型的能力,让它自由生成答案,结果遇到幻觉和格式不稳定。好的工程做法是:把动作空间缩到最小,让大模型只做“选择”而不是“创造”。

8. 最佳实践与工程建议

8.1 从“小词典跑通”开始,再换全量词典

这是我在做小型 AI 项目时最推荐的方式。先用 10 个词跑通逻辑,再换 1000 个词,最后换全量词典。每一次换词表,你都有可能发现新边界问题,但定位 bug 的成本会低很多。如果一开始就上全量词典,遇到问题时你甚至无法判断是词典问题还是逻辑问题。

8.2 状态显式化,不要依赖记忆

在这个项目里,候选词列表就是 Agent 的“记忆”。在真实的 Agent 工程中,你应当把关键状态放到显式的变量、数据库或配置里,而不是依赖大模型的上下文记忆。大模型对长上下文的注意力会衰减,对状态描述的精确度也会下降。显式状态 + 工具更新,是当前最稳定的 Agent 模式。

8.3 工具函数要单一职责

evaluate_guess只做评估、filter_candidates只做过滤、best_guess_by_entropy只做选择。这个设计也许看起来有点“多此一举”,但当你把一个普通脚本改造成 Agent 工具时,单一职责的价值会立刻体现出来——每个函数都能独立测试,也都能独立被大模型调用。如果一开始就把所有逻辑写在一个大函数里,后面很难拆分。

8.4 日志是 Agent 调试的命脉

Agent 项目比普通脚本难调试得多,因为每一轮都有“决策”环节。建议把下面这些信息全部打出来:

  • 当前轮次和状态
  • 调用的工具名
  • 工具返回结果
  • 状态更新后的变化(候选词数量、剩余次数等)

run_agent_demo里的打印就是一个最小示例。生产级项目里,应该换成结构化日志,把这些信息写入文件或日志系统,方便回放和定位。

8.5 控制大模型的调用次数

接真实大模型时,每一次 API 调用都对应一次成本。Wordle 这个小问题六轮就能解决,但如果你不加限制,Agent 可能因为解析失败、重试、各种异常路径,实际调用几十次。建议在 Agent 循环中设置全局最大调用次数,并且在每次调用前都检查是否已触发上限。这个机制不是可选项,是生产环境的必需项。

8.6 安全边界与权限最小化

虽然是演示项目,但“Agent 调用工具”这个模式进入生产环境后,工具本身可能涉及文件读取、数据库写入、第三方 API 等操作。原则是:能不放权就不放权,能只读就不要写,能局部作用就不要全局作用。每一个工具函数都应该校验入参,并明确它能做什么、不能做什么。在 Wordle 项目里体现为:所有函数都只接受字符串和列表,不访问文件系统,不执行外部命令。

9. 总结与后续学习方向

“Wordle but small AI mini challenges”这个项目最有价值的地方,不是把猜词游戏做得多完美,而是通过一个足够小的场景,把 AI 工程里的核心链路完整走了一遍:规则建模、状态更新、策略选择、工具封装、Agent 循环。你得到的不是一个只能用于猜词的玩具,而是一套可以迁移到其他任务的模板。

下一步可以从三个方向继续深入。第一,换一个更大的真实词典,统计信息熵版本在全量词表上的平均猜测次数,看看和排除法相差多少;第二,接入真实大模型,把run_agent_demo里的固定决策序列替换成 LLM 调用,比较“LLM 直接猜词”和“LLM 选择工具”两种模式的效果差异;第三,把同样的架构迁移到其他游戏或任务上,比如数字猜谜、二十问、甚至简单的命令行工具编排。

这个项目也顺带解释了热搜里常看到的几个概念:AI Agent 开发到底在做什么,AI 幻觉为什么可怕,credits 在 AI 调用里为什么重要。说到底,一个可靠的 Agent 不是让模型自由发挥,而是给模型一个受控环境、一组清晰工具、一条可回滚的逻辑链路。Wordle 只是第一步,但它是一块非常通透的敲门砖。建议收藏备用,动手敲一遍代码,收获会比只看文章大得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询