要判断一个Python脚本是否由DeepSeek或ChatGPT等AI生成,可以通过以下特征进行分析:
一、代码层面的标志性特征
1. 注释风格
- AI生成注释:
- 注释内容与代码逻辑高度同步(如每个函数都带详细参数说明)
- 使用模板化语言(如"Load data from file"、"Process the data"等通用描述)
- 中英混杂的注释(取决于用户输入语言),这个其实最常见,就是1个人其他的脚本都是中文,或者大部分是中文的,然后1个更加简单的脚本,有注释的地方写满了英文,最离谱的就是注释中会出现一些莫名其妙的英文,就是专业术语但是又不至于是专业术语,更偏向于计算机中的一些基础知识,但是按理来说不应该出现在这里
- 人类注释:
- 注释可能包含业务逻辑的特殊说明
- 存在调试代码残留(如
# TODO
或# FIXME
)
2. 代码结构
- AI常见模式:
- 过度使用
argparse
模块(即使脚本很简单) - 函数拆分过于细致(如将2行代码封装为独立函数)
- 存在未优化的循环或冗余变量(如
for item in list:
搭配index += 1
)
- 过度使用
- 人类代码:
- 更倾向使用
if __name__ == "__main__":
入口 - 存在个性化代码风格(如特定缩进/空行习惯)
- 更倾向使用
3. 异常处理
- AI代码:
- 异常捕获范围过大(如
except Exception as e
) - 缺少具体错误处理逻辑(仅
print(e)
)
- 异常捕获范围过大(如
- 人类代码:
- 针对性错误类型捕获(如
except FileNotFoundError
) - 包含重试机制或资源释放代码
- 针对性错误类型捕获(如
二、内容层面的判断依据
1. 变量命名
- AI命名特征:
- 单调的命名模式(如
data1
,result_list
) - 缺乏业务场景关联性(如用
process_data()
代替calculate_gene_density()
)
- 单调的命名模式(如
- 人类命名:
- 变量名反映具体业务含义
- 存在拼写错误或不规范缩写(如
calc_gene_dens
)
2. 依赖库使用
- AI倾向性:
- 偏好标准库(如
os
/sys
/collections
) - 使用热门第三方库(如
pandas
/numpy
) - ps:导入的库自相矛盾,比如说导入os以及subprocess;或者导入的库自己都不使用,就纯粹导入在头部,但是从头到尾都不使用,哪怕整体是1个非常简单的脚本
- 偏好标准库(如
- 人类特征:
- 包含小众依赖库(如
pybedtools
) - 存在版本兼容代码(如
try...import
回退逻辑)
- 包含小众依赖库(如
3.编写逻辑
- **AI倾向性:
**在1个函数中嵌套定义多个函数,哪怕这个函数只要1行就完成了,还是会在里面嵌套定义
三、生成模型的指纹特征
1. DeepSeek标识
- 中文注释中混用全角标点(如
。
代替.
) - 函数文档字符串使用三引号分列式排版:
def func():
"""
功能说明
:param param1: 参数说明
:return: 返回值说明
"""
2. ChatGPT标识
- 倾向使用
f-string
格式化(即使Python版本不明确) - 包含类似"Note that…"的英文注释段落
- 过度使用类型注解(如
def process(data: List[str]) -> Dict[int, str]:
)
四、实战检测技巧
1. 代码复杂度分析
pip install radon
radon cc script.py -s
AI生成的代码通常呈现:
- 低圈复杂度(过度拆分函数)
- 高抽象层次(缺乏具体实现细节)
2. 模式匹配检测
# 检测AI常见模式
patterns = [
r"argparse\.ArgumentParser\(\)",
r"except Exception as e:",
r"# Load .+ from file"
]
3. 元数据分析
# 检查编码声明(AI常用UTF-8)
with open('script.py', 'rb') as f:
print(f.read(100)) # 查看文件头字节
五、典型示例对比
AI生成代码特征
import argparse
from typing import List
def process_data(input_data: List[str]) -> List[str]:
"""
处理输入数据
:param input_data: 输入数据列表
:return: 处理后的数据列表
"""
return [x.strip() for x in input_data]
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", required=True)
args = parser.parse_args()
with open(args.input) as f:
data = process_data(f.readlines())
print(f"Processed {len(data)} lines.")
人类编写代码特征
# 快速去重并保留顺序(Python 3.7+)
import sys
def dedup(items):
seen = set()
return [x for x in items if not (x in seen or seen.add(x))]
if __name__ == '__main__':
# 从stdin读取数据
lines = [line.rstrip('\n') for line in sys.stdin]
# 临时调试用
# print(f"Raw input: {lines[:2]}...")
try:
unique = dedup(lines)
except TypeError as e:
print(f"Invalid data type: {e}", file=sys.stderr)
sys.exit(1)
print('\n'.join(unique))
六、注意事项
- 提示工程影响:用户输入的指令细节会显著影响输出特征
- 模型进化:新一代模型会修复旧版本的生成缺陷
- 混合创作:存在人工修改AI生成代码的中间形态
另外:
在文件头部添加 #!/usr/bin/env python3
(shebang 行)不能单独作为判断代码是否由 AI 生成的依据。这一行的存在与否更多反映开发环境和编程习惯,但结合其他特征可辅助判断:
一、人类/AI 都可能使用该行的场景
- 跨平台兼容性需求
开发者希望脚本直接在 Unix/Linux/macOS 终端运行:
chmod +x script.py # 赋予执行权限
./script.py # 直接运行(无需显式调用python3)
- 项目规范要求
团队约定需明确指定解释器版本(尤其是同时使用 Python 2/3 的环境)。
二、可能暗示 AI 生成的标志
若出现以下组合特征时,可能更倾向 AI 生成:
- 与冗余代码组合
在简单脚本中同时包含argparse
+if __name__ == "__main__"
+ shebang:
#!/usr/bin/env python3
import argparse # 即使脚本无参数需求也强制添加
def main():
print("Hello World")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
# ... 实际未使用任何参数的冗余代码
main()
- 不符合平台特性
Windows 环境专属脚本中出现该行(因 Windows 不依赖 shebang 解析):
#!/usr/bin/env python3 # 冗余(Windows 下由文件关联决定解释器)
import winreg # Windows 专属操作
三、人类开发者可能的特殊模式
- 动态解释器选择
结合环境条件动态调整(AI 较少主动实现):
#!/bin/sh
''' 2>/dev/null # 兼容性技巧:同时支持 Shell 和 Python
echo "This is a shell script"
exit 0
'''
# Python 代码从这里开始
import sys
print("Python version:", sys.version)
- 版本容错处理
针对不同 Python 版本的降级兼容:
#!/usr/bin/env python3
import sys
assert sys.version_info >= (3, 8), "需要 Python 3.8+"
四、综合判断建议
通过以下组合特征提高判断准确率:
特征 | AI 倾向性 | 人类倾向性 |
---|---|---|
Shebang + argparse | ✅ 高频组合 | ⚠️ 仅当需要参数时使用 |
Shebang + 无执行逻辑 | ✅ 存在冗余 | ❌ 罕见 |
Shebang + 中文注释 | ✅ 中英混合常见 | ⚠️ 取决于开发者习惯 |
Shebang + 全角标点 | ✅ DeepSeek 特征 | ❌ 罕见 |
五、典型案例对比
AI 生成代码(过度规范)
#!/usr/bin/env python3
import argparse
def process_data(input_file: str):
"""处理输入文件"""
with open(input_file) as f:
data = f.read()
return data.upper()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", required=True)
args = parser.parse_args()
result = process_data(args.input)
print(result)
人类编写代码(场景适配)
#!/usr/bin/env python3
# 快速文本小写化(适用于Linux管道)
import sys
if __name__ == "__main__":
# 从 stdin 或文件读取
text = sys.stdin.read() if len(sys.argv) == 1 else open(sys.argv[1]).read()
print(text.lower())
结论
单独使用 #!/usr/bin/env python3
不能判定代码来源,但若同时存在以下特征可增加 AI 生成的可能性:
- 在无需直接执行的脚本中添加该行
- 与模板化的参数解析代码冗余组合
- 出现中英混杂注释 + 全角标点符号
还有一些结构或者说是指标:
通过代码中出现的 1_000_000
数字写法、assert
语句以及 your_xxx
类占位符,可以辅助判断代码是否由 AI 生成。以下是具体分析:
一、特征分析
1. 1_000_000
** 数字分隔符**
-
AI 生成倾向:
- 模型倾向于使用 Python 3.6+ 的新特性(下划线分隔符)以提高可读性。
- 在生成示例代码时,会刻意展示“现代写法”(即使上下文不需要)。
-
人类代码特征:
- 实际项目中可能直接写
1000000
(除非数值极大且需要强调可读性)。 - 更关注实际业务需求,而非刻意展示语法特性。
示例对比:
- 实际项目中可能直接写
# AI 生成(刻意使用分隔符)
population = 1_000_000 # 模拟一百万数据点
# 人类编写(直接写数值)
MAX_RETRIES = 1000000 # 重试次数上限
2. assert
** 语句的滥用**
-
AI 生成标志:
- 过度依赖
assert
做参数校验,而非正式的错误处理。 - 缺少上下文相关的异常处理(如文件不存在时的
FileNotFoundError
)。
- 过度依赖
-
人类代码特征:
- 使用
raise ValueError
或自定义异常。 - 结合
try...except
处理潜在错误。
示例对比:
- 使用
# AI 生成(简单断言)
def calculate_density(mass, volume):
assert mass > 0, "Mass must be positive"
assert volume != 0, "Volume cannot be zero"
return mass / volume
# 人类编写(详细校验)
def calculate_density(mass, volume):
if mass <= 0:
raise ValueError("Mass must be positive")
if volume == 0:
raise ZeroDivisionError("Volume cannot be zero")
return mass / volume
3. your_xxx
** 类占位符**
-
AI 生成标志:
- 使用通用占位符(如
your_file.csv
、your_variable
)表示需要用户替换的内容。 - 常见于示例代码或教学模板。
- 使用通用占位符(如
-
人类代码特征:
- 直接使用具体变量名(如
input_file = "data.csv"
)。 - 占位符仅出现在文档或注释中,而非实际代码。
示例对比:
- 直接使用具体变量名(如
# AI 生成(占位符在代码中)
df = pd.read_csv("your_data.csv") # 替换为你的文件路径
# 人类编写(具体变量名)
INPUT_FILE = "sales_2023.csv"
df = pd.read_csv(INPUT_FILE)
二、组合特征增强判断
当以下特征同时出现时,代码大概率由 AI 生成:
特征组合 | AI 生成概率 | 人类代码概率 |
---|---|---|
1_000_000 + assert | ✅ 高 | ❌ 低 |
your_xxx + 无实际错误处理 | ✅ 极高 | ❌ 极低 |
三者同时出现 | ✅ 确定性高 | ❌ 几乎不可能 |
三、典型案例对比
AI 生成代码
#!/usr/bin/env python3
import pandas as pd
def process_data(your_file: str) -> float:
"""处理你的数据文件"""
data = pd.read_csv(your_file)
assert not data.empty, "文件不能为空"
total = 1_000_000 # 模拟总预算
return data["value"].sum() / total
if __name__ == "__main__":
result = process_data("your_data.csv")
print(f"占比: {result:.2%}")
人类编写代码
# 从配置加载预算值
import sys
from config import BUDGET # BUDGET = 1e6
def calculate_ratio(data_path: str) -> float:
try:
data = pd.read_csv(data_path)
except FileNotFoundError:
sys.exit(f"错误:文件 {data_path} 不存在")
if data.empty:
raise ValueError("输入数据为空")
return data["value"].sum() / BUDGET
if __name__ == "__main__":
ratio = calculate_ratio("sales.csv")
print(f"预算占比: {ratio:.2f}")
以上都是废话,你和我,都可以快速地从网上看到这篇文章,然后呢?
带着这篇文章的偏见去审判其他人,抑或是自己(我想这个概率在今天会越来越高)。
我们想做的是什么,是如何不让AI毁掉自己,尤其是自己有业务需求,真正觉得应该老老实实从头学编程的,以及离开了象牙塔,离开了AI,但是你所依仗的、你最后要靠着吃饭的、你必须得赖以为生的饭碗,你觉得这个饭碗飘在半空,会是一种什么感觉。
我觉得对于真有需求写代码的人来说,这是一种慢性自杀(我清楚地知道这也是在cue我自己)。
说得鸡汤以及玄乎点,就是你要知道自己从哪里来,到哪里去,你目前要做什么,在做什么(这些的目的都是为了到达你要去的地方),你是在饮鸩止渴/拔苗助长(混个3-4-5年到时候就混吃等死,什么技能都没有,都退化了),还是这个时候一步一步地走,尽管要学的东西有很多,然后每一天学的都很少。
下面是我真正要在这里说的:
我知道很多人,包括我自己,在编程中其实越来越难以脱离开AI IDE了,我们想要的是解决问题快的同时,真正能够学到点东西,而且,最重要的是,这个学到的东西,你必须得脱离程序自己能够独立复现出来,说得难听点,你必须得默写出来!
所以问题是如何自救,确乎是这个字眼,自救,因为从长远来看得不偿失。
1,尽量去学一门真正的编程语言,而不是语法糖居多、调包居多的语言。我就直说了,建议生信的,以python为基础语言,而R,实际上无论是画图、统计,还是一些简单的数据处理(我想你真要学python,最好还是用pandas取代掉tidyverse),你实际上能够看到很多的调包语法糖,或者所谓的指南文档。
所以对于语法糖的part,完全没有那种紧迫性或者是非常的必要性让你或者是我天天来抄这个代码流程。对于这一部分,其实我的建议是——备份+弃!
备份的逻辑很简单,你知道这种代码不是真实逻辑的代码,基本上都是语法糖;所以你只需要理解处理流程的逻辑,并备份一次代码(说难听点,就是备份个指示文档),然后直接放弃学习成本(当然我指的是你备份的时候要完全理解流程上的逻辑,并且最好是一次就学会它)。
既然你已经学会了这种调包式的lj语法糖部分,那么你为什么还要在这种廉价的代码上面继续花费重复时间的功夫呢?直接放弃,去学真正的语法以及编程语言。
这个其实是基础,我想的是,你哪怕是用AI生成,所谓的学习,至少也不要在lj语言上浪费AI生成的时间!
2,AI确实是无法完全避免的,如果你在使用IDE,建议你关闭自动补全。
道理很简单,关闭的只是自动补全,但是你向AI求助的渠道实际上是还在的,我只是建议你在最容易、最触手可及的地方关闭自动补全,或者是AI部分,尤其是评估自己coding能力不过关的(在初学阶段尤其如此)。
我知道AI是关不掉的,取消了自动补全,但是还有copilot等聊天窗口,以及你还有网页浏览器上的AI聊天窗口,但是在你最近的地方设置最起码的一道屏障。
3,判断你需要AI的地方,我其实是不指望这一点的。并且与直觉反着来!
方法很简单,将你的任务流程细分,也就是规划你的逻辑链条,把你的任务大致细分成每一步要做什么任务(或者是解决什么问题),这是最起码的逻辑思维。然后,只要你能够细分这个任务,也就是逻辑思维chain链条,那么我觉得你既然已经能够细分到每一步每一个原子级任务了,你最好(这里我建议是必须)是自己上网查询自学该怎么实现这简简单单的原子级别一步,而不是直接倒向AI。
再重申一下,一个任务你已经能够很清晰地拆分逻辑思维chain了,你就不要依靠AI生成了,这一步走一步就是学一步!
4,如果迫不得已你真的依靠AI了,注意这个时候我建议你还是按照上面3的流程来处理,就是你是在原子级别的问题上卡壳得求助AI了。
那么我的建议是你向AI求助的时候,最好要求它至少
(1)给你讲清楚你要解决的问题的解决思路是什么,也就是它的答案你必须得看懂,注意,在看懂这个代码的过程中你还可以当作是任务,去上网查询那些最基本的流程逻辑,也就是在这个过程中继续学习;
也就是你必须得看懂AI的答案,但是答案只是第一层,你看懂必须得自己去自学,而且必须得自己上网去学,不能够二次AI;(也就是说,凡是遇到学习的地方,都建议独立于AI)
(2)给你的答案你除了要看懂之外,最好是建议它生成更多的示例,也就是你最好将与AI求助的过程的prompt最好是当成真正举一反三学习的过程,也就是你需要按照学生向老师问问题的那种方式去构建你的prompt,而不是直接要一个答案,这一点其实就是你的prompt方式要不忘初心的
(3)最关键的一点就是:
当你真的抄下了你在AI prompt中获取的代码的时候,你要用以下2种中至少1种方式来进行备份:
(注意按照3的说法,这里应该是原子级别的任务了,所以我觉得代码量不会太大,所以默写或者复现应该压力不会很大)
1️⃣默写,将你抄下的原子级别任务的code模块默写一遍,必须得加注释(自己写注释),默写或者背下来,建议默
2️⃣换一个实例复现这个代码,也就是举一反三,至于复现,不是简单地换变量名,
你必须自己构建1个任务,来使用这段代码,也就是说我建议你是在1个任务场景下去复写重构这段代码;
这个时候我建议是让AI给你出题,轮到你做AI,然后用相同的代码逻辑去解决这个问题;
然后你自然自己写出来的东西,你就可以自己备份了。
最后还是那句话,记住从哪里来,到哪里去!
(评判标准在于:你拿AI生成代码的时间,空出来要做要学的事,如果比学编程还low,那你为什么要把这个时间浪费掉!不要成为AI圈养的猪,看看你身边的人吧!难道你也要成为猪的一员吗,毕业即失业!?)
https://nmn.gl/blog/ai-illiterate-programmers
https://www.oschina.net/news/334479/ai-illiterate-programmers
https://blog.csdn.net/dQCFKyQDXYm3F8rB0/article/details/144706843
你今天靠AI写了10几行代码,以后都要还回来的。
做生信的,你最基本的任务难道很困难吗?
base python+numpy+pandas+API自学文档,需要几天?1周学会,重复3-4-5年难道还学不会?会很难?你要靠AI?