前面的系列已经覆盖了 API 调用、上下文组织、结构化输出、错误处理和安全边界。本篇用一个小项目把这些知识串起来:输入一段 Python 函数源码,让模型生成文档字符串草稿,再由 Python 校验格式并输出待审核文件。工具不会自动改写源码,重点是把模型的生成能力放在一个可检查、可回退的边界内。
先划定工具的职责 程序接收一个 JSON 文件,其中包含函数名和源码;模型只负责提出摘要、参数说明和返回值说明。Python 负责读取配置、调用 API、解析 JSON、检查字段,并把结果保存为草稿。这样的分工很重要:模型输出不是事实证明,也不是可以直接执行的补丁;任何合入代码库的动作都应该由人审核。
本例使用 OpenAI Python SDK 当前文档中的 Responses API。安装依赖并设置环境变量:
1 2 3 4 5 python -m venv .venv source .venv/bin/activatepython -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 jsonimport osimport sysfrom pathlib import Pathfrom openai import OpenAIREQUIRED = {"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), ) 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 负责协议和副作用边界。今后无论扩展到测试用例草稿、变更说明还是数据抽取,都可以沿用同一原则:让模型提出候选,让程序验证结构,让人决定是否采用。