Vibe Coding进阶版,如何写出漂亮的代码
Vibe Coding进阶版,如何写出漂亮的代码
- 前言
- 一、先把 Vibe Coding 的概念摆在桌面上
- 二、不同场景下,应该选择什么机制
- 三、从“模型写代码”走向“Agent 工程”
前言
作为从一开始使用vibe coding编程的一批人,非常震惊AI的发展,同时也逐渐的升级编码的方式,从一开始的写小功能点,再到后面的大功能,最后到实现一个具体的需求。随着需求的不断提升,规范工具也是非常重要的,以下是我近期的一些感悟,尤其是Hooks的概念。
一、先把 Vibe Coding 的概念摆在桌面上
1. 从 Vibe Coding 到工程化 Agent

1.1 Vibe Coding 到底是什么
Vibe Coding 通常指通过自然语言描述目标,让大模型生成、修改、运行和调试代码,人主要负责表达意图、观察结果并继续反馈。这个说法在 2025 年由 Andrej Karpathy 推广开来,最初带有一种很轻松甚至略显随意的意味:先让想法跑起来,再边看边改。
这种方式非常适合原型、小工具和探索性开发,但一旦进入长期维护的生产项目,问题也会迅速出现:
(1)模型可能记住目标,却漏掉约束
例如需求是实现登录功能,模型通常不会忘记登录本身,却可能在长任务后半段忘记日志脱敏、业务步骤注释、StopWatch 阶段命名、数据库兼容性等细节。
(2)上下文越长,不等于细节越可靠
上下文窗口解决的是“能放进去多少信息”,并不保证每条信息都获得相同注意力。大量代码、日志、测试输出和中间推理会让重要规则被噪声淹没。
(3)生产级 Vibe Coding 需要一个工程闭环
我更愿意把生产级 Vibe Coding 理解为:
人负责目标、边界和验收标准,Agent 负责执行,Rules 负责持续提醒,Tools 与 MCP 提供能力,Hooks 与 CI 负责校验。
它不是“忘记代码存在”,而是把人的关注点从逐行输入代码,提升到意图、架构、风险和验证。
1.2 Agent Harness

Agent Harness 可以理解为大模型外面的运行框架。它通常由模型、指令、工具、上下文管理、权限、执行循环和验证机制组成。Cursor 将其概括为 Instructions、Tools 与 Model 三个核心部分,实际产品还会加入沙箱、Hooks、子 Agent、压缩和审计。

