
假设用户输入的需求是:"写一个判断字符串是否为回文(正读反读都一样)的函数,需要忽略大小写和空格。"
我们来模拟整个数据流向,看看文件里到底写了什么,以及它是怎么被测试的。
🎬 镜头一:AI 的真实返回(JSON 结构化输出)
当代码走到 response = structured_llm.invoke(prompt) 时,Claude 在底层其实不是返回一段聊天文本,而是严格按照我们定义的 Pydantic Schema,返回了一个类似这样的 JSON 数据包:
{
"code": "def is_palindrome(s: str) -> bool:\n \"\"\"判断字符串是否为回文\"\"\"\n clean_s = s.replace(' ', '').lower()\n return clean_s == clean_s[::-1]",
"test_code": "assert is_palindrome('racecar') == True\nassert is_palindrome('hello') == False\nassert is_palindrome('A man a plan a canal Panama') == True",
"description": "实现了回文判断,并处理了空格和大小写问题。"
}
我们的 real_coder_node 收到这个对象后,把它们分别存入全局的 State 字典中:
state["current_code"] = response.code
state["current_test_code"] = response.test_code
🎬 镜头二:临时文件里到底写了什么?
现在流程走到了 tester_node。
代码执行到了这一步:full_executable_code = f"{func_code}\n\n# --- 测试用例 ---\n{test_code}"
然后,tempfile 模块在你的操作系统里(比如 /tmp/tmp_a1b2c3.py)悄悄创建了一个文件。如果你在这个时候用记事本打开这个临时文件,你看到的内容将是一段完美拼装的、可独立运行的 Python 脚本:
# /tmp/tmp_a1b2c3.py 文件里的真实内容:
def is_palindrome(s: str) -> bool:
"""判断字符串是否为回文"""
clean_s = s.replace(' ', '').lower()
return clean_s == clean_s[::-1]
# --- 测试用例 ---
assert is_palindrome('racecar') == True
assert is_palindrome('hello') == False
assert is_palindrome('A man a plan a canal Panama') == True
🎬 镜头三:它被执行,就是直接进行测试了吗?
是的!绝对是的! 这正是 Python 语言中 assert(断言)关键字的精妙之处。
当我们的系统调用 subprocess.run(["python", "/tmp/tmp_a1b2c3.py"]) 时,就相当于你在黑色的命令行窗口里敲下了回车键运行这个脚本。
这里会出现两种结局(这就构成了测试):
结局 A:代码完美无 Bug(测试通过)
- Python 解释器从上到下运行。
- 遇到
assert is_palindrome('racecar') == True,计算结果确实是 True。Python 什么都不会说,继续往下走。 - 所有代码悄无声息地运行完毕。
- 子进程以状态码
0正常退出(代表 Success)。 - 我们的
subprocess捕获到returncode == 0,判断测试通过。它清空报错信息,让 LangGraph 走向结束节点。
结局 B:代码有 Bug(测试失败触发循环)
假设 Claude 脑抽了,忘记写 .lower() 转换大小写。
- Python 解释器运行到第三个测试用例:
assert is_palindrome('A man a plan a canal Panama') == True - 因为没转大小写,
'A' != 'a',计算结果变成了False。 - 轰隆!
assert关键字一旦遇到 False,就会立刻引发解释器级别的崩溃,抛出AssertionError。 - 子进程异常终止,状态码非 0(比如退出码 1)。
- 这个报错的详细日志(Traceback)会被抛出到标准错误流(
stderr)中。
此时,我们的 tester_node 捕获到了这段"临终遗言",把它塞进了全局 State 的 error_message 里:
Traceback (most recent call last):
File "/tmp/tmp_a1b2c3.py", line 11, in <module>
assert is_palindrome('A man a plan a canal Panama') == True
AssertionError
紧接着,路由函数看到有了报错,就会把带有这段报错信息的 State 踢回给 Coder 节点。Claude 下一次就会看到自己因为 **'A man a plan a canal Panama'**这个测试用例没过而翻车,进而恍然大悟:"哦,我忘了处理大小写!"