本文来自《AI 应用开发课程》月份 1 课程文档,已整理为网站文章版本。
学习目标
学完本节后,你应当能够:
- 使用
try/except处理异常。 - 读写文本文件和 JSON 文件。
- 理解为什么 AI 应用开发中大量使用 JSON。
- 为后面的配置管理、结构化输出和日志记录打基础。
前置知识
- 已完成前 3 节 Python 课程
1. 为什么异常处理重要
月份 1 后面常见的失败点:
- API Key 缺失
- 网络请求失败
- JSON 解析失败
- 工具参数不合法
如果你不会做异常处理,代码通常会在最脆弱的位置直接崩掉。
2. try/except 基本写法
try:
value = int("123")
except ValueError:
print("无法转换为整数")
带错误对象
try:
value = int("abc")
except ValueError as error:
print(f"发生错误: {error}")
else 和 finally
try:
value = int("123")
except ValueError:
print("失败")
else:
print("成功")
finally:
print("结束")
3. 什么时候应该主动抛出异常
def ensure_api_key(api_key: str | None) -> str:
if not api_key:
raise ValueError("DEEPSEEK_API_KEY 未配置")
return api_key
主动抛异常的意义:
- 让错误更早暴露
- 让调用方知道失败原因
- 避免错误静默传播
4. 文件读写
写文本文件
with open("note.txt", "w", encoding="utf-8") as file:
file.write("Hello, Python!")
读文本文件
with open("note.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)
with 的作用是自动关闭文件资源。
5. JSON 读写
写 JSON
import json
data = {"role": "user", "content": "hello"}
with open("message.json", "w", encoding="utf-8") as file:
json.dump(data, file, ensure_ascii=False, indent=2)
读 JSON
import json
with open("message.json", "r", encoding="utf-8") as file:
data = json.load(file)
print(data["content"])
6. 为什么 AI 应用里 JSON 如此重要
因为它经常用于:
- API 请求体和响应体
- 结构化输出
- 工具参数
- 配置文件
- 实验记录
月份 1 后面你会多次处理 JSON,所以这里必须先熟悉。
7. 异常和 JSON 结合示例
import json
def load_json_file(path: str) -> dict:
try:
with open(path, "r", encoding="utf-8") as file:
return json.load(file)
except FileNotFoundError as error:
raise FileNotFoundError(f"文件不存在: {path}") from error
except json.JSONDecodeError as error:
raise ValueError(f"JSON 格式错误: {path}") from error
这个模式在后面读取 Prompt 模板、工具结果和配置文件时都很常见。
8. 实操任务
- 写一个函数,把一条聊天消息保存为 JSON 文件。
- 写一个函数,从 JSON 文件读取聊天消息。
- 人为制造一个错误 JSON,观察异常内容。
9. 自测题
- 为什么不能所有异常都用一个裸
except吞掉? - 为什么文件操作推荐配合
with? - 为什么结构化输出通常会和 JSON 强绑定?
10. 作业与验收
作业:
- 实现一个
save_messages(messages: list[dict], path: str)函数。 - 实现一个
load_messages(path: str)函数。
验收标准:
- 正常数据能成功保存和读取
- 文件不存在时能给出清晰报错
- JSON 格式错误时能定位问题
11. 常见错误
- 打开文件时忘记指定编码
- 用裸
except把所有错误吞掉 - JSON 文件里使用非法逗号或引号
12. 延伸阅读
- Python 官方文档中的 exceptions
- Python 官方文档中的 json 模块
13. 本章与前文关系
前几章让你具备了:
- 基础语法能力
- 模块组织能力
- 结构化对象表达能力
这一章开始把这些能力放进更真实的工程场景里:程序不总会成功,数据也不总留在内存里。你需要学会:
- 错误出现时怎么处理
- 数据怎么保存到文件
- 为什么 JSON 是后续 AI 应用里最常见的数据格式之一
14. 本章在研发助手项目中的位置
研发助手项目里,这一章的知识会反复出现:
- 读取
.env配置失败 - 模型返回的 JSON 无法解析
- 工具结果要写入日志或本地文件
- 结构化输出要从文本变成对象
换句话说,本章学的不是“普通 Python 文件操作”,而是 AI 应用里最常见的失败与数据流转基础。
15. 为什么异常处理是月份 1 的硬能力
初学者很容易把异常处理看成“锦上添花”。但在月份 1,异常处理其实是基础能力,因为后面你会频繁遇到这些失败点:
- API Key 缺失
- 网络请求超时
- JSON 结构不合法
- 工具调用参数错误
- 文件不存在
如果你不会做异常处理,就会出现两种糟糕结果:
- 程序直接崩掉,但你不知道为什么。
- 你把错误吞掉,程序看起来没报错,但结果已经不可靠。
16. 错误示例 vs 正确示例
错误示例:裸 except
try:
data = json.load(file)
except:
print("something went wrong")
问题:
- 你不知道到底是哪种错误
- 错误上下文丢失
- 后面几乎无法排查
正确示例:按具体异常类型处理
import json
try:
data = json.load(file)
except FileNotFoundError as error:
raise FileNotFoundError("目标文件不存在") from error
except json.JSONDecodeError as error:
raise ValueError("JSON 格式错误") from error
这种方式的核心价值是:错误更可见,更可解释,也更容易在日志里分类。
17. 完整文件级示例:json_store.py
"""消息保存与加载示例。
本文件展示三件事:
1. 如何把结构化消息保存为 JSON
2. 如何从 JSON 读取回来
3. 如何在失败时给出清晰异常
"""
from __future__ import annotations
import json
from pathlib import Path
def save_messages(messages: list[dict[str, str]], path: str) -> None:
"""将消息列表写入 JSON 文件。"""
file_path = Path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
with file_path.open("w", encoding="utf-8") as file:
json.dump(messages, file, ensure_ascii=False, indent=2)
def load_messages(path: str) -> list[dict[str, str]]:
"""从 JSON 文件中读取消息列表。"""
file_path = Path(path)
if not file_path.exists():
raise FileNotFoundError(f"文件不存在: {file_path}")
try:
with file_path.open("r", encoding="utf-8") as file:
data = json.load(file)
except json.JSONDecodeError as error:
raise ValueError(f"JSON 格式错误: {file_path}") from error
if not isinstance(data, list):
raise TypeError("消息文件的顶层结构必须是列表")
return data
def main() -> None:
"""演示保存、读取和打印流程。"""
demo_messages = [
{"role": "system", "content": "你是研发助手"},
{"role": "user", "content": "请总结这个错误日志"},
]
path = "data/messages.json"
save_messages(demo_messages, path)
loaded_messages = load_messages(path)
print(loaded_messages)
if __name__ == "__main__":
main()
18. 逐段解释这份完整示例
Path 的使用
这里没有直接用字符串拼路径,而是使用 pathlib.Path。这是 Python 工程里非常常见的文件路径处理方式,后面做配置、日志和测试时也很实用。
mkdir(parents=True, exist_ok=True)
这行代码意味着:如果目录不存在,就自动创建。它能让保存动作更稳定,而不是要求你手工先建目录。
先检查文件是否存在,再读
这能让错误更早暴露,也更容易给出清晰提示。
读取后检查顶层结构
即使 JSON 能成功解析,也不代表内容一定是你想要的结构。所以还需要做结构检查。
19. 为什么 JSON 在月份 1 会被反复使用
因为它恰好处在“人类可读”和“程序可处理”的交叉点:
- API 请求和响应经常是 JSON
- 结构化输出经常要落成 JSON
- 工具调用参数本质上也是结构化 JSON 风格数据
- 调试时把中间结果保存成 JSON 非常方便
月份 1 学会 JSON,不只是为了做文件读写,而是为了理解后面所有结构化数据流。
20. 一个更接近后续项目的增强版示例
你可以在 json_store.py 基础上继续增强:
- 增加日志打印
- 增加时间戳字段
- 在读取后校验每条消息是否都包含
role和content
增强版思路:
def validate_message(message: dict[str, str]) -> None:
if "role" not in message or "content" not in message:
raise ValueError("每条消息都必须包含 role 和 content")
这一步很重要,因为它已经在向后面的 Pydantic 校验思维靠近。
21. 调试与排错:本章最值得刻意练习的 4 类错误
练习一:文件不存在
把路径改成一个不存在的文件,看你是否能得到清晰错误。
练习二:JSON 写错格式
手动修改 JSON,故意删掉一个引号或逗号,再观察报错。
练习三:顶层结构不对
把原本的列表改成对象,看看你的结构检查是否能拦住。
练习四:字段缺失
删掉某条消息的 content,思考后续如果不拦住会有什么后果。
22. 本章完成后你应该具备的能力
完成本章后,你应当做到:
- 能写出最小
try/except结构。 - 能解释为什么不应使用裸
except。 - 能读写 UTF-8 文本和 JSON 文件。
- 能理解为什么解析成功后仍然需要进一步校验结构。
23. 如果你卡在这里,先回看哪几章
- 看不懂函数拆分:回看 02-函数-模块-包.md
- 结构化对象表达不清楚:回看 03-类-数据类-类型注解.md
24. 从本章过渡到下一章的桥接说明
接下来进入 05-asyncio基础.md。
这一步的逻辑非常自然:你已经知道本地文件如何读写,也知道失败时如何处理。下一步要进入的是月份 1 后面最常见的“等待型操作”场景,也就是网络请求和异步执行。后面的 DeepSeek API 调用和 FastAPI 异步接口都会直接建立在这里。