同一个模型放进不同 Harness,编码效果可能差异很大。原因并不神秘:模型是否能准确找到代码、是否能运行测试、失败后是否继续修正、结束前是否被强制复核,都会直接改变最终结果。
2. Rules 与 AGENTS.md
2.1 Rules 是持续注入的项目约定
Rules 用来告诉 Agent:
- 当前项目如何组织代码;
- 哪些技术与写法允许使用;
- 哪些行为禁止发生;
- 修改完成后需要做什么验证;
- 面对歧义、风险和只读任务时如何处理。
Cursor 的项目规则位于 .cursor/rules/*.mdc,可以设置为始终加载、按文件 Glob 自动加载、由 Agent 按描述选择,或者手动引用。Codex 则主要使用 AGENTS.md 承载自然语言项目约定。
这里有一个容易混淆的地方:Codex 文档中的 Rules 还可以特指命令权限规则,而代码风格、架构和验证要求应放在 AGENTS.md。因此跨平台迁移时,不能只看名称相同就认为语义完全一致。
2.2 Rules 适合放什么
(1)适合
- 命名与分层原则;
- 构建、测试和格式化命令;
- 架构边界与安全禁令;
- 修改范围、沟通方式和验收要求;
- 指向项目标准实现的简短索引。
(2)不适合
- 完整复制几十页编码手册;
- 很少触发的特殊业务流程;
- 可以由编译器、Linter 或测试确定判断的全部细节;
- 容易变化的版本、令牌和临时环境状态;
- 希望百分之百强制执行的安全边界。
Rules 本质上仍然是上下文。它能够提高遵循概率,却不是强制执行引擎。
2.3 Codex 的 32 KiB 限制
Codex 会从全局到项目目录逐层发现 AGENTS.md,越靠近当前工作目录的规则优先级越高。默认情况下,合并后的项目指令达到 32 KiB 就会停止继续加载。这个限制可以配置,但简单粗暴地扩大容量通常不是最佳答案。
更好的做法是:
- 根目录只放全项目必须知道的约定;
- 模块差异放在更靠近代码的嵌套
AGENTS.md; - 详细教程移到普通文档或 Skill 的 references;
- 机械规则交给 Hook、静态分析和 CI。
官方细节可参考 Codex AGENTS.md 文档。
3. Skills
3.1 Skill 是按需加载的工作方法
Skill 不是普通规则的另一种写法。它更像一个面向特定任务的操作包,至少包含 SKILL.md,还可以包含:
scripts/:可执行脚本;references/:需要时读取的参考资料;assets/:模板、图片或固定资源。
Agent 启动时通常只看到 Skill 的名称和描述,确认任务相关后才加载完整内容。这种渐进式加载可以减少长期上下文污染。Agent Skills 规范建议将主 SKILL.md 控制在 500 行以内,详细资料按需拆分。
3.2 Skill 与 Rule 的核心区别
Rule 回答“这个项目始终应该怎么做”,Skill 回答“遇到这类任务时,完整流程应该怎么走”。
例如:
- Java 命名、禁止循环 SQL:Rule;
- 根据 Figma 设计实现页面并截图验收:Skill;
- 飞书文档查询、导出、编辑的完整流程:Skill;
- 每次 Java 修改都要执行相关测试:Rule 或 Hook。
4. Tools 与 MCP
4.1 Tool 是 Agent 真正执行动作的接口
大模型本身只能生成内容。读取文件、修改代码、运行命令、访问浏览器、查询数据库,都需要通过 Tool 完成。
一个好的 Tool 应该具备:
- 明确的名称和使用场景;
- 结构化输入与输出;
- 尽可能小而稳定的职责;
- 清晰的权限和失败信息;
- 可被日志与 Hook 观察。
Tool 是否存在,不代表模型一定会正确调用它。工具描述模糊、工具数量过多、返回内容过长,都会降低选择质量。
4.2 MCP 是工具与上下文的标准连接层
MCP,即 Model Context Protocol,是 AI 应用连接外部系统的开放标准。它解决的不是“模型会不会写代码”,而是“不同 Agent 如何用统一方式接入代码搜索、数据库、GitHub、飞书、浏览器和内部平台”。
MCP Server 主要可以暴露三类能力:
- Tools:模型主动调用的动作;
- Resources:应用提供给模型的上下文资源;
- Prompts:由用户触发的可复用模板。
完整定义可参考 MCP Server Concepts。
4.3 MCP 不等于 Tool
Tool 是可执行能力本身,MCP 是发现、描述和调用这些能力的一种标准协议。内置 Shell 是 Tool,但不一定来自 MCP;CodeGraph 可以通过 MCP 暴露代码查询 Tool;飞书服务也可以通过 MCP 同时暴露文档资源和写入动作。
5. Hooks

5.1 Hook 是 Agent 生命周期中的自动检查点
Hook 会在会话开始、工具执行前后、上下文压缩前后或 Agent 准备停止时自动运行脚本。它不依赖模型是否记得调用,因此很适合解决“长任务后规则淡化”的问题。
常见事件包括:
- SessionStart:会话开始;
- PreToolUse:工具执行前;
- PostToolUse:工具执行后;
- PreCompact / PostCompact:上下文压缩前后;
- Stop:Agent 准备结束;
- SubagentStart / SubagentStop:子 Agent 开始或结束。
不同产品支持的事件名和返回格式并不完全相同。Cursor 使用 .cursor/hooks.json,Codex 使用 .codex/hooks.json 或 config.toml。配置不能直接复制,但底层检查脚本可以共享。
5.2 Hook 最适合做什么
(1)执行前拦截
例如禁止裸 mvn、禁止使用旧版 Maven、禁止永久修改全局 JAVA_HOME、阻止明显危险命令。
(2)执行后记录
记录本轮修改过的文件、工具调用结果和审计信息,为结束检查准备准确范围。
(3)结束前复核
运行静态检查、测试或生成复核清单。如果失败,就让主 Agent 继续修复,而不是带着问题结束任务。
Codex 当前主要执行 command 类型 Hook;某些特殊工具路径也可能绕开标准 Hook 路径,因此 Hook 是重要护栏,但不能代替沙箱、权限与 CI。详细行为可参考 Codex Hooks 文档和 Cursor Agent Best Practices。
6. Subagents
6.1 Subagent 是被委派边界任务的独立 Agent
主 Agent 可以把代码探索、测试分析、安全审查或资料检索交给不同子 Agent,然后汇总结果。它的价值不只是并行,更重要的是隔离噪声:测试日志和大范围搜索留在子线程,主线程继续保留需求、决策和最终输出。
Codex 官方将这类问题称为 Context Pollution 和 Context Rot,并建议优先把读取密集型任务并行化,对多个 Agent 同时写代码保持谨慎。Codex Subagents 文档还支持在 .codex/agents/*.toml 中定义项目级自定义 Agent。
6.2 Subagent 的代价
- 每个子 Agent 都会消耗自己的 Token 和工具调用;
- 错误结论可能被主 Agent继续传播;
- 多个 Agent 同时修改代码容易冲突;
- 任务拆分、结果格式和完成条件不清楚时,沟通成本可能超过并行收益;
- 主 Agent 仍然必须验证结果,不能把“子 Agent 已完成”当成“任务已正确完成”。
7. Plugins、Plan、Tests 与 CI
7.1 Plugin 是能力分发包
Plugin 可以把 Rules、Skills、Subagents、MCP、Hooks 和资源打包安装。它适合跨项目、跨团队分发成熟能力,而不是用来替代每个概念本身。Cursor 已经把这些能力组合进插件体系,说明生态正在从零散配置走向可版本化的 Agent 工程组件。
7.2 Plan 是长任务的临时执行合同
Plan 不属于永久规则。它针对当前任务,把需求文档转成可核对的步骤、文件范围和验收标准。Rule 解决长期一致性,Plan 解决本次任务细节不丢失。
7.3 Tests 与 CI 是最后的硬边界
Rules 和模型复核都具有概率性;Hook 也可能因为事件覆盖、运行环境或配置问题没有触发。编译器、测试、静态分析和 CI 才是合并前稳定、可重复的硬门禁。
我习惯把它们分成三层:
- Rules:事前告诉 Agent 应该怎么做;
- Hooks:过程中发现遗漏并要求修正;
- CI:最终决定代码能不能进入主分支。
二、不同场景下,应该选择什么机制
1. 小型、一次性任务
1.1 场景
例如修改一个错误提示、补一个单元测试、调整某个 DTO 字段名。
1.2 最佳组合
(1)Prompt + 精简 Rules + 相关测试
这类任务不需要创建 Skill,也不需要启动多个子 Agent。Prompt 中给出当前目标和验收标准,Rules 提供项目已有风格,修改后运行最小相关测试即可。
这么做的原因是任务边界已经很小。额外编排会增加上下文和沟通成本,却不会明显提升结果。
1.3 限制
如果所谓“小修改”会改变数据库结构、权限或公共 API,就不能只按代码行数判断风险。任务小不代表影响面小。
2. 长任务与详细设计文档
2.1 场景
例如根据一份完整设计文档实现注册、登录、租户、审计和 TraceId 链路。模型通常能记住大目标,却可能在十几个文件之后遗漏某个字段、失败分支或日志要求。
2.2 最佳组合
(1)Rules 保留稳定原则
保留命名、分层、日志脱敏、禁止循环 SQL、验证要求等跨需求仍然成立的内容。
(2)Plan 展开当前设计
把设计文档转成可核对清单:
1. 建立数据结构
验证:字段、约束、迁移脚本与设计文档一致
2. 实现业务入口
验证:成功、拒绝、异常路径完整
3. 接入日志与耗时
验证:关键节点可追踪,无敏感信息
4. 补充测试
验证:核心分支与回归场景通过
(3)Stop Hook 强制回看
在 Agent 准备结束时,让 Hook 检查本轮变更,并返回语义复核清单,要求主 Agent 对照设计文档再检查一次。
这么做比继续向 Rules 追加内容更有效,因为 Plan 与 Stop Hook 会在任务开始和任务结束两个关键时点重新提升细节权重。
2.3 推荐流程
3. 代码风格、注释和耗时统计
3.1 为什么不能只靠 Rules
“业务方法需要 Step 注释”与“所有方法都要 Step 注释”不是一回事。“性能敏感阶段需要 StopWatch”也不能简单等价为“所有 Service 方法都加 StopWatch”。
这类约束同时包含机械部分和语义部分:
- 是否出现禁用 import,可以机械判断;
- Step 注释是否描述真实业务动作,需要语义判断;
- StopWatch 是否成对停止,可以部分机械判断;
- 某个方法到底需不需要计时,需要结合业务判断。
3.2 最佳组合
(1)高置信度规则交给脚本
项目的 scripts/agent-hooks/policy.json 维护可扩展策略,例如:
- 禁止 Spring
StringUtils/CollectionUtils做通用判空; - 禁止
UUID.randomUUID().toString(); - 禁止内联项目全限定类名;
- 限制
TraceIdHolder.get()的使用边界; - 阻止错误 Maven 与永久
JAVA_HOME修改。
新增同类校验时,通常只增加一条配置,而不是重写 Hook 生命周期代码。
(2)语义问题交给主 Agent
当前项目的 Stop Hook 会要求主 Agent复核:
- 设计文档是否遗漏;
- Step 注释是否必要且准确;
- 日志是否充分、过量或泄密;
- StopWatch 阶段是否合理;
- 是否存在循环 SQL、事务或数据库兼容风险;
- 测试是否覆盖成功与失败路径。
(3)不要让 Hook 自动修改业务代码
Hook 负责发现和反馈,主 Agent 负责理解上下文并修改。否则很容易出现:
- Hook 修改触发新的 Hook,形成递归;
- 脚本和 Agent 同时写文件,产生覆盖;
- 自动修复改变业务语义,却没有明确责任方;
- 出错后难以审计修改来源。
3.3 推荐项目的双平台结构
因为我使用codex和cursor配合进行编码,主力还是cursor,codex扮演的是决策者,一般在需求无法完整描述的时候才会让codex进行编码。
.cursor/
├── rules/*.mdc
├── hooks.json
└── hooks/run-agent-hook.ps1
.codex/
├── hooks.json
└── hooks/run-agent-hook.ps1
AGENTS.md
scripts/agent-hooks/
├── agent-hook.mjs
└── policy.json
Cursor 与 Codex 的规则位置、Hook 事件名和返回 JSON 不同,因此保留各自配置;真正的检查逻辑只维护一份。这个结构比复制两套脚本更不容易漂移。
3.4 真实示例,重点展示Hooks
这里我们注意,不要过多的去研究怎么写Hooks,只需要明白这是干什么的,具体让AI去执行。
hooks.json
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File .cursor/hooks/run-agent-hook.ps1 -Platform cursor -Event pre-shell",
"timeout": 10
}
],
"postToolUse": [
{
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File .cursor/hooks/run-agent-hook.ps1 -Platform cursor -Event track-change",
"timeout": 10
}
],
"stop": [
{
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File .cursor/hooks/run-agent-hook.ps1 -Platform cursor -Event stop",
"loop_limit": 3,
"timeout": 30
}
]
}
}
agent-hook.mjs
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
const [platform = "codex", event = "stop"] = process.argv.slice(2);
const rawInput = await readStdin();
const input = parseHookInput(rawInput);
const repoRoot = findRepoRoot(input);
const policy = readJson(path.join(repoRoot, "scripts", "agent-hooks", "policy.json"));
const statePath = getStatePath(repoRoot, input);
const state = readJson(statePath, { files: [], stopAttempts: 0 });
if (event === "pre-shell") {
handlePreShell();
} else if (event === "track-change") {
handleTrackChange();
} else if (event === "stop") {
handleStop();
} else {
emit({});
}
function handlePreShell() {
const command = findCommand(input);
const violation = checkShellCommand(command);
if (!violation) {
emit({});
return;
}
if (platform === "cursor") {
emit({
permission: "deny",
user_message: violation,
agent_message: `${violation} 请改用规则中允许的命令。`
});
return;
}
emit({
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: violation
}
});
}
function handleTrackChange() {
const files = extractCandidateFiles(input)
.map((file) => normalizeCandidate(file))
.filter(Boolean);
state.files = [...new Set([...state.files, ...files])].sort();
writeJson(statePath, state);
emit({});
}
function handleStop() {
const changedFiles = [...new Set([...state.files, ...readGitChangedFiles()])]
.filter((file) => file.endsWith(".java"))
.filter((file) => fs.existsSync(path.join(repoRoot, file)));
if (changedFiles.length === 0) {
resetState();
emit({});
return;
}
const violations = changedFiles.flatMap(checkJavaFile);
state.stopAttempts = Number(state.stopAttempts || 0) + 1;
writeJson(statePath, state);
if (violations.length > 0) {
const reason = formatViolations(violations);
if (state.stopAttempts >= 3) {
resetState();
if (platform === "cursor") {
emit({ user_message: reason });
} else {
emit({ continue: false, stopReason: reason, systemMessage: reason });
}
return;
}
continueAgent(reason);
return;
}
const alreadyContinued = Boolean(input.stop_hook_active)
|| Number(input.loop_count || 0) > 0
|| Boolean(state.semanticReviewRequested);
if (!alreadyContinued) {
state.semanticReviewRequested = true;
writeJson(statePath, state);
const checklist = policy.semanticReviewChecklist
.map((item, index) => `${index + 1}. ${item}`)
.join("\n");
continueAgent(
`静态 Hook 已通过。结束前请由当前主 Agent 对本轮改动执行一次语义复核并直接修正发现的问题:\n${checklist}\n复核后重新运行必要的编译/测试,再结束任务。`
);
return;
}
resetState();
emit({});
}
function checkShellCommand(command) {
if (!command) {
return null;
}
for (const rule of policy.forbiddenShellRules || []) {
if (new RegExp(rule.pattern, rule.flags || "").test(command)) {
return `[${rule.id}] ${rule.message}`;
}
}
const normalized = command.replaceAll("/", "\\");
const invokesBareMaven = /(^|[\s;&|])(?:mvn|mvn\.cmd)(?=\s|$)/i.test(command);
const hasRequiredMaven = normalized.toLowerCase()
.includes(policy.requiredMavenPath.toLowerCase());
if (invokesBareMaven && !hasRequiredMaven) {
return `[MAVEN-PATH] 禁止使用裸 mvn;请使用 ${policy.requiredMavenPath}`;
}
return null;
}
function checkJavaFile(relativePath) {
const absolutePath = path.join(repoRoot, relativePath);
const lines = fs.readFileSync(absolutePath, "utf8").split(/\r?\n/);
const violations = [];
for (const rule of policy.javaLineRules || []) {
if ((rule.allowedPathPatterns || []).some((pattern) => new RegExp(pattern).test(absolutePath))) {
continue;
}
const regex = new RegExp(rule.pattern);
const skipPatterns = (rule.skipLinePatterns || []).map((pattern) => new RegExp(pattern));
lines.forEach((line, index) => {
if (skipPatterns.some((skip) => skip.test(line))) {
return;
}
if (regex.test(line)) {
violations.push({
id: rule.id,
file: relativePath,
line: index + 1,
message: rule.message
});
}
});
}
return violations;
}
function continueAgent(reason) {
if (platform === "cursor") {
emit({ followup_message: reason });
} else {
emit({ decision: "block", reason });
}
}
function formatViolations(violations) {
const displayed = violations.slice(0, 20);
const lines = displayed.map(
(item) => `- ${item.file}:${item.line} [${item.id}] ${item.message}`
);
if (violations.length > displayed.length) {
lines.push(`- 另有 ${violations.length - displayed.length} 项未展开。`);
}
return `Hook 静态校验未通过,请修正后重新验证:\n${lines.join("\n")}`;
}
function extractCandidateFiles(value) {
const text = JSON.stringify(value);
const candidates = [];
const patterns = [
/\*\*\* (?:Add|Update|Delete) File:\s*([^\r\n]+)/g,
/(?:file_path|path|target_file|filePath)"?\s*[:=]\s*"([^"]+\.(?:java|xml|ya?ml|properties))"/gi,
/([A-Za-z]:[\\/][^"\r\n]*?\.(?:java|xml|ya?ml|properties))/gi,
/((?:src|pom\.xml|config)[\\/][^"\r\n]*?\.(?:java|xml|ya?ml|properties))/gi
];
for (const regex of patterns) {
for (const match of text.matchAll(regex)) {
candidates.push(match[1]);
}
}
return candidates;
}
function normalizeCandidate(candidate) {
const cleaned = candidate
.replaceAll("\\\\", "\\")
.replace(/^["']|["']$/g, "")
.trim();
const absolute = path.isAbsolute(cleaned)
? path.resolve(cleaned)
: path.resolve(repoRoot, cleaned);
const relative = path.relative(repoRoot, absolute);
if (relative.startsWith("..") || path.isAbsolute(relative)) {
return null;
}
return relative.replaceAll("\\", "/");
}
function readGitChangedFiles() {
try {
const output = execFileSync(
"git",
["diff", "--name-only", "--diff-filter=ACMR", "--", "*.java"],
{ cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
);
return output.split(/\r?\n/).filter(Boolean).map((item) => item.replaceAll("\\", "/"));
} catch {
return [];
}
}
function findCommand(value) {
if (!value || typeof value !== "object") {
return "";
}
const direct = value.command
|| value.tool_input?.command
|| value.toolInput?.command;
if (typeof direct === "string") {
return direct;
}
return "";
}
function findRepoRoot(value) {
const starts = [
...(Array.isArray(value.workspace_roots) ? value.workspace_roots : []),
value.cwd,
process.cwd()
].filter((item) => typeof item === "string" && item.length > 0);
for (const start of starts) {
let current = path.resolve(start);
while (true) {
if (fs.existsSync(path.join(current, "pom.xml"))
&& fs.existsSync(path.join(current, "scripts", "agent-hooks", "policy.json"))) {
return current;
}
const parent = path.dirname(current);
if (parent === current) {
break;
}
current = parent;
}
}
return process.cwd();
}
function getStatePath(root, value) {
const session = value.turn_id
|| value.conversation_id
|| value.session_id
|| "default";
const hash = crypto.createHash("sha256")
.update(`${root}|${session}`)
.digest("hex")
.slice(0, 24);
const stateDir = process.env.AGENT_HOOK_STATE_DIR
|| path.join(os.tmpdir(), "duan-agent-hooks");
fs.mkdirSync(stateDir, { recursive: true });
return path.join(stateDir, `${hash}.json`);
}
function resetState() {
try {
fs.rmSync(statePath, { force: true });
} catch {
// Hook 状态只用于审计循环;清理失败不应阻塞正常任务。
}
}
function parseHookInput(raw) {
if (!raw.trim()) {
return {};
}
try {
return JSON.parse(raw);
} catch {
const start = raw.indexOf("{");
const end = raw.lastIndexOf("}");
if (start >= 0 && end > start) {
try {
return JSON.parse(raw.slice(start, end + 1));
} catch {
return {};
}
}
return {};
}
}
function readJson(file, fallback) {
try {
return JSON.parse(fs.readFileSync(file, "utf8"));
} catch (error) {
if (fallback !== undefined) {
return fallback;
}
throw error;
}
}
function writeJson(file, value) {
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, "utf8");
}
function emit(value) {
process.stdout.write(`${JSON.stringify(value)}\n`);
}
async function readStdin() {
let value = "";
process.stdin.setEncoding("utf8");
for await (const chunk of process.stdin) {
value += chunk;
}
return value;
}
policy.json
{
"version": 1,
"requiredMavenPath": "C:\\Program Files\\JetBrains\\IntelliJ IDEA 2026.1.1\\plugins\\maven\\lib\\maven3\\bin\\mvn.cmd",
"forbiddenShellRules": [
{
"id": "ENV-PERSISTENT-JAVA-HOME",
"pattern": "(setx\\s+JAVA_HOME|SetEnvironmentVariable\\s*\\(\\s*['\\\"]JAVA_HOME['\\\"])",
"flags": "i",
"message": "禁止永久修改全局 JAVA_HOME;只允许在当前终端会话临时设置。"
},
{
"id": "MAVEN-LEGACY",
"pattern": "D:\\\\env\\\\maven\\\\apache-maven-3\\.6\\.1",
"flags": "i",
"message": "禁止使用 Maven 3.6.1;请使用项目约定的 IDEA 捆绑 Maven 3.9.11。"
}
],
"javaLineRules": [
{
"id": "JAVA-SPRING-GENERIC-UTIL",
"pattern": "import\\s+org\\.springframework\\.util\\.(StringUtils|CollectionUtils)\\s*;",
"message": "业务代码禁止使用 Spring StringUtils/CollectionUtils 做通用判空,请改用 Hutool。"
},
{
"id": "JAVA-UUID-STRING",
"pattern": "UUID\\.randomUUID\\(\\)\\.toString\\(\\)",
"message": "UUID 字符串请使用 IdUtil.simpleUUID() 或 IdUtil.randomUUID()。"
},
{
"id": "JAVA-INLINE-FQN",
"pattern": "\\bcom\\.duan\\.agent\\.(?:[a-zA-Z_$][\\w$]*\\.)+[A-Z_$][\\w$]*",
"message": "禁止内联项目全限定类名;请先 import,再使用短类名。",
"skipLinePatterns": [
"^\\s*(package|import)\\s+",
"Class\\.forName\\s*\\(",
"\\{@link\\s+"
]
},
{
"id": "JAVA-TRACE-HOLDER-BOUNDARY",
"pattern": "\\bTraceIdHolder\\.get\\s*\\(",
"message": "业务代码禁止直接调用 TraceIdHolder.get(),请从 RequestContextHolder 获取 traceId。",
"allowedPathPatterns": [
"[\\\\/]TraceIdFilter\\.java$",
"[\\\\/]RequestContextBuilder\\.java$"
]
}
],
"semanticReviewChecklist": [
"逐项对照需求/设计文档,确认没有遗漏字段、分支、异常路径和验收标准。",
"检查业务方法的 Step 注释是否覆盖关键阶段且描述业务动作,简单方法不要机械加注释。",
"检查关键链路日志是否充分、是否泄露敏感信息、是否在循环中产生高频 info。",
"检查是否真的需要 StopWatch;需要时阶段应粗粒度、lowerCamelCase、成对停止并在 finally 汇总。",
"检查循环 SQL、批量访问、目标数据库兼容性、事务边界和异常处理。",
"检查命名、分层、接口注入、JavaDoc、文件头、Hutool 与 TraceId 约定。",
"检查测试是否覆盖成功路径、失败路径和本次修复的回归场景,并运行与改动范围相称的验证。"
]
}
4. 大型代码库的代码查询
4.1 场景
Agent 需要回答“某个请求从 Controller 到数据库经历了哪些调用”“修改这个类会影响哪些调用方”“接口实现在哪里被动态调用”。
4.2 最佳组合
(1)使用代码索引类 MCP
当前项目使用 CodeGraph。Agent 可以通过 MCP 查询符号源码、调用路径和影响范围,不必反复执行文本搜索和逐文件读取。
示例:
查询 AuthLoginService.login 到 TokenService.createToken 的完整调用路径,
同时返回涉及方法源码、调用方和现有测试。
为什么适合 MCP:
- 它需要访问实时项目索引;
- 输出具有稳定结构;
- 多个 IDE 或 Agent 可以复用同一能力;
- 比把整个仓库塞进 Prompt 更节省上下文。
4.3 限制
- 索引可能落后于刚写入的文件;
- 动态调用和反射只能做到尽力解析;
- MCP 返回“谁调用谁”,不等于证明代码正确;
- 工具描述和返回内容过大仍会污染上下文;
- 写操作必须配置权限,不能因为接入 MCP 就默认可信。
5. 特定且重复的业务流程
5.1 场景
例如:
- 根据 Figma 页面实现前端并截图比对;
- 从飞书目录读取需求文档、生成汇总并回写;
- 固定格式处理 PDF、Excel 或发布流程;
- 每次发布都执行相同的构建、制品和验证步骤。
5.2 最佳组合
(1)优先使用 Skill
Skill 可以同时携带步骤、脚本、模板和参考资料,又只在任务相关时加载。
以飞书为例,lark-cli 命令、身份排查、文档导出和高风险写入确认是一套完整工作流,更适合 Skill。只有“高风险操作必须确认”这种跨流程都成立的要求,才需要放进 Rule 或 Hook。
5.3 限制
- Skill 的 description 写得太宽会误触发,太窄会无法发现;
- 下载第三方 Skill 前要审查脚本、依赖、网络与权限;
- 很少使用不是拒绝 Skill 的理由,按需加载本来就是它的优势;
- 如果某个 Skill 每次都被触发,说明其中一部分可能应该上移为 Rule 或公共 Tool。
6. 并行研究、测试与审查
6.1 适合 Subagents 的任务
(1)读取密集且可独立
- 一个 Agent 查安全风险;
- 一个 Agent 查测试缺口;
- 一个 Agent 核对框架官方文档;
- 一个 Agent 分析运行日志;
- 主 Agent 等待全部结果后统一判断。
这种拆分能降低主线程的上下文噪声,也能缩短墙钟时间。
6.2 不适合直接并行的任务
(1)多个 Agent 同时修改同一模块
如果 A 修改 Service,B 同时修改其测试,C 又重构 DTO,而三者依赖的接口仍在变化,最后的合并和验证成本可能高于单 Agent 顺序完成。
(2)强依赖前序结论
如果第二步必须建立在第一步的精确结果上,就不是真正独立的并行任务。
6.3 最佳实践
- 先让子 Agent 以只读方式探索和审查;
- 每个子任务只有一个明确交付物;
- 要求返回证据、文件位置、假设和未确认项;
- 主 Agent 不直接相信摘要,要抽查关键证据;
- 写操作尽量只有一个明确所有者;
- 小任务不要为了“看起来先进”强行拆 Agent。
当前项目暂不主动引入自定义 Subagent 是合理的。等到安全审查、测试分析或文档核对形成稳定且可独立的工作单元,再逐步加入,收益会更确定。
7. 安全、审计与不可绕过的约束
7.1 场景
例如禁止读取密钥、禁止执行危险删除、禁止向生产环境写入、依赖安装必须经过审查。
7.2 最佳组合
(1)权限与沙箱
从根源限制 Agent 能访问的文件、网络和命令。
(2)PreToolUse Hook
根据具体参数阻止明显不合规调用,并记录原因。
(3)CI 与服务端策略
将真正不能绕过的检查放在版本库、流水线、代码托管平台或目标服务端。不要把安全完全寄托在模型是否听话,或者客户端 Hook 是否成功触发。
7.3 为什么 LLM 审查不能取代脚本
LLM 擅长理解语义,却不是稳定的访问控制器。同一段命令在不同上下文下可能得到不同判断;模型调用也可能超时、失败或增加成本。
最合适的组合是:
- 确定性边界使用脚本和权限;
- 复杂语义使用模型审查;
- 高风险动作仍需人工确认;
- 最终结果由 CI 或服务端再次验证。
8. 快速选择表

8.1 按问题选择机制
| 需求 | 首选机制 | 为什么 | 主要限制 |
|---|---|---|---|
| 统一命名、分层、沟通方式 | Rules / AGENTS.md | 每次任务都需要 | 上下文会淡化;Codex 默认合并上限 32 KiB |
| 特定任务的完整操作流程 | Skill | 按需加载,可带脚本与模板 | 依赖触发描述和第三方信任 |
| 读取代码、数据库或外部系统 | Tool / MCP | 提供实时、结构化能力 | 权限、延迟、工具选择和上下文开销 |
| 阻止错误命令 | PreToolUse Hook | 执行前确定性拦截 | 事件覆盖并非绝对安全边界 |
| 修改后静态检查 | PostToolUse / Stop Hook | 不依赖模型主动记得 | 脚本只能判断可编码规则 |
| 注释、日志、设计细节复核 | Stop Hook + 主 Agent | 在结束前恢复语义注意力 | 仍是模型判断,需要测试佐证 |
| 并行代码探索、测试分析 | Subagents | 隔离噪声并缩短时间 | Token 与协调成本,写冲突 |
| 跨项目分发整套能力 | Plugin | 可版本化安装 Rules、Skills、MCP、Hooks | 供应链与版本兼容风险 |
| 决定代码能否合并 | CI | 可重复、可审计、难绕过 | 反馈通常晚于本地 Hook |
三、从“模型写代码”走向“Agent 工程”
1. 这几年真正变化的是什么
1.1 第一阶段:补全代码
早期 AI 编程的核心体验是补全一行、一个函数或一段样板代码。人掌握执行过程,模型只是输入法的升级。
1.2 第二阶段:对话式修改
随后模型开始理解多个文件,开发者通过对话要求它解释、重构和修复。此时 Prompt 与上下文管理成为关键。
1.3 第三阶段:单 Agent 闭环执行
Agent 开始自己搜索代码、修改文件、运行测试,并根据错误继续迭代。Rules、Tools、MCP、Hooks、沙箱和长期任务开始进入主流产品。
1.4 第四阶段:多 Agent 与长期自治
当前行业明显在向多 Agent、后台任务和长期运行演进。OpenAI 在 GPT‑5.6 中加入了 Multi-agent beta 与 Programmatic Tool Calling;Cursor 在持续推进长任务、云 Agent、异步子 Agent和插件生态;Anthropic 的研究系统也采用了 orchestrator-worker 结构。
这并不意味着多 Agent 是刚刚发明的。早期软件 Agent、角色协作和多智能体研究早就存在。真正的变化是模型的工具使用、上下文保持、执行可靠性和基础设施成熟到了可以把这些概念产品化的阶段。
2. 为什么行业越来越少谈参数量
2.1 不是参数不重要,而是参数不再足够说明问题
参数规模仍然影响容量与能力上限,但它已经很难单独回答这些更实际的问题:
- 一个真实需求能否完整做完;
- 需要多少 Token、工具调用和时间;
- 长任务中能否保持目标;
- 能否正确调用代码搜索、浏览器和测试;
- 失败后能否发现并修正;
- 每次任务的成本与成功率是多少。
对于 MoE 模型,总参数、激活参数和专家路由本来就是不同概念。只比较一个总参数数字,很容易把模型容量、推理成本和实际能力混为一谈。
2.2 GPT‑5.6 是一个很典型的例子

OpenAI 对 GPT‑5.6 的公开介绍重点不是参数数量,而是:
- 单位 Token 能完成更多有效工作;
- Programmatic Tool Calling;
- Multi-agent;
- 更强的长期任务、工具使用与计算机操作;
- Sol、Terra、Luna 三档能力与成本;
- 推理强度、缓存、延迟和成功率。
官方甚至直接用更少 Token、更少工具调用、更短时间和更低估算成本描述进步。GPT‑5.6 发布说明与模型使用指南共同反映了一个趋势:模型竞争正在从“谁更大”转向“谁能在完整工作流中更稳定、更便宜地完成任务”。
说得直白一点,我并不太关心一个模型到底藏了多少参数。我更关心的是,它改完十几个文件之后,是否还记得最开始那条不起眼但很重要的日志约束;测试失败后,它是解释失败,还是继续把问题修好。
3. Kimi K3 带来的另一种观察
3.1 参数规模仍然有传播力
Kimi K3 官方公布的总参数为 2.8T,原生支持视觉与 100 万 Token 上下文,并采用更稀疏的 MoE 结构,每次激活 896 个专家中的 16 个。它证明开放模型仍然可以继续冲击规模前沿。
但这个例子也恰好说明,参数不是全部。Kimi 官方同时强调了 Kimi Delta Attention、Attention Residuals、长周期编码、知识工作、推理能力,以及不同 Harness 下的评测结果。Kimi K3 官方技术博客还明确说明,完整模型权重计划在 2026 年 7 月 27 日前发布;当把它表述为已经提供产品与 API、正在完成开放权重落地,而不是提前把完整权重发布写成既成事实。
3.2 开放模型会推动 Harness 标准化
当模型可以替换时,真正长期积累价值的部分会逐渐上移:
- 项目自己的 Rules 与验收标准;
- 可跨模型复用的 Skills;
- 标准化 MCP Server;
- Hooks、权限和审计;
- 测试集、Evals 与历史任务数据;
- 团队自己的 Agent 工作流。
闭源模型和开放模型会继续竞争,但工程团队不应该把全部能力锁死在某一个模型名字上。一个成熟的 Agent 工程体系,应当允许在不同任务中选择不同模型,同时保留相同的项目规则、工具和验证标准。
4. 我对后续趋势的判断
4.1 多 Agent 会发展,但不会无限增加 Agent 数量
未来更常见的不会是“几十个 Agent 一起热闹地写代码”,而是少量边界清晰的角色:
- 主 Agent:维护目标、决策与最终责任;
- Explorer:只读探索代码;
- Reviewer:审查正确性、安全和测试;
- Worker:拥有明确代码修改范围;
- Domain Agent:通过专用 MCP 或 Skill 处理数据库、前端、飞书等领域任务。
评价多 Agent 系统的标准也不会是 Agent 数量,而是任务是否能独立拆分、结果能否验证、协调成本是否低于收益。
4.2 Rules 会变短,策略会越来越可执行
团队会逐渐发现,把所有经验都写进一个超长 AGENTS.md 并不可靠。稳定趋势应该是:
- Rules 保留原则和索引;
- 详细流程进入 Skills;
- 机械约束进入 Policy as Code;
- 生命周期检查进入 Hooks;
- 最终门禁进入 CI;
- 复杂判断由 Reviewer Agent 或主 Agent完成。
4.3 MCP 会成为基础设施,而不是卖点
现在大家还会特意说“我们支持 MCP”。以后它可能像 HTTP、LSP 一样退到基础设施层。真正形成差异的会是:
- Tool 设计是否清楚;
- 数据是否实时可信;
- 权限是否最小化;
- 返回内容是否适合模型处理;
- 是否有稳定的审计、缓存和失败恢复。
4.4 人的角色不会消失,而是向上移动
我自己的体会是,Vibe Coding 最舒服的部分,是可以少写大量重复代码;最危险的部分,也是很容易因为“它看起来已经完成了”而降低警惕。
未来开发者更像是系统导演:
- 把模糊需求变成可验证目标;
- 选择合适的模型与 Agent;
- 设计 Rules、Skills、Tools 和 Hooks;
- 决定哪些事情可以自动,哪些必须确认;
- 用测试和 Evals 判断能力,而不是凭一次漂亮演示下结论。
真正高质量的 Vibe Coding,不是把代码全部交给模型,然后祈祷结果正确;而是建立一个即使模型偶尔忘记、误判或跑偏,也能被工程系统拉回来的闭环。
5. 最后的总结
5.1 一套更稳妥的默认方法
(1)长期原则放 Rules
保持短、清楚、可执行,不复制整本手册。
(2)当前需求放 Plan
把文档转成步骤、文件范围和验收清单。
(3)实时能力交给 Tools 与 MCP
让 Agent 查询真实代码和系统,不依赖记忆猜测。
(4)特殊流程封装为 Skills
只在需要时加载,并复用脚本与模板。
(5)机械约束放 Hooks
执行前阻止,执行后记录,结束前检查。
(6)语义问题由主 Agent 或 Reviewer 复核
注释、日志、耗时和架构合理性不能只靠正则。
(7)可独立的读取任务再交给 Subagents
先从探索、测试和审查开始,不急着让多个 Agent 同时写代码。
(8)CI 保留最终否决权
任何 Agent 的“已经完成”,都不应该自动等价于“可以合并”。
如果把这些机制放在正确的位置,Vibe Coding 就不再只是一次灵感驱动的代码生成,而会逐渐成为一种可维护、可审计、可扩展的软件工程方法。
6. 参考资料
6.1 官方文档与延伸阅读
更多推荐

所有评论(0)