Cursor 与 GitHub Copilot 怎么选:用一个 Python 项目测出真实差异

2026-09-23 37 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:12 分钟

比较 Cursor 和 GitHub Copilot,不能只看谁补全代码更快。真正影响 Python 开发效率的,是工具能否读懂项目结构、跨文件修改、运行测试、定位异常,并让开发者清楚地审查每一次改动。

与其比较演示视频里的“魔法时刻”,不如让两者完成同一个小型 Python 项目,再记录成功率、修改范围和人工干预次数。下面给出一套可以直接复用的测试方法。

两者的核心差异不只是补全能力

Cursor 更接近一个围绕 AI 工作流重新设计的编辑器。它强调项目级上下文、对话式修改和代理任务,适合让 AI 同时查看多个文件,再完成一段连续工作。

GitHub Copilot 则通常嵌入已有编辑器和 GitHub 开发流程。它的优势往往是迁移成本较低:如果团队已经使用 VS Code、JetBrains IDE 和 GitHub,可以在不更换主要工具的情况下加入代码补全、聊天和代理能力。

可以把选择问题拆成几项:

维度 重点观察什么
行内编程 补全是否符合当前函数和项目风格
项目理解 能否找到相关模块、测试和配置文件
代理执行 能否连续完成编辑、运行命令、修复失败测试
调试能力 是否根据堆栈和失败用例找到根因
代码审查 改动是否集中、可解释、容易回滚
团队成本 是否需要更换编辑器、策略或现有工作流

产品能力和套餐会持续变化,因此不要仅凭某个功能名称做决定。更可靠的办法,是用团队自己的代码库和任务类型进行试用。

建立一个可重复的 Python 对照项目

下面的项目读取 JSON 工时记录,按分类汇总小时数。它足够小,可以快速运行;同时又包含输入校验、Decimal 计算、命令行接口和测试,适合观察 AI 是否会跨文件理解需求。

先创建目录:

mkdir -p ai-editor-test/src ai-editor-test/tests
cd ai-editor-test

创建 pyproject.toml:

[project]
name = "ai-editor-test"
version = "0.1.0"
requires-python = ">=3.10"

[project.optional-dependencies]
dev = ["pytest>=8.0"]

[tool.pytest.ini_options]
pythonpath = ["src"]
testpaths = ["tests"]

创建 src/task_report.py:

import argparse
import json
from collections import defaultdict
from decimal import Decimal
from pathlib import Path
from typing import Any


def summarize(items: list[dict[str, Any]]) -> dict[str, str]:
    totals: dict[str, Decimal] = defaultdict(Decimal)

    for index, item in enumerate(items):
        category = str(item.get('category', 'uncategorized')).strip()
        category = category or 'uncategorized'

        try:
            hours = Decimal(str(item['hours']))
        except (KeyError, ValueError, TypeError) as exc:
            raise ValueError(f'invalid hours at item {index}') from exc

        if hours < 0:
            raise ValueError(f'hours cannot be negative at item {index}')

        totals[category] += hours

    return {
        category: format(hours.quantize(Decimal('0.01')), 'f')
        for category, hours in sorted(totals.items())
    }


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument('input', type=Path)
    args = parser.parse_args()

    items = json.loads(args.input.read_text(encoding='utf-8'))
    if not isinstance(items, list):
        raise ValueError('input must be a JSON array')

    print(json.dumps(summarize(items), ensure_ascii=False, indent=2))


if __name__ == '__main__':
    main()

创建 tests/test_task_report.py:

import pytest

from task_report import summarize


def test_summarize_groups_and_rounds_hours() -> None:
    items = [
        {'category': 'backend', 'hours': 1.2},
        {'category': 'backend', 'hours': '2.35'},
        {'category': 'docs', 'hours': 0.5},
    ]

    assert summarize(items) == {
        'backend': '3.55',
        'docs': '0.50',
    }


def test_missing_category_is_uncategorized() -> None:
    assert summarize([{'hours': 1}]) == {'uncategorized': '1.00'}


def test_negative_hours_are_rejected() -> None:
    with pytest.raises(ValueError, match='cannot be negative'):
        summarize([{'category': 'backend', 'hours': -1}])

安装并运行:

python -m venv .venv
source .venv/bin/activate  # Windows PowerShell: .venv\Scripts\Activate.ps1
python -m pip install -e '.[dev]'
pytest -q

