前面的系列已经覆盖了模型调用、提示词、结构化输出、错误处理、评估、日志和安全边界。本篇在学习路线完成后做一个新的综合小项目:给模型一份“变更说明”和几个受影响模块,让它生成影响分析草案,再由 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 输出是可复核的建议,不是未经验证的变更授权。