[!note] Agent不是银弹,没必要也不应该全用Agent——当任务可以用确定性的类/函数解决时,不应该用 Agent。
核心洞察
传统软件开发:
┌─────────────────────────────────────┐
│ class ResumeParser { │
│ fun parse(file) -> Resume │
│ } │
│ │
│ class MatchEngine { │
│ fun match(jd, resumes) -> [] │
│ } │
│ │
│ // 开发者写死的逻辑 │
│ // 控制流完全确定 │
└─────────────────────────────────────┘
Agent 开发:
┌─────────────────────────────────────┐
│ JD Worker: │
│ "解析 JD,返回结构化 Schema" │
│ │
│ Resume Worker: │
│ "解析简历,返回结构化数据" │
│ │
│ Match Worker: │
│ "计算匹配度,返回评分+理由" │
│ │
│ // 控制流由 Prompt + LLM 决定 │
│ // 存在不确定性 │
└─────────────────────────────────────┘
本质相同
| 维度 | OOP 范式 | Agent 范式 |
|---|---|---|
| 核心抽象 | Class / Object | Agent / Tool |
| 接口定义 | Interface | Tool Definition |
| 依赖注入 | DI Container | Context / Memory |
| 组合模式 | Composite | Orchestrator |
| 职责分离 | SOLID 原则 | Single-purpose Agent |
| 可测试性 | Mock / Stub | 单元测试 Prompt |
| 组装流程 | 依赖图 | Pipeline / DAG |
关键区别
| 区别 | OOP | Agent |
|---|---|---|
| 控制流 | 确定性 (if/else) | LLM 参与决策 (不确定性) |
| 逻辑表达 | 代码 | Prompt + 示例 |
| 运行时 | 编译时确定 | 每次调用可能有差异 |
| 调试方式 | 断点 / 日志 | 检查 Prompt / 输出 |
| 但思维模式要转变 |
OOP 思维:
─────────────────────────────
"我要写一个 MatchEngine 类,
实现 match(jd, resumes) 方法,
算法是用加权评分"
Agent 思维:
─────────────────────────────
"我需要一个 Match Worker Agent,
它能理解 JD 和简历的语义,
评分要可解释,
Prompt 应该怎么写?输出格式是什么?"
AI 开发 = 70% 软件工程 + 20% Prompt 工程 + 10% 新范式适配
├── 软件工程 (不变)
│ ├── 系统设计
│ ├── 数据存储
│ ├── 网络通信
│ ├── 故障恢复
│ └── 监控告警
│
├── Prompt 工程 (新技能)
│ ├── 如何清晰表达任务
│ ├── 如何约束输出格式
│ ├── 如何写 Few-shot 示例
│ └── 如何处理边界情况
│
└── 新范式适配 (新思维)
├── 接受不确定性
├── 设计可容错的 Pipeline
└── 权衡:Agent vs 直接调用
Agent 不是银弹——当任务可以用确定性的类/函数解决时,不应该用 Agent。
就像招聘筛选场景:
- 简历解析 → 可以用 Agent(格式多样,需要理解)
- 向量索引 → 不需要 Agent(确定性的数学运算)
- 批量匹配 → 可以用 Agent(需要推理和解释)
- 结果排序 → 不需要 Agent(确定的比较运算)
总结:Agent 是工具,不是架构。 用对了是杠杆,用错了是负担。