阿毛
It's me !
想你所想

工具调用-掩蔽token的logits

https://manus.im/zh-cn/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus

随着代理能力的增强,其行动空间自然变得更加复杂——简单来说,工具数量爆炸式增长。最近流行的MCP只会火上浇油。如果你允许用户自定义工具,相信我:总会有人将数百个神秘工具插入到你精心策划的行动空间中。结果,模型更可能选择错误的行动或采取低效的路径。简而言之,你武装过度的代理变得更加愚蠢。
一个自然的反应是设计一个动态行动空间——可能是使用类似于RAG的方法按需加载工具。我们在mAnus中也尝试过这种方法。但我们的实验表明了一个明确的规则:除非绝对必要,避免在迭代过程中动态添加或移除工具。这主要有两个原因:
1.在大多数LLM中,工具定义在序列化后位于上下文的前部,通常在系统提示之前或之后。因此任何更改都会使后续所有动作和观察的KV缓存失效。
2.当先前的动作和观察仍然引用当前上下文中不再定义的工具时,模型会感到困惑。如果没有约束解码,这通常会导致模式违规或幻觉动作。
为了解决这个问题并仍然改进动作选择,Manus使用上下文感知的状态机来管理工具可用性。它不是移除工具,而是在解码过程中掩蔽token的logits,以基于当前上下文阻止(或强制)选择某些动作。
https://file.blog.humh.cn/wp-content/uploads/2026/08/20260825111308891-1024x576.png
掩蔽工具token的logits

在理解 Manus 的 掩蔽 logits 实践之前,我们先要知道这么做的背景是什么?

一、缓存

上下文缓存

调用大模型时,不同推理请求可能出现输入内容的重叠(例如多轮对话或对同一本书的多次提问)。上下文缓存(Context Cache)技术可以缓存这些请求的公共前缀,减少推理时的重复计算。这能提升响应速度,并在不影响回复效果的前提下降低您的使用成本。

为满足不同场景的需求,上下文缓存提供两种工作模式,可以根据对便捷性、确定性及成本的需求进行选择,这里我们主要讨论「隐式缓存」。

隐式缓存:此为自动模式,无需额外配置,且无法关闭,适合追求便捷的通用场景。系统会自动识别请求内容的公共前缀并进行缓存,但缓存命中率不确定。对命中缓存的部分,按输入 Token 标准单价的 20% 计费。

提升命中缓存的概率

隐式缓存的命中逻辑是判断不同请求的前缀是否存在重复内容。为提高命中概率,请将重复内容置于提示词开头,差异内容置于末尾。

文本模型:
假设系统已缓存”ABCD”,则请求”ABE”可能命中”AB”部分,而请求”BCD”则无法命中。

视觉理解模型:
对同一图像或视频进行多次提问:将图像或视频放在文本信息前会提高命中概率。
对不同图像或视频提问同一问题:将文本信息放在图像或视频前面会提高命中概率。

https://file.blog.humh.cn/wp-content/uploads/2026/08/20260825112143256.png

工具Token的痛点

openai工具调用协议

# 定义工具列表
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。",
                    }
                },
                "required": ["location"],
            },
        },
    },
]


# 模拟天气查询工具
def get_current_weather(arguments):
    weather_conditions = ["晴天", "多云", "雨天"]
    random_weather = random.choice(weather_conditions)
    location = arguments["location"]
    return f"{location}今天是{random_weather}。"


# 封装模型响应函数
def get_response(messages):
    completion = client.chat.completions.create(
        model="qwen-plus",
        messages=messages,
        tools=tools,
    )
    return completion

tools是作为输入请求(会被序列化并插入到提示词(prompt)上下文中)中的一部分传入大模型

https://file.blog.humh.cn/wp-content/uploads/2026/08/20260825112405610-1024x433.png

工具或MCP多不重要,有超长的上下文支持,但会导致准确性幻觉问题。如果我们A请求选择支持工具ABC,B请求支持DEF,则KV失效。

https://file.blog.humh.cn/wp-content/uploads/2026/08/20260825112559874-1024x319.png

上图的这段话讨论的是AI代理(Agent)在复杂工具环境下的行动空间管理问题,核心是解决“工具过多导致代理变笨”的困境。


二、Manus的实践

问题背景

  • 工具爆炸:随着AI代理能力增强,可用工具数量激增(如MCP框架、用户自定义工具)
  • 代理变笨:工具越多,代理反而越容易选错工具或走低效路径
  • 动态加载的陷阱:看似合理的“按需加载工具”(类似RAG)方案,实则带来两个严重问题:
    1. KV缓存失效:工具定义通常在上下文开头,修改工具会使后续所有计算的缓存失效,大幅降低效率
    2. 模型困惑:当历史记录中引用了已被移除的工具,模型会产生幻觉或违反格式规范

Manus的解决方案

不移除工具,而是用“掩码技术”动态约束可用工具

  • 上下文感知状态机:根据当前状态决定哪些工具可用
  • 掩蔽token的logits:通过数学方法“屏蔽”不可用工具对应的token
  • 响应预填充:巧妙利用预填充技术约束模型输出
  • 命名规范设计:工具命名使用统一前缀(如browser_shell_),便于批量约束

