前面的系列已经覆盖了 API 调用、上下文组织、结构化输出、错误处理和安全边界。本篇用一个小项目把这些知识串起来:输入一段 Python 函数源码,让模型生成文档字符串草稿,再由 Python 校验格式并输出待审核文件。工具不会自动改写源码,重点是把模型的生成能力放在一个可检查、可回退的边界内。

先划定工具的职责

程序接收一个 JSON 文件,其中包含函数名和源码;模型只负责提出摘要、参数说明和返回值说明。Python 负责读取配置、调用 API、解析 JSON、检查字段,并把结果保存为草稿。这样的分工很重要:模型输出不是事实证明,也不是可以直接执行的补丁;任何合入代码库的动作都应该由人审核。

本例使用 OpenAI Python SDK 当前文档中的 Responses API。安装依赖并设置环境变量:

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

密钥只从环境变量读取,不写入源码、输入文件或输出日志。模型名也由环境变量提供,避免把某个具体模型名称当成永久不变的配置。

准备输入和输出协议

新建 function.json:

1
2
3
4
{
"name": "chunk_text",
"source": "def chunk_text(text, size):\n return [text[i:i + size] for i in range(0, len(text), size)]"
}

为了让后续程序容易校验,要求模型只返回下面形状的 JSON:

1
2
3
4
5
6
{
"summary": "按固定大小切分文本。",
"parameters": [{"name": "text", "description": "待切分的文本。"}],
"returns": "由文本片段组成的列表。",
"warnings": ["size 必须为正整数。"]
}

协议不是装饰。把自由文本改成固定字段后,程序才能检查缺失字段、错误类型和空内容;校验失败时应保留原始响应并停止,而不是猜测模型本来想表达什么。

编写最小可运行程序

创建 draft_doc.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
import json
import os
import sys
from pathlib import Path

from openai import OpenAI


REQUIRED = {"summary", "parameters", "returns", "warnings"}


def load_input(path: Path) -> dict:
data = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(data.get("name"), str) or not data["name"].strip():
raise ValueError("name 必须是非空字符串")
if not isinstance(data.get("source"), str) or not data["source"].strip():
raise ValueError("source 必须是非空字符串")
return data


def make_prompt(data: dict) -> str:
return (
"请为下面的 Python 函数生成文档草稿。只返回合法 JSON,字段必须为 "
"summary、parameters、returns、warnings。parameters 是对象数组,"
"每个对象包含 name 和 description;warnings 是字符串数组。"
f"\\n函数名:{data['name']}\\n源码:\\n{data['source']}"
)


def validate(result: object) -> dict:
if not isinstance(result, dict) or set(result) != REQUIRED:
raise ValueError("JSON 字段必须恰好包含 summary、parameters、returns、warnings")
if not isinstance(result["summary"], str) or not result["summary"].strip():
raise ValueError("summary 必须是非空字符串")
if not isinstance(result["returns"], str):
raise ValueError("returns 必须是字符串")
if not isinstance(result["parameters"], list) or not all(
isinstance(item, dict)
and isinstance(item.get("name"), str)
and isinstance(item.get("description"), str)
for item in result["parameters"]
):
raise ValueError("parameters 的每一项都必须包含字符串 name 和 description")
if not isinstance(result["warnings"], list) or not all(
isinstance(item, str) for item in result["warnings"]
):
raise ValueError("warnings 必须是字符串数组")
return result


def generate(data: dict) -> dict:
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.responses.create(
model=os.environ["MODEL_NAME"],
instructions="你是严谨的 Python 文档助手,不要臆测源码中不存在的行为。",
input=make_prompt(data),
)
# output_text 是 SDK 提供的文本汇总;JSON 仍需由本地程序解析和校验。
return validate(json.loads(response.output_text))


def main() -> None:
if len(sys.argv) != 3:
raise SystemExit("用法:python draft_doc.py function.json draft.json")
source_path, output_path = map(Path, sys.argv[1:])
data = load_input(source_path)
draft = generate(data)
output_path.write_text(
json.dumps({"function": data["name"], "draft": draft}, ensure_ascii=False, indent=2)
+ "\n",
encoding="utf-8",
)
print(f"已生成待审核草稿:{output_path}")


if __name__ == "__main__":
main()

运行命令是 python draft_doc.py function.json draft.json。成功时,程序只会写入新的 JSON 文件,不会修改输入源码。由于真实结果取决于模型和服务状态,这里不预先声称某段输出一定出现;可验证的结果包括进程退出状态、输出文件存在,以及文件能否再次被 json.loads 读取。

为什么还要本地校验

提示词中的“只返回 JSON”不是安全边界。模型可能输出 Markdown 代码围栏、遗漏字段,或者把 parameters 写成字符串。validate 把这些情况变成明确的失败,让调用方可以修正提示词、记录错误或交给人工处理。更严格的项目还可以用 inspect 解析源码中的真实参数列表,再检查模型是否遗漏参数;也可以限制每个字段的长度,防止异常响应进入后续系统。

不要为了“让流程继续”而在解析失败时截取字符串或默默填默认值。自动修复可以作为独立步骤,但修复后的结果仍必须重新解析、校验,并保留原始响应供审计。对于文档草稿,人工复核尤其必要:源码中的动态行为、异常条件和外部副作用,模型都可能判断错误。

常见问题

为什么不让模型直接修改 Python 文件? 文档生成也可能改变代码行为,自动写回会扩大错误影响范围。先输出独立草稿,审核通过后再由开发者采用,是更容易回滚的边界。

调用失败怎么办? 实际项目应捕获超时、认证失败和限流错误,记录错误类型与尝试次数,并设置有限重试。不要把 API 密钥、完整敏感源码或原始响应无期限写入日志。

如何测试而不消耗 API 额度? 把 generate 改成接收一个客户端,测试时注入返回固定 output_text 的假对象,覆盖合法 JSON、缺字段和错误类型三类输入。真实 API 只保留少量集成测试。

能否使用结构化输出能力? 如果所用模型和 SDK 版本支持官方结构化输出参数,可以按官方文档配置来减少解析失败;但本地校验仍然需要保留,因为业务约束、长度限制和参数对应关系依旧属于应用代码的责任。

小结

这个小项目没有把模型包装成不可控的自动改代码机器人,而是建立了“输入快照—模型草稿—本地校验—人工审核”的小闭环。Responses API 负责生成,环境变量负责配置,Python 负责协议和副作用边界。今后无论扩展到测试用例草稿、变更说明还是数据抽取,都可以沿用同一原则:让模型提出候选,让程序验证结构,让人决定是否采用。