site logo

Marico's space

没有 AI 代码生成和自动补全,开发者如何生存

编程技术 2026-04-28 11:56:14 5

原文:How to survive as a developer without AI code-gen and without autocomplete|译者前言:本文来自 dev.to,观点有价值,转写发布供读者参考。

嘿,大家好!我叫 Viktor,是一家电商公司的资深后端开发,负责物流团队。

今天我来聊聊我是如何在 surrounded by vibe-coders、code-writing agents 和一个 legacy 项目的环境中存活的。也会讲讲我是怎么用 LLMs 的,以及我使用神经网络的经验等等。

TL;DR

  • 自 2019 年起我就开始使用 AI 工具(Tabnine → Cursor → 后续新工具),但有意选择手写代码。为了自我提升、更深入理解项目等目的。
  • 一个 13 年的 legacy 项目,AI 代码生成会产生幻觉、破坏约定。用于 review 的 AI 则是另一回事——效果很好。
  • 我构建了 1 个 orchestrator + 7 个 specialized subagents(logic、style、security、perf-db、ops、ai-smell、tests),在 diff vs master 上并行运行。
  • AI-smell agent 专门 catch vibe-coded PRs from teammates,也 catch 我自己因疲劳而出错的代码——两者都会被标记为同类噪声。
  • 这一切都只是 .claude/agents/*.md markdown 文件——可复制粘贴到任何项目。设置细节和踩坑记录在末尾。

快速说明一下——我不是昨天才接触 AI 工具的人。自 2019 年起我就用过 Tabnine,然后迁移到 Cursor,再看了更新的工具。所以我对手写代码的选择——不是因为不懂,而是有意为之,为了自我提升、更深入理解项目等。

主要问题——这是一个超过 13 年的项目:代码写得不一致,函数、类和变量的使用不符合预期,其他逻辑被硬塞进去,或者它们就是死代码,标记着 TODOFIXME。由于这一切加上庞大的代码库,目前我们正在剥离 domain logic,但目前还活在这样的世界里 :) 。在这样的项目上工作时,AI 模型会产生幻觉、生成不一致的代码,经常搞错。

同样重要的是——我们组织中的 devs 与他们的 domain 紧密绑定,所以部分分析工作、与 onboarding managers 和 analysts 的沟通、写文档——所有这些都需要对项目代码有非常深入的理解。这让你能够高效地研究任务、处理 incidents、预测新任务和需求的时间线。

处理代码的主要工具是代码质量工具:ruff、mypy、通过 pytest 写测试,现在还有用于项目详细 review 的 AI。我构建了一套 agents——1 个 orchestrator 和 7 个 subagents——从不同方向工作:security check、algorithm logic check、code style check、autotests check、code organization and bloat check、devops check 和 DB check。我来简要介绍每一个。

这是 orchestrator 最终报告的样子(匿名示例,来自一个真实 PR 的发现):

IDSevAspectFile:lineDescriptionFix
B1BLOCKERLogicpayments/insurance.py:355MIN_INSURANCE_COST = 1 以主货币设置,但 provider 以分为单位 → 保险比预期低 100 倍设置为 100,注释 # in cents
B2BLOCKERSecurityscripts/executor.py:57exec() 带完整 __builtins__,仅靠 IS_PRODUCTION 标志保护——任何 prod 配置错误 = 完全 RCERestrictedPython 或 subprocess + seccomp
B3BLOCKERMigrationorders/migrations/0021_add_entity.pyAddField(entity, FK, default=None) 没有 null=True——在非空表上部署时会崩溃两步走:null=True + 在单独迁移中回填
H1HIGHLogic/Typesfeature/config.py:130list[int] 用于白名单,但 provider_item.idstrstr in list[int] 永远是 False → 功能完全 dead替换为 list[str]
H2HIGHLogic/Opsfeature/service.py:102空白名单时 fail-open:all(x in [] for x in set())True → gate 反转,本应放行时放行了所有内容添加 if whitelist and not providers: return False
H3HIGHPerf-DBfeature/service.py:102@cached_property 读取两个 OneToOne(不在 select_related 中)+ ORM fallback → 每个 item 最多 3 条 SQL,在 serializer 中每个 order 调用两次(热路径 GET /orders/select_related('outlet', 'delivery_info', 'ppo_info')
H4HIGHSecuritypayments/processor.py:172每次请求都记录完整 payload(姓名、电话、邮箱、商品)——PII 泄露到日志存储Mask PII 或仅记录 payment_id + amount + status
H5HIGHAI-smellutils/helpers.py:42from utils.misc import flatten——模块/符号在项目中不存在,幻觉 importlist(itertools.chain.from_iterable(items))
M1MEDIUMTestsfeature/tests/test_x.py:260Magic number expected_insurance_sum = 1——应该引用 Conf.MIN_INSURANCE_COST,这样常量改变时测试会失败expected_insurance_sum = Conf.MIN_INSURANCE_COST
M2MEDIUMLoggingfeature/service.py:101新增拒绝分支没有日志——incident 时无法区分"provider 不在白名单"和"provider_item=None"log_info('feature.provider_not_allowed', extra={...})
结论:NEEDS WORK——3 个 Blocker 必须修复后才能合并。

每个发现都包含 author + sha(来自 git blame)和一个 Verify by: 命令,但我裁剪了这些列以便表格显示。现在介绍各个 agents。

Style agent

这个 agent 关注的是普通 linter 抓不到的内容。Ruff 已经标记了未使用的 imports、line length、引号和 PEP8 命名——那是 routine,自动化处理得很好。所以我在 style agent 的 prompt 中直接写:"不要重复 ruff"。它的职责是语义。例如,函数名 process_data 对读者毫无意义,但 apply_discount 则说明了一切。或者一个叫 get_user 的函数内部还更新了记录——这已经是个陷阱。或者布尔 flag 命名为 flag 而不是 is_bulkshould_retry。或者变量名中没有单位但实际很重要的——price 而不是 price_kopeckstimeout 而不是 timeout_ms

除了命名,agent 还检查 signatures(5+ 个位置参数、mutable default)、重复逻辑、超过 40 行的函数,以及与项目其他部分构建方式的不一致。还有 systemically——如果同样的错误出现在 5 个地方,那不是 5 个发现,而是一个并注明"这是你 diff 中到处都是的问题"。

Logic agent

"不要 deploy bug"类中最重要的 agent。关注点——运行时正确性:types、algorithms、async、datetime/Decimal、错误处理。任何项目中经常出现的特定模式:

  • Truthy 陷阱:value or default0""[]Decimal('0.00') 时都会触发——但你本意是"if None"
  • Naive vs aware datetime,跨 timezone 比较 datetime
  • Decimal 四舍五入位置错误,或混用 Decimal + float
  • 遗漏 await,async-view 内的阻塞 requests.get,可以用 gather 的地方用了 sequential await
  • except Exception: pass——静默错误吞没
  • 差一错误、反转 if、改变函数语义但不迁移测试

每个发现必须包含行号、具体场景和 Verify by:——即可用于验证的命令或测试。没有这些,agent 被禁止写"这里可能有个 bug"——否则它开始产生幻觉、制造噪声。

Security agent

标准 OWASP 加几个现代内容。经典内容——SQL/Command/Template 注入、代码或日志中的 secrets、SSRF(外部 HTTP 没有 allowlist 和 timeout)、open(user_input) 时的路径遍历、不安全反序列化(pickle、没有 SafeLoader 的 yaml、eval)、遗漏 @login_required/permission_classes、IDOR。

不太明显但很痛的内容:

  • DB-Lock-DoS on migrations——单独分类。带 volatile DEFAULT(now()gen_random_uuid())的 ALTER TABLE ADD COLUMNCREATE INDEX 没有 CONCURRENTLYSET NOT NULL 没有两步 schema(NOT VALID + VALIDATE)、DDL 没有 lock_timeout。这不是经典意义上的"漏洞",但和 SQL 注入一样能让 prod 宕机。
  • Supply-chain——新依赖中的 typosquatting(reqeusts vs requests)、pin 在 pre-release 版本上、降级有安全补丁的库。
  • LLM-specific——如果代码为神经网络构建 prompts,检查 prompt injection(用户输入直入 prompt)、通过记录的 prompts 泄露 secrets、使用 LLM 输出作为 SQL/shell 而不验证。

重要细节:agent 检查 reachability。如果危险模式仅在测试代码或未从外部 reach 的 dev 脚本中——要么 Low 要么不记录。否则你会收到大量 false positives,人们就不再使用了。

Performance and DB agent

所有在负载下暴露的问题。Algorithmic complexity(可以用 set 做查找的地方出现了 O(n²))、memory(将完整数据集加载到 RAM 而不是 streaming)、async(async 中的阻塞 I/O、无限 gather 对一千个任务)、但主要的是——DB and ORM

N+1——legacy 项目的头号问题。循环中查询、没有 select_related/prefetch_related、没有 prefetch 的 SerializerMethodField。然后——查询质量:WHERE LOWER(email) = ... 会 kill 索引、LIKE '%pattern' 也会、JSONB 过滤没有 GIN 索引、COUNT() on 大表(EXISTS 更快)、SELECT .only() 可以、SELECT FOR UPDATE 没有 SKIP LOCKED 的长事务。Cache——cache key 没有 user/tenant(用户间数据泄露)、没有 TTL、cache stampede。

每个发现包含 Hot path: Yes/No/Unknown——因为一个月拉取一次的 endpoint 的 N+1 和热路径的 N+1 是不同优先级。如果 agent 无法判断——写 Unknown,不猜测。

Ops and compatibility agent

会 break deploy 或 monitoring,或破坏向后兼容的内容。新的 required env 没有 default——break pod 启动。Migration 中 NOT NULL 没有 DEFAULT——break 零停机 deploy。不经两步 deploy 删除列——旧 pods crash。Celery task 签名变更不 fan-out migration——旧 workers 开始失败。Kafka 消息 schema 变更——旧 schema 的 consumers break。

单独——observability。重要的新逻辑没有日志、错误级别(critical 用 debug,normal 分支用 error)、新的外部调用没有 metrics 和 span、结构化日志项目中没有使用结构化日志。还有 feature flags——有没有不通过 release 就能关闭功能的方法,有没有 rollback 计划。

每个发现 agent 写 Deploy risk:(什么会在什么时候 break——在 deploy 时 / 在 prod 中 / 随着时间推移)和 Rollback:(有没有 rollback 计划)。这将 review 变成 deploy 讨论的 checklist,而不是抽象的"可能出问题"。

AI-smell agent

2026 年"社会效益"最高的 agent,因为很大一部分 PR 都是 AI 辅助的。我也用这个 review 来跑其他 devs 的代码——可能部分或完全 vibe-coded,没有这个过滤器很多垃圾会进入 master。另外,它 catch 我自己因疲劳而出错的代码:当你整天做一个任务时,像 try/except: pass、忘记的 print、多余的 helper 或注释掉的块"我待会儿删"——你就是注意不到它们。Agent 是一次新鲜的视角。

它的职责是把代码拉回到最小必要解。它 catch 的内容:

  • 幻觉——不存在的模块/函数的 import、调用不存在名称的方法、引用缺失的属性、settings.XXXXX(配置中不存在的 key)。通过 grep 项目验证。
  • 过度工程——一个函数的任务用 class Helper/Manager/Factory、单一实现的 ABC/Protocol、单一 type-param 的 generic、一次性的 decorator、一个 import 就够的地方用 DI。
  • 防御性噪声——x = get_x() 返回 X 后紧接着 if x is not None、无缘无故的 try/except: pass、对内部数据用 isinstance、验证已经被 serializer 验证过的输入。
  • 嘈杂的 docstrings——逐字重复签名、一行函数的多行 docstrings、# increment countercounter += 1 前。
  • 重构后的死代码——没有调用者的私有函数、"以防万一"注释掉的块、函数参数在内部不再使用、分支代码相同的 if/else
  • 重复——已有现成实现的新 helper(通过 grep 找到)、重新发明 stdlib 的 flatten/chunked/lru_cache
  • AI-tells——过于"教科书"式的命名(项目用 calc_total 时用 calculate_total_price_with_discount)、日志中其他任何地方都没有的 emoji、"Successfully completed..."日志、过于礼貌的错误消息。

Agent 的主要规则:"删除它会让代码变差吗?如果不会——就是噪声。"

Test agent

可选——只在 docker-stack 启动时工作。从不运行完整 suite(那得好几小时),而是做三件事:找到与 diff 相关的测试、并行运行它们、计算变更行的 differential coverage。

相关性通过四种方式同时计算:按目录结构(app/module.pyapp/tests/test_module.py)、按 imports(grep from .module import)、按测试中的函数/类名(grep 符号)、以及 diff 中的测试文件本身。然后——dedup,限制 20 个文件,pytest --lf -n auto -x(先跑上次失败的,按核心并行,遇到第一个失败就停)。

Differential coverage——最有用的部分。我们取 coverage json,将 executed_lines 与 diff 中的变更行比较,计算百分比。如果低于 50%——High。这比整体项目覆盖率诚实多了:可能整体是 80%,但实际新代码零测试。

如果 docker 不可用——状态 SKIPPED,agent 仍然返回测试列表供手动运行。这不是错误,是正常模式。

Orchestrator

连接一切的指挥。这里有个 Claude Code SDK 的架构限制:subagent 不能 spawn 其他 subagents。所以 orchestrator 不是单独的 subagent,它是一个 playbook,主 agent 在其主 context 中读取并遵循。

五个步骤:

  1. Diff collection——git diff --merge-base origin/master HEAD,stats,文件列表,commits。保存到 /tmp。
  2. Pre-analysis——在 subagents 之前的确定性检查:ruff 带 auto-fix、搜索 diff 中的 migrations 和依赖、正则扫描硬编码 secrets。这减少 LLM 负载和幻觉——具体 bug 由正则 catch,agents 处理正则抓不到的内容。
  3. Blame——对每个新增行运行 git blame -L,构建 file:line → author + sha。这个块然后进入最终表——每个发现旁边显示是谁的代码。对 PR review 讨论很有用。
  4. Parallel dispatch——主 agent 在一条消息中通过 Agent tool 启动所有 6 个(或 7 个含 tests)subagents。这很关键——只有这样它们才能并行运行,而不是顺序运行。
  5. Report synthesis——解析 agents 的响应,按 key (file, line, category)去重(同一 file+lines 在多个 agents 中 = 表中一行,"Aspect"列通过斜杠列出所有 agents)、按 Blocker → High → Medium → Low 优先级、格式化最终 markdown 表,结论为 READY TO MERGE / NEEDS ATTENTION / NEEDS WORK。

我踩过的坑:

  • Artifacts 以文本形式进入 prompt,而不是 /tmp 路径。Subagents 一直挂在 cat /tmp/review_diff.patch 上——原来某些运行时它们的 /tmp 和主 agent 的 /tmp 不同。解决方案:将 diff 直接粘贴到 prompt 中,放在 === DIFF === ... === END DIFF === 之间。每个 artifact ~20 KB 限制。
  • 小 diff 的短路——少于 20 行,仅 .md/docs,没有 .py/.sql/migrations → 跳过并行运行,返回 minimal report。节省 tokens。
  • 仅相对路径server/path/file.py:LINE,不是 /Users/...)——否则 IDE 链接无法点击,与同事共享也会坏。
  • 每个发现的置信度(High/Med/Low)。Low = agent 不确定,人工检查。比让它对怀疑保持沉默更诚实。
  • 每个发现必须有 Verify by:——一个具体命令。防止 agent 写抽象的"这里可能有问题"。
  • (file, line_range, category)去重——否则一个 bug 被三个 agent 报告 3×,review 变得无法阅读。

所有 agents 放在 .claude/agents/.md,skills 放在 .claude/commands/.md。这些只是带 frontmatter(namedescriptionmodeltools)和正文中 instructions 的 markdown 文件。Claude Code 自动加载它们。全局安装——同样内容放在 ~/.claude/agents/~/.claude/commands/

Minimal subagent 模板:

---
name: my-reviewer
description: Brief description for the main agent — when to call
model: sonnet
tools: [Read, Grep, Glob, Bash]

What to do, what to check, response format.

实践踩坑心得:

  • 工具名称——PascalCaseReadBashGrepGlobAgent)。read_file/bash 不工作。
  • Model IDs——简短sonnet / opus / haiku / inherit。Orchestrator 用 inherit 最好——它在当前 session 的模型上运行。
  • 不要给 subagents Agent 工具——它们不能 spawn 其他 subagents,如果尝试,runtime 会以模糊错误 crash。
  • 严格的输出格式——每个 agent 返回结构化块(=== AGENT: name === ... === END: name ===),FINDINGS 包含 ID、Severity、Confidence、Location、Fix、Verify by。自由格式输出 → 合成变得一团糟。
  • Severity ≠ Confidence——Severity 是多可怕(Blocker/High/Med/Low),Confidence 是 agent 多确定。两轴,不是一轴。
  • Prompt 用团队语言,术语用英文——如果团队不是英语母语,agents 这样读起来更自然。
  • Pre-analysis 是确定性的——linter、正则、blame 在 LLM 之前运行。Agent 检查预过滤材料比从零搜索便宜。

Skills(slash-commands)——更简单。Markdown 描述如何处理来自 $ARGUMENTS 的用户输入。我做的好用 home-skills:

  • /diff——分支相对于 master 的变更摘要(文件、commits、影响的函数、migrations)。PR 前 handy。
  • /lint——make format(ruff + auto-fix)的 wrapper。
  • /find-usage ——搜索符号的所有用法(定义 → imports → 调用 → tests → templates)。
  • /debug ——traceback 分析:最后一帧、在正确行读源码、修复建议。
  • /logs [search] [--lines=N] [--level=ERROR]——在 docker-container 内查看结构化日志。
  • /orm ——在 docker 内运行 Django ORM 或原始 SQL(带 auto-LIMIT,DELETE/UPDATE 时请求确认)。
  • /new-test [name]——按项目约定生成 pytest 文件并通过 docker 运行。
  • /make [args]——Makefile 的代理,带分类引用。

Skills 的要点——它们应该 。不是"通用 devops agent",而是"一个命令的 wrapper 加几个 if-else"。Skill 自由度越小,结果越可预测。

除了代码 review agents,还有一个选项可以让 bots 从语言理解层面指出开发者的不足——写的代码可以通过所有检查但不是最理想的(没有这种事)。Bot 也可以建议你可以改进的地方、观察你的成长动态并给予建议。

步骤:

  1. 在分支中写代码
  2. 在特性分支与主分支的 diff 上运行 agents
  3. 查看 review、学习、修复或保留原样
  4. 获得更少 bug 和更好的代码,同时不丢失开发者的项目 context

!图片

已经存在的问题:agents 写代码成本相当高 + agents 无法处理 legacy 代码 + 项目 devs 和 analysts 对其成长很重要。新的方法正在出现,有潜力解决这些问题,但我们仍然需要在这个世界中生存,应该尽可能高效地工作,以便存活更长时间、成长快过新的 AI 模型 :)

!图片

最重要的事——Bender 是我们的兄弟,不进则退!