Skip to content

Agent 开发3 (4月8日报)

约 841 字大约 3 分钟

日报Agent

2026-04-08

  • Learn Claude Code 的教材更新了,学习思路貌似有点不一样了,打算重新看一遍,笔记的话就添加到之前写的了
  • 打算重制一下 agent 相关的日报内容了,把知识拿出来做条理一点

上下文压缩

"上下文总会满, 要有办法腾地方" -- 三层压缩策略, 换来无限会话。

问题

上下文窗口是有限的。读一个 1000 行的文件就吃掉 ~4000 token; 读 30 个文件、跑 20 条命令, 轻松突破 100k token。不压缩, 智能体根本没法在大项目里干活

解决方案

三层压缩, 激进程度递增:

					    Every turn:
					+------------------+
					| Tool call result |
					+------------------+
					        |
					        v
		[Layer 1: micro_compact]        (silent, every turn)
		  Replace tool_result > 3 turns old
		  with "[Previous: used {tool_name}]"
			        |
			        v
			[Check: tokens > 50000?]
			   |               |
			   no              yes
			   |               |
			   v               v
			continue    [Layer 2: auto_compact]
			              Save transcript to .transcripts/
			              LLM summarizes conversation.
			              Replace all messages with [summary].
			                    |
			                    v
		            [Layer 3: compact tool]
		              Model calls compact explicitly.
		              Same summarization as auto_compact.

工作原理

第一层 -- micro_compact: 每次 LLM 调用前, 将旧的 tool result 替换为占位符
def micro_compact(messages: list) -> list:
    tool_results = []
    # enumerate() 同时把索引 `i` 和里面的内容 `msg` 给你
    for i, msg in enumerate(messages):
    # 确认是否为 user 的消息,且内容是不是 list
        if msg["role"] == "user" and isinstance(msg.get("content"), list):
            for j, part in enumerate(msg["content"]):
    # 判断此代码块类型是否为 'tool_result'
                if isinstance(part, dict) and part.get("type") == "tool_result":
    # 将坐标和对象本身存起来
                    tool_results.append((i, j, part))
    # [:-KEEP_RECENT] 意思是:排除掉最后 1 个(最新的一次),只把前面的旧记录拿出来
    if len(tool_results) <= KEEP_RECENT:
        return messages
    # 处理除了最近 KEEP_RECENT 条以外的所有旧记录
    # 最后面是 py 的切片语法 等价于 list[0 : len(list) - KEEP_RECENT]
    # 意思是 取“前面所有元素”,但去掉最后 KEEP_RECENT 个
    for _, _, part in tool_results[:-KEEP_RECENT]:
    # 判断:如果这个旧记录的字数超过了 100 个字
        if len(part.get("content", "")) > 100:
    # 将其总结为一句话 “Previous: used {tool_name}”
            part["content"] = f"[Previous: used {tool_name}]"
    return messages

假设我们的 message 是这样的,里面有几条很长的工具调用日志

[
  // i=0
  {"role": "user", "content": [ 
      // j=0
      {"type": "tool_result", "content": "第1次代码运行日志:几万字..."} 
  ]},
  // i=1
  {"role": "user", "content": [
      // j=0
      {"type": "tool_result", "content": "第2次代码运行日志:几万字..."} 
  ]}
]

我们假设 KEEP_RECENT = 1(也就是只保留最近 1 次的详细记录,其他的全折叠)

第二层 -- auto_compact: token 超过阈值时, 保存完整对话到磁盘, 让 LLM 做摘要
def auto_compact(messages: list) -> list:
    # 备份
    # Save transcript for recovery
    transcript_path = TRANSCRIPT_DIR / f"transcript_{int(time.time())}.jsonl"
    with open(transcript_path, "w") as f:
    # 一行行往文件写入 json 
    # default 默认 str 防止报错
        for msg in messages:
            f.write(json.dumps(msg, default=str) + "\n")
    # LLM summarizes
    response = client.messages.create(
        model=MODEL,
        messages=[{"role": "user", "content":
            "Summarize this conversation for continuity..."
            + json.dumps(messages, default=str)[:80000]}],
        max_tokens=2000,
    )
    # 返回总结好的文字
    return [
        {"role": "user", "content": f"[Compressed]\n\n{response.content[0].text}"},
        {"role": "assistant", "content": "Understood. Continuing."},
    ]
第三层 - manual compactcompact 工具按需触发同样的摘要机制
第四层

循环整合三层

def agent_loop(messages: list):
    while True:
        micro_compact(messages)                        # Layer 1
        # estima_tokens 是估算 token 使用的函数
        # 如果大于阈值则进行
        if estimate_tokens(messages) > THRESHOLD:
            messages[:] = auto_compact(messages)       # Layer 2
        response = client.messages.create(...)
        # ... tool execution ...
        if manual_compact:
            messages[:] = auto_compact(messages)       # Layer 3

完整历史通过 transcript 保存在磁盘上。信息没有真正丢失, 只是移出了活跃上下文