token、采样与上下文窗口如何决定 Agent 的行为边界
Token
- 一个 token 可以是半个字、一个字、两个字、一个词、一段字母,取决于分词词表,不是看语义上能不能拆开。
- Tokenizer 实现id与token的映射,大模型通过id再去拿向量去计算
1 | 人类文本:我爱AI |
采样
大模型回复是一个个token输出出来的,每输出一个token需要做下一个token的选择,这里称为采样。
假设现在上下文是:春天来了,公园里开了
- 先经过 tokenizer,转成一串 ID 给模型
- Transformer 跑完计算,输出一个巨大的列表:词表里面每一个 token,作为下一个词的概率示例(简化):
概率总和 = 1。理论上,下一个 token 可以是词表里任意一个,只是大部分概率极低。
| token | 概率 |
|---|---|
| 花 | 0.70 |
| 树 | 0.15 |
| 草 | 0.08 |
| 鸟 | 0.04 |
| 坦克 | 0.0001 |
| …… | …… |
重点:模型本身只算出这堆原始概率,它不负责 “挑哪个”。挑哪个,就是采样器干的活,temperature /top_p/top_k 都是采样器参数。
原始概率分布 → top_k 过滤 → top_p 过滤 → temperature 缩放概率 → 随机采样选出下一个 token
top_k:按数量砍候选,其他PASS。topK为1top_k=1:只留第一名,永远选概率最高的,结果固定不变(贪心解码)top_k=0 / 不填:关闭,不做这一步过滤如果合理的候选有 5 个,你设 k=3,强行砍掉 2 个合理选项;如果只有 2 个高概率词,k=100 也没用。
top_p:按累计概率砍候选,从最高概率开始累加,加到总和 ≥ p,就停,后面全部删掉
temperature:增大压缩概率差距
- T < 1 放大强者优势
- T = 1 概率不变
- T > 1 减小强者和弱者的差距,输出更随机
注意:top_k 和 top_p 都是直接删掉一些候选 token,删掉之后,剩下的概率会重新归一化(等比例放大,总和变回 1),再进入下一步
1 | Admin@MacBook-Pro qoder % ./minimax_param_test.sh |
上下文窗口
- 长上下文中间部分的影响力被显著削弱,首尾反而更受关注
- 输入无关内容会造成伤害
- Agent 循环每轮重放历史 → 上下文随步数近似线性增长 → 成本、延迟、首 token 延迟同步上升;长会话还会出现「忘了最初目标」、重复调用同一工具、不收敛
三者合起来 = Agent 的三条边界
问题
为什么t=0了,还是会输出不一样?
temperature=0 在绝大多数实现里不是「从多个候选里按概率挑」,而是贪心解码:把分布削到只剩概率最大的那个 token,其余置 -inf,argmax 取值。所以「概率大的更可能被选中」这句在 T=0 时不成立——它就是那个大的
T=0只是关掉了模型本身随机选字的逻辑,但GPU底层计算、服务器调度这些外部环节没法保证完全一致,所以输出依然可能不一样。
温度改的是分布形状,T=0 的「确定性」是工程近似,不是数学保证。
为什么会出现重复调用工具?
重复调用本身是症状,先看它是不是被闸门挡住了。排查要能区分三种可能
- 闸门缺失(没有重复检测、没有步数上限)
- 采样路径分叉(同一 query 走出不同参数)
- 上下文稀释(目标被挤掉,模型重新发明一个任务)
现象 A: 参数完全一样,重复出现 判据:
params_hash连续两轮相同。这是工程闸门缺失,不是模型笨——模型看不到”我刚做过这件事”。修:编排层加”无进展熔断”。现象 B: 参数在变,越改越花 判据:
params_hash每轮都不同,且偏离原始目标越来越远。这是目标漂移:目标句被工具结果挤到上下文尾部、被截断、或处在中间位置被忽略(长上下文里中段信息最容易被跳过)。修:目标固定在不被裁剪的位置;工具结果用”摘要 + 指针”替换原文;给模型一份结构化的”已尝试清单”。现象 C: 参数几乎一样,模型在”再确认一次” 判据:工具返回没有明确终态——没有
has_more: false、没有 cursor 结束标记、没有”未找到”的语义。模型无法判断该继续还是该停。修:返回 schema 里把终态写清楚,错误信息也要可读(”未找到 ERROR 级别日志” ≠ 空字符串)。
如果检索出来5w行日志,agent如何处理?
L0 工具契约层: 工具需要照顾到如果查询内容多如何去处理,比如说,限制最大limit、或者定义filter。原则一句话:工具返回”决策所需的最小充分信息”,不是数据源的全量导出。
- 比如说查询一段时间的日志,可以返回总行数、样本topN、错误码分组
L1 上下文装配层(token 预算分配器): 系统提示 / 工具定义 / 历史 / 工具结果 / 预留输出留一部分预算,告诉AI在遇到工具返回超预算的时候,保留 top-N + 聚合统计 + 显式标注” 这是 5 万行中的 200 行样本,完整数据在 SLS 查询 X”。不标注,模型就会以为它看全了 → 自信编造(能力边界那条直接落地)
L2 模型调用层(硬参数)
max_tokens、context_length,加上”自动截断最旧消息”这件事要知道——它会静默删掉你的早期目标句。L3 编排层(Agent 循环真正的闸门)
max_steps、单工具调用上限、参数指纹熔断(同一 tool + 归一化参数 hash 连续命中 N 次 → 停止或强制换路径)、token/成本闸门。L4 状态层 checkpoint 里存的是指针 + 摘要(doc id 列表、聚合数、ES 查询 URL),不是 5 万行原文。你的 Redis checkpoint 正好是这个位置。
L5 观测层 Trace 字段要能回答”这一轮发生了什么”:
prompt_tokens、tool_result_tokens、params_hash、truncated、circuit_broken。你现有 Trace Hook 缺的就是params_hash和truncated这两个字段——没有它们,线上重复调用你只能靠猜。
如何设计日志工具返回的json?
先说结论:这个 JSON 有两个可能的消费者——人和 LLM。如果它是喂给模型的 tool 结果,设计的硬约束只有三条:体积有界、状态显式、留好续查句柄。你提的”topN / 剩余量 / 是否读完 / 索引 / sheet”正好对应这三条。
关键点:
matched必须来自全量扫描后的统计,不是”当前返回窗口”的计数。否则remaining永远是错的,模型也无法判断”是不是还有没读到的 ERROR”。
1 | { |