Context Engineering vs Harness Engineering vs Prompt Engineering
这三个概念构成了 LLM 应用开发的三层技术栈,从微观到宏观、从单次交互到系统架构层层递进。
一、Prompt Engineering(提示词工程)
概念
设计、优化输入给 LLM 的文本指令,以获取最佳输出。这是最基础、最微观的层次。
核心特点
- 作用范围:单次 LLM 调用
- 输入输出:文本 → 文本
- 优化目标:输出质量、准确性、格式
- 调试方式:人工调整提示词,观察输出变化
典型技术
javascript
// 1. 角色扮演
const prompt = `你是一位资深 Python 工程师,请审查以下代码...`;
// 2. 思维链(Chain of Thought)
const prompt = `请逐步推理:1. 首先... 2. 然后...`;
// 3. Few-shot 示例
const prompt = `示例1: ... → ...\n示例2: ... → ...\n现在处理:`;
// 4. 输出格式约束
const prompt = `以 JSON 格式输出:{"field1": "", "field2": ""}`;
局限性
- ❌ 无状态管理(每次调用独立)
- ❌ 无法处理超长上下文
- ❌ 不能调用外部工具
- ❌ 无错误恢复机制
二、Harness Engineering(脚手架工程)
概念
构建 LLM 应用的外部系统和工具链,包括工具定义、状态管理、错误处理、成本控制等。这是中间层,负责"编排"。
核心特点
- 作用范围:整个 Agent/应用会话
- 主要组件:工具、状态、缓存、降级、监控
- 优化目标:系统可靠性、成本控制、用户体验
- 调试方式:日志、链路追踪、A/B 测试
核心组件
javascript
class Harness {
// 1. 工具定义(让 LLM 调用外部能力)
tools = {
read_file: { handler: async (path) => {...} },
execute_bash: { handler: async (cmd) => {...} },
search_web: { handler: async (query) => {...} }
};
// 2. 状态管理(上下文窗口控制)
manageContext() {
if (tokenCount > 0.75 * maxTokens) {
this.compress(); // 压缩历史
}
}
// 3. 缓存策略(降低成本)
setupCache() {
// 标记稳定前缀,复用 KV Cache
this.cachePrefix = [systemPrompt, toolDefinitions];
}
// 4. 错误处理与降级
async callWithFallback(prompt) {
try {
return await callModel('opus', prompt);
} catch {
return await callModel('sonnet', prompt); // 降级
}
}
// 5. 成本控制
estimateCost(messages) {
return countTokens(messages) * 0.015 / 1000;
}
}
解决的核心问题
| 问题 | Harness 解决方案 |
|---|---|
| 上下文溢出 | 压缩、摘要、滑动窗口 |
| 工具调用失败 | 重试、降级、超时控制 |
| 成本失控 | Token 计数、缓存、模型路由 |
| 响应不稳定 | 多次采样、投票机制 |
| 安全风险 | 工具白名单、沙箱执行 |
三、Context Engineering(上下文工程)
概念
设计、管理、优化 LLM 在整个任务生命周期中能看到的所有信息。这是最宏观的层次,关注"信息生态"。
核心特点
- 作用范围:跨会话、跨 Agent、长期任务
- 主要对象:信息选择、压缩、记忆、共享
- 优化目标:信息密度、长期一致性、知识复用
- 调试方式:信息检索评估、记忆召回率
上下文的层次结构
markdown
Level 4: 长期记忆(跨会话)
├─ 向量数据库
├─ 知识图谱
└─ 用户偏好
Level 3: 工作区上下文(项目级)
├─ CLAUDE.md / README
├─ 项目结构
└─ 近期修改
Level 2: 会话上下文(本次对话)
├─ 历史消息
├─ 已执行操作
└─ 中间决策
Level 1: 瞬时上下文(当前消息)
├─ 用户最新输入
└─ 临时工具结果
关键技术
javascript
// 1. 信息选择(只加载相关内容)
class ContextSelector {
async select(query, codebase) {
// 从 10 万行代码中检索最相关的 5 个文件
const relevant = await vectorDB.search(query, { topK: 5 });
// 每个文件只提取相关函数(而非全部)
return relevant.map(f => this.extractRelevantSnippets(f));
}
}
// 2. 信息压缩(有损但保留关键结构)
class ContextCompressor {
compress(code, level) {
if (level === 1) return this.extractSignatures(code); // 只剩函数名
if (level === 2) return this.extractSignaturesAndComments(code);
return this.simplifyImplementation(code); // 保留逻辑但简化
}
}
// 3. 长期记忆(跨会话存储)
class LongTermMemory {
async memorize(decision) {
await vectorDB.add(decision.embedding, decision.summary);
await knowledgeGraph.createRelation(decision.from, decision.to);
}
async recall(question) {
return await vectorDB.search(question); // 语义检索
}
}
三者的核心区别
对比表格
| 维度 | Prompt Engineering | Harness Engineering | Context Engineering |
|---|---|---|---|
| 作用范围 | 单次 LLM 调用 | Agent 会话生命周期 | 跨会话、跨 Agent |
| 时间尺度 | 毫秒级 | 分钟-小时级 | 天-周级 |
| 主要对象 | 文本指令 | 工具、状态、错误 | 信息选择、压缩、记忆 |
| 优化目标 | 输出质量 | 系统可靠性、成本 | 信息密度、长期一致性 |
| 典型问题 | "怎么让模型理解意图?" | "Agent卡住了怎么办?" | "如何记住上周的决定?" |
| 失败后果 | 回答质量差 | 系统崩溃、成本失控 | 重复工作、决策矛盾 |
| 调试方式 | 人工调整提示词 | 日志、链路追踪 | 检索评估、召回率 |
| 依赖关系 | 独立 | 依赖好的 Prompt | 依赖 Harness 提供存储 |
一句话总结
- Prompt Engineering :怎么说(微观表达)
- Harness Engineering :怎么做(中观系统)
- Context Engineering :知道什么(宏观信息)
协作关系图
arduino
用户需求
↓
┌─────────────────────────────────────────┐
│ Context Engineering(宏观) │
│ "应该加载哪些历史决策?" │
│ "如何压缩10万行代码到2000 token?" │
│ "如何记住上周的架构决策?" │
└─────────────┬───────────────────────────┘
↓ 提供精选/压缩后的信息
┌─────────────────────────────────────────┐
│ Harness Engineering(中观) │
│ "如何定义工具让Agent调用?" │
│ "上下文快满了,要不要压缩?" │
│ "工具调用失败,重试还是降级?" │
└─────────────┬───────────────────────────┘
↓ 构建请求、管理状态
┌─────────────────────────────────────────┐
│ Prompt Engineering(微观) │
│ "系统提示词怎么写?" │
│ "这个工具描述怎么让模型理解?" │
│ "如何用few-shot示例引导输出格式?" │
└─────────────┬───────────────────────────┘
↓ 构造最终 Prompt
LLM 调用
实际案例对比
场景:构建一个代码审查 Agent
javascript
// ========== 1. Prompt Engineering ==========
// 关注:如何让模型输出高质量审查意见
const reviewPrompt = `
你是一位资深代码审查专家。审查以下代码:
规则:
1. 安全性:检查 SQL 注入、XSS
2. 性能:检查 N+1 查询
3. 可维护性:检查命名、注释
输出格式:
- 🔴 严重问题
- 🟡 建议改进
代码:
${code}
`;
// ========== 2. Harness Engineering ==========
// 关注:如何让 Agent 稳定运行、控制成本
class CodeReviewHarness {
async review(prUrl) {
// 工具定义
const tools = {
get_diff: () => fetchDiff(prUrl),
run_linter: (code) => eslint(code),
get_file_history: (path) => gitLog(path)
};
// 状态管理:避免上下文溢出
let context = { reviewedFiles: [], comments: [] };
// 错误处理:工具调用失败时降级
try {
const diff = await tools.get_diff();
for (const file of diff.files) {
const issues = await this.reviewFile(file);
context.comments.push(...issues);
}
} catch (error) {
// 降级:只做静态分析
return this.staticAnalysisOnly();
}
// 成本控制:预估 token 消耗
if (this.estimateCost(context) > 0.5) {
await this.compressHistory();
}
return context;
}
}
// ========== 3. Context Engineering ==========
// 关注:应该加载哪些信息?如何压缩?如何记忆?
class CodeReviewContext {
async buildContext(pr) {
return {
// 当前 PR 的变更
currentDiff: await this.getDiff(pr),
// 相关历史决策(从向量库检索)
pastDecisions: await this.vectorDB.search(
pr.description,
{ filter: { type: 'architectural', limit: 5 } }
),
// 项目规范(从 CLAUDE.md 加载)
projectRules: await this.loadClaudeMd(),
// 压缩策略:只保留关键信息
compressedHistory: this.compress(pastDecisions, {
keepSignatures: true,
dropImplementation: true
})
};
}
}
发展路径
javascript
const learningPath = {
beginner: "只做 Prompt Engineering",
intermediate: "Prompt + Harness(工具、状态、错误处理)",
advanced: "完整三层(重点是 Context Engineering)"
};
// 投入产出比
const roi = {
promptEngineering: "低成本、高回报(适合简单任务)",
harnessEngineering: "中成本、中回报(适合生产环境)",
contextEngineering: "高成本、高回报(适合复杂长期任务)"
};
总结
核心区别
| 层次 | 核心问题 | 关键产出 |
|---|---|---|
| Prompt | 如何表达意图? | 高质量的单次回复 |
| Harness | 如何构建可靠系统? | 稳定运行的 Agent |
| Context | 如何管理信息生态? | 长期一致的知识体系 |
现代 LLM 应用的竞争壁垒
yaml
2023 年:Prompt Engineering 是核心技能
2024 年:Harness Engineering 成为标配
2025 年:Context Engineering 是真正的护城河
原因 :当基础能力(提示词、工具调用)被模型内置后,真正区分产品优劣的是如何管理信息------知道保留什么、丢弃什么、从哪里检索、如何压缩。这也是 Claude Code、Cursor 等产品的核心竞争力所在。