四哥还会聊AI

80后突围:当程序员不再是第一个梦想,AI成了第四个梦想

第7篇-提示工程即架构-别让AI陷入过度工程化的陷阱

提示工程即架构:别让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",看这些提示工程技巧如何在实际项目中创造价值

微信搜索"汐屿笔记",看这些架构原则如何在实际项目中应用

← 返回首页