Agent 难做的往往不是调用模型,而是把上下文管理、工具执行、错误恢复和停止条件组合到恰到好处。Claude Code、Codex 这类产品已经替开发者完成了这层工程;轮到自建 Agent 时,那种“开箱即用”的顺滑感却很容易消失。Strands 的思路,是把组装好的通用 Agent harness 开源出来,让开发者用接近一行代码的入口连接不同模型,同时保留定制空间。来源给出的结果是:在相同模型下,Token 消耗降低了 28%。
Harness 才是 Agent 的工程主体
一次普通模型调用很简单:发送消息,等待回复。但 Agent 需要持续运行一个循环:
- 整理系统提示、用户任务和历史状态;
- 让模型决定是否调用工具;
- 校验参数并执行工具;
- 把结果放回上下文;
- 判断任务是否完成、失败或应当重试。
真正影响稳定性和成本的,通常是这个循环,而不只是模型本身。手工实现时,常见问题包括重复注入工具说明、把完整日志不断塞回上下文、失败后无条件重试,以及模型已经得到答案却仍继续规划。
一个成熟的 harness 会把这些策略集中管理。Strands 的价值不只是“少写几行代码”,而是提供一套已经组合好的运行边界。所谓“一行接任意模型”,更适合理解为:上层 Agent 循环保持不变,模型差异由适配层消化,而不是所有模型天然拥有完全一致的工具调用和消息格式。
28% Token 降幅应该怎样理解
在相同模型下减少 Token,通常意味着优化发生在编排层,例如压缩历史记录、避免重复提示、筛选工具返回值,或者更早终止无效循环。来源没有给出完整测试条件,因此 28% 应视为特定评测中的报告结果,而不是每个项目都能直接获得的固定折扣。
评估时至少要同时观察四项指标:
- 任务成功率:Token 少了,但任务不能做错;
- 输入与输出 Token:两者价格和优化方式可能不同;
- 工具调用次数:Token 下降可能伴随更多外部 API 请求;
- 端到端延迟:更短的上下文不一定代表更少的执行轮次。
此外,还要固定模型版本、温度、工具集合、最大步数和测试任务。否则,两个 harness 的结果没有可比性。
用自己的任务集验证节省幅度
不要只用一道演示题验证 Agent。可以从真实业务中抽取 30~100 个脱敏任务,让旧 harness 和 Strands 分别运行,并把结果写成 JSONL。每行采用下面的统一结构:
{"task_id":"ticket-001","success":true,"prompt_tokens":1240,"completion_tokens":180,"tool_calls":2,"latency_ms":3120}
下面的脚本只依赖 Python 标准库,可以直接比较两组结果。将它保存为 compare_harness.py:
#!/usr/bin/env python3
import json
import statistics
import sys
from pathlib import Path
def load(path):
rows = {}
for line in Path(path).read_text(encoding='utf-8').splitlines():
if not line.strip():
continue
row = json.loads(line)
rows[row['task_id']] = row
return rows
def summarize(rows):
values = list(rows.values())
return {
'tasks': len(values),
'success_rate': sum(bool(x['success']) for x in values) / len(values),
'tokens': sum(x['prompt_tokens'] + x['completion_tokens'] for x in values),
'tool_calls': sum(x.get('tool_calls', 0) for x in values),
'median_latency_ms': statistics.median(x['latency_ms'] for x in values),
}
def pct_change(old, new):
return (new - old) / old * 100 if old else 0.0
if len(sys.argv) != 3:
raise SystemExit('Usage: python compare_harness.py baseline.jsonl strands.jsonl')
baseline = load(sys.argv[1])
candidate = load(sys.argv[2])
common = baseline.keys() & candidate.keys()
if not common:
raise SystemExit('No matching task_id values found')
base_summary = summarize({k: baseline[k] for k in common})
new_summary = summarize({k: candidate[k] for k in common})
print(f'Matched tasks: {len(common)}')
print(f'Baseline success: {base_summary["success_rate"]:.1%}')
print(f'Candidate success: {new_summary["success_rate"]:.1%}')
print(f'Baseline tokens: {base_summary["tokens"]}')
print(f'Candidate tokens: {new_summary["tokens"]}')
print(f'Token change: {pct_change(base_summary["tokens"], new_summary["tokens"]):+.1f}%')
print(f'Tool-call change: {pct_change(base_summary["tool_calls"], new_summary["tool_calls"]):+.1f}%')
print(
'Median latency change: '
f'{pct_change(base_summary["median_latency_ms"], new_summary["median_latency_ms"]):+.1f}%'
)
运行方式:
python compare_harness.py baseline.jsonl strands.jsonl
如果 Token 降低 28%,但成功率下降、工具调用翻倍或延迟明显增加,就不能直接判定新方案更优。对于带副作用的工具,例如付款、删库或发布操作,还应在评测环境中使用 mock,避免 Agent 重放真实动作。
接入时保留模型和工具边界
虽然 Strands 强调通用接入,生产项目仍应把模型、工具和业务状态隔离开。可以采用下面的项目边界;这是一种可实践的组织方式,并非 Strands 官方目录约定:
agent-app/
├── agent.py # 创建并运行 harness
├── model_adapter.py # 模型鉴权、超时和响应转换
├── tools.py # 工具定义与参数校验
├── policies.py # 最大步数、重试和危险操作审批
└── evals/
├── tasks.jsonl
└── compare_harness.py
这样做的好处是,替换模型时不必改工具,替换 harness 时也不必重写业务 API。工具层还应执行严格的 schema 校验,并给写操作增加幂等键、权限检查和人工审批点。
是否值得采用:看迁移成本,也看可观测性
Strands 适合那些已经能跑 Agent、但编排代码逐渐膨胀的团队。建议先挑选低风险、可判分的任务做影子测试,再逐步放量。上线前可以检查:
- 是否能记录每一步模型输入、工具调用和终止原因;
- 是否能限制最大轮数、总 Token 和单次工具超时;
- 是否能针对敏感工具设置独立权限;
- 是否能固定版本并复现评测结果;
- 模型适配层是否允许未来切换供应商。
开源 harness 的意义,不是消灭 Agent 的复杂度,而是把复杂度收拢到一个可复用、可审查的位置。28% 的 Token 降幅值得关注,但真正决定能否投入生产的,仍是成功率、边界控制和长期可维护性。