跑一次测试改半天?AI 给的代码像一锅粥,函数、类、全局变量混在一起,改一个地方崩三个地方。更让人头疼的是,明明需求很简单,AI 却给你整出个抽象基类加工厂模式,代码量翻了三倍,读都读不懂。
如果你也遇到过这种情况,问题不在 AI,而在于「你没有在提示词里明确告诉 AI 该用什么结构」。AI 只会按它默认的“能跑就行”风格生成代码,你得给它一套清晰的“组织说明书”——而这套说明书本身,就是你要写进提示词里的内容。
先看看常规提示词有什么问题
大多数人让 AI 写 Python 代码时,提示词长这样:
❝“帮我写个 Python 脚本,读取文本文件,按句号分割句子,保存到另一个文件。”
❞
AI 可能给你一个全挤在 main() 里、甚至没有函数的脚本。能跑,但改不动。想换输入源?得改全局。想加个去重?得硬塞。想复用?复制粘贴吧。
「根本原因」:你没有在提示词里限定组织方式。AI 默认选它认为“最直接”的方式,而“最直接”往往不等于“最可维护”。
所以,「你需要在提示词里明确写出对代码结构的要求」——就像你给同事做代码审查时提意见一样。下面三组“提示词示例”里的“组织要求”部分,你可以直接复制,一个字都不用改。
核心思路:输入 → 处理 → 输出
无论需求多复杂,代码组织的最基本单元永远是“输入 → 处理 → 输出”三段式。你可以在提示词的开头就植入这个理念:
「通用提示词前缀(建议每个需求都带上)」:
❝“请按‘输入 → 处理 → 输出’的三段式结构组织代码。输入部分明确数据来源,处理部分拆成多个小函数,输出部分清晰呈现结果。每个函数只做一件事,命名用动词开头,加上类型注解和文档字符串。”
❞
这个前缀适用于所有场景。接下来我们再根据复杂度增加更具体的约束。
场景一:简单需求 —— 函数管道就够了
「适用场景」:计算器、文本格式转换、批量文件重命名、基础数据分析。「目标读者」:Python 初学者,或只需一次性脚本的非专业开发者。
代码模板(AI 会按提示词生成类似这样的代码)
# ===== 定义处理步骤 =====defread_data(path: str) -> str:"""读取文件内容,返回原始文本"""with open(path, 'r', encoding='utf-8') as f:return f.read()defclean_text(text: str) -> str:"""去除多余空格和换行"""return' '.join(text.split())defsplit_sentences(text: str) -> list:"""按句号分割成句子列表"""return text.split('。')defsave_results(sentences: list, out_path: str) -> None:"""将句子列表写入文件,每行一句"""with open(out_path, 'w', encoding='utf-8') as f: f.write('\n'.join(sentences))# ===== 输入 → 管道 → 输出 =====raw = read_data("input.txt") # 输入cleaned = clean_text(raw) # 处理步骤 1sentences = split_sentences(cleaned) # 处理步骤 2save_results(sentences, "out.txt") # 输出
提示词示例
【需求描述】(在这里用自然语言描述你要做什么,越具体越好。例如:读取 input.txt,清洗文本,按句号分割成句子,写入 output.txt)【组织要求】1. 使用函数管道,每个处理步骤(读取、转换、输出)分别写成独立函数。2. 禁止定义类。3. 每个函数添加类型注解和文档字符串。4. 主程序用注释标出“输入”、“处理”、“输出”三个阶段。5. 保持单文件,不拆分模块。
「为什么这样写」:每个函数都可以独立测试,想替换输入源只需要改 read_data,想增加处理步骤直接在管道中插入。
场景二:涉及外部服务 —— 按服务分组,明确边界
当需要写一个包含外部服务调用的脚本时,我经常用这个模板——它简单直接,又能把网络请求、数据库操作和纯计算逻辑干净地分开,调试时一眼就知道问题出在哪一环。
「适用场景」:调用网络接口、读写数据库、使用消息队列、与硬件交互、发送邮件或短信。
代码模板(AI 会按提示词生成类似这样的代码)
# ===== 外部服务:天气接口 =====deffetch_weather(city: str, api_key: str) -> dict:"""调用天气接口,返回原始 JSON 数据"""import requests url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}"return requests.get(url).json()defextract_temperature(raw: dict) -> float:"""从原始 JSON 中提取温度(开尔文转摄氏度)"""return raw['main']['temp'] - 273.15# ===== 外部服务:数据库 =====defget_db_connection(config: dict):import sqlite3return sqlite3.connect(config['db_path'])defsave_record(city: str, temp: float, conn) -> None: conn.execute("INSERT INTO weather VALUES (?, ?, datetime('now'))", (city, temp)) conn.commit()# ===== 内部业务逻辑(纯计算) =====defgenerate_summary(city: str, temp: float) -> str:returnf"{city} 当前温度:{temp:.1f}°C"# ===== 主管道 =====defmain(): API_KEY = "your-key" raw = fetch_weather("Beijing", API_KEY) temp = extract_temperature(raw) summary = generate_summary("Beijing", temp) conn = get_db_connection({'db_path': 'weather.db'}) save_record("Beijing", temp, conn) print(summary)if __name__ == "__main__": main()
提示词示例
【需求描述】(在这里描述你的业务需求。例如:根据城市名称调用天气接口获取温度,生成摘要文字,同时存入数据库)【组织要求】1. 使用函数管道,每个处理步骤分别写成独立函数。2. 禁止定义类。3. 每个函数添加类型注解和文档字符串。4. 用注释分区明确区分“内部处理”(纯计算)和“外部服务”(接口调用、数据库读写)。5. 调用同一个外部服务的函数在物理上相邻放置。6. 配置信息(接口密钥、数据库路径等)作为函数参数传入,不要硬编码在函数内部。7. 保持单文件,不拆分模块。
「为什么这样写」:外部服务函数集中在一起,改接口密钥或数据库配置时一目了然;内部逻辑与外部解耦,可以单独做单元测试。
如果项目继续膨胀(每个服务有 5 个以上的辅助函数,或需要在多个脚本中复用),你可以在提示词里补充一句:“请把各外部服务分别拆到 services/ 目录下的独立模块中,主程序通过 import 调用。” 但初学者建议先保持单文件,熟悉后再进阶。
场景三:复杂度爆炸 —— 用数据类和普通类收拢
当你的项目从脚本成长为中大型程序,会遇到两类复杂度,需要分别应对。
数据变复杂 → 用数据类
「症状」:函数参数超过 4 个且常成组传递;字典键名容易拼错;需要对数据做校验、设置默认值。
「提示词示例」:
【需求描述】(例如:处理订单数据,涉及订单ID、用户ID、商品列表、折扣率等字段)【组织要求】1. 使用 @dataclass 定义输入和输出数据模型,每个字段加类型注解。2. 有默认值的字段放在最后。3. 将数据模型定义放在使用它的函数上方,或拆到 models.py 中(超过 3 个模型时)。
「代码模板」:
from dataclasses import dataclassfrom typing import List@dataclassclassOrderInput: order_id: str user_id: int items: List[str] discount_rate: float = 0.0@dataclassclassOrderOutput: order_id: str total_price: float final_price: float status: str = "processed"
管道步骤变复杂 → 用普通类承载共享状态
「症状」:管道步骤超过 5 个,多个步骤共享同一份配置、缓存或数据库连接池;每个函数都要传配置和连接池,导致参数过长。
「提示词示例」:
【需求描述】(例如:处理订单的完整流程,包含校验、价格计算、库存扣减、生成单据等 6 个步骤)【组织要求】1. 定义一个普通的类来承载管道,类名用业务名称 + Pipeline。2. 在 __init__ 中接收共享状态(配置、连接池等)并保存为实例属性。3. 每个处理步骤写成实例方法,方法之间通过 self.共享状态 传递数据。4. 提供一个 run() 方法按顺序执行所有步骤。5. 禁止使用抽象基类或继承任何父类——这个类只是普通容器,不是抽象层。6. 如果单个类超过 200 行或方法超过 10 个,按阶段拆成多个普通类,然后在主管道类中组合使用。
「代码模板」:
classOrderPipeline:"""一个普通的容器类——没有继承任何抽象类"""def__init__(self, config: dict, db_pool): self.config = config self.db_pool = db_pooldefstep1(self, raw_data):# 使用 self.configreturn processeddefstep2(self, step1_output):# 使用 self.db_poolreturn step2_outputdefrun(self, input_data): out1 = self.step1(input_data) out2 = self.step2(out1)return out2# 调用pipeline = OrderPipeline(config, db_pool)result = pipeline.run(raw_order)
通用提示词模板
如果你不想每次单独写,可以把下面这段直接贴在每次提问的开头,然后再加上具体的【需求描述】:
【组织要求】- 采用“输入 → 处理 → 输出”的三段式结构。- 使用函数管道,每个函数只做一件事,命名用动词开头(get_/process_/save_)。- 所有函数添加类型注解和文档字符串。- 涉及外部服务(接口、数据库等)时,用注释分区明确区分内外,同一服务的函数相邻放置。- 除非管道步骤超过 5 个且共享状态,否则不要定义类;如果使用类,只用作普通容器,禁止定义抽象基类。- 数据字段超过 4 个时使用 @dataclass 定义结构体。- 保持单文件,除非代码超过 300 行或需要跨脚本复用。
什么时候该用这套提示词,什么时候别用
「✅ 适合」:
- 你正在用 AI 生成代码,希望输出可维护、可读性高。
- 项目是脚本或中小型应用,团队成员对 Python 面向对象不熟悉。
「❌ 不适合」:
- 大型企业级框架,有严格的分层架构和依赖注入约定(那套体系有它自己的写法)。
- 需要深度定制元编程、动态类生成等高阶特性(这种场景已超出“让 AI 生成易懂代码”的范畴)。
❝「注意」:不要为了“以后可能扩展”而在提示词里要求提前设计抽象层。先做简单,后做重构——这条原则在这里非常适用。先用最简单的函数管道,等真的需要了再重构,让 AI 按新提示词重写也很快。
❞