还可以创建 sample.json 手动验证命令行:

[
  {"category": "backend", "hours": 1.25},
  {"category": "backend", "hours": 2},
  {"category": "docs", "hours": 0.75}
]
python src/task_report.py sample.json

用同一组任务测试代理工作流

不要分别给两个工具随意提问。固定代码版本、提示词和验收命令,结果才有可比性。可以依次测试以下任务。

任务一:增加功能

把下面的提示词原样交给两个工具:

为这个项目增加 --min-total 参数。只输出总工时大于等于该值的分类。使用 Decimal,保持现有输出格式,补充正常值、边界值和非法值测试。先说明计划,再修改代码,最后运行测试。不要添加运行时依赖。

观察重点不是 AI 是否输出了很多代码,而是:

  • 是否同时修改命令行入口和测试;
  • 是否复用 Decimal,而不是引入 float 精度问题;
  • 是否测试“恰好等于阈值”的边界;
  • 是否真的运行 pytest -q;
  • 是否改动了与任务无关的文件。

任务二:定位一个刻意植入的缺陷

把这行代码:

category = str(item.get('category', 'uncategorized')).strip()

暂时改成:

category = str(item['category']).strip()

然后给两个工具相同的调试请求:

运行测试,解释失败的根因,并做最小修复。不要删除或放宽现有测试。修复后重新运行完整测试集,并总结改动。

这个任务可以检查代理是否先读取失败信息,再定位具体代码,而不是重写整个函数。一个成熟的调试流程应该保留测试、缩小问题范围,并给出可验证的结果。

任务三:处理模糊需求

再提供一个有意不完整的需求:

支持 CSV 输入。

理想行为不是立即写代码,而是先确认:

  • CSV 列名是否固定为 category,hours;
  • 是根据扩展名自动判断,还是增加 --format;
  • 空行和额外列如何处理;
  • 错误信息是否需要包含行号;
  • 是否允许增加第三方依赖。

AI 工具会不会识别需求缺口,通常比它一次生成多少代码更能反映实际工程价值。

记录结果,而不是依赖主观印象

为每个工具重复任务,建议至少记录这些指标:

指标 记录方式
首次通过率 第一次修改后完整测试是否通过
人工干预次数 需要补充提示或手动修改多少次
无关改动 是否重排、重命名或重写无关代码
测试质量 是否覆盖正常、边界和错误路径
调试路径 是否读取报错、复现问题并验证修复
可审查性 diff 是否小而清晰,解释是否对应代码
完成时间 从提交提示词到测试通过的时间

可以采用一个简单的五分制,但不要只计算总分。比如,代理速度快但经常产生大范围 diff,可能适合原型项目,却不一定适合审查严格的生产仓库。

测试时还应重置 Git 工作区,确保每轮从相同状态开始:

git init
git add .
git commit -m 'baseline'

# 每轮测试结束后恢复基线;执行前确认没有需要保留的改动
git reset --hard HEAD
git clean -fd

git clean -fd 会删除未跟踪文件,运行前必须确认目录中没有重要文件。

选型时别忽略权限和上下文边界

代理能力越强,越需要明确它可以读取什么、修改什么、执行什么。评估期间应避免把真实密钥、客户数据和生产配置放进测试仓库,并检查组织的数据处理策略。

建议设置以下边界:

  • 将 .env、凭据和生产数据排除在上下文之外;
  • 命令执行、依赖安装和数据库迁移需要人工确认;
  • 每个 AI 改动都通过 diff、测试和静态检查;
  • 对认证、支付、权限控制等高风险模块坚持人工审查;
  • 记录模型、编辑器版本和关键设置,避免结果无法复现。

怎么做最终选择

如果你希望获得更完整的编辑器级 AI 体验,并经常让代理跨多个文件推进任务,Cursor 值得优先试用。如果团队已经稳定使用现有 IDE 和 GitHub 工作流,希望以较低迁移成本加入 AI 能力,GitHub Copilot 往往更自然。

但真正的结论应该来自实际项目,而不是产品定位。选取三到五个日常任务,固定提示词和代码基线,比较首次通过率、人工干预、diff 质量与安全边界。对 Python 团队来说,最好的 AI 编辑器不是生成代码最多的那个,而是能在测试约束下做出最小、正确且易于审查改动的那个。


相关推荐