Agent 的成本不在写代码:用上下文交接减少重复阅读

2026-07-24 18 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

AI 编程 Agent 的账,往往不是写了多少行代码,而是读了多少遍代码。Stencil 的 Can Boluk 对 SWE-Bench 基准测试数据进行拆解后发现:在约 1.81 亿个 token、近 200 万次工具调用中,编辑和写入只占 9%,其余 91% 都消耗在读取文件、执行 grep、查看命令输出等活动上。

这改变了优化方向。提高生成速度只能影响成本的一小部分;真正值得优化的是 Agent 如何寻找上下文、如何保存已经确认的事实,以及多个 Agent 之间如何交接工作。相关实践据称可以把成本降低约四成,但具体收益仍取决于仓库规模、任务类型、模型和工具设计。

先把“读代码”拆开看

Agent 的阅读成本通常来自四类重复动作:

  • 不知道入口在哪里,于是递归列目录、反复搜索关键词。
  • 已经看过某个模块,但下一轮又重新读取完整文件。
  • 一个 Agent 调查问题,另一个 Agent 接手时只能从头建立上下文。
  • 命令输出过长,真正有用的几行被淹没,Agent 只好再次执行命令。

这些调用对人类开发者来说可能只是几秒钟,但对长上下文模型来说,每次读取都会增加输入 token,并可能挤占真正需要保留的代码、错误信息和约束条件。

因此,优化目标不应只是“让 Agent 少调用几个工具”,而应是让每次调用都带来新的信息。一个好的上下文交接记录,至少要包含:

  • 已确认的入口文件和调用链。
  • 与问题相关的文件及其职责。
  • 已排除的假设,以及排除它们的证据。
  • 当前复现命令和失败输出的关键行。
  • 尚未验证的假设。
  • 下一步建议,以及不要重复做的搜索。

把探索结果变成可复用的工作包

可以把一次调查看成一个小型“上下文工作包”,而不是一串不可复用的聊天记录。下面是一个适合放进仓库临时目录或 Agent 工作目录的格式:

# .agent/context/task-184.yaml
task: "修复订单取消后库存没有释放的问题"
status: "investigating"
entrypoints:
  - path: "src/orders/cancel_order.py"
    reason: "HTTP handler 调用的业务入口"
  - path: "src/inventory/reservation.py"
    reason: "负责创建和释放库存预留"
confirmed:
  - "cancel_order() 会更新订单状态,但当前调用链没有 release_reservation()"
  - "订单状态写入使用事务,库存释放由独立服务完成"
evidence:
  - command: "rg -n \"release_reservation|cancel_order\" src tests"
    result: "只在 reservation.py 和测试夹具中找到 release_reservation"
  - command: "pytest tests/orders/test_cancel_order.py -q"
    result: "现有测试通过,但没有断言库存预留被释放"
excluded:
  - "不是数据库连接失败:取消接口日志中事务提交成功"
unknowns:
  - "库存释放是否必须与订单状态更新保持同一事务"
next_steps:
  - "阅读 reservation.py 的幂等性实现"
  - "补充取消订单后的库存断言"
avoid_repeating:
  - "不要重新扫描整个 src 目录"
  - "不要重复运行没有过滤条件的 pytest 全量测试"

这个文件不需要记录所有读过的内容,只记录能改变决策的事实。接手任务的 Agent 可以先读工作包,再定向打开两个入口文件,避免从仓库根目录重新开始探索。

如果团队使用编排器,可以把工作包作为阶段之间的显式输入:调查阶段输出 context.yaml,实现阶段读取它,验证阶段只补充新的证据。这样做的价值不只是减少 token,也让错误假设更容易被审查。

用“先索引、后局部读取”替代全量扫描

在大型仓库中,Agent 常见的低效路径是:列出全部文件,读取多个大文件,再通过关键词搜索确认入口。更稳定的方式是维护一份轻量索引,先回答“应该读哪些文件”,再读取局部内容。

下面的 Python 示例演示了一个简单的上下文收集器。它只读取命中的文件,并对每个文件限制输出行数;实际项目中可以把关键词、路径白名单和测试命令替换成任务专属配置。

