Day 3:上下文工程 — API 消息结构 + Agent 核心循环 + KV Cache

目标

从 API 层面搞清楚:每次调用 LLM 时,上下文到底长什么样,以及为什么"前缀不能动、末尾可以追加"这条铁律如此重要


核心知识点

1. 上下文 = 消息列表

从 API 视角看,每次调用 LLM 时的上下文就是一个消息列表(messages),列表中每条消息都有一个角色(role)

角色 谁产生的 做什么
system 开发者写的 Agent 的"岗位说明书"——身份、规则、约束。通常只有一条,放在最前面
user 终端用户 用户的输入请求
assistant 模型生成的 之前的回复,包括文本 + 工具调用请求。被放回消息列表让模型"记住"自己说过什么
tool Agent 框架 工具执行后的结果,通过 tool_call_id 关联对应的调用

此外,工具定义(tools) 作为请求的独立字段(不是消息),告诉模型有哪些工具可用。

关键事实:每次调用都是无状态的。 模型不会"记住"上一次的对话,Agent 框架必须每次都把完整历史送回去。

2. Agent 核心循环:一个 while 循环

书中给了一段最简的 Python 实现,核心逻辑只有一个循环 + 一个判断

messages = [
    {"role": "system", "content": "You are a helpful assistant..."},
    {"role": "user", "content": "What's the current time and weather?"},
]

while True:
    response = client.chat.completions.create(
        model="...", messages=messages, tools=tools
    )
    assistant_message = response.choices[0].message
    messages.append(assistant_message)  # 追加模型回复

    if not assistant_message.tool_calls:
        print(assistant_message.content)  # 无工具调用 = 任务完成
        break

    for tool_call in assistant_message.tool_calls:
        result = execute_tool(tool_call.function.name, tool_call.function.arguments)
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": result,
        })
    # 回到循环顶部,带着更新后的 messages 再次调用模型

5 个步骤

  1. 模型推理 → 2. 判断是否有工具调用 → 3. 执行工具 → 4. 结果追加到上下文 → 5. 无工具调用则退出

消息列表的增长过程:

初始: [system, user]
第1轮后: [system, user, assistant(tool_calls), tool, tool]    ← 模型决定调2个工具
第2轮后: [system, user, assistant(tool_calls), tool, tool, assistant(content)]  ← 最终回复

Agent 框架的核心工作就是管理这个 messages 列表——所有上下文工程技术,本质上都是在优化这个列表的内容和结构。

3. 上下文的构成:静态前缀 + 轨迹

把 API 结构和 Day 1 学的"五个组成部分"对应起来:

┌─────────────────────────────────────────┐
│  静态前缀(不变,可缓存)                    │
│  ├─ system 消息(系统提示词)                │
│  └─ tools 字段(工具定义)                   │
├─────────────────────────────────────────┤
│  轨迹(动态增长,随交互累积)                  │
│  ├─ user 消息(用户输入 + RAG 检索的外部知识)  │
│  ├─ assistant 消息(回复 + 思考 + 工具调用)    │
│  └─ tool 消息(工具执行结果)                  │
└─────────────────────────────────────────┘

理解了这个结构,就能理解后面所有上下文工程技术的出发点:前面不能动(缓存),后面可以压缩(管理长度)


4. KV Cache:为什么前缀不能动

这是今天技术密度最高的部分。作者专门提示:可以跳过原理推导,只需记住三条核心结论。

直觉:做菜的比喻

大模型在处理上下文时,会把前面已经处理过的内容缓存起来,下次只需要处理新增的部分。就像做菜——如果前几步完全一样(同样的食材、同样的刀工),你可以直接从上次切好的地方继续;但如果前面任何一步变了(换了一种食材),后面所有步骤都得重来。

没有缓存时的问题

Agent 在第 6 轮对话时,上下文累积了 2000 个 token。没有缓存的情况下,模型每生成一个新 token 都要重新计算这 2000 个 token 的中间结果——相当于第 6 轮要像第 1 轮那样从头算,而且前缀更长,代价比第 1 轮大得多。计算量随上下文长度平方级增长

KV Cache 怎么解决

把已算过的 K(键)和 V(值)向量缓存起来,新 token 只需计算自身的 K/V,然后与缓存中的一起完成注意力计算。

为什么改一个字就全废了

大模型由几十到上百层 Transformer 堆叠。各层串联:第 1 层的输出喂给第 2 层,层层向下传递。如果修改了第 1 个 token(比如系统提示词改了一个字),第 1 层的输出就变了,第 2 层的输入随之改变——所有层的缓存都必须重算

一个真实故事

某团队的客服 Agent 每天处理 10 万次对话。某天工程师为了让 Agent "知道"当前时间,在系统提示词里加了一行 Current time: {{now}}。第二天监控告警:所有对话的首 token 延迟从 0.5 秒涨到 3-5 秒,月度推理账单几乎翻了一倍。

原因:那一行时间戳让系统提示词每次都不同,KV Cache 在每次请求都完全失效。

三条核心结论(必记!)
# 结论 做什么
1 系统提示词和工具定义一旦确定就不要改 哪怕多一个空格,都会导致缓存全部失效,延迟成倍增加
2 动态信息永远追加到末尾 时间戳、用户状态等变化内容,作为新消息追加到对话末尾
3 使用标准 API 格式,不要自行拼接消息 结构化消息会被 Chat Template 翻译成模型训练时见过的固定 token 序列
常见错误模式
错误模式 问题 正确做法
动态系统提示词(嵌入时间戳) 每次请求前缀都变,缓存全废 时间信息追加到末尾
动态用户配置(每次更新余额) 破坏缓存 用专门的状态管理机制
工具定义动态排序 改变顺序导致缓存失效 保持固定顺序
滑动窗口历史 破坏前缀一致性 + 丢失关键工具结果 用压缩策略代替
文本格式化(USER:… ASSISTANT:…) 偏离训练格式,削弱模型能力 用标准 role-content 格式
KV Cache vs Prompt Cache

两个层级的缓存,容易混淆:

KV Cache Prompt Cache
层级 模型内部(单次推理) API 服务层(跨请求)
作用 加速单次请求内 token 生成 减少跨请求的重复计算成本
原理 缓存已算的 K/V 向量 前缀相同则复用之前的 KV Cache
经济影响 影响延迟 直接影响 API 计费

两者都要求前缀稳定,但 Prompt Cache 的经济影响更大——它直接影响你的 API 账单。

思考练习

  1. 画出你的 Agent 消息列表结构:假设你要做一个"帮用户查股票行情"的 Agent,写出:

    • system 消息应该包含什么?
    • 需要哪些工具?工具定义怎么写?
    • 用户问"帮我查下 601567 的实时价格",前两轮的消息列表会长什么样?
  2. 判断对错:以下做法哪些会破坏 KV Cache?为什么?

    • (a) 在系统提示词中写 当前时间: {datetime.now()}
    • (b) 每轮对话末尾追加一条工具调用计数消息
    • © 根据用户输入动态选择 3 个工具放入 tools 字段
    • (d) 把 tool 结果的 JSON 格式改为 USER: {result} 纯文本

自测题

  1. API 消息有哪四种角色?各自由谁产生?
  2. Agent 核心循环的 5 个步骤是什么?什么时候循环结束?
  3. KV Cache 的三条核心结论是什么?
  4. 为什么修改对话历史中间的消息会导致性能下降?(从 Transformer 层级串联的角度解释)
  5. KV Cache 和 Prompt Cache 有什么区别?哪个对 API 计费影响更大?
  6. 滑动窗口对话历史有什么两个严重问题?
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