前面的系列已经覆盖了模型调用、提示词、结构化输出、错误处理、评估、日志和安全边界。本篇在学习路线完成后做一个新的综合小项目:给模型一份“变更说明”和几个受影响模块,让它生成影响分析草案,再由 Python 检查格式和范围。程序只写出报告,不修改代码、不执行部署,最后由人复核后决定是否采用。

先定义安全边界

影响分析适合做候选整理,不适合直接替人批准变更。模型可能漏掉依赖、误解业务规则,或者把不确定的推测写成结论。因此输入中要明确模块清单,输出中要区分 direct(直接影响)、indirect(间接影响)和 unknown(需要核对)。程序只接受清单中的模块名,并拒绝无法解析的结果。

这个边界也让项目容易验证:模型负责自然语言判断,Python 负责 JSON 解析、枚举值和引用范围检查。即使没有 API 密钥,也可以用固定 JSON 测试校验函数;真正调用模型则属于集成测试。

准备环境和输入

使用独立虚拟环境安装官方 Python SDK:

1
2
3
python -m venv .venv
source .venv/bin/activate
python -m pip install openai

密钥和模型名只从环境变量读取:

1
2
export OPENAI_API_KEY="替换为你的真实密钥"
export MODEL_NAME="替换为你可用的模型名称"

新建 change.json。示例数据不包含真实项目机密:

1
2
3
4
{
"change": "将订单查询接口的默认排序改为按更新时间倒序,并增加分页上限。",
"modules": ["订单服务", "后台报表", "移动端订单列表", "数据导出脚本"]
}

真实项目还应限制文件大小,对提交说明和模块名称做脱敏,并确认这些资料允许发送到所选模型服务。不要把密钥写入 JSON、源码或 Git。

编写最小分析器

创建 impact_analyzer.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import json
import os
import sys
from pathlib import Path

from openai import OpenAI


ALLOWED_LEVELS = {"direct", "indirect", "unknown"}


def load_case(path: str) -> dict:
case = json.loads(Path(path).read_text(encoding="utf-8"))
if not isinstance(case.get("change"), str) or not case["change"].strip():
raise ValueError("change 必须是非空字符串")
modules = case.get("modules")
if not isinstance(modules, list) or not modules or not all(
isinstance(item, str) and item.strip() for item in modules
):
raise ValueError("modules 必须是非空字符串列表")
return case


def validate_report(report: dict, modules: list[str]) -> list[str]:
problems = []
items = report.get("items")
if not isinstance(items, list) or not items:
return ["items 必须是非空列表"]
known = set(modules)
seen = set()
for item in items:
if not isinstance(item, dict):
problems.append("每个影响项必须是对象")
continue
module, level = item.get("module"), item.get("level")
if module not in known:
problems.append(f"未知模块:{module}")
if level not in ALLOWED_LEVELS:
problems.append(f"非法影响级别:{level}")
if module in seen:
problems.append(f"模块重复:{module}")
seen.add(module)
if not isinstance(item.get("reason"), str) or not item["reason"].strip():
problems.append(f"{module} 缺少 reason")
return problems


def analyze(case: dict) -> dict:
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
prompt = {
"change": case["change"],
"modules": case["modules"],
"output_schema": {
"items": [
{"module": "清单中的模块名", "level": "direct|indirect|unknown",
"reason": "简短理由"}
],
"summary": "一句话总结"
}
}
response = client.responses.create(
model=os.environ["MODEL_NAME"],
instructions=(
"你是变更影响分析助手。只返回合法 JSON,不要 Markdown 围栏。"
"必须覆盖输入中的每个模块;无法判断时使用 unknown。"
"不要提出执行命令,不要声称已经验证代码。"
),
input=json.dumps(prompt, ensure_ascii=False),
)
return json.loads(response.output_text)


def main() -> int:
if len(sys.argv) != 2:
print("用法:python impact_analyzer.py change.json", file=sys.stderr)
return 2
case = load_case(sys.argv[1])
report = analyze(case)
problems = validate_report(report, case["modules"])
if problems:
print("报告校验失败:" + ";".join(problems), file=sys.stderr)
return 1
print(json.dumps(report, ensure_ascii=False, indent=2))
print("报告通过格式校验,请人工复核。", file=sys.stderr)
return 0


if __name__ == "__main__":
raise SystemExit(main())

运行:

1
python impact_analyzer.py change.json

responses.create 负责请求,output_text 是 SDK 提供的文本结果;代码随后用 json.loads 解析,并调用纯校验函数。脚本没有预先写死模型答案,也不会把输出写回代码仓库。退出码 0 只表示报告符合机械规则,1 表示模型返回了但报告不合格,2 表示命令行用法错误。

为什么必须做范围校验

让模型“覆盖所有模块”并不等于它真的会覆盖。validate_report 检查三件事:模块必须来自输入清单,影响级别必须是有限枚举,每个模块只能出现一次且必须有理由。这里没有检查理由是否事实正确,因为字符串规则做不到这一点;事实和依赖关系仍需查看代码、测试、监控或请领域负责人确认。

可以在不调用模型的情况下测试校验逻辑:

1
2
3
4
5
6
7
case_modules = ["订单服务", "后台报表"]
valid = {"items": [
{"module": "订单服务", "level": "direct", "reason": "读取排序参数"},
{"module": "后台报表", "level": "unknown", "reason": "需要检查查询复用关系"},
]}
assert validate_report(valid, case_modules) == []
assert validate_report({"items": [{"module": "未知模块", "level": "direct", "reason": "x"}]}, case_modules)

测试应放在独立测试文件中,并增加空响应、非法 JSON、重复模块和缺少字段的案例。网络调用不要成为每次单元测试的前置条件。

常见问题

为什么不让模型直接修改配置或执行迁移? 影响分析本身是不确定的推断。把它连接到写文件、部署或数据库操作,会把一次误判变成真实副作用。应该先输出报告,经过人工确认,再由专门的确定性工具执行,并保留审批记录。

JSON 解析失败怎么办? 当前脚本会让解析异常终止,这适合暴露失败。实际服务可捕获 JSONDecodeError,记录脱敏后的请求编号,并有限重试或转人工队列;不能把一段普通文本“猜测性修复”为成功 JSON。

为什么用 unknown 而不是强迫模型二选一? 不确定性是有价值的信息。强迫每个模块归类会制造虚假的确定性;unknown 能提醒复核者检查依赖、测试和运行数据。

模型说“已验证”可以相信吗? 不可以。提示词要求它不要这样说,但真正的验证必须由测试、静态检查或人工完成。自然语言声明不是执行证据。

小结

这个综合项目把模型调用、结构化约束、确定性校验和人工审批边界串成了一个小闭环。模型只生成影响分析候选,Python 检查引用范围和格式,人与工程工具负责最终判断和执行。以后可以加入结果版本、审查人、测试链接和脱敏日志,但每次扩展都应保持一个原则:AI 输出是可复核的建议,不是未经验证的变更授权。