#!/usr/bin/env python3
from pathlib import Path
import re
import sys

ROOT = Path(sys.argv[1] if len(sys.argv) > 1 else ".")
TERMS = ["release_reservation", "cancel_order", "reservation"]
MAX_MATCHES_PER_FILE = 12
MAX_LINES_PER_FILE = 80

ignored = {".git", "node_modules", "dist", "build", ".venv", "__pycache__"}
pattern = re.compile("|".join(re.escape(term) for term in TERMS), re.IGNORECASE)

for path in ROOT.rglob("*"):
    if not path.is_file() or any(part in ignored for part in path.parts):
        continue
    if path.suffix not in {".py", ".js", ".ts", ".java", ".go", ".rb", ".rs"}:
        continue

    try:
        lines = path.read_text(encoding="utf-8").splitlines()
    except (OSError, UnicodeDecodeError):
        continue

    matches = [i for i, line in enumerate(lines) if pattern.search(line)]
    if not matches:
        continue

    print(f"\n## {path}")
    for index in matches[:MAX_MATCHES_PER_FILE]:
        start = max(0, index - 3)
        end = min(len(lines), index + 4)
        print(f"-- lines {start + 1}-{end} --")
        for number in range(start, end):
            print(f"{number + 1:4}: {lines[number]}")

运行方式:

python3 collect_context.py ./src > context-snippets.txt

这个脚本不是通用代码搜索器,也不能替代人工理解。它的作用是把第一次探索限制在与任务相关的局部窗口中。对于跨语言仓库,还可以加入 go.modpackage.jsonpom.xml 等依赖入口,或者改用现有的语言服务器和代码索引工具。

输出也要有预算

读取代码不是唯一的 token 消耗点,工具输出同样可能制造浪费。测试命令、日志和编译器输出应当优先保留错误摘要、失败测试名和相关堆栈,而不是把完整日志无条件交给模型。

例如,CI 或 Agent 工具可以把测试输出压缩成如下结构:

status: failed
failed_tests:
  - tests/orders/test_cancel_order.py::test_releases_reservation
error_summary: AssertionError: expected released=True, got released=False
relevant_trace:
  src/orders/cancel_order.py:48
  src/inventory/reservation.py:91
rerun: pytest tests/orders/test_cancel_order.py::test_releases_reservation -q

这类摘要必须保留可复现命令和定位信息,否则压缩会损失调试能力。一个实用的规则是:可以删除噪声,但不能删除证据;可以缩短堆栈,但不能删除文件、行号和失败断言。

怎么判断优化是否有效

不要只看模型回答是否变快。建议在 Agent 编排层记录至少四项指标:

  • 每个任务的输入 token、输出 token 和缓存命中量。
  • read_file、搜索、目录遍历、测试等工具调用次数。
  • 重复读取的文件比例。
  • 从任务开始到首次有效修改、最终测试通过的时间。

可以先选一批相似任务做基线,再加入上下文工作包、路径索引和输出摘要,比较相同成功标准下的成本。成功标准应包含测试通过或补丁被人工接受,不能只比较 token 数,因为过度压缩上下文可能导致 Agent 漏掉关键约束。

落地清单

  1. 为调查、实现、验证阶段定义统一的上下文交接文件。
  2. 记录入口、证据、已排除假设和下一步,不记录无关阅读历史。
  3. 对搜索和文件读取设置路径、文件类型、行数和输出大小限制。
  4. 把长日志转换为带文件、行号、错误摘要和复现命令的结构化结果。
  5. 统计重复读取率与成功率,确认节省 token 没有换来更多返工。
  6. 对高风险改动保留完整原始输出,摘要只作为模型输入优化,不作为唯一审计记录。

SWE-Bench 数据揭示的是一个容易被忽略的事实:Agent 的主要工作量是建立和重建上下文。只要把探索结果变成可交接的资产,把全量阅读改成有边界的定向读取,再对工具输出设置预算,成本优化就从“少写几行提示词”进入了工程系统设计的范围。


相关推荐