Files
2026-07-13 21:36:37 +08:00

7.5 KiB
Raw Permalink Blame History

上下文组件:技术参考

本文档提供代理系统中每个上下文组件的详细技术参考。

系统提示词工程

章节结构

将系统提示词组织成具有清晰边界的独立章节。推荐的结构如下:

<BACKGROUND_INFORMATION>
关于领域、用户偏好或项目特定细节的上下文
</BACKGROUND_INFORMATION>

<INSTRUCTIONS>
核心行为准则和任务指令
</INSTRUCTIONS>

<TOOL_GUIDANCE>
何时以及如何使用可用工具
</TOOL_GUIDANCE>

<OUTPUT_DESCRIPTION>
预期的输出格式和质量标准
</OUTPUT_DESCRIPTION>

这种结构使代理能够快速定位相关信息,并在高级实现中支持选择性上下文加载。

抽象层级校准

指令的"抽象层级"指的是抽象的层次。请参考以下示例:

过低(脆弱):

如果用户询问定价,请查看 docs/pricing.md 中的定价表。
如果表格显示的是美元,则使用 config/exchange_rates.json 中的
汇率转换为欧元。如果用户位于欧盟,则按 config/vat_rates.json
中适用的税率添加增值税。使用货币符号、两位小数以及关于增值税的
说明来格式化回复。

过高(模糊):

帮助用户解决定价问题。要乐于助人且准确。

最优(启发式驱动):

对于定价咨询:
1. 从 docs/pricing.md 获取当前费率
2. 应用用户所在地调整(参见 config/location_defaults.json
3. 使用适当的货币和税务考虑进行格式化

优先使用精确数字而非估算值。当费率不可用时,
明确说明,而不是进行推测。

最优的抽象层级在提供明确步骤的同时,允许在执行过程中保持灵活性。

工具定义规范

Schema 结构

每个工具应定义如下内容:

{
    "name": "tool_function_name",
    "description": "清晰说明工具的用途及使用时机",
    "parameters": {
        "type": "object",
        "properties": {
            "param_name": {
                "type": "string",
                "description": "该参数控制的内容",
                "default": "reasonable_default_value"
            }
        },
        "required": ["param_name"]
    },
    "returns": {
        "type": "object",
        "description": "工具的返回值及其结构"
    }
}

描述工程

工具描述应回答以下问题:该工具做什么、何时使用它、以及它产生什么。应包含使用上下文、示例和边界情况。

较弱的描述:

搜索数据库以获取客户信息。

较强的描述:

通过 ID 或邮箱检索客户信息。

使用时机:
- 用户询问特定客户的详细信息、历史记录或状态时
- 用户提供了客户标识符并需要相关信息时

返回客户对象,包含:
- 基本信息(姓名、邮箱、账户状态)
- 订单历史摘要
- 支持工单数量

如果未找到客户则返回 null。如果数据库不可达则返回错误。

检索文档管理

标识符设计

设计能够传达含义并支持高效检索的标识符:

较差的标识符:

  • data/file1.json
  • ref/ref.md
  • 2024/q3/report

较强的标识符:

  • customer_pricing_rates.json
  • engineering_onboarding_checklist.md
  • 2024_q3_revenue_report.pdf

即使没有搜索工具,较强的标识符也能让代理定位到相关文件。

文档分块策略

对于大型文档,应策略性地分块以保持语义连贯性:

# 语义分块的伪代码
def chunk_document(content):
    """在自然的语义边界处拆分文档。"""
    boundaries = find_section_headers(content)
    boundaries += find_paragraph_breaks(content)
    boundaries += find_logical_breaks(content)
    
    chunks = []
    for i in range(len(boundaries) - 1):
        chunk = content[boundaries[i]:boundaries[i+1]]
        if len(chunk) > MIN_CHUNK_SIZE and len(chunk) < MAX_CHUNK_SIZE:
            chunks.append(chunk)
    
    return chunks

避免使用会在句子中间或概念中间截断的任意字符长度限制。

消息历史管理

Turn 表示

结构化消息历史以保留关键信息:

{
    "role": "user" | "assistant" | "tool",
    "content": "消息文本",
    "reasoning": "可选的思维链",
    "tool_calls": [如果 role  "assistant" 则为列表],
    "tool_output": "如果 role 为 "tool" 则为输出",
    "summary": "如果对话较长则为紧凑摘要"
}

摘要注入模式

对于长对话,按间隔注入摘要:

def inject_summaries(messages, summary_interval=20):
    """按固定间隔注入摘要以保留上下文。"""
    summarized = []
    for i, msg in enumerate(messages):
        summarized.append(msg)
        if i > 0 and i % summary_interval == 0:
            summary = generate_summary(summarized[-summary_interval:])
            summarized.append({
                "role": "system",
                "content": f"对话摘要:{summary}",
                "is_summary": True
            })
    return summarized

工具输出优化

响应格式

提供响应格式选项以控制 token 用量:

def get_customer_response_format():
    return {
        "format": "concise | detailed",
        "fields": ["id", "name", "email", "status", "history_summary"]
    }

简洁格式仅返回必要字段;详细格式返回完整对象。

观察结果屏蔽

对于冗长的工具输出,可考虑使用屏蔽模式:

def mask_observation(output, max_length=500):
    """用紧凑引用替换长观察结果。"""
    if len(output) <= max_length:
        return output
    
    reference_id = store_observation(output)
    return f"[之前的观察结果已省略。完整内容存储于引用 {reference_id}]"

这样在减少 token 用量的同时保留了信息访问能力。

上下文预算估算

Token 计数近似

在规划时,对于英文文本,可按大约每 4 个字符约等于 1 个 token 进行估算:

1000 个单词 ≈ 7500 个字符 ≈ 1800-2000 个 token

这是一个粗略估算;实际分词因模型和内容类型而异。

上下文预算分配

在不同组件之间分配上下文预算:

组件 典型范围 说明
系统提示词 500-2000 tokens 整个会话中保持稳定
工具定义 每个工具 100-500 tokens 随工具数量增加而增长
检索文档 可变 通常是最大的消费者
消息历史 可变 随对话进行而增长
工具输出 可变 可能占据大部分上下文

在开发过程中监控实际使用情况,以建立基线分配。

渐进式披露实现

技能激活模式

def activate_skill_context(skill_name, task_description):
    """当任务匹配技能描述时加载技能上下文。"""
    skill_metadata = load_all_skill_metadata()
    
    relevant_skills = []
    for skill in skill_metadata:
        if skill_matches_task(skill, task_description):
            relevant_skills.append(skill)
    
    # 仅加载最相关技能的完整内容
    for skill in relevant_skills[:MAX_CONCURRENT_SKILLS]:
        skill_context = load_skill_content(skill)
        inject_into_context(skill_context)

引用加载模式

def get_reference(file_reference):
    """仅在明确需要时加载引用文件。"""
    if not file_reference.is_loaded:
        file_reference.content = read_file(file_reference.path)
        file_reference.is_loaded = True
    return file_reference.content

这种模式确保文件只加载一次并在整个会话中缓存。