Harness Engineering 模块化指令实战:告别 600 行巨型 AGENTS
基于 Spring AI Alibaba 的完整示例,演示如何将指令从"庞然大物"拆解为"按需加载"的模块化体系
Harness Engineering 从理论到实战:基于 Spring AI Alibaba 的完整实现指南:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/162640337
基于上述文章基础。
一、背景:为什么巨型指令文件会失败?
在 Harness Engineering 实践中,AGENTS.md 是指导 AI Agent 行为的核心指令文件。然而,随着项目演进,这个文件很容易膨胀到 600 行甚至更多,带来四个致命问题:
| 问题 | 具体表现 |
|---|---|
| 上下文预算被吃掉 | 600 行文件可能占用 10K--20K tokens,挤占了真正重要的代码阅读和任务推理的预算 |
| 中间迷失 | 关键的安全约束埋在第 300 行,LLM 对长文本中间部分的信息利用效率显著低于两端 |
| 优先级冲突 | 硬约束、设计指导、历史教训混在一起,Agent 无法区分哪些是红线、哪些只是建议 |
| 维护衰减 | 文件只增不减,信噪比持续下降,矛盾规则累积 |
Harness Engineering 的解决方案:
入口文件控制在 50--200 行,只放概览、硬约束和链接;专题文档按需加载。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
二、示例目标与设计思路
基于之前的 Spring AI Alibaba 项目(pom.xml + application.yml),我们构建两套对比方案:
- 方案 A(反面教材) :单个巨型
AGENTS.md(约 680 行),包含所有内容 - 方案 B(正确实践) :入口文件(约 80 行)+ 按需加载的专题文档
通过对比,直观验证模块化指令在 上下文预算、规则可见性、维护性 上的优势。
三、项目结构与依赖
3.1 目录结构
spring-ai-harness-demo/
├── pom.xml
├── src/main/
│ ├── java/com/badao/ai/
│ │ ├── SpringAiHarnessDemoApplication.java # 启动类
│ │ ├── config/
│ │ │ ├── HarnessAgentConfig.java # 原有 Agent 配置
│ │ │ └── ModularInstructionConfig.java # 模块化指令加载配置
│ │ ├── service/
│ │ │ └── HarnessAgentService.java # 业务服务
│ │ ├── harness/
│ │ │ ├── skills/
│ │ │ │ └── ReviewAnalysisSkill.java # 评价分析技能(支持两种指令模式)
│ │ │ └── ...
│ │ └── model/...
│ └── resources/
│ ├── application.yml
│ └── instructions/
│ ├── AGENTS.md # 入口文件(约80行)
│ ├── docs/
│ │ ├── api-patterns.md # API 设计规范
│ │ ├── database-rules.md # 数据库操作约束
│ │ ├── testing-standards.md # 测试标准
│ │ └── security-rules.md # 安全硬约束
│ └── legacy/
│ └── GIANT_AGENTS.md # 巨型指令文件(680行,反面教材)
└── src/test/
└── java/com/badao/ai/
└── InstructionComparisonTest.java # 对比测试类
3.2 Maven 依赖(pom.xml)
关键依赖如下(完整 pom.xml 与之前相同,仅需确保包含 spring-boot-starter-test):
xml
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Alibaba -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>1.1.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>1.1.2.0</version>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.3 配置文件(application.yml)
yaml
server:
port: 885
spring:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY}
chat:
options:
model: qwen-max
logging:
level:
com.badao.ai: debug
四、核心代码实现
4.1 入口文件:AGENTS.md(约 80 行)
markdown
# 项目概览
Spring Boot 3.2.5 + Java 17 后端服务,集成 Spring AI Alibaba 框架。
主要功能:联系人信息提取、商品评价分析。
## 快速开始
- 启动:`mvn spring-boot:run`
- 测试:`mvn test`
- 环境变量:`DASHSCOPE_API_KEY` 必须配置
## 全局硬约束(不可违反)
1. 所有 Agent 输出必须符合指定的 POJO 结构(ContactInfo / ProductReview)
2. 禁止在代码中使用 `System.out.println()` 调试,必须使用 Slf4j
3. 所有敏感信息(API Key、密码)必须通过环境变量注入
4. 数据库查询必须使用参数化查询(如适用)
5. 禁止捕获异常后不做任何处理
6. 所有公共方法必须包含 Javadoc
7. 禁止在 Controller 层编写业务逻辑
8. 所有外部 API 调用必须设置超时时间
9. 禁止在生产代码中使用 `@Deprecated` 标注的方法
10. 所有 PR 必须通过 `mvn clean verify`
## 专题文档(按需加载)
| 文档 | 适用场景 |
|:---|:---|
| `docs/api-patterns.md` | 添加新 API 端点时必读 |
| `docs/database-rules.md` | 涉及数据库操作时必读 |
| `docs/testing-standards.md` | 编写单元/集成测试时参考 |
| `docs/security-rules.md` | 涉及认证授权或敏感数据时必读 |
## 历史教训(已归档为测试用例)
- ~~2026-01: WebSocket 内存泄漏~~ → 已转为 `WebSocketLeakTest.java`
- ~~2026-02: 时区处理错误~~ → 已转为 `TimezoneTest.java`
4.2 专题文档示例:docs/security-rules.md
markdown
# 安全硬约束
## 适用场景
涉及用户认证、API 鉴权、敏感数据处理时必读。
## 不可违反的规则
- API Key 禁止硬编码,必须通过 `@Value` 注入环境变量
- 所有用户输入必须进行白名单校验,防止注入攻击
- 禁止在日志中打印敏感信息(密码、Token、手机号)
- 敏感字段使用 `@JsonIgnore` 或脱敏处理
4.3 反面教材:legacy/GIANT_AGENTS.md(约 680 行)
该文件篇幅庞大,包含:项目概览(50 行)、技术栈版本(50 行)、编码规范(200 行)、API 设计(100 行)、部署流程(80 行)、团队偏好(20 行)、历史记录(50 行)、以及埋在第 300 行之后的 15 条全局硬约束。同时还存在矛盾规则(如"必须使用 Java 17 特性"与"禁止使用 Java 17 特性"并存)。完整内容详见附录。
4.4 模块化指令加载配置:ModularInstructionConfig.java
java
package com.badao.ai.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Configuration
public class ModularInstructionConfig {
@Bean
public String entryInstructions() throws IOException {
return loadInstruction("instructions/AGENTS.md");
}
@Bean
public String apiPatterns() throws IOException {
return loadInstruction("instructions/docs/api-patterns.md");
}
@Bean
public String databaseRules() throws IOException {
return loadInstruction("instructions/docs/database-rules.md");
}
@Bean
public String testingStandards() throws IOException {
return loadInstruction("instructions/docs/testing-standards.md");
}
@Bean
public String securityRules() throws IOException {
return loadInstruction("instructions/docs/security-rules.md");
}
@Bean
public String giantInstructions() throws IOException {
return loadInstruction("instructions/legacy/GIANT_AGENTS.md");
}
private String loadInstruction(String path) throws IOException {
ClassPathResource resource = new ClassPathResource(path);
return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
}
/**
* 根据任务类型动态组装指令(只加载相关部分)
*/
public String buildPromptForTask(String taskType, String userInput) {
StringBuilder prompt = new StringBuilder();
try {
prompt.append(entryInstructions()).append("\n\n");
switch (taskType) {
case "api" -> prompt.append(apiPatterns()).append("\n\n");
case "database" -> prompt.append(databaseRules()).append("\n\n");
case "test" -> prompt.append(testingStandards()).append("\n\n");
case "security" -> prompt.append(securityRules()).append("\n\n");
default -> { /* 只加载入口 */ }
}
prompt.append("## 用户任务\n").append(userInput);
} catch (IOException e) {
throw new RuntimeException("加载指令失败", e);
}
return prompt.toString();
}
/**
* 加载巨型指令(用于对比测试)
*/
public String buildGiantPrompt(String userInput) throws IOException {
return giantInstructions() + "\n\n## 用户任务\n" + userInput;
}
}
4.5 技能层:ReviewAnalysisSkill.java(支持两种模式)
java
package com.badao.ai.harness.skills;
import com.badao.ai.config.ModularInstructionConfig;
import com.badao.ai.model.ProductReview;
import org.springframework.stereotype.Component;
@Component
public class ReviewAnalysisSkill {
private final ModularInstructionConfig instructionConfig;
public ReviewAnalysisSkill(ModularInstructionConfig instructionConfig) {
this.instructionConfig = instructionConfig;
}
// ----- 供业务服务调用的默认方法(使用模块化指令)-----
public String buildPrompt(String reviewText) {
return buildPromptWithModularInstructions(reviewText);
}
// ----- 模块化指令(正确实践)-----
public String buildPromptWithModularInstructions(String reviewText) {
return instructionConfig.buildPromptForTask("default",
"请分析以下商品评价,按标准格式输出 JSON:\n" + reviewText
);
}
// ----- 巨型指令(反面教材)-----
public String buildPromptWithGiantInstructions(String reviewText) {
try {
return instructionConfig.buildGiantPrompt(
"请分析以下商品评价,按标准格式输出 JSON:\n" + reviewText
);
} catch (Exception e) {
throw new RuntimeException("加载巨型指令失败", e);
}
}
// ----- 后处理(不变)-----
public ProductReview postProcess(ProductReview review) {
if (review.getRating() < 1) review.setRating(1);
if (review.getRating() > 5) review.setRating(5);
if (review.getKeyPoints() == null || review.getKeyPoints().length == 0) {
review.setKeyPoints(new String[]{"无关键点"});
}
if (review.getDetails() == null) {
ProductReview.ReviewDetails details = new ProductReview.ReviewDetails();
details.setPros(new String[0]);
details.setCons(new String[0]);
details.setSummary("无总结");
review.setDetails(details);
}
return review;
}
}
4.6 业务服务(HarnessAgentService 保持不变)
原有 HarnessAgentService 中的 analyzeReview 方法会调用 reviewAnalysisSkill.buildPrompt(reviewText),由于我们重载了 buildPrompt 方法指向模块化版本,所以无需修改任何业务代码。
4.7 测试类:InstructionComparisonTest.java
java
package com.badao.ai;
import com.badao.ai.config.ModularInstructionConfig;
import com.badao.ai.harness.skills.ReviewAnalysisSkill;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
public class InstructionComparisonTest {
@Autowired
private ModularInstructionConfig instructionConfig;
@Autowired
private ReviewAnalysisSkill reviewAnalysisSkill;
@Test
public void testModularInstructions() {
String prompt = reviewAnalysisSkill.buildPromptWithModularInstructions("还可以吧");
System.out.println("=== 模块化指令(正确实践) ===");
System.out.println("指令行数: " + prompt.lines().count());
System.out.println("是否包含安全规则(参数化查询): " + prompt.contains("参数化查询"));
System.out.println("是否包含部署说明: " + prompt.contains("部署"));
System.out.println("估算 Token 占用: ~" + (prompt.length() / 4));
}
@Test
public void testGiantInstructions() throws IOException {
String prompt = reviewAnalysisSkill.buildPromptWithGiantInstructions("还可以吧");
System.out.println("=== 巨型指令文件(反面教材) ===");
System.out.println("指令行数: " + prompt.lines().count());
System.out.println("是否包含安全规则(参数化查询): " + prompt.contains("参数化查询"));
System.out.println("是否包含部署说明: " + prompt.contains("部署"));
System.out.println("估算 Token 占用: ~" + (prompt.length() / 4));
}
@Test
public void testHardConstraintVisibility() throws IOException {
String giant = instructionConfig.buildGiantPrompt("");
String modular = instructionConfig.buildPromptForTask("default", "");
String giantFirst100 = giant.lines().limit(100).reduce("", (a, b) -> a + b);
boolean foundInGiant = giantFirst100.contains("参数化查询");
String modularFirst100 = modular.lines().limit(100).reduce("", (a, b) -> a + b);
boolean foundInModular = modularFirst100.contains("参数化查询");
System.out.println("=== 硬约束可见性测试 ===");
System.out.println("巨型文件前100行是否包含'参数化查询'? " + foundInGiant);
System.out.println("模块化入口前100行是否包含'参数化查询'? " + foundInModular);
}
}
五、测试结果与对比
运行 mvn test -Dtest=InstructionComparisonTest 后,控制台输出如下:
text
=== 模块化指令(正确实践) ===
指令行数: 78
是否包含安全规则(参数化查询): true
是否包含部署说明: false
估算 Token 占用: ~2100
=== 巨型指令文件(反面教材) ===
指令行数: 682
是否包含安全规则(参数化查询): true
是否包含部署说明: true
估算 Token 占用: ~18500
=== 硬约束可见性测试 ===
巨型文件前100行是否包含'参数化查询'? false
模块化入口前100行是否包含'参数化查询'? true

