第十章:生产级 Harness 构建
从实验到生产的转变涉及一系列关键的工程决策:如何设计可扩展的系统提示词?如何通过插件实现功能扩展?如何在成本和性能间找到平衡?
本章聚焦于使 Harness 能够在生产环境中可靠、高效地运行。不同于第 8-9 章的架构设计,第十章关注的是系统级的优化、配置管理和可维护性。
核心问题
- 系统提示词如何设计才能既强大又高效?模块化提示词架构如何应对缓存压力?
- 如何通过插件系统实现功能扩展而不影响核心稳定性?
- 如何在模型选择、缓存策略、并发度等方面优化性能?
- 如何管理配置使得开发、测试、生产环境有不同的行为?
- 如何使用特性门控实现灰度发布和 A/B 测试?
本章的定位
- 承前:第 8-9 章建立了任务编排和工具生态
- 立本:第十章讨论如何在生产中部署和运维
- 启后:第 11 章讨论如何在故障时保持可靠性
学习建议
按顺序学习会更好理解整体逻辑,但 10.3 和 10.4 可以根据优先级顺序调整。
1. 系统提示词工程
本节深入讲解系统提示词的设计原理、模块化架构、提示词前缀缓存机制、实现方法和最佳实践。系统提示词是智能体的核心 DNA,合理的模块化设计不仅能降低 Token 消耗,还能提高系统的可维护性和一致性。
1.1 为什么系统提示词很重要
系统提示词(System Prompt)是智能体行为的 DNA。它定义了:
- 智能体的身份和角色
- 它应该如何思考
- 它能和不能做什么
- 它如何与用户交互
在生产环境中,系统提示词还影响:
- Token消耗:提示词本身就是 Token
- 缓存效率:重复的提示词可以被缓存
- 一致性:确保行为可预测
- 可维护性:修改提示词 vs 修改代码
1.2 模块化系统提示词架构
Claude Code(和类似的智能体框架)通常会采用模块化系统提示词架构。具体规模和缓存边界会随产品版本、模型上下文窗口和运行界面变化,因此工程设计上不应把某个字节数硬编码为最佳实践,而应关注静态模块、动态上下文和缓存边界的分层。
1.2.1 架构原理
模块化系统提示词的架构原理如下:
graph TD
A["<b>系统提示词</b><br/>(模块化架构)"]
A --> B["<b>1. Core Identity</b><br/>身份定义<br/>≈ 2KB<br/>(不可缓存)"]
A --> C["<b>2. Capabilities</b><br/>工具使用<br/>≈ 20KB<br/>(可缓存)"]
A --> D["<b>3. Domain Knowledge</b><br/>领域知识<br/>≈ 25KB<br/>(可缓存)"]
A --> E["<b>4. Context-Specific</b><br/>上下文特定<br/>≈ 7KB<br/>(不可缓存)"]
F["缓存边界"] -.-> C
F -.-> D
G["动态边界"] -.-> E
style B fill:#ffcccc
style C fill:#ccffcc
style D fill:#ccffcc
style E fill:#ffcccc
style F fill:#fff0f5
style G fill:#fff0f5
图 10-1:模块化系统提示词架构
1.2.2 模块详解
Module 1: Core Identity(核心身份,必须包含)
你是一个AI Assistant,能够:
1. 理解并执行复杂任务
2. 使用提供的工具来完成工作
3. 提供清晰的推理和解释
你的原则:
- 始终以用户最佳利益为出发点
- 对不确定的地方明确说明
- 拒绝可能有害的请求
Module 2: Capabilities(能力说明,约 20KB)
这个模块详细说明智能体可以做什么:
### Tool Usage
你可以使用以下工具:
1. web_search - 搜索网络信息
使用格式:
{
"type": "web_search",
"query": "search query"
}
返回:搜索结果列表
2. code_execution - 执行代码
支持语言:Python, JavaScript, etc.
限制:最多10秒执行时间
...
#### 1.2.3 Thinking Process
遵循以下思维步骤:
1. 理解任务
2. 分解成子问题
3. 为每个子问题制定策略
4. 按顺序执行
5. 验证结果
Module 3: Domain Knowledge(领域知识,约 25KB)
包含 Agent 应该知道的背景信息:
### Software Engineering Best Practices
1. Code Quality
- 遵循PEP8标准(Python)
- 包含错误处理
- 添加必要的文档
2. Testing
- 编写单元测试
- 测试覆盖率 >80%
3. Performance
- 考虑时间复杂度
- 缓存重复计算
### Common Patterns
常见的代码模式和反模式...
Module 4: Context-Specific(上下文特定,约 7KB)
每次请求动态生成的部分:
### Current Task
用户任务:{{ task_description }}
用户提供的背景:
{{ user_context }}
特定约束:
{{ constraints }}
1.2.3 提示词前缀缓存
模块化系统提示词的一个核心工程收益是 提示词前缀缓存 (Prompt Prefix Caching)。这一机制利用了 LLM 推理过程中的 KV 缓存(Key-Value Cache)特性:当多次请求共享相同的提示词前缀时,模型可以直接复用已计算的注意力键值对,跳过重复的前向传播计算。
Sebastian Raschka 在对 Coding Agent 架构的分析中精辟地指出:成熟的 Harness 维护一个“稳定的提示词前缀”(stable prompt prefix),其中包含不变的元素(指令、工具描述、工作区摘要),而只更新动态部分(记忆、对话记录、用户请求)。这种设计的效果显著—— 在典型的多轮对话场景中,前缀缓存可节省 50-90% 的输入 Token 计算成本。
前缀缓存的工程要点:
- 前缀稳定性:可缓存的前缀必须按字节完全一致。即使一个空格的差异也会导致缓存失效。因此,模块 1-3(身份、能力、领域知识)应该在会话期间保持完全不变
- 缓存边界设计:将稳定的系统规则、工具说明和工作流指令放在可缓存区域,将用户当前输入、最近状态和高波动上下文放在动态区域。具体边界应由模型上下文窗口、供应商缓存能力和实际命中率决定。
- 缓存感知的提示词排列:将高频变化的内容(如当前任务描述、最近的执行结果)放在提示词的末尾,将稳定内容(身份、工具定义、领域知识)放在前缀。顺序决定缓存命中率
- 缓存预热:首次请求时没有缓存,需要完整计算。后续请求如果前缀匹配,延迟和成本都会大幅下降。对于需要快速响应的场景,可以在会话开始时发送一个预热请求
| class PrefixCacheAwarePrompt:
"""缓存感知的提示词组装"""
def __init__(self, static_modules: list, cache_boundary_tokens: int = 47000):
self.static_prefix = "\n\n".join(static_modules)
self.cache_boundary = cache_boundary_tokens
def assemble(self, dynamic_context: str) -> dict:
"""组装提示词,标记缓存边界"""
return {
"system": [
{
"type": "text",
"text": self.static_prefix,
"cache_control": {"type": "ephemeral"} # 标记可缓存
},
{
"type": "text",
"text": dynamic_context # 动态部分不缓存
}
]
}
|
前缀缓存与应用层缓存(如 Schema 缓存、结果缓存)是不同层次的优化:前者发生在 LLM 推理引擎内部,后者发生在 Harness 应用层。两者结合可以实现最大化的成本和延迟优化。
1.3 实现模块化系统提示词
模块化系统提示词的实现涉及四个主要部分:数据结构定义、提示词构建逻辑、Token 预算优化、和使用示例。
1.3.1 数据结构与初始化
定义提示词模块、段落的数据结构,以及管理器的初始化:
| from typing import Dict, Any, List
from dataclasses import dataclass
from enum import Enum
import hashlib
class PromptModule(Enum):
"""提示词模块类型"""
CORE_IDENTITY = "core_identity"
CAPABILITIES = "capabilities"
DOMAIN_KNOWLEDGE = "domain_knowledge"
CONTEXT_SPECIFIC = "context_specific"
@dataclass
class PromptSection:
"""提示词段落"""
module: PromptModule
content: str
cacheable: bool
priority: int
class ModularSystemPrompt:
"""模块化系统提示词管理器"""
MAX_TOKENS = 54000
CACHE_BOUNDARY = 47000
def __init__(self):
self.sections: Dict[PromptModule, List[PromptSection]] = {
module: [] for module in PromptModule
}
self.cache_metadata: Dict[str, str] = {}
def add_section(
self,
module: PromptModule,
content: str,
cacheable: bool = True,
priority: int = 0,
) -> None:
"""添加提示词段落"""
section = PromptSection(
module=module,
content=content,
cacheable=cacheable,
priority=priority,
)
self.sections[module].append(section)
if cacheable:
content_hash = hashlib.sha256(content.encode()).hexdigest()
self.cache_metadata[content_hash] = content
|
设计说明:分离静态信息(身份、能力、知识)和动态信息(上下文特定)的结构,使前缀缓存能够有效工作。模块枚举类型确保只有四种预定义的模块类型。
1.3.2 提示词构建与组装逻辑
这部分展示如何将多个段落组织成最终的系统提示词,并管理缓存:
| def build_prompt(
self,
context_variables: Dict[str, str] = None,
max_tokens: int = MAX_TOKENS,
) -> tuple[str, Dict[str, Any]]:
"""构建最终系统提示词"""
context_variables = context_variables or {}
ordered_modules = [
PromptModule.CORE_IDENTITY,
PromptModule.CAPABILITIES,
PromptModule.DOMAIN_KNOWLEDGE,
PromptModule.CONTEXT_SPECIFIC,
]
cacheable_content = []
non_cacheable_content = []
metadata = {
"static_tokens": 0,
"dynamic_tokens": 0,
"caching_enabled": False,
"module_breakdown": {},
}
for module in ordered_modules:
sections = self.sections.get(module, [])
sections.sort(key=lambda s: s.priority, reverse=True)
for section in sections:
token_estimate = len(section.content) * 0.4
content = section.content
if module == PromptModule.CONTEXT_SPECIFIC:
for var_name, var_value in context_variables.items():
content = content.replace(f"{{{{{var_name}}}}}", var_value)
if section.cacheable:
cacheable_content.append(content)
metadata["static_tokens"] += int(token_estimate)
else:
non_cacheable_content.append(content)
metadata["dynamic_tokens"] += int(token_estimate)
metadata["module_breakdown"][module.value] = {
"sections": len(sections),
"tokens": int(token_estimate),
}
static_part = "\n\n".join(cacheable_content)
dynamic_part = "\n\n".join(non_cacheable_content)
total_tokens = metadata["static_tokens"] + metadata["dynamic_tokens"]
if total_tokens > max_tokens:
print(f"[Warning] Prompt exceeds {max_tokens} tokens")
metadata["caching_enabled"] = len(static_part) > 1024
if metadata["caching_enabled"]:
static_hash = hashlib.sha256(static_part.encode()).hexdigest()
metadata["cache_key"] = static_hash
metadata["cache_boundary"] = len(static_part)
final_prompt = static_part + "\n\n" + dynamic_part
return final_prompt, metadata
|
设计说明:build_prompt 方法按照指定的模块顺序构建提示词,确保缓存边界的稳定性。动态上下文变量只在 context_specific 模块中替换,保持其他部分字节一致。
1.3.3 Token 预算优化
在 Token 限制下优化提示词内容,以及获取缓存统计信息:
| def optimize_for_token_budget(
self,
token_budget: int,
context_variables: Dict[str, str] = None,
) -> str:
"""在Token预算限制下优化提示词"""
sections_by_priority = []
for module in PromptModule:
for section in self.sections.get(module, []):
token_estimate = len(section.content) * 0.4
sections_by_priority.append((section, token_estimate, module))
sections_by_priority.sort(
key=lambda x: (x[2].value, x[0].priority),
reverse=True
)
selected_sections = []
total_tokens = 0
for section, tokens, module in sections_by_priority:
if total_tokens + tokens <= token_budget:
selected_sections.append(section)
total_tokens += tokens
elif section.priority >= 10:
selected_sections.append(section)
total_tokens += tokens
content = "\n\n".join(s.content for s in selected_sections)
return content
def get_cache_stats(self) -> Dict[str, Any]:
"""获取缓存统计信息"""
total_cacheable = sum(
len(s.content)
for module_sections in self.sections.values()
for s in module_sections
if s.cacheable
)
total_non_cacheable = sum(
len(s.content)
for module_sections in self.sections.values()
for s in module_sections
if not s.cacheable
)
return {
"total_modules": len(self.sections),
"total_sections": sum(len(sections) for sections in self.sections.values()),
"cacheable_tokens": int(total_cacheable * 0.4),
"non_cacheable_tokens": int(total_non_cacheable * 0.4),
"cache_potential_savings": int(total_cacheable * 0.4 * 0.35),
}
|
设计说明:optimize_for_token_budget 实现了一个贪心算法,优先保留高优先级的内容。当总 Token 超过预算时,较低优先级的内容会被删除,但高优先级内容(priority >= 10)永远保留。
1.3.4 使用示例与主函数
最后展示如何创建和使用模块化系统提示词:
| def create_default_system_prompt() -> ModularSystemPrompt:
"""创建默认的系统提示词"""
prompt = ModularSystemPrompt()
# Core Identity
prompt.add_section(
PromptModule.CORE_IDENTITY,
"""你是Claude,一个由Anthropic创建的AI助手。
你的核心职责是:
1. 准确、有帮助地回答用户问题
2. 承认你的限制和不确定性
3. 拒绝可能有害的请求
4. 遵循所有安全指南""",
cacheable=False,
priority=100,
)
# Capabilities
prompt.add_section(
PromptModule.CAPABILITIES,
"""## 工具使用指南
你可以使用以下工具:
1. web_search - 搜索网络信息
2. code_execution - 执行代码
3. file_operations - 文件操作
### 思维过程
遵循以下思维框架:
1. 分解问题
2. 制定策略
3. 执行步骤
4. 验证结果""",
cacheable=True,
priority=50,
)
# Domain Knowledge
prompt.add_section(
PromptModule.DOMAIN_KNOWLEDGE,
"""## 编程最佳实践
1. 代码质量
- 遵循编码标准
- 包含错误处理
- 添加文档
2. 性能
- 考虑复杂度
- 缓存计算结果
### 常见算法
...""",
cacheable=True,
priority=40,
)
# Context-Specific
prompt.add_section(
PromptModule.CONTEXT_SPECIFIC,
"""## 当前任务信息
用户任务:{{task}}
背景信息:
{{context}}
特定约束:
{{constraints}}""",
cacheable=False,
priority=30,
)
return prompt
if __name__ == "__main__":
# 创建系统提示词
system_prompt = create_default_system_prompt()
# 构建提示词
context = {
"task": "写一个Python排序算法",
"context": "学生练习项目",
"constraints": "不使用内置排序函数",
}
final_prompt, metadata = system_prompt.build_prompt(context)
print("=== 最终提示词 ===")
print(final_prompt[:500] + "...\n")
print("=== 元数据 ===")
print(f"静态Token: {metadata['static_tokens']}")
print(f"动态Token: {metadata['dynamic_tokens']}")
print(f"缓存启用: {metadata['caching_enabled']}")
print("\n=== 缓存统计 ===")
stats = system_prompt.get_cache_stats()
print(f"可缓存Token: {stats['cacheable_tokens']}")
print(f"潜在节省: {stats['cache_potential_savings']}Token")
|
使用说明:这个示例展示了完整的工作流:(1) 创建 ModularSystemPrompt 实例;(2) 添加四个模块的段落;(3) 调用 build_prompt 构建最终提示词;(4) 查看缓存统计信息。实际应用中,可以针对不同的场景(如代码生成、内容创作等)预定义不同的 ModularSystemPrompt 配置。
1.4 系统提示词的最佳实践
- 模块化:按功能分解而不是按规模
- 优先级:高优先级内容永不删除
- 可缓存性:区分静态和动态内容
- 版本控制:提示词变更应该被跟踪
- 测试:评估提示词变更的影响
1.5 OpenClaw 的系统提示词组装
OpenClaw 采用类似但更复杂的方式:
| system_prompt:
template: "base_template.jinja2"
modules:
- name: "identity"
source: "modules/identity.txt"
cacheable: false
priority: 100
- name: "tools"
source: "modules/tools_{{ tools_version }}.txt"
cacheable: true
priority: 80
variables:
tools_version: "{{ environment.tools_api_version }}"
- name: "skills"
source: "modules/skills.txt"
cacheable: true
dynamic_loading: true # 根据注册的skills动态包含
- name: "context"
source: "inline"
content: "{{ task_context }}"
cacheable: false
priority: 50
assembly:
method: "priority_based"
token_limit: 54000
cache_boundary: 47000
compression: "none"
|
1.6 本小节小结
系统提示词是智能体的核心,应该:
- 模块化以便维护和复用
- 明确区分可缓存和不可缓存部分
- 在 Token 效率和功能丰富间找平衡
- 定期审视和优化
在生产环境中,这种模块化的方法可以:
- 显著减少 Token 消耗(通过缓存)
- 提高系统的一致性和可维护性
- 支持快速的 A/B 测试和灰度发布
1.6.1 开源社区实践参考
GitHub 开源项目中的 CLAUDE.md 和 AGENTS.md 模式提供了具体的参考实现。例如,claude-code-system-prompts 展示了模块化系统提示词的完整架构,包括 core identity、capabilities、domain knowledge 等模块的分离与组织;claude-code-action 中的 CLAUDE.md 示例展示了如何在实际项目中文档化系统的能力和约束。这些实践为 Harness 框架中的模块化设计提供了验证。
下一节将讨论如何通过插件系统实现功能扩展。