1. 从自然语言公式到 Lean 可编译证明:这条链路到底卡在哪
你可能已经在各种资讯里看到过「大模型证明数学定理」的标题,但真正动手时会发现,卡住你的往往不是模型不够聪明,而是整条链路没有打通。自然语言里的「a+b+c = a+c+b」看起来简单,可要让 Lean 编译器点头,你得把它翻译成严格的类型签名、导入正确的 mathlib 模块、选对策略序列,还要处理 Lean 版本与库版本之间的兼容问题。任何一个环节出错,编译器都会甩给你一堆看不懂的报错。
我试过把 ChatGPT 直接生成的 Lean 代码粘进编辑器,结果十次里有七八次跑不通。原因很具体:模型不知道你本地装的是 Lean 3 还是 Lean 4,不知道 mathlib 的 commit 哈希,也不知道你项目里已经定义了哪些辅助引理。它只能凭训练数据里的「印象」写代码,而这些印象往往对应的是某个特定版本的语法。LeanDojo 的价值就在这里——它把 Lean 变成一个可交互的环境,让模型能观察当前证明状态、执行策略、接收反馈,而不是一次性盲写。
这条链路的完整形态是这样的:你用自然语言描述一个定理,ChatGPT 生成高层证明计划和初步策略,LeanDojo 负责把这些策略送进 Lean 环境执行,返回成功或失败的状态,ChatGPT 再根据反馈调整。反复几轮之后,你得到一个能通过lake build的证明脚本。听起来像是一个 Agent 循环,实际上也确实如此。而要让这个循环稳定跑起来,你需要一个统一的 API 入口来管理模型调用——这就是 TaoToken 在整条链路里的位置。
适合读这篇的人:对 Lean 有基本了解、想用 LLM 辅助定理证明的开发者;正在搭建 LeanDojo 环境但被版本问题折磨的人;以及想理解「LLM + 形式化验证」到底怎么落地的人。下面我会从环境搭建开始,一步步走到可编译的证明脚本,中间所有配置片段都可以直接复制。
2. TaoToken 统一 Key 在 Lean 定理证明链路里的前置准备
在讲 LeanDojo 之前,先解决模型调用的问题。LeanDojo 的 ReProver 本身是一个专门训练的检索增强证明器,但如果你想让 ChatGPT 参与进来做「草稿生成 + 错误解释」,就需要一个稳定的 API 入口。TaoToken 在这里的作用是:用一个 Key 统一访问多个模型,避免你在 LeanDojo 的 Python 脚本里到处硬编码不同厂商的 endpoint 和密钥。
你需要准备的东西不多:一个 TaoToken 账号、一个 API Key、以及本地已经装好的 Python 3.10+ 和 Lean 工具链。Lean 工具链的安装方式取决于你用 Lean 3 还是 Lean 4,LeanDojo 两个版本都支持,但 Lean 4 的生态更新更快,建议新项目直接上 Lean 4。
先拿 Key。访问 TaoToken 的 API Keys 页面(https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=lean_dojo_setup),创建一个新 Key,复制下来。这个 Key 的权限范围建议只勾选「模型调用」,不要开管理权限,减少泄露风险。
拿到 Key 之后,不要急着写代码。先确认你的网络环境能正常访问https://taotoken.net/api,用 curl 测一下:
curl -s -o /dev/null -w "%{http_code}" https://taotoken.net/api/v1/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY"如果返回 200,说明 Key 和网络都没问题。如果返回 401,检查 Key 是否复制完整;如果返回 403,检查 Key 的权限设置。
接下来是 LeanDojo 的环境。LeanDojo 的官方仓库在 GitHub 上,安装方式有两种:pip 安装和源码安装。pip 安装适合快速体验,源码安装适合你需要改 LeanDojo 内部逻辑的情况。我建议先用 pip 把流程跑通:
python -m venv leandojo-env source leandojo-env/bin/activate pip install lean-dojo安装完成后,验证一下 LeanDojo 是否能正常导入:
import lean_dojo print(lean_dojo.__version__)如果这一步报ModuleNotFoundError,大概率是虚拟环境没激活,或者 pip 装到了系统 Python 里。用which python确认当前解释器路径。
Lean 本身的安装推荐用 elan(Lean 的版本管理器),它和 Rust 的 rustup 是同一个设计思路:
curl https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -sSf | sh source $HOME/.elan/env elan install leanprover/lean4:stable安装完成后,lean --version应该输出类似Lean (version 4.x.x, ...)的信息。记住这个版本号,后面配置 LeanDojo 的lean_version参数时要填一致。
3. 可复制的 TaoToken + LeanDojo 配置片段与工程初始化
这一节给出完整的配置文件。你不需要全部照抄,但建议至少把taotoken_config.json和lean-toolchain两个文件按原样创建,它们是后续所有步骤的基础。
先建项目目录:
mkdir lean-llm-proof && cd lean-llm-proof mkdir -p src scripts config创建config/taotoken_config.json,这是 TaoToken 的统一调用配置。注意base_url不要加 UTM 参数,API 调用地址就是纯https://taotoken.net/api:
{ "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "default_model": "gpt-4o", "fallback_model": "claude-3-5-sonnet", "timeout_seconds": 120, "max_retries": 3, "retry_backoff": 2.0, "models": { "proof_draft": "gpt-4o", "error_explain": "claude-3-5-sonnet", "tactic_suggest": "gpt-4o" } }把 API Key 写进环境变量,不要硬编码在 JSON 里:
export TAOTOKEN_API_KEY="sk-你的实际Key"如果你用 zsh,把这行加到~/.zshrc;用 bash 就加到~/.bashrc。Windows 用户可以在 PowerShell 里用$env:TAOTOKEN_API_KEY="sk-...",但这种方式只在当前会话有效,建议写进系统环境变量。
创建lean-toolchain文件,内容就是 Lean 版本号。这个文件必须和 elan 安装的版本一致:
leanprover/lean4:stable创建lakefile.lean,这是 Lean 4 的项目构建文件。注意require mathlib的版本要和你的 Lean 版本匹配,mathlib 的master分支通常跟随 Lean stable:
import Lake open Lake DSL package «lean-llm-proof» where leanOptions := #[ ⟨`autoImplicit, false⟩ ] require mathlib from git "https://github.com/leanprover-community/mathlib4.git" @[default_target] lean_lib «LeanLlmProof» where srcDir := "src"然后运行lake update拉取 mathlib。这一步会比较慢,因为 mathlib 的缓存文件很大。如果卡在下载阶段,可以设置LAKE_CACHE_DIR到磁盘空间充足的目录。
创建scripts/leandojo_config.toml,这是 LeanDojo 的环境配置:
[lean] version = "leanprover/lean4:stable" project_root = "." build_command = "lake build" [dojo] cache_dir = ".leandojo_cache" max_interaction_steps = 50 timeout_per_tactic = 30 [taotoken] config_path = "config/taotoken_config.json" model_for_draft = "gpt-4o" model_for_explain = "claude-3-5-sonnet"这里有一个容易踩的坑:project_root必须是绝对路径或者相对于 LeanDojo 运行目录的路径。如果你在scripts/目录下运行 Python 脚本,project_root要写成..,否则 LeanDojo 找不到lakefile.lean。
初始化 Lean 工程:
lake build如果这一步成功,你会看到.lake/build目录生成。如果报unknown package 'mathlib',说明lake update没跑完,重新执行一次。
4. 验证请求:让 ChatGPT 在 Lean 里证明 a+b+c=a+c+b
现在进入核心环节。我们用一个具体的定理来走完整流程:a + b + c = a + c + b。这个定理在自然数、整数、实数上都成立,Lean 的 mathlib 里已经有现成的引理,但我们要让 ChatGPT 自己生成证明脚本。
先写一个 Python 脚本scripts/prove_theorem.py,它做三件事:调用 TaoToken 让 ChatGPT 生成证明草稿、把草稿送进 LeanDojo 执行、根据执行结果决定是否让 ChatGPT 修正。
import json import os import subprocess from pathlib import Path from openai import OpenAI TAOTOKEN_CONFIG = json.loads(Path("config/taotoken_config.json").read_text()) client = OpenAI( base_url=TAOTOKEN_CONFIG["base_url"], api_key=os.environ["TAOTOKEN_API_KEY"], ) THEOREM_STATEMENT = """ theorem add_comm_three (a b c : Nat) : a + b + c = a + c + b := by sorry """ def ask_chatgpt_for_proof(statement: str, error_context: str = "") -> str: prompt = f"""You are a Lean 4 theorem proving assistant. Given the following theorem statement, generate a complete proof that compiles with Lean 4 and mathlib. Theorem: {statement} {"Previous attempt failed with this error:\n" + error_context if error_context else ""} Rules: - Use only tactics available in Lean 4 mathlib. - Do not use `sorry`. - Output only the Lean code, no markdown fences, no explanation. """ response = client.chat.completions.create( model=TAOTOKEN_CONFIG["models"]["proof_draft"], messages=[{"role": "user", "content": prompt}], temperature=0.2, ) return response.choices[0].message.content.strip() def write_and_build(proof_code: str) -> tuple[bool, str]: proof_file = Path("src/AddCommThree.lean") proof_file.write_text(proof_code) result = subprocess.run( ["lake", "build"], capture_output=True, text=True, cwd=".", ) if result.returncode == 0: return True, "" return False, result.stderr + result.stdout def main(): statement = THEOREM_STATEMENT for attempt in range(5): print(f"--- Attempt {attempt + 1} ---") proof_code = ask_chatgpt_for_proof(statement) print(proof_code) success, error = write_and_build(proof_code) if success: print("Proof compiled successfully.") return print(f"Build failed:\n{error[:800]}") statement = THEOREM_STATEMENT + f"\n\nPrevious error:\n{error[:500]}" print("Max attempts reached.") if __name__ == "__main__": main()运行这个脚本之前,确保src/AddCommThree.lean的模块名和lakefile.lean里的lean_lib配置一致。如果lake build报unknown module prefix,检查src/目录下是否有LeanLlmProof.lean作为入口文件。
第一次运行时,ChatGPT 可能会生成这样的证明:
theorem add_comm_three (a b c : Nat) : a + b + c = a + c + b := by rw [Nat.add_assoc, Nat.add_comm b c, ← Nat.add_assoc]这个证明在 Lean 4 里能编译通过。rw策略按顺序重写目标,先把a + b + c结合成a + (b + c),再用交换律把b + c变成c + b,最后反向结合回a + c + b。
如果 ChatGPT 第一次生成了带sorry的代码,脚本会把它送进 Lean 编译,Lean 会报declaration uses 'sorry'警告。脚本捕获这个警告后,把错误信息拼进下一轮 prompt,ChatGPT 通常会去掉sorry并给出真正的证明。
验证成功的标志是lake build返回 0,并且src/AddCommThree.lean里没有sorry。你可以用grep -r "sorry" src/确认。
5. 常见报错排查清单:从 401 到 reading choices 的实战对照
这一节列出你在整条链路里最可能遇到的报错,以及对应的排查动作。每一条都来自实际运行时的日志。
401 Unauthorized / invalid api key
这是 TaoToken 调用时最常见的错误。先确认环境变量是否生效:
echo $TAOTOKEN_API_KEY如果输出为空,说明环境变量没设置。如果输出有值但仍然是 401,检查 Key 是否被撤销或过期。在 TaoToken 控制台重新生成一个 Key,替换环境变量后重试。注意不要在代码里写api_key="sk-..."这种硬编码,LeanDojo 的日志可能会把 Key 打印出来。
local proxy failed / connection refused
这个报错通常出现在 LeanDojo 尝试连接 Lean 语言服务器时。LeanDojo 会在本地启动一个 Lean server 进程,通过 socket 通信。如果防火墙拦截了本地端口,或者 Lean server 启动失败,就会报这个错。排查步骤:先手动运行lake serve看 Lean server 能否正常启动;如果lake serve报错,说明 Lean 工具链本身有问题,重新跑elan install和lake update。
Error: reading choices: unexpected end of JSON input
这个报错来自 TaoToken 的流式响应解析。当stream=True时,如果网络中断或者服务端提前关闭连接,客户端会读到不完整的 JSON。解决方法是在 TaoToken 配置里把timeout_seconds调大,并且关闭流式模式:
{ "stream": false, "timeout_seconds": 180 }如果你用的是 OpenAI Python SDK,在client.chat.completions.create()里显式传stream=False。
OAuth token expired / refresh failed
如果你在 TaoToken 控制台开启了 OAuth 类型的 Key,需要定期刷新。LeanDojo 的长时运行脚本可能会在几小时后遇到这个错误。建议在脚本里加一个重试逻辑,捕获 401 后重新读取环境变量里的 Key。如果 Key 本身是长期有效的,检查系统时间是否准确,OAuth 对时间偏差很敏感。
LeanDojo: theorem not found in cache
LeanDojo 会把提取的定理缓存到.leandojo_cache目录。如果你换了 Lean 版本或者 mathlib commit,缓存会失效。删除缓存目录后重新运行:
rm -rf .leandojo_cachelake build 报 unknown identifier 'Nat.add_comm'
这是 mathlib 导入不完整导致的。在 Lean 文件开头加上import Mathlib,或者更精确地import Mathlib.Data.Nat.Basic。Lean 4 的 mathlib 模块粒度很细,只导入需要的模块能加快编译速度。
ChatGPT 生成的证明里出现by sorry但脚本没检测到
Lean 对sorry的处理是发出警告而不是错误,所以lake build的返回码仍然是 0。你需要在脚本里额外检查输出里是否包含declaration uses 'sorry':
if "declaration uses 'sorry'" in result.stdout: return False, "Proof contains sorry"TaoToken 返回 model not found
检查config/taotoken_config.json里的模型名是否拼写正确。TaoToken 的模型 ID 和 OpenAI 官方可能略有差异,比如gpt-4o和gpt-4o-2024-08-06是两个不同的 ID。在 TaoToken 的模型对话页面(https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=lean_model_check)可以查看当前可用的模型列表。
6. 把这条链路变成日常工具:接入文档与长期编码方案
跑通一次a+b+c=a+c+b只是开始。真正有价值的是把这条链路变成你日常写 Lean 证明时的辅助工具。你可以把prove_theorem.py改成一个 CLI 工具,接受任意定理语句作为输入,自动完成「生成草稿 → 编译 → 修正」的循环。
更进一步的用法是接入 LeanDojo 的交互式环境。LeanDojo 提供了Dojo类,可以让你在 Python 里逐步执行策略,观察证明状态的变化。结合 TaoToken 的模型调用,你可以实现一个「策略建议器」:每次 Lean 返回当前目标状态,你把它发给 ChatGPT,让它建议下一步用什么策略,然后你手动或自动执行。
如果你打算长期做 LLM + 定理证明的开发,建议关注 TaoToken 的 Coding Plan(https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=lean_coding_plan),它针对高频代码生成场景做了优化,适合需要反复调用模型做证明搜索的工作流。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=lean_doc,里面有完整的 API 参数说明和错误码对照。
最后给一个实用技巧:在 Lean 文件里用set_option trace.Meta.Tactic.simp true可以打印simp策略的详细执行过程,这对理解 ChatGPT 生成的证明为什么能通过很有帮助。当你看到simp把目标一步步化简到rfl时,你就知道这个证明是可靠的,而不是碰巧编译通过。