本篇把模型调用、结构化输出和人工审批组合成小项目:输入变更摘要,生成草稿,校验后等待确认。

功能边界

模型只改写事实,不得补充不存在的功能。输出固定为 title、summary 和 items。提示词不是边界,JSON 检查和人工确认才是。

准备环境

1
2
3
python -m venv .venv
source .venv/bin/activate
python -m pip install openai python-dotenv
1
2
3
OPENAI_API_KEY=替换为你的真实密钥
MODEL_NAME=替换为你可用的模型名称
OPENAI_BASE_URL=

最小实现

新建 release_notes.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
import json, os, sys
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL") or None)
model = os.environ["MODEL_NAME"]
allowed = {"feature", "fix", "change", "breaking"}
rules = "只返回 JSON,字段为 title、summary、items;每项为 type、text、needs_attention。type 只能是 feature/fix/change/breaking,只改写输入事实。"

def check(raw):
try: data = json.loads(raw)
except json.JSONDecodeError as e: raise ValueError("返回不是 JSON") from e
if set(data) != {"title", "summary", "items"} or not data["items"]:
raise ValueError("顶层字段无效")
if not all(isinstance(data[k], str) and data[k].strip() for k in ("title", "summary")):
raise ValueError("标题或摘要为空")
for x in data["items"]:
if set(x) != {"type", "text", "needs_attention"} or x["type"] not in allowed:
raise ValueError("条目字段无效")
if not isinstance(x["text"], str) or not x["text"].strip() or not isinstance(x["needs_attention"], bool):
raise ValueError("条目类型无效")
return data

def main():
if len(sys.argv) != 2: raise SystemExit("用法:python release_notes.py change-summary.txt")
source = Path(sys.argv[1]).read_text(encoding="utf-8")
if not source.strip() or len(source) > 8000: raise SystemExit("输入为空或过长")
r = client.responses.create(model=model, instructions=rules, input=source)
draft = check(r.output_text)
print(json.dumps(draft, ensure_ascii=False, indent=2))
if input("确认保存?[y/N] ").lower().strip() == "y":
Path("release-notes.json").write_text(json.dumps(draft, ensure_ascii=False, indent=2), encoding="utf-8")
print("已保存")

if __name__ == "__main__": main()

准备 change-summary.txt:

1
新增 CSV 导出;修复空筛选条件报错;CSV 暂不支持自定义列顺序。

执行 python release_notes.py change-summary.txt。非法 JSON、缺字段、错误类型会在保存前被拒绝,输入 n 不会写文件。

常见问题

为什么提示词要求 JSON 后仍要校验? 提示词只是引导,程序必须拒绝未知字段、空文本和错误枚举。

调用失败怎么办? 增加超时和有限次数的指数退避,区分认证、限流、网络错误;失败时不要写入空草稿。

小结

这个项目形成了“准备事实—请求模型—校验结构—人工确认”的闭环。模型负责改写,Python 负责类型和副作用;职责分开才能让原型可维护。