提示工程即架构:别让AI陷入过度工程化的陷阱
🎯 AI编程的终极悖论:你越聪明,代码越复杂
在AI编程中,有一个反直觉的现象:你的需求描述越清晰,AI生成的代码往往越复杂。
为什么?因为AI不理解依赖维护的成本。当它考虑到所有可能的边界条件时,就会陷入过度工程化的陷阱,生成"完美但无法维护"的代码。
在希语低喃心绪笔记开发中,我总结出了一套"提示工程即架构"的方法论,核心原则就是:先定接口,再谈实现。
🧠 AI的过度工程化倾向:为什么AI总是想太多?
1. 边界条件考虑过多的问题
AI的思维模式:
用户需求:做一个用户登录功能
AI的过度思考:
- 万一用户忘记密码怎么办?→ 需要密码重置功能
- 万一密码太简单怎么办?→ 需要密码强度验证
- 万一有人暴力破解怎么办?→ 需要登录失败锁定
- 万一网络不稳定怎么办?→ 需要重试机制
- 万一服务器宕机怎么办?→ 需要熔断器
- 万一数据泄露怎么办?→ 需要数据加密
- 万一...
结果: 一个简单的登录功能,变成了包含10个子模块的复杂系统。
现实是: 你的小程序可能只有10个用户,根本不需要这些"万一会"。
2. AI不理解维护成本
AI的设计逻辑:
- ✅ 功能完整性高
- ✅ 错误处理全面
- ✅ 扩展性良好
- ❌ 维护成本极高
- ❌ 代码复杂度爆炸
- ❌ 学习成本巨大
AI的盲点:
AI认为:多加一个验证函数 → 多10行代码 → 没什么大不了
实际:多加一个验证函数 → 需要测试 → 需要文档 → 需要维护 →
团队成员需要学习 → 未来可能需要修改 → 技术债务累积
3. 真实的过度工程化案例
案例1:简单的消息存储功能
你的需求: "存储用户发送的消息"
AI的过度设计:
// AI生成的复杂消息系统
interface MessageSystem {
// 消息本体
message: {
id: string
content: string
type: 'text' | 'image' | 'voice' | 'video'
metadata: {
device: string
location: GeoLocation
sentiment: SentimentAnalysis
priority: 'low' | 'medium' | 'high' | 'urgent'
}
}
// 消息状态
status: {
delivered: boolean
read: boolean
archived: boolean
deleted: boolean
retry_count: number
last_error?: ErrorInfo
}
// 版本控制
version: {
current: number
history: MessageVersion[]
conflict_resolution: ConflictStrategy
}
// 安全相关
security: {
encrypted: boolean
checksum: string
access_level: AccessLevel
audit_trail: AuditEntry[]
}
}
// 对应的复杂实现(500+行代码)
class ComplexMessageService {
constructor(
private messageRepository: MessageRepository,
private encryptionService: EncryptionService,
private auditService: AuditService,
private sentimentService: SentimentService,
private geoService: GeoService,
private versionService: VersionService
) {}
async createMessage(input: CreateMessageInput): Promise<MessageSystem> {
// 50行的验证逻辑
// 30行的加密处理
// 40行的情感分析
// 20行的地理位置处理
// 60行的版本控制
// 80行的审计记录
// ...
}
}
实际需要的简单设计:
// 简单直接的设计
interface Message {
id: string
sessionId: string
content: string
createdAt: Date
}
class MessageService {
constructor(
private messageRepository: MessageRepository,
private logger: Logger
) {}
async createMessage(sessionId: string, content: string): Promise<Message> {
try {
const message = await this.messageRepository.create({
sessionId,
content,
createdAt: new Date()
})
this.logger.info(`Message created: ${message.id}`)
return message
} catch (error) {
this.logger.exception('Failed to create message', error)
throw error
}
}
}
对比结果:
- 代码行数:500行 vs 30行
- 依赖数量:6个 vs 2个
- 维护成本:极高 vs 极低
- 功能满足度:100% vs 100%
🏗️ 接口优先原则:先定契约,再谈实现
为什么必须先定接口?
传统开发的问题:
开发者A:我先实现用户功能
开发者B:我先实现消息功能
2周后:发现两个模块的接口不兼容,需要重构
接口优先的优势:
产品经理:我们先定义好所有接口
- 用户服务接口
- 消息服务接口
- AI服务接口
然后分别实现,确保兼容性
接口设计的黄金法则
法则1:接口即契约
- 一旦定义,不可随意更改
- 所有实现必须严格遵循
- 接口变更需要团队评审
法则2:简单至上
- 接口数量尽可能少
- 参数数量尽可能少
- 返回值结构尽可能简单
法则3:职责单一
- 每个接口只做一件事
- 避免复合操作接口
- 保持接口的原子性
希语低喃心绪笔记的接口设计实践
第一步:定义核心接口
// types/interfaces.ts - 核心接口定义
// 用户相关接口
interface UserService {
create(userData: CreateUserInput): Promise<User>
findById(id: string): Promise<User>
findByWxCode(wxCode: string): Promise<User>
}
// 会话相关接口
interface SessionService {
create(userId: string): Promise<Session>
findById(id: string): Promise<Session>
updateStatus(id: string, status: SessionStatus): Promise<void>
}
// 消息相关接口
interface MessageService {
create(sessionId: string, content: string): Promise<Message>
findBySessionId(sessionId: string): Promise<Message[]>
}
// AI服务相关接口
interface LLMService {
generateSuggestion(sessionData: SessionData): Promise<string>
}
第二步:基于接口设计提示词
// 提示词模板:基于接口约束AI实现
const promptTemplate = `
请基于以下接口定义实现对应的服务类:
接口定义:
${interfaceDefinition}
约束条件:
1. 严格遵循接口签名,不可更改参数和返回值
2. 使用TypeScript严格模式,禁止any类型
3. 函数实现不超过50行
4. 错误处理使用logger.exception + throw
5. 不添加接口未定义的功能
技术栈:
- 数据库:Prisma + PostgreSQL
- 日志:Winston
- 验证:简单的if判断,不使用额外库
请实现上述接口。
`
第三步:用接口约束AI行为
// AI生成的实现必须符合接口约束
export class UserServiceImpl implements UserService {
constructor(
private userRepository: UserRepository,
private logger: Logger
) {}
// 严格遵循接口定义
async create(userData: CreateUserInput): Promise<User> {
try {
const user = await this.userRepository.create(userData)
this.logger.info(`User created: ${user.id}`)
return user
} catch (error) {
this.logger.exception('Failed to create user', error)
throw error
}
}
// 不可添加接口未定义的方法
// ❌ async validateComplexRules(): void { ... }
// ❌ async sendWelcomeEmail(): void { ... }
// ❌ async createBackup(): void { ... }
}
🎯 提示工程的架构化方法论
1. 三层提示词架构
第一层:需求层(What)
明确要做什么,而不是怎么做
示例:
❌ 不要说:实现一个企业级的用户认证系统
✅ 要说:实现用户注册和登录功能
❌ 不要说:设计一个高性能的消息队列
✅ 要说:实现消息的发送和接收
第二层:约束层(How Not)
明确边界和限制,防止AI过度发挥
示例:
约束条件:
- 单个函数不超过50行
- 不使用第三方库(除数据库ORM)
- 错误直接抛出,不提供降级方案
- 接口不可更改,实现必须严格遵循
- 不考虑未来扩展,专注当前需求
第三层:实现层(How)
在约束范围内让AI自由发挥
示例:
技术要求:
- 使用TypeScript严格模式
- 使用Prisma进行数据库操作
- 使用Winston进行日志记录
- 遵循Repository模式
2. 防过度工程化的提示词模板
模板1:功能约束模板
我要实现 [功能名称],请严格遵循以下要求:
## 接口定义(不可更改)
[具体的TypeScript接口]
## 业务规则(必须遵守)
1. 只实现接口定义的功能,不添加额外功能
2. 不考虑未来可能的扩展需求
3. 不处理极端的边界条件
4. 错误直接抛出,不提供降级方案
## 技术约束(强制执行)
1. 单个函数不超过50行
2. 不使用any类型
3. 错误处理:logger.exception + throw
4. 不添加新的依赖包
5. 保持代码简洁直接
## 实现要求
请基于上述接口和约束,实现对应的类。
模板2:接口优先模板
我们需要实现 [模块名称],请先设计接口,再实现功能:
## 第一步:接口设计
请设计满足以下需求的接口:
- 功能描述:[具体功能描述]
- 输入参数:[参数列表]
- 输出要求:[返回值描述]
- 边界条件:[需要考虑的正常情况]
## 第二步:实现约束
接口设计完成后,我会确认接口定义,然后请你:
1. 严格遵循接口实现
2. 不添加接口未定义的功能
3. 保持代码简洁
4. 专注核心业务逻辑
请先设计接口,不要直接实现代码。
模板3:KISS约束模板
实现 [功能名称],必须遵循KISS原则:
## 禁止做的事情
❌ 不考虑未来扩展
❌ 不处理极端边界条件
❌ 不使用复杂的设计模式
❌ 不添加额外的配置和选项
❌ 不提供降级方案
❌ 不使用第三方验证库
## 必须做的事情
✅ 直接实现核心功能
✅ 使用简单的if判断验证
✅ 错误直接抛出
✅ 保持代码可读性
✅ 专注当前需求
## 参考标准
如果代码超过200行,说明过度设计了
如果函数超过10个,说明过度设计了
如果需要配置文件,说明过度设计了
请实现最简单的版本。
3. 依赖维护成本的提示词策略
策略1:依赖数量限制
在实现过程中,请遵循依赖最小化原则:
## 允许的依赖
- 数据库ORM:Prisma
- 日志:Winston
- 测试框架:Jest
- Node.js内置模块
## 禁止的依赖
- 任何工具库(lodash等)
- 任何验证库(yup、zod等)
- 任何HTTP客户端库(axios等)
- 任何状态管理库
## 依赖选择原则
能用原生实现的,绝不用第三方库
能用简单方案解决的,绝不用复杂方案
如果需要额外的依赖,必须提供充分的理由。
策略2:复杂度控制
控制代码复杂度的具体要求:
## 函数复杂度
- 单个函数不超过50行
- 嵌套层级不超过3层
- 单个函数参数不超过5个
## 类复杂度
- 单个类不超过300行
- 方法数量不超过10个
- 依赖注入不超过5个
## 文件复杂度
- 单个文件不超过500行
- 导入的模块不超过10个
- 导出的接口不超过5个
如果超出上述限制,请重新设计简化方案。
🎪 实战案例:用接口优先驯服AI
案例1:用户系统的接口优先实现
错误的做法:直接让AI实现
你:帮我实现一个用户系统
AI(过度设计):
- 用户注册、登录、密码重置
- 权限管理、角色分配
- 操作审计、登录日志
- 邮箱验证、手机验证
- 账户锁定、安全策略
- 用户画像、行为分析
- ...
结果:800行代码,10个依赖,过度复杂
正确的做法:接口优先
你:我们先定义用户系统的接口
你:我需要以下功能:
1. 用户注册(用户名、密码)
2. 用户登录(用户名、密码)
3. 获取用户信息(用户ID)
请先设计TypeScript接口,不急着实现代码
AI(接口设计):
interface UserService {
register(username: string, password: string): Promise<User>
login(username: string, password: string): Promise<User>
getUserById(userId: string): Promise<User>
}
你:接口很好,现在请基于这个接口实现功能,约束条件:
- 单个函数不超过30行
- 不使用任何验证库
- 错误直接抛出
- 不添加额外功能
AI(简洁实现):
export class UserServiceImpl implements UserService {
constructor(
private userRepository: UserRepository,
private logger: Logger
) {}
async register(username: string, password: string): Promise<User> {
if (!username || !password) {
throw new Error('Username and password required')
}
try {
const user = await this.userRepository.create({ username, password })
this.logger.info(`User registered: ${user.id}`)
return user
} catch (error) {
this.logger.exception('Registration failed', error)
throw error
}
}
async login(username: string, password: string): Promise<User> {
const user = await this.userRepository.findByUsername(username)
if (!user || user.password !== password) {
throw new Error('Invalid credentials')
}
return user
}
async getUserById(userId: string): Promise<User> {
const user = await this.userRepository.findById(userId)
if (!user) {
throw new Error('User not found')
}
return user
}
}
结果:80行代码,2个依赖,简洁清晰
案例2:AI集成功能的约束实现
问题:AI倾向于过度设计LLM集成
AI的过度设计倾向:
// AI想要的复杂LLM集成
class ComplexLLMService {
constructor() {
this.promptManager = new PromptManager()
this.conversationManager = new ConversationManager()
this.responseParser = new ResponseParser()
this.cacheManager = new CacheManager()
this.rateLimiter = new RateLimiter()
this.errorHandler = new ErrorHandler()
this.metricsCollector = new MetricsCollector()
}
async generateResponse(input: UserInput): Promise<AIResponse> {
// 100行的复杂逻辑
// 包含重试、缓存、限流、监控等
}
}
接口优先的约束实现:
你:我们需要集成AI对话功能,请先设计接口
AI:interface LLMService {
generateSuggestion(sessionMessages: Message[]): Promise<string>
}
你:很好,现在请实现这个接口,约束条件:
- 只调用OpenAI API,不处理其他服务商
- 不做缓存,直接调用API
- 不做重试,失败直接抛错
- 函数不超过30行
AI(约束实现):
export class LLMServiceImpl implements LLMService {
constructor(
private openaiClient: OpenAIClient,
private logger: Logger
) {}
async generateSuggestion(sessionMessages: Message[]): Promise<string> {
try {
const messages = sessionMessages.map(msg => ({
role: msg.isUser ? 'user' : 'assistant',
content: msg.content
}))
const response = await this.openaiClient.chat.completions.create({
model: 'gpt-4',
messages: messages,
temperature: 0.7,
max_tokens: 1000
})
return response.choices[0].message.content
} catch (error) {
this.logger.exception('Failed to generate suggestion', error)
throw error
}
}
}
案例3:防止AI添加不必要的功能
常见场景:AI喜欢"贴心"地添加额外功能
你:实现消息保存功能
AI(可能添加的额外功能):
- ✅ 消息保存
- ❌ 消息搜索(你没有要求)
- ❌ 消息分类(你没有要求)
- ❌ 消息统计(你没有要求)
- ❌ 消息导出(你没有要求)
- ❌ 消息加密(你没有要求)
预防性提示词:
请实现消息保存功能,严格遵循以下要求:
## 只实现的功能
- 保存消息到数据库
- 按会话ID查询消息
## 绝对不要添加的功能
❌ 消息搜索
❌ 消息分类
❌ 消息统计
❌ 消息导出
❌ 消息加密
❌ 任何其他未明确要求的功能
## 接口定义
interface MessageService {
save(sessionId: string, content: string): Promise<Message>
getBySessionId(sessionId: string): Promise<Message[]>
}
请严格按照上述接口实现,不要添加任何额外功能。
🌟 总结:提示工程即架构的核心原则
三大核心认知
1. AI不理解维护成本
- AI追求功能完整性,人类追求可维护性
- AI认为"多一个功能没什么",人类知道"多一个功能多一份维护负担"
- 必须用提示词为AI设定维护成本的约束
2. 接口即架构,约束即质量
- 先定义接口,再谈实现
- 用接口约束AI的行为边界
- 用约束条件控制代码复杂度
3. 简单是最高级的复杂
- 能用10行解决的,绝不用100行
- 能用1个接口的,绝不用3个接口
- 能直接实现的,绝不绕弯子
实践检查清单
提示词设计检查:
- 是否明确定义了接口?
- 是否设定了明确的约束条件?
- 是否限制了功能范围?
- 是否禁止了不必要的功能?
- 是否控制了复杂度?
AI输出检查:
- 是否严格遵循接口定义?
- 是否有接口未定义的额外功能?
- 代码行数是否合理?
- 依赖数量是否最小化?
- 是否过度考虑边界条件?
架构质量检查:
- 接口设计是否简洁清晰?
- 实现是否直接简单?
- 维护成本是否可控?
- 新人理解成本是否低?
- 扩展是否在合理范围内?
记住这些金句
关于AI的本质:
- "AI很聪明,但没有维护代码的责任"
- "AI追求完美,但现实需要够用就好"
- "AI不知道每一个功能背后的人力成本"
关于架构思维:
- "接口即契约,约定即质量"
- "约束不是限制,而是质量的保障"
- "简单不是简陋,而是最高级的复杂"
关于实践原则:
- "先谈做什么,再谈怎么做"
- "先定接口,再写实现"
- "先约束边界,再发挥创意"
最终记住:好的提示工程,就是让AI在正确的轨道上发挥才华,而不是让它自由发挥地制造复杂。
下一篇预告: 《AI编程的认知陷阱:为什么你觉得简单,AI却觉得复杂》
微信搜索"四哥还会聊AI",看这些提示工程技巧如何在实际项目中创造价值