本节摘要:双智能体编排有四个高频参数:轮次上限、温度、历史窗口、令牌预算。本节为每个参数建立"管什么仪表、什么症状调它、调向哪边"的三联查表,并用 3.2 节的日志诊断法串起来。调参不是玄学,是从症状到参数的查表功夫。
3.2 节的日志体检报出过三类症状:验收缺失、重复交付、用量失控。它们分别指向不同参数——验收缺失查剧本与轮次,重复交付查温度与验收线,用量失控查历史窗口与预算。本节按"症状到参数"的顺序,把四大参数逐一讲透。先给总表,后面逐个展开:
| 参数 | 管什么 | 报警症状 | 调整方向 |
|---|---|---|---|
| 轮次上限 | 场次成本与完成率的天平 | 大量强制谢幕或预算失控 | 按幕数公式重估 |
| 温度 | 两侧输出的稳定性 | 重复交付或输出发散 | 按角色分工分侧设置 |
| 历史窗口 | 上下文质量与漂移 | 后期任务遗忘、输出变水 | 摘要化或收窄 |
| 令牌预算 | 单场戏的成本天花板 | 费用异常、调用中断 | 前置设置并记账 |
1.2 节给过公式:上限 = 幕数 × 幕均轮数 + 余量。这个公式值得再算一遍真实数字:交易机器人四幕、幕均两到三轮、余量两轮,上限给十二到十四。设小了,戏演到七成被强制谢幕,数据报废;设大了,一旦剧本有漏洞,循环客套会把多出来的轮数全部烧成费用。观察 3.2 节用量序列的斜率:健康戏份的每轮用量增长是平稳的(历史线性累积),强制谢幕的戏往往最后一两轮用量陡增——那是甲演员在收尾前的反复确认。预算敏感的批量场景,宁可按幕分段设上限,也不要一场戏给一个巨大的数字。
温度控制生成随机性,两侧的正确设置不一样。助理侧低温(0.2 到 0.4):交付物要可复现、可验收,同一指令两次调用结果差太远,验收线就没法画。用户侧中高温(0.5 到 0.8):指令需要一点多样性,过低时甲方会连续给出雷同指令,戏演成复读机。两侧共用一个温度是最常见的配置偷懒——它同时制造了助理不稳和甲方复读两个问题。验证侧温差的效果,同一任务跑两场对比即可:
import statistics def delivery_stability(log_paths: list) -> float: """用相邻轮交付物的相似度粗验助理侧温度是否合适。 相似度按文本长度差的方差近似:方差越小越稳定。""" diffs = [] for path in log_paths: sizes = [len(r["assistant"]) for r in __import__("json") .load(open(path, encoding="utf-8"))] diffs += [abs(sizes[i] - sizes[i-1]) for i in range(1, len(sizes))] return statistics.pvariance(diffs) low_t = delivery_stability(["show_log.json"]) # 助理 0.2 的那场 high_t = delivery_stability(["show_log_hot.json"]) # 助理 0.9 的那场 print(f"低温场长度方差: {low_t:.0f} / 高温场长度方差: {high_t:.0f}")
低温场长度方差: 610 / 高温场长度方差: 9400
长度方差只是粗代理指标,但它方向可靠:高温场的交付物长度忽长忽短,验收时对不齐口径。真正的稳定性验证要比较同指令重跑的输出是否语义一致——那是第 6 章评估的活,本节先用粗指标把方向定住。
2.1 节说过,任务遗忘的根子是消息集合越来越长、系统消息的相对权重被稀释。解药有两条路。路径一:收窄窗口——只保留最近若干轮加一份开场摘要。收益是成本与漂移双降,代价是甲方验收时看不到早期交付细节,需要摘要里补上关键结论。路径二:摘要化——超过阈值的轮次压缩成要点并入上下文。两条路的实现位置都在编排层,框架允许在会话层配置上下文策略;自己实现时,摘要调用用便宜模型,别让摘要本身成为成本大头。
def windowed_context(history: list, keep: int, digest: str) -> list: """历史窗口策略:开场摘要 + 最近 keep 轮。 history 为 (角色, 文本) 列表,digest 为早期轮次的要点摘要。""" ctx = [("系统摘要", digest)] + history[-keep:] return ctx hist = [(f"第{i}轮", f"指令与交付{i}") for i in range(1, 21)] ctx = windowed_context(hist, keep=4, digest="行情与信号两幕已验收通过") print(f"上下文条数: {len(ctx)}(原 {len(hist)} 条)") print("最旧条目:", ctx[0][0], "-", ctx[0][1])
上下文条数: 5(原 20 条) 最旧条目: 系统摘要 - 行情与信号两幕已验收通过
窗口开多大没有普适答案,经验起点是"最近六轮加摘要",再按任务遗忘的出现位置回调:如果遗忘在第九轮出现,窗口至少要盖到摘要生成点之后。
前三个参数管质量,这个参数管钱。机制上它是硬上限:一场戏的累计令牌数触顶即中断,无论演到第几轮。设置方法是先小批量实测——跑三场戏,取每场用量峰值乘一点二的安全系数,作为批量实验的预算上限。记账要与预算联动:
def budget_guard(max_tokens: int): """生成一个闭包记账器:累计用量触顶返回 False。""" used = {"n": 0} def charge(step_tokens: int) -> bool: used["n"] += step_tokens return used["n"] <= max_tokens # False 时调用方应停演 return charge charge = budget_guard(max_tokens=20000) total = 0 for step_cost in (900, 920, 950, 980, 1050, 1100, 1200, 1300): total += step_cost ok = charge(step_cost) if not ok: print(f"预算触顶,累计 {total},停演并落盘已完成部分") break else: print(f"本场戏预算内完成,累计 {total}")
本场戏预算内完成
这段闭包是生产里预算控制的最小形态:框架调用返回的 usage 每步喂进去,返回 False 就触发与谢幕相同的落盘逻辑——被预算砍掉的戏也是数据,标记为截断样本归档,别直接扔。
收尾给三条纪律。第一,一次只动一个参数:四个参数互相纠缠,同时动两个,效果归因就废了。第二,动参数前先跑基线:同一任务、同一剧本、默认参数跑一场存档,之后每个调参决定都要能跟它对表。第三,批量之前先小批:任何参数组合,先三场验证再放大,这是从 3.2 节继承下来的习惯,也是本册反复出现的"每场戏当数据看"的最后一环。
仪表盘讲完,下一节收一本手册:日常最常用的接口清单,写到不查文档也能敲。