📢 转载信息
原文链接:https://www.kdnuggets.com/ai-writes-python-code-but-maintaining-it-is-still-your-job
原文作者:Bala Priya C
Image by Author
# 引言
人工智能编码工具在编写可运行的Python代码方面变得非常出色。它们可以在几分钟内构建完整的应用程序和实现复杂的算法。然而,AI生成的代码在维护起来常常令人头疼。
如果你正在使用Claude Code、GitHub Copilot或Cursor的智能体模式等工具,你很可能经历过这种情况。AI能帮你快速交付可用的代码,但其代价会稍后显现。你可能在几周后重构一个臃肿的函数只是为了弄懂它的工作原理。
问题不在于AI写了糟糕的代码——尽管有时确实如此——而在于AI优化的是“立即工作”并满足提示中的要求,而你需要的是长期可读且可维护的代码。本文将着重介绍Python特有的策略,教你如何跨越这一鸿沟。
# 避免空白画布陷阱
开发人员犯的最大错误是让AI从零开始编写代码。AI智能体在有约束和指导的情况下工作效果最佳。
在写下第一个提示之前,请自己设置好项目的基本要素。这意味着你需要确定项目结构——安装核心库并实现几个可运行的示例——为后续工作定下基调。这看起来可能适得其反,但它有助于AI编写出更符合你应用需求的代码。
首先手动构建一两个功能。如果你正在构建一个API,请自己实现一个完整的端点,包含你想要的所有模式:依赖注入、适当的错误处理、数据库访问和验证。这成为了参考实现。
假设你手动编写了第一个端点:
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
router = APIRouter()
# 假设 get_db 和 User 模型已在别处定义
async def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
当AI看到这种模式时,它就理解了我们如何处理依赖、如何查询数据库以及如何处理缺失的记录。
这同样适用于你的项目结构。创建目录、设置导入并配置测试框架。AI不应该做这些架构决策。
# 让Python的类型系统承担重任
Python的动态类型非常灵活,但这种灵活性在AI编写代码时会成为一种负担。请将类型提示作为应用程序代码中的必需安全护栏,而不是可有可无的选项。
严格的类型检查可以在AI犯错并推向生产之前将其捕获。当你要求每个函数签名都带有类型提示,并在严格模式下运行mypy时,AI就无法走捷径。它不能返回模糊的类型,也不能接受可能是字符串或列表的参数。
更重要的是,严格的类型会促使更好的设计。例如,一个尝试编写接受data: dict函数的AI智能体可以对该字典包含的内容做出很多假设。然而,一个接受data: UserCreateRequest(其中UserCreateRequest是一个Pydantic模型)的函数,AI智能体只有一种解释。
# 这会约束AI从 pydantic 导入 BaseModel, EmailStr
class UserCreateRequest(BaseModel):
name: str
email: EmailStr
age: int
class UserResponse(BaseModel):
id: int
name: str
email: EmailStr
def process_user(data: UserCreateRequest) -> UserResponse:
pass
# 而不是这样
# def process_user(data: dict) -> dict:
# pass
使用强制执行契约的库:2.0版本的SQLAlchemy配合类型检查模型,以及支持响应模型的FastAPI都是绝佳的选择。这些不仅仅是好的实践;它们是确保AI沿着正确轨道运行的约束条件。
将mypy设置为严格模式,并让通过类型检查成为不可协商的条件。当AI生成的代码未能通过类型检查时,它会不断迭代直到通过。这种自动反馈循环产生的代码质量要优于任何提示工程。
# 创建文档来指导AI
大多数项目都有开发者会忽略的文档。对于AI智能体,你需要它们真正使用的文档——比如一份包含指导方针的README.md文件。这意味着在项目根目录下放一个包含清晰、具体规则的单一文件。
在项目根目录下创建一个CLAUDE.md或AGENTS.md文件。不要写得太长。重点关注项目独有的内容,而不是通用的Python最佳实践。
你的AI指导方针应明确规定:
- 项目结构以及不同类型的代码存放位置
- 常见任务应使用哪些库
- 应遵循的具体模式(指向示例文件)
- 明确禁止的模式
- 测试要求
以下是一个AGENTS.md文件的示例:
# 项目指导方针
## 结构
/src/api - FastAPI 路由器
/src/services - 业务逻辑
/src/models - SQLAlchemy 模型
/src/schemas - Pydantic 模型
## 模式
- 所有服务都继承自 BaseService(参见 src/services/base.py)
- 所有数据库访问都通过仓库模式(参见 src/repositories/)
- 对所有外部依赖使用依赖注入
## 标准
- 所有函数都有类型提示
- 使用 Google 风格的文档字符串
- 函数长度少于 50 行
- 提交前运行 `mypy --strict` 和 `ruff check`
## 严禁
- 禁止裸露的 except 子句
- 禁止 type: ignore 注释
- 禁止可变默认参数
- 禁止全局状态
关键在于具体。不要只说“遵循最佳实践”。要指向演示该模式的确切文件。不要只说“正确处理错误”;要展示你想要的错误处理模式。
# 编写指向示例的提示
泛泛的提示产生泛泛的代码。引用你现有代码库的特定提示会产生更易于维护的代码。
不要只要求AI“添加身份验证”,而是引导它实现过程,并引用你的模式。这是一个指向示例的提示的例子:
在 src/services/auth_service.py 中实现 JWT 身份验证。遵循 src/services/user_service.py 中 UserService 的相同结构。使用 bcrypt 进行密码哈希处理(已在 requirements.txt 中)。
在 src/api/dependencies.py 中创建身份验证依赖项,遵循 get_db 的模式。
在 src/schemas/auth.py 中创建类似于 user.py 的 Pydantic 模式。
在 tests/test_auth_service.py 中使用 conftest.py 中的 fixtures 添加 pytest 测试。
注意看,每一个指令都指向一个现有的文件或模式。你不是让AI去构建架构;你是在要求它将你的需求应用于一个新功能。
当AI生成代码时,对照你的模式进行审查。它是否使用了相同的依赖注入方法?是否遵循了相同的错误处理方式?是否以相同的方式组织了导入?如果不是,指出差异并要求它与现有模式保持一致。
# 在实现前进行规划
AI智能体移动速度很快,但这有时会降低其有用性,如果速度是以牺牲结构为代价的话。在任何代码被编写之前,请使用计划模式或要求提供实现计划。
规划步骤能迫使AI思考依赖和结构。它还为你提供了一个机会,在架构问题(如循环依赖或冗余服务)被实现之前捕获它们。
要求一个明确指出以下内容的计划:
- 将被创建或修改的文件
- 组件之间的依赖关系
- 将遵循哪些现有模式
- 需要哪些测试
像审查设计文档一样审查这份计划。确认AI理解你的项目结构。验证它是否使用了正确的库,并确认它没有在重复已经存在的功能。
如果计划看起来不错,就让AI执行。如果不好,在任何代码写入前修正计划。修正一个糟糕的计划比修正糟糕的代码要容易得多。
# 编写真正进行测试的测试
AI在编写测试方面非常出色且速度极快。然而,除非你明确定义了“有用”的含义,否则AI不会高效地编写出有用的测试。
AI默认的测试行为是只测试“快乐路径”(即一切顺利的情况),而这恰恰是你最不需要测试的时候。
明确指定你的测试要求。对于每项功能,要求包括:
- 快乐路径测试
- 验证错误测试,检查无效输入时会发生什么
- 边缘情况测试,包括空值、None、边界条件等
- 错误处理测试,针对数据库或外部服务失败等情况
将AI指向你现有的测试文件作为示例。如果你已经有了好的测试模式,AI也会编写出有用的测试。如果还没有好的测试,请先自己编写一些。
# 系统化地验证输出
AI生成代码后,不要仅仅检查它是否能运行。要通过一个清单来运行它。
你的验证清单应包括以下问题:
- 它是否通过了mypy严格模式检查
- 它是否遵循了现有代码中的模式
- 所有函数是否都少于50行
- 测试是否覆盖了边缘情况和错误
- 所有函数是否都有类型提示
- 它是否正确使用了指定的库
将你能自动化的部分自动化。设置pre-commit钩子来运行mypy、Ruff和pytest。如果AI生成的代码未能通过这些检查,就不能被提交。
对于无法自动化的部分,在审查了足够的AI代码后,你会发现常见的反模式——例如,功能过多的函数、吞噬异常的错误处理,或者将验证逻辑与业务逻辑混在一起。
# 实施实用的工作流程
现在让我们把到目前为止讨论的所有内容整合起来。
你启动一个新项目。你花时间设置结构、选择和安装库,并编写了几个示例功能。你创建了带有指导方针的CLAUDE.md,并编写了具体的Pydantic模型。
然后你要求AI实现一个新功能。你编写了一个详细的提示,指向你的示例。AI生成了一个计划。你审查并批准它。AI编写代码。你运行类型检查和测试。一切通过。你根据你的模式审查代码。它匹配。你提交。
从提示到提交,实现一个原本需要你手动编写一小时的功能可能只需要大约15分钟。但更重要的是,你得到的代码更容易维护——它遵循了你建立的模式。
下一项功能会因为AI有了更多示例可供学习而进行得更快。随着时间的推移,代码会变得更加一致,因为每一项新功能都会强化现有的模式。
# 总结
随着AI编码工具被证明非常有用,你作为开发人员或数据专业人员的工作正在改变。你现在花在编写代码上的时间更少,而花在以下方面的时间更多:
- 设计系统和选择架构
- 创建模式的参考实现
- 编写约束和指导方针
- 审查AI输出并维护质量标准
最重要的技能不是写代码的速度有多快。而是设计能够约束AI以编写可维护代码的系统。是知道哪些实践是可扩展的,哪些会产生技术债务。我希望这篇文章对你有帮助,即使你选择的编程语言不是Python。让我们知道,我们还能做些什么来保持AI生成的Python代码的可维护性。继续探索!
Bala Priya C是来自印度的一名开发人员和技术作家。她喜欢在数学、编程、数据科学和内容创作的交叉点上工作。她的兴趣和专业领域包括DevOps、数据科学和自然语言处理。她喜欢阅读、写作、编程和咖啡!目前,她正致力于通过撰写教程、操作指南、观点文章等方式学习并与开发者社区分享自己的知识。Bala还创建引人入胜的资源概述和编码教程。
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区