结果分析
| 维度 | 模块化指令 | 巨型指令 | 差异 |
|---|---|---|---|
| 行数 | 78 行 | 682 行 | 减少 88.6% |
| Token 占用 | ~2,100 | ~18,500 | 节省 88.6% |
| 安全规则可见性(前100行) | ✅ 可见 | ❌ 不可见(埋在第300行) | 避免中间迷失 |
| 无关信息(部署、偏好) | ❌ 不包含 | ✅ 包含 | 避免干扰 |
| 矛盾规则 | ❌ 无 | ✅ 存在(Java 17 版本冲突) | 避免混乱 |
结论:模块化指令在保证 Agent 能力的前提下,大幅提升了上下文利用效率、规则可发现性和文档可维护性。
六、API 功能验证(确保模块化未破坏业务功能)
启动应用,调用评价分析接口:
bash
curl -X POST "http://localhost:885/api/harness/review?reviewText=这款手机屏幕清晰,续航时间长,但拍照一般&sessionId=test"
返回正确的结构化 JSON(示例):
json
{
"success": true,
"data": {
"rating": 4,
"sentiment": "positive",
"keyPoints": ["屏幕清晰", "续航时间长", "拍照一般"],
"details": {
"pros": ["屏幕清晰", "续航时间长"],
"cons": ["拍照一般"],
"summary": "整体不错,拍照有待提升"
}
},
"sessionId": "test"
}
说明模块化指令没有影响 Agent 的核心任务执行能力。
七、总结
本示例完整演示了如何运用 Harness Engineering 的原则,将巨型指令文件重构为模块化体系:
- 入口文件仅 80 行,只含项目概览、硬约束和专题链接
- 专题文档按需加载,避免无关信息占用上下文预算
- 硬约束放在顶部,避免"中间迷失"
- 历史教训转为测试用例,避免文档膨胀
- 消除矛盾规则,提升指令质量
这套实践可直接应用于任何 Spring AI Alibaba 项目,让 AI Agent 的"说明书"更加清晰、高效、可持续维护。
附录:巨型指令文件(GIANT_AGENTS.md)完整内容
## 1. 项目概览
本项目是一个基于 Spring Boot 3.2.5 和 Java 17 的后端服务,集成了 Spring AI Alibaba 框架,主要功能包括:
- 联系人信息提取(从非结构化文本中提取姓名、邮箱、电话)
- 商品评价分析(提取评分、情感、关键点、优缺点总结)
- 未来可能扩展更多 AI 能力
项目使用 Maven 作为构建工具,使用 DashScope API 作为大模型后端。
## 2. 技术栈详细版本
- Java: 17.0.10 (Amazon Corretto)
- Spring Boot: 3.2.5
- Spring AI Alibaba: 1.1.2.0
- Maven: 3.9.6
- Jackson: 2.17.2
- SLF4J: 2.0.13
- Logback: 1.4.14
- JUnit: 5.10.2
- Mockito: 5.11.0
注意:以上版本已经过严格测试,请不要随意升级,除非经过团队评审。
## 3. 编码规范(详细版)
### 3.1 命名规范
- 包名:全部小写,用点分隔,如 `com.badao.ai.service`
- 类名:大驼峰 (PascalCase),如 `ContactInfoService`
- 接口名:大驼峰,通常以 `I` 开头或不加前缀,如 `IContactService` 或 `ContactService`
- 方法名:小驼峰 (camelCase),如 `extractContact`
- 常量名:全大写,下划线分隔,如 `MAX_RETRY_COUNT`
- 变量名:小驼峰,有意义,避免单字母(除循环变量 i, j, k)
- 枚举:类名大驼峰,枚举值全大写
### 3.2 代码格式
- 缩进:4 个空格,禁止使用 Tab
- 行宽:120 字符,超长换行
- 大括号:左括号不换行,右括号单独一行
- 空行:类成员之间用空行分隔,方法之间空一行
- import:不使用通配符 `*`,按 java、javax、org、com 顺序排列
### 3.3 注释规范
- 类注释:必须包含作者、日期、简短描述
- 方法注释:必须包含功能描述、参数说明、返回值说明、异常说明
- 字段注释:重要字段必须注释
- 代码内注释:解释复杂逻辑,不要注释显而易见的代码
- 禁止使用 `// TODO` 提交到主分支,必须在 PR 前解决
### 3.4 异常处理
- 不要捕获 `Exception` 或 `Throwable` 除非在顶层
- 使用自定义业务异常,如 `BusinessException`
- 日志记录异常堆栈,使用 `log.error("message", e)`
- 不要在循环中捕获异常
### 3.5 日志规范
- 使用 SLF4J API,不要直接使用 Logback
- 日志级别:ERROR(系统错误)、WARN(可疑操作)、INFO(关键步骤)、DEBUG(调试)、TRACE(详细跟踪)
- 生产环境只输出 INFO 及以上级别
- 禁止在日志中打印敏感信息(密码、Token、身份证号等)
## 4. 项目目录结构(参考)
```
src/main/java/com/badao/ai/
├── config/ # 配置类
├── controller/ # REST 控制器
├── service/ # 业务逻辑
├── model/ # POJO 实体
├── harness/ # Harness 组件(rules, skills, gates, tools)
├── evaluation/ # 评估与反馈
└── util/ # 工具类
```
测试目录:
```
src/test/java/com/badao/ai/
├── unit/ # 单元测试
├── integration/ # 集成测试
└── mock/ # Mock 辅助类
```
## 5. API 设计规范(详细)
### 5.1 URL 设计
- 使用复数名词,如 `/api/contacts`
- 版本号放在路径第一段,如 `/api/v1/contacts`
- 资源嵌套使用,如 `/api/users/{userId}/orders`
### 5.2 HTTP 方法
- GET:查询,幂等
- POST:创建,非幂等
- PUT:全量更新,幂等
- PATCH:部分更新,非幂等
- DELETE:删除,幂等
### 5.3 响应格式
统一使用以下 JSON 结构:
```json
{
"code": 0,
"message": "success",
"data": { ... },
"timestamp": "2026-07-08T12:00:00Z"
}
```
错误码范围:
- 0: 成功
- 1001-1999: 客户端错误(参数、认证、权限)
- 2001-2999: 服务端错误(系统异常、超时)
- 3001-3999: 业务错误(数据不存在、状态冲突)
### 5.4 参数校验
- 使用 Jakarta Validation (`@Valid`, `@NotNull`, `@Size`, etc.)
- 在 Controller 层进行校验,Service 层假定参数已合法
- 自定义校验器:如 `@PhoneNumber`、`@EmailFormat`
### 5.5 分页查询
- 使用 `PageRequest` 和 `Page<T>` 返回
- 参数:`page`(从0开始)、`size`(默认20)、`sort`(如 `name,asc`)
## 6. 数据库操作规范(如适用)
### 6.1 JPA 使用
- 实体类使用 `@Entity`,必须提供无参构造函数
- 主键使用 `@Id` 和 `@GeneratedValue(strategy = GenerationType.IDENTITY)`
- 关联关系使用懒加载 `FetchType.LAZY`
- 禁止使用 `@OneToMany` 的 `CascadeType.ALL`,除非严格评估
### 6.2 SQL 规范
- 使用参数化查询,防止 SQL 注入
- 不使用 `select *`,明确列出字段
- 复杂查询使用 QueryDSL 或 JPA Criteria
- 避免在循环中执行 SQL,使用批量操作
## 7. 安全规范(重要)
### 7.1 身份认证
- 使用 Spring Security 6.x
- JWT Token 有效期 30 分钟,可刷新
- 密码加密使用 BCrypt
### 7.2 敏感数据
- 禁止在日志、响应、异常消息中暴露敏感信息
- 使用 `@JsonIgnore` 序列化忽略
- 数据库字段加密存储
### 7.3 输入验证
- 防止 XSS:对用户输入进行转义
- 防止 CSRF:使用 CSRF Token(非 REST API 通常禁用)
- 文件上传:限制大小、类型,扫描病毒
## 8. 测试规范
### 8.1 单元测试
- 使用 JUnit 5 + Mockito
- 测试覆盖率目标:核心业务逻辑 > 80%
- 测试命名:`methodName_should_expectedBehavior_when_condition`
- 使用 `@ExtendWith(MockitoExtension.class)`
- 对于 Service 层,Mock 所有外部依赖
### 8.2 集成测试
- 使用 `@SpringBootTest`,配合 `@AutoConfigureMockMvc`
- 测试真实数据库(使用 H2 或 Testcontainers)
- 测试环境与生产环境隔离
### 8.3 性能测试
- 关键接口需要 JMeter 或 Gatling 压测
- 响应时间 < 200ms(P95)
## 9. 部署流程(生产环境)
### 9.1 构建
```bash
mvn clean package -DskipTests
```
### 9.2 容器化
- 基础镜像:`openjdk:17-jre-slim`
- 暴露端口 885
- 环境变量:`DASHSCOPE_API_KEY`、`SPRING_PROFILES_ACTIVE`
### 9.3 Kubernetes 部署
- Namespace: `prod`
- Deployment: 2 副本,滚动更新
- Service: ClusterIP,内部访问
- Ingress: 外部域名 `api.example.com`
### 9.4 健康检查
- 使用 Actuator `/actuator/health`
- 就绪探针:`/actuator/health/readiness`
- 存活探针:`/actuator/health/liveness`
## 10. 团队成员偏好(仅供参考)
- 张三:倾向使用 Stream API 而非 for 循环
- 李四:喜欢使用 Lombok 减少样板代码
- 王五:坚持使用 `final` 修饰不可变变量
- 赵六:偏爱构造函数注入,不喜欢 `@Autowired` 字段注入
## 11. 历史问题修复记录
### 11.1 2025-11-15:时区问题
使用 `ZonedDateTime` 替代 `LocalDateTime` 存储 UTC 时间,避免服务器时区影响。
### 11.2 2025-12-03:WebSocket 内存泄漏
在 WebSocket 会话关闭时,必须移除相关监听器,否则导致内存泄漏。
### 11.3 2026-01-07:数据库连接池超时
配置 HikariCP:`connection-timeout=30000`,`maximum-pool-size=10`。
### 11.4 2026-02-14:DashScope API 重试
实现指数退避重试,最多 3 次,避免临时网络故障导致失败。
## 12. 第三方集成注意事项
### 12.1 DashScope API
- 请求超时 10 秒
- 并发限制 5 QPS
- 错误重试需注意 idempotency
### 12.2 Redis 缓存
- 缓存 key 前缀 `app:`
- 默认 TTL 3600 秒
- 使用 `@Cacheable` 注解
### 12.3 消息队列(如使用)
- Topic 命名:`app.{service}.{event}`
- 确保消息可靠性,使用手动 ACK
## 13. 代码评审清单
- [ ] 是否有单元测试?
- [ ] 是否处理所有异常?
- [ ] 日志是否合适?
- [ ] 是否添加了必要的注释?
- [ ] 是否遵循命名规范?
- [ ] 是否考虑了性能?
- [ ] 是否安全?(SQL注入、XSS等)
- [ ] 是否更新了文档?
## 14. 分支管理策略
- main:生产环境,只接受 PR
- develop:开发环境,定期合并 feature 分支
- feature/*:新功能
- hotfix/*:紧急修复
## 15. 发布流程
1. 从 develop 创建 release 分支
2. 更新版本号 `mvn versions:set -DnewVersion=1.2.0`
3. 进行回归测试
4. 合并到 main 并打 tag
5. 部署到生产环境
## 16. 全局硬约束(重要!请务必遵守)
(注意:这些规则很重要,但埋在了第 300 行之后,容易被忽略)
- 所有数据库查询必须使用参数化查询(PreparedStatement),绝对禁止拼接 SQL 字符串。
- 禁止在代码中使用 `System.out.println()` 或 `System.err.println()`,必须使用日志框架。
- 所有敏感信息(API Key、数据库密码、JWT Secret)必须通过环境变量或配置中心注入,严禁硬编码。
- 捕获异常后必须进行适当处理(记录日志、返回错误码),禁止空 catch 块。
- 所有公共方法必须包含 Javadoc,描述功能、参数、返回值和可能抛出的异常。
- Controller 层严格禁止编写任何业务逻辑,必须委托给 Service 层。
- 所有外部 API 调用必须设置超时时间(HTTP 连接超时 5 秒,读取超时 10 秒)。
- 禁止在生产代码中使用 `@Deprecated` 标注的方法或类。
- 所有 Pull Request 必须通过 `mvn clean verify` 构建,确保测试和代码规范检查通过。
- 禁止使用 `eval()` 或类似动态执行代码的机制。
- 日志中绝对禁止打印 Token、密码、身份证号、手机号等个人隐私信息。
- 使用 JPA 时,所有关联关系必须明确 `fetch` 类型,避免 N+1 查询问题。
- REST API 必须正确使用 HTTP 状态码,不能统一返回 200 然后在 body 里区分。
- 文件操作必须使用 try-with-resources 确保资源释放。
- 定时任务必须使用分布式锁(如 ShedLock)防止多实例重复执行。
## 17. 矛盾规则(请注意!)
- 规则 A:所有新代码必须使用 Java 17 新特性(如 Record、Switch 表达式)。
- 规则 B:为了兼容性,禁止使用 Java 17 特性,保持与 Java 11 一致。
- 规则 C:使用 Lombok 简化 POJO。
- 规则 D:禁止使用 Lombok,因为它隐藏了生成的代码,不利于调试。
## 18. 性能优化建议
- 使用 Caffeine 本地缓存频繁查询的数据
- 批量操作时使用 `saveAll()` 而非循环 `save()`
- 对于大 JSON 响应,考虑使用 Streaming 或分页
- 避免在循环中拼接字符串,使用 `StringBuilder`
## 19. 监控与告警
- 使用 Micrometer 暴露指标(请求数、耗时、错误率)
- 集成 Prometheus + Grafana 面板
- 关键指标告警:错误率 > 5%,P95 响应时间 > 1s
## 20. 日志文件配置
- 日志文件路径:`/var/log/app/`
- 滚动策略:按大小(100MB)和按天
- 保留最近 30 天
## 21. 故障排查手册
### 21.1 常见错误码
- 1001:参数校验失败,检查请求参数格式
- 1002:认证失败,检查 Token 是否过期
- 2001:系统内部错误,查看日志获取堆栈
### 21.2 调试技巧
- 使用 `curl -v` 查看请求头
- 使用 `jstack` 分析线程堆栈
- 使用 `jmap` 分析堆内存
## 22. 附录
### 22.1 常用命令
```bash
mvn clean package
mvn spring-boot:run
mvn test -Dtest=ContactServiceTest
```
### 22.2 环境变量清单
- `DASHSCOPE_API_KEY`: DashScope API 密钥(必需)
- `DB_URL`: 数据库连接 URL
- `DB_USERNAME`: 数据库用户名
- `DB_PASSWORD`: 数据库密码
- `REDIS_HOST`: Redis 主机
- `REDIS_PORT`: Redis 端口
### 22.3 相关文档链接
- 内部 Wiki: http://wiki.company.com/ai-service
- Swagger UI: http://localhost:885/swagger-ui.html
## 23. 废弃规则归档(已不再适用)
- ~~2025-08 旧版认证方式~~ 已替换为 JWT
- ~~2025-09 异步处理方案~~ 已改为同步加缓存
## 24. 结尾寄语
请每个开发者在提交代码前仔细阅读以上全部规范,确保高质量交付。若有疑问,及时在团队群内沟通。
```
参考资源:
Happy Hacking! 🚀