DEV Community

ggg party
ggg party

Posted on

AI自动生成类型注解和docstring,我们踩坑后总结了一套三关流程

去年我们接手了一个老项目,三千多个函数,没有类型注解,docstring全靠写代码的人心情。每次接手别人的模块,光读懂参数类型就要花半天。后来我们决定引入AI助手自动生成类型注解和docstring,本以为能一劳永逸,结果发现AI生成的代码,有时候比不写还危险。

AI助手确实能看懂上下文,能根据函数体推断参数类型,能写出像模像样的docstring。但它不是你的团队成员,它不知道你们的命名习惯,不知道哪些参数是外部接口必须保持兼容的,更不知道哪些docstring里藏着业务规则的坑。

我们踩过最大的坑,是AI生成的类型注解与运行时实际类型不符。比如一个函数接收一个字典,AI根据函数体里的访问方式推断出这个字典的键是字符串,值也是字符串,于是生成了dict[str, str]的注解。但实际调用方传进来的字典,值里有整数也有布尔值。Python是动态语言,类型注解只是给人看的,但一旦有人真的用mypy做了严格检查,这种错误注解就会让整个项目爆红。

后来我们总结了一套流程,现在AI生成的代码,必须经过三个关卡才能合入主干。

第一关是AI生成前的输入规范。我们不能直接把整个函数丢给AI让它自由发挥。我们会先定义一个prompt模板,里面要求AI必须基于函数签名、参数命名、返回值使用方式这三个维度来推断类型。而且我们明确告诉AI,如果某个参数在函数体里没有被使用,或者返回值路径不统一,必须显式标注为Unknown,而不是强行给一个类型。这一步看起来多花了点时间,但能减少后期至少一半的返工。

第二关是AI生成后的自动校验。我们写了一个小脚本,用ast模块解析AI生成的代码,提取出所有函数签名和注解,然后与项目里已有的配置文件做比对。比如我们强制要求所有公开函数的参数必须有注解,docstring里必须包含参数说明、返回值说明和异常列表。脚本会自动检查这些字段是否存在,缺失就报错。这个脚本跑一遍大概需要几秒,能挡住大部分格式问题。

第三关是人工review,但review的重点不是代码风格,而是类型语义。我们要求reviewer必须回答三个问题:这个类型注解是否覆盖了所有可能的输入分支?docstring里的描述是否与函数实际行为一致?有没有把业务逻辑的隐式假设写进docstring,比如某个参数不能为空,但函数体里并没有做空值检查?如果这三个问题有一个不通过,就打回重新生成。

刚开始实施这套流程的时候,团队里有人觉得太重了。一个简单的工具函数,本来几行代码,现在要过三道关卡。但跑了两个月之后,大家发现收益很明显。

最直观的变化是代码审查速度变快了。以前review代码要逐行看逻辑,还要猜参数含义。现在AI生成的docstring把参数和返回值的语义写得很清楚,reviewer可以直接把注意力放在算法和边界条件上。我们统计过,一次性通过review的PR比例从原来的百分之四十提高到了百分之七十三。

还有一个隐性的好处是,AI生成的docstring倒逼团队把函数写得更单薄。因为AI是根据函数体来推断类型和行为的,如果函数逻辑太复杂,AI生成的docstring就会显得很啰嗦,甚至前后矛盾。这让我们意识到,那些复杂的函数其实应该拆分成多个小函数,每个小函数职责单一,docstring自然就清晰了。

但AI也不是万能的,有些场景我们必须禁止使用。

第一种是涉及外部接口的函数。比如我们暴露给其他团队使用的API函数,参数类型和docstring的措辞都是要写进接口文档的,稍有改动就会影响下游调用方。这种函数我们一律人工编写,不经过AI。因为AI可能会为了追求类型准确,把参数类型从自定义的UserModel改成dict,虽然类型上更精确,但破坏了接口的稳定。

第二种是包含敏感逻辑的函数。比如涉及金额计算、权限校验、加密解密的地方,AI生成的docstring可能会把实现细节暴露得太清楚,比如写明了使用哪种加密算法,这在安全审计时是不合适的。我们要求这类函数只写参数和返回值的描述,不写内部实现原理。

第三种是递归或生成器。AI对递归函数的类型推断经常出错,因为递归的终止条件和递归调用的类型可能不一致。生成器涉及yield和yield from,AI生成的docstring经常把返回类型写成list,但实际上应该写为Iterator。这两种情况我们都需要人工重写。

除了流程规范,我们还制定了一个命名约定,用来帮助AI更准确地生成注解。我们要求所有函数参数命名必须采用小写蛇形命名法,并且参数名要能体现类型。比如用user_list表示列表,用user_id_str表示字符串类型的ID。AI在生成注解时,参数名本身就是一个很强的信号。我们还要求返回值变量在函数内部尽量使用统一的命名模式,比如result_dict、total_count,这样AI就能从变量名推断出返回类型。

实际运行下来,这套命名约定让AI生成的类型注解准确率提高了不少。我们做了一个小实验,随机抽取了一百个函数,让AI在有无命名约定的两套代码上分别生成注解。有命名约定的那组,人工审核通过率为百分之八十六,无命名约定的只有百分之五十七。这说明AI不是万能的,它需要人类提供足够的上下文线索。

最后说说工具选型。我们试过好几款AI编程助手,有的是IDE插件,有的是命令行工具。最终我们选择了一款支持本地部署的模型,因为我们的代码不能上传到外部服务器。本地部署的模型虽然前期配置麻烦,但速度更快,而且可以针对自己的代码库做微调。我们给模型喂了一批历史上有良好注解和docstring的函数作为示例,让它学习我们团队的风格。这个微调过程大概花了两天时间,但之后生成的代码风格统一度明显提升。

现在团队里已经没有人手写docstring了,但也没有人盲目信任AI。我们每个人都记住了那套三关流程,并在日常开发里严格执行。AI帮我们省去了重复性的描述工作,但真正保证代码质量的,还是那套自己定义的规范。

如果你也想在团队里推行AI生成类型注解和docstring,我的建议是不要一开始就全面铺开。先选一个不太重要的模块试运行一周,把流程跑通,收集问题,然后再推广。最重要的是,一定要让团队里最资深的那个人来定义AI的输入规范,因为只有真正理解代码的人,才知道哪些信息是AI容易误解的。

Top comments (0)