三种约束模式(以Hermes格式为例)

  1. 自动模式:模型可自由选择是否调用工具 → 预填充回复前缀
  2. 必需模式:模型必须调用工具,但可自由选择 → 预填充到工具调用令牌
  3. 指定模式:模型必须从特定工具子集中选择 → 预填充到函数名称开头(如{"name": "browser_

关键概念详细解释

1. 什么是 Logits

简单理解:Logits是大模型输出的“原始分数”,表示模型对每个可能token的“偏好程度”。

技术详解

  • 在LLM中,模型最后一层输出的是未归一化的数值,这些就是logits
  • 例如:模型考虑下一个token时,对”apple”的logit=5.2,”banana”的logit=3.8,”cat”的logit=-2.1
  • 这些logits会通过softmax函数转换为概率:P(token) = exp(logit) / Σexp(all_logits)
  • 关键特性:logit值越大,该token被选中的概率越高;logit为负无穷时,概率为0
from openai import OpenAI

client = OpenAI(api_key="your-api-key")

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "请用一个词描述天空的颜色"}
    ],
    logit_bias={
        # token ID 15043 = "blue",设置为100表示强烈偏好
        "15043": 100,
        # token ID 1315 = "red",设置为-100表示几乎禁止
        "1315": -100
    },
    max_tokens=10
)
print(response.choices[0].message.content)
# 几乎肯定会输出 "blue" 而不是 "red"

在文章中的意义
当Manus说“掩蔽token的logits”,指的是将不可用工具对应token的logits设为负无穷,使模型完全不可能选择这些工具。

2. 什么是 “掩蔽token的logits”

简单理解:这是一种“软性屏蔽”技术,在不修改模型结构的情况下,强制模型忽略某些选项。

技术详解

# 伪代码示例:掩蔽logits
original_logits = model.get_next_token_logits()  # 原始logits
mask = create_mask(available_tools)             # 创建掩码:可用工具为1,不可用为0
masked_logits = original_logits + (mask - 1) * 1e9  # 不可用工具的logits变成极小值
next_token = sample_from_logits(masked_logits)  # 采样时不可用工具概率≈0

在文章中的应用场景

  • 当用户新输入消息时,Manus需要立即回复而非执行工具 → 掩蔽所有工具调用token的logits
  • 当处于“需要搜索”状态时,只允许浏览器工具 → 掩蔽所有非browser_前缀工具的logits
  • 不修改工具定义:工具仍在上下文中,只是被“数学屏蔽”了

优势

  • ✅ 保持KV缓存有效(不修改上下文)
  • ✅ 避免模型困惑(历史引用仍有效)
  • ✅ 响应速度快(无需重新加载上下文)

3. 什么是 “响应预填充”?

简单理解:在让模型生成响应前,预先“写好”响应的开头部分,引导模型按特定格式继续生成。

技术详解

  • 预填充(Prefill):将部分响应内容直接插入到生成序列的开头
  • 强制格式:通过预填充JSON结构、函数名前缀等,约束模型输出格式
  • 与logits掩码协同:预填充 + logits掩码 = 精确控制生成内容
  • 豆包火山大模型-续写
  • 阿里云百炼大模型-续写

文章中的三种模式详解

自动模式(Auto)

// 预填充内容:
""
// 模型可自由选择:
// - 直接回复用户
// - 或生成工具调用
  • 实现:仅预填充空字符串或回复前缀
  • 用途:让模型自主决定是否需要工具

必需模式(Required)

// 预填充内容:
"{"name": ""
// 模型必须:
// 1. 生成有效的JSON
// 2. 填写函数名称
// 3. 不能直接回复用户
  • 实现:预填充到工具调用开始令牌
  • 用途:强制模型调用工具,但不限制具体哪个工具

指定模式(Specified)

// 预填充内容:
"{"name": "browser_
// 模型必须:
// 1. 从browser_开头的工具中选择
// 2. 不能选择shell_或其他工具
// 3. 例如只能生成"browser_search"或"browser_scrape"
  • 实现:预填充到函数名称开头
  • 用途:精确限制工具选择范围

命名规范的重要性
文章提到设计browser_shell_等统一前缀,这样只需预填充"{"name": "browser_就能批量约束所有浏览器工具,无需逐个设置logits掩码。


三、核心价值总结

Manus的实践揭示了一个反直觉的AI工程原则
“不是工具越少越好,而是要让模型‘看不见’不该用的工具”

传统思路:❌ 移除不用的工具 → 破坏上下文,降低性能
Manus方案:✅ 保留所有工具但“遮住眼睛” → 保持效率,避免混淆

这种技术的意义

  • 性能优化:避免KV缓存失效,推理速度提升2-5倍
  • 认知稳定:模型不会因历史引用消失而困惑
  • 工程友好:无需复杂的状态管理,用数学方法解决工程问题
  • 成本节约:减少重复计算,降低API调用成本

实际应用场景

  • 客服机器人:用户新消息进来时,立即回复而非执行后台工具
  • 代码助手:在编辑文件时,只暴露file_相关工具,屏蔽browser_工具
  • 数据分析Agent:在数据加载阶段,只允许data_前缀工具

这种“掩码+预填充”的组合拳,代表了现代AI代理架构的先进实践——用软件工程思维解决AI认知问题,而非简单堆砌工具。

humh

文章作者

站长本人,一个憨批!

发表评论

textsms
account_circle
email

想你所想

工具调用-掩蔽token的logits
https://manus.im/zh-cn/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus随着代理能力的增强,其行动空间自然变得更加复杂——简单来说,工具数量爆炸式增长。最近流…
扫描二维码继续阅读
2026-01-12