ExecuTorch遇上Muse Glimmer:端侧Agentic AI实战笔记
2026/9/4 2:43:28 网站建设 项目流程

在端侧设备上跑 Agentic AI,听起来很美好,但真正动手做过一遍的人都会有一个共同的感受:模型推理已经很难了,模型之上的“智能循环”更难。模型要推理、要理解用户意图、要决定调用哪些工具、还要把工具结果组织成下一次上下文,这一整条链路的延迟、内存、功耗和稳定性都要在手机或者边缘设备上做到可接受,难度比单纯部署一个聊天模型大得多。

ExecuTorch 给了我们一个可行的 PyTorch 端侧运行底座,而当 ExecuTorch 遇上面向 Agentic AI 的轻量编排层 Muse Glimmer 后,事情会变得更有可操作性。本文基于近期在端侧 Agentic AI 方案上的实践与调研,整理了一套从概念、架构到模型导出、Agent 循环搭建、性能调优和排错的完整笔记。内容尽量覆盖新手需要理解的理论背景,也给出有经验的开发者可以快速上手的示例思路与排查清单。

1. 背景:为什么 Agentic AI 要跑到设备端

1.1 什么是 Agentic AI

Agentic AI 可以理解为“具备自主行动能力的人工智能系统”。它不再只是回答用户“什么是量子计算”或者“帮我翻译一句话”,而是能接收一个相对模糊的目标,例如“帮我在本地相册里找到上周拍的所有夜景,并把其中模糊的照片挑出来”,然后自己拆解任务、调用不同模块、观察结果、修正计划,最终输出一个结果。

拆开来看,一个典型的 Agentic AI 系统通常包含四个核心组件:

  • 意图理解 / 规划模块:理解用户请求,将目标拆成可执行的子任务。
  • 工具调用层:提供搜索、相机、相册、传感器、日历、文件系统等外部能力。
  • 执行与反馈循环:调用工具后,观察返回结果,决定继续执行还是调整策略。
  • 记忆与上下文管理:保存和动态更新会话历史、任务状态和长期偏好。

当这些能力全部在云侧完成时,就是大家熟知的“云端 Agent”。当这些能力全部或大部分在本地设备上完成时,就是本文要讨论的 On-Device Agentic AI,也常被写成端侧 Agentic AI。

1.2 端侧 Agentic AI 的机遇与挑战

在云侧跑 Agentic AI,优势是算力充裕、模型体积不受限制、生态成熟。但问题也很突出:数据需要上传、每次交互有网络延迟、弱网环境几乎不可用,同时长期服务的推理成本会随用户量线性增长。

端侧 Agentic AI 之所以被重视,主要有三个关键价值:

  • 隐私保护:照片、健康记录、通讯录、行为习惯等敏感数据不需要离开设备,可以在本地完成分析处理。
  • 低延迟:省去“设备→云端→设备”的往返时间,尤其是多轮工具调用场景,延迟收益很明显。
  • 离线可用:飞机、地铁、野外等弱网甚至离线环境下,依然可以完成部分智能任务。

但端侧不是没有代价。手机和边缘设备的计算资源、内存带宽、电池容量都很有限。Agentic AI 的循环结构会带来比普通单次推理更大的挑战。

1.3 ExecuTorch + Muse Glimmer 的组合思路

面对端侧 Agentic AI,我们需要同时解决两个层面的问题:第一层是怎么把 PyTorch 训练好的模型高效部署到端侧设备上,第二层是怎么在端侧设备上把模型、工具调用、上下文管理组织成一个可靠的 Agent 循环。

ExecuTorch 是 PyTorch 生态中的端侧推理运行时解决方案,目标是让 PyTorch 模型可以直接导出并高效运行在手机、嵌入式设备等资源受限环境。它是“模型跑起来”的基础层。

Muse Glimmer 可以理解为建立在端侧模型之上的一层轻量级 Agent 编排组件。它本身的关注点并不是从零训练一个模型,而是如何让模型在设备上具备“感知用户请求、规划动作、调用工具、观察结果、继续决策”的能力。

我们可以把 ExecuTorch 看作是发动机,把 Muse Glimmer 看作是整个驾驶系统:ExecuTorch 负责把发动机燃效调到最优,Muse Glimmer 则负责处理路径规划、传感器融合和执行决策。

接下来,我会按工程落地的顺序拆开讲。

2. 整体架构:从 ExecuTorch 到 Muse Glimmer

2.1 ExecuTorch 的核心能力

先理解 ExecuTorch 在整条链路中的位置。PyTorch 是训练框架,模型的训练、微调、量化模拟都发生在 PyTorch 环境中。训练完成后,如果要部署到移动设备,不能直接把整个 PyTorch 框架打进去,那样体积和性能都不可接受。

ExecuTorch 做的事情,是把 PyTorch 模型转换成一种适合端侧运行的中间表示和运行时格式,然后通过 C++ 运行时加载并执行。这个过程包括:

  • 模型导出:将 PyTorch 的nn.Module导出为 ExecuTorch 程序。
  • 图优化:对计算图进行算子融合、内存规划等优化。
  • 量化:将 FP32 权重转换为 int8、int4 等低精度格式,降低内存占用和计算压力。
  • 算子分派:将算子映射到对应的后端实现,例如 CPU、GPU、NPU 等。
  • 端侧运行时加载:在 Android、iOS 或其他嵌入式平台上加载并执行模型。

ExecuTorch 尤其适合大语言模型(LLM)的端侧部署,因为 Agentic AI 的意图理解和规划部分通常依赖在设备上运行的 LLM。LLM 在端侧部署的主要挑战是显存/内存占用高、自回归解码慢、KV Cache 持续增长,ExecuTorch 的量化能力、内存规划和针对 Transformer 算子的优化,就是针对这些问题设计的。

2.2 Muse Glimmer 的角色

如果说 ExecuTorch 关注的是“怎么把模型跑快”,那么 Muse Glimmer 关注的是“模型跑起来之后怎么成为一个真正能干活的 Agent”。

在我的理解中,Muse Glimmer 希望抽离出一套通用的端侧 Agentic AI 编排能力。它需要解决几个典型问题:

  • 工具描述与注册:设备上的相册、定位、日历、系统设置、健康数据等能力,如何用一种模型可以理解的“结构化格式”暴露给模型。
  • 提示词与上下文组织:不同模型对工具描述的敏感程度不一样。Muse Glimmer 在这一层负责把工具描述、系统提示词、历史对话、工具返回结果整理成当前模型能处理的输入格式。
  • 动作决策与解析:模型输出一段文本,其中包含“调用某个工具”的意图。Muse Glimmer 负责解析这段输出,将其转换成实际函数调用。
  • 循环控制:工具执行完成后,结果要重新回到模型上下文,让模型决定下一步动作。这个循环什么时候停止,什么时候需要澄清用户意图,都需要控制逻辑。
  • 端侧资源调度:Agent 循环中每一步推理都可能消耗可观资源。Muse Glimmer 需要与 ExecuTorch 运行时配合,管理模型加载、KV Cache 复用、内存释放等问题。

从分层角度看,可以把 ExecuTorch 与 Muse Glimmer 的关系画成下面的简图:

[ PyTorch 模型 / 量化模型 ] ↓ [ ExecuTorch 运行时:加载、推理、KV Cache ] ↓ [ Muse Glimmer 编排层:提示词、工具注册、动作解析、循环控制 ] ↓ [ 端侧系统能力:相册 / 定位 / 日历 / 文件系统 / 网络请求 ]

2.3 从用户请求到工具调用的完整链路

结合一个真实场景来看,假设用户说“帮我把明天上午的所有会议整理成一份摘要,并保存到备忘录”。

在端侧 Agentic AI 系统中的流程是这样的:

  1. 语音或文字输入经过输入模块处理后,转换为文本。
  2. Muse Glimmer 将系统提示词、可用工具描述、用户请求和历史上下文组装成模型输入。
  3. ExecuTorch 运行时将输入交给设备上的 LLM 执行推理。
  4. LLM 输出一个规划结果:第一步,读取日历数据;第二步,生成摘要;第三步,写入备忘录。
  5. Muse Glimmer 解析输出结果,调用日历 API 获取明天的会议列表。
  6. 工具返回数据被转换为文本格式,作为新的上下文返回给模型。
  7. 模型二次推理,生成摘要草稿。
  8. Muse Glimmer 调用备忘录 API,写入结果。
  9. 循环结束,Muse Glimmer 向用户返回执行结果。

可以看到,完整链路中涉及至少两到三次模型推理,而不是一次问答。每一次推理的耗时、内存占用都会累积。这就解释了为什么“On-Device Agentic AI”特别强调 Fast,也就是端侧性能不优化好,整个 Agent 循环会非常慢。

3. 环境准备与版本说明

3.1 推荐环境范围

在正式动手之前,需要先准备开发环境。ExecuTorch 仍属于快速演进中的项目,版本变化会比较频繁,所以不建议在没有官方版本声明的情况下直接固定使用某个版本号。本文示例以常见的 Linux 开发机加 Android 模拟器/真机环境为例,主要演示配置和执行思路。

建议的环境如下:

  • 操作系统:Ubuntu 20.04 / 22.04,或者 macOS 12 以上。Windows 也可以尝试,但编译端侧库时会更麻烦。
  • Python:3.9 到 3.11 之间,具体看所安装 PyTorch 版本要求。
  • PyTorch:建议使用与 ExecuTorch 官方文档匹配的版本,避免版本错位。
  • Android 开发环境:Android Studio、Android SDK、NDK、CMake,用于构建和部署端侧 APK 或测试工程。
  • 设备或模拟器:Android 真机优先,因为 ExecuTorch 在真实 CPU/内存环境下更容易暴露性能问题。

ExecuTorch 对 Python 环境比较敏感,强烈建议使用独立的 Python 虚拟环境。

python3 -m venv executorch_env source executorch_env/bin/activate

3.2 获取相关代码与依赖

ExecuTorch 的安装方式通常是从 GitHub 克隆源码,再执行安装脚本。官方仓库中会包含 Python 包、运行时 C++ 源码、示例工程和导出工具链。

建议的操作顺序是:

git clone https://github.com/pytorch/executorch.git cd executorch git submodule sync git submodule update --init

需要注意的是,ExecuTorch 的源码依赖一些子模块,例如算子库等。如果漏掉子模块更新,后面导出或编译时经常会遇到头文件缺失的错误。

接下来安装 Python 依赖。

pip install -r requirements.txt

这一步会安装 PyTorch、torchvision 等依赖。具体版本务必与 ExecuTorch 当前分支的 CI 配置一致。

如果你的方案中包含 Muse Glimmer 这一类编排组件,需要看对应组件当前推荐的接入方式。由于相关组件也在快速迭代,下面核心代码只演示设计思路,具体 API 建议以官方示例为准。

3.3 准备模型文件

端侧 Agentic AI 中的规划模型通常是参数量在 1B 到 7B 之间的语言模型。以现在端侧设备的趋势来看,4B 以内的量化模型是相对稳妥的选择。

模型可以来自 Hugging Face、PyTorch Hub 或企业内部微调模型。在下载模型时注意确认模型的许可证是否允许部署和商用。

本文将使用一个虚构的角色扮演示例模型demo-agent-model来演示流程,重点展示技术路径,不绑定具体模型权重。

4. ExecuTorch 模型导出与部署流程

4.1 准备 PyTorch 模型

首先在 Python 环境中定义一个用于演示的简单模型。实际使用中,这里应该替换为你自己的语言模型或任务模型。

# 文件路径:prepare_model.py import torch class DemoAgentModel(torch.nn.Module): def __init__(self, vocab_size=512, embed_dim=64, hidden_dim=128): super().__init__() self.embedding = torch.nn.Embedding(vocab_size, embed_dim) self.lstm = torch.nn.LSTM(embed_dim, hidden_dim, batch_first=True) self.fc = torch.nn.Linear(hidden_dim, vocab_size) def forward(self, input_ids): x = self.embedding(input_ids) x, _ = self.lstm(x) return self.fc(x) model = DemoAgentModel() model.eval() example_input = torch.randint(0, 512, (1, 16)) torch.save(model.state_dict(), "demo_agent_model.pt") print("模型准备完成,已保存权重 demo_agent_model.pt")

工作目录中可以先创建下面的结构:

executorch_demo/ ├── models/ # 存放原始权重和导出后的 exec 文件 ├── scripts/ # 导出、量化、部署脚本 ├── android_demo/ # Android 客户端工程 ├── agent_python/ # 面向 Python 侧的 Agent 编排示例 └── requirements.txt

在命令行创建目录:

mkdir -p executorch_demo/{models,scripts,android_demo,agent_python} cd executorch_demo

4.2 使用 ExecuTorch 导出模型

如果已经有 PyTorch 训练好的模型权重,下一步就是把它导出为 ExecuTorch 格式。

导出的关键 API 是torch.exportexecutorch.exir。ExecuTorch 官方教程中通常这样导出模型:

# 文件路径:scripts/export_model.py import torch from torch.export import export from executorch.exir import EdgeProgramManager, ExecutorchProgramManager, to_edge from prepare_model import DemoAgentModel model = DemoAgentModel() model.load_state_dict(torch.load("demo_agent_model.pt")) model.eval() example_input = torch.randint(0, 512, (1, 16)) # 1. 先转换为 TorchScript 或 torch.export 后的计算图 exported_program = export(model, (example_input,)) # 2. 转换为 ExecuTorch 的 Edge 格式 edge_program = to_edge(exported_program) # 3. 导出为端侧可执行程序 executorch_program = ExecutorchProgramManager(edge_program) with open("demo_agent_model.pte", "wb") as f: executorch_program.emit_to_file(f) print("导出完成:demo_agent_model.pte")

上面这段代码是 ExecuTorch 导出标准路径。真实项目中模型可能带有更复杂的输入结构、KV Cache 或采样参数,这时需要把模型的 forward 分成 prefill 和 decode 两个阶段,或者使用 ExecuTorch 针对 LLM 的专门导出示例。

关于示例代码,有一个很重要的说明:ExecuTorch 的 API 在快速演变,上面to_edge的调用方式在一些版本中可能需要额外的参数。建议以官方仓库中的examples目录为准。

4.3 量化与体积优化

导出后的模型精度如果仍是 FP32,端侧部署会面临两个问题:文件体积大,推理占用内存高。对于语言模型,参数量 1B 的 FP32 权重需要 4GB 内存,这几乎无法在手机上流畅运行。因此,量化是端侧部署不可跳过的一步。

常见的量化方案对比:

量化方案权重精度相对内存说明
FP3232 bit1x 基准精度最高,端侧不推荐直接使用
FP1616 bit约 0.5x精度损失小,可跑在部分 GPU/加速器
INT88 bit约 0.25x精度损失中等,CPU 推理常见选择
INT44 bit约 0.125x内存占用低,精度损失需评估

在 ExecuTorch 中,量化通常依赖torch.ao.quantization或后续推出的量化工具链。核心思路是先在 PyTorch 侧做量化校准,收集权重和激活值的分布,再导出生效的量化计算图。

# 文件路径:scripts/quantize_model.py # 以下为量化思路示例,需要按实际 ExecuTorch 版本调整 from executorch.extension.quantization import quantize quantized_program = quantize( exported_program=exported_program, quantization_config={ "mode": "int8", "weight_dtype": torch.int8, }, )

量化最需要关注的不是代码本身,而是精度损失。建议量化后准备一组覆盖 Agent 工具调用场景的测试样本,对比量化前后模型输出的工具调用格式是否仍然稳定。工具调用格式如果出错,Agent 循环几乎无法继续。

4.4 在 Android 端加载 ExecuTorch 模型

ExecuTorch 提供了 Android 端 Java/Kotlin 接口,用于加载.pte文件并执行推理。下面以 Kotlin 代码展示加载思路。

// 文件路径:android_demo/app/src/main/java/com/example/executorchdemo/MainActivity.kt class MainActivity : AppCompatActivity() { private var module: Module? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 加载 .pte 文件,注意文件要放到 assets 目录 val buffer = assets.open("demo_agent_model.pte").use { it.readBytes() } module = Module(buffer) } fun runInference(inputIds: LongArray): FloatArray { val inputTensor = Tensor.fromBlob(inputIds, longArrayOf(1, inputIds.size.toLong())) val output = module?.forward(inputTensor) return output?.data?.asFloatArray() ?: FloatArray(0) } }

上面代码中只展示了最基本的加载和执行方式。在真实 Agent 项目中,模型推理不能放在 Android 主线程,必须放到后台线程或专门的推理线程中执行,否则应用会因为没有及时绘制 UI 而出现 ANR。

此外,模型文件如果放在assets目录中,需要注意 APK 体积。一个 2GB 的大型语言模型放在 assets 中会显著增加安装包体积,实际产品通常会采用“首次启动时从服务器下载模型到私有目录”的方案,同时要做好模型文件完整性校验。

5. 构建 On-Device Agent 循环

5.1 设计工具接口

在 Muse Glimmer 这一类编排层中,工具是整个 Agent 循环的核心。工具描述必须结构清晰,并且方便模型理解。

假设我们想在端侧暴露两个能力:查询日历以及写入备忘录。工具接口设计如下:

# 文件路径:agent_python/tools.py class CalendarTool: """读取设备日历事件。""" name = "query_calendar" description = "查询某个时间范围内的日历事件。" parameters = { "start_time": {"type": "string", "description": "开始时间,例如 2025-06-20 09:00"}, "end_time": {"type": "string", "description": "结束时间,例如 2025-06-20 18:00"} } def execute(self, start_time: str, end_time: str): # 实际代码在这里调用 Android 日历 ContentProvider events = ["团队晨会", "需求评审", "与客户沟通"] return events class MemoTool: """写入备忘录。""" name = "create_memo" description = "向系统备忘录写入一条文字记录。" parameters = { "content": {"type": "string", "description": "要写入的备忘录内容"} } def execute(self, content: str): # 实际代码在这里调用备忘录 API return "备忘录保存成功"

工具接口设计中有一个很容易被忽略的点:模型不一定每次都会按照工具定义输出完全正确的参数。因此,工具执行层需要对参数做容错处理,例如日期格式解析失败、字段缺失、超范围输入等。建议在execute方法中增加防御性判断,并返回可供模型理解的结构化错误信息,而不是抛异常让整个 Agent 崩溃。

5.2 配置 Agent 循环

Muse Glimmer 的编排思路可以参照下面的伪代码来理解。这个例子展示 Agent 循环的三个关键阶段:组消息、推理、执行工具。

# 文件路径:agent_python/agent_loop.py import json TOOL_SCHEMA = [ { "type": "function", "function": { "name": "query_calendar", "description": "查询某个时间范围内的日历事件。", "parameters": { "type": "object", "properties": { "start_time": {"type": "string"}, "end_time": {"type": "string"} }, "required": ["start_time", "end_time"] } } }, { "type": "function", "function": { "name": "create_memo", "description": "向系统备忘录写入一条文字记录。", "parameters": { "type": "object", "properties": { "content": {"type": "string"} }, "required": ["content"] } } } ] SYSTEM_PROMPT = ( "你是一个运行在用户设备上的智能助手。你可以根据需要调用工具。" "当用户请求涉及多种操作时,按步骤拆解,并在每步后观察工具返回结果。" ) class LocalAgent: def __init__(self, model_runner, tools): self.model_runner = model_runner self.tools = {tool.name: tool for tool in tools} self.history = [] def run(self, user_text): self.history.append({"role": "user", "content": user_text}) for step in range(5): # 最大循环次数保护 messages = self._build_messages() response = self.model_runner.generate(messages) self.history.append({"role": "assistant", "content": response}) action = self._parse_action(response) if action is None: return response # 模型认为任务已完成,输出最终回复 tool = self.tools.get(action["name"]) if tool is None: self.history.append({ "role": "tool", "name": action["name"], "content": "工具不存在,请重新选择工具" }) continue result = tool.execute(**action["arguments"]) self.history.append({ "role": "tool", "name": action["name"], "content": json.dumps(result, ensure_ascii=False) }) # 工具执行结果回到循环顶部,再次推理 return "抱歉,操作步骤太多,我未能完成任务。" def _build_messages(self): messages = [{"role": "system", "content": SYSTEM_PROMPT}] # 真实场景中,工具描述会放在系统中,或按模型约定放在 user 消息中 messages.append({ "role": "user", "content": "可用工具:" + json.dumps(TOOL_SCHEMA, ensure_ascii=False) }) messages.extend(self.history) return messages def _parse_action(self, text): # 不同模型的输出格式不同。这里假设模型输出 JSON。 try: return json.loads(text) except json.JSONDecodeError: return None

上面的代码片段是 Agent 循环的最小骨架,有几个设计细节需要特别强调:

第一,最大循环次数必须限制。在复杂任务中模型可能陷入“调用工具→观察结果→再次调用”的死循环中。设一个上限能有效防止系统无限消耗资源。

第二,历史上下文中的工具返回结果如果一直累积,会迅速占满模型的上下文窗口。实际工程中,通常只保留最近几轮的完整内容,更早的内容转换成摘要,而不是无限地全量保留。

第三,_parse_action方法需要兼容模型输出的多种格式。有的模型习惯在文本中夹杂代码块,有的只输出 JSON,有的遵循 Function Calling 标准。方案应支持多级解析:先看是否是标准 JSON,再看是否包含 Markdown 代码块,最后再用正则提取。这样做会大大提升 Agent 在实际运行中的稳定性。

5.3 与 ExecuTorch 模型运行时对接

在真实系统中,model_runner.generate的实现需要调用 ExecuTorch 运行时。可以简单封装为一个 Python 类:

# 文件路径:agent_python/model_runner.py class ExecuTorchModelRunner: """模拟 ExecuTorch 模型运行器的调用接口。""" def __init__(self, model_path: str): self.model_path = model_path # 实际项目在这里初始化 ExecuTorch Module # 例如通过 PyBind 或 JNI 调用 C++ 运行时 def generate(self, messages) -> str: # 1. 将消息列表 tokenize 成 token id # 2. 调用端侧推理 # 3. 将推理结果 detokenize 成文本 return '{"name": "query_calendar", "arguments": {...}}'

从架构上看,Python 更适合做开发和调试,但在 Android 产品环境中,Agent 编排层通常是 Kotlin 代码,模型运行则由 ExecuTorch 的 C++/Java 层完成。两边的交互逻辑是一致的:输入 token id,输出 token id,Agent 层自己负责文本解析和工具调用控制。

5.4 运行 Agent Demo

当模型文件、工具实现和 Agent 循环都准备好后,可以执行一个简单的命令行调用:

cd agent_python python demo_main.py --model ../models/demo_agent_model.pte

预期输出类似:

用户:帮我把明天上午的会议整理成摘要,并写入备忘录。 [Agent] 调用工具: query_calendar [Tool] 查询结果: ['团队晨会', '需求评审', '与客户沟通'] [Agent] 调用工具: create_memo [Tool] 备忘录保存成功 [Agent] 已完成任务。

这个 Demo 虽然简单,但已经覆盖了端侧 Agentic AI 最核心的链路:模型推理、工具选择、工具结果回传、多步执行。后续可以在此基础上加入语音输入、UI 展示、长期记忆等能力。

6. 性能优化:端侧 Agentic AI 的“Fast”法则

6.1 明确性能目标

在端侧做 Agentic AI,性能目标不能拍脑袋定。建议先确定两个指标:

  • 单次推理时延:从模型输入到模型输出完整回复的耗时。
  • Agent 循环总时延:从用户发起请求到所有工具执行完毕、最终回复到达的总耗时。

Agent 循环总时延才是用户真正感知到的指标。一次任务涉及 N 次模型推理,那么模型单次时延哪怕只降低 100ms,整个任务可能会有 500ms 以上的体验改善。

在需求阶段就要确认目标设备范围。是旗舰手机,还是中低端手机,还是工业边缘盒子?不同设备的 CPU、内存带宽差异很大,性能调优策略也不一样。

6.2 推理侧的优化手段

ExecuTorch 在模型侧支持的优化手段主要包括:

  • 量化:将 FP32 权重压缩到 INT8 或 INT4。内存占用降低的同时,CPU 缓存命中率可能会提升,推理速度在某些场景下反而更快。
  • 算子融合:减少计算图执行过程中对中间张量的读写。
  • KV Cache 管理:在 Agent 循环中,上一轮预填充计算出的 KV Cache 可以保留到下一轮,避免每轮都重新计算历史 token。这对多轮工具调用场景非常重要。
  • 采样参数控制:例如限制最大生成长度、使用贪心搜索代替复杂采样,可以显著降低解码耗时。

在 Agent 场景里,另一个重要优化是控制提示词长度。每次 Agent 循环的第一步是“预填充阶段”,也就是模型读取全部历史上下文并计算 KV Cache。上下文越长,预填充时间越长。如果工具返回结果很长,或者历史记录持续累积,预填充会越来越慢。

一个实践方法是:不要把所有历史对话都塞进模型,而是定期做对话摘要,然后只保留“摘要 + 最近两到三轮完整消息”。这个方法在端侧项目里价值非常大。

6.3 模型侧缓存与预热

在设备内存允许的情况下,尽量让模型常驻内存,避免每次工具循环都重新加载模型。模型加载是一个开销较高的操作,尤其对于 1B 参数以上的量化模型,冷启动加载可能需要数秒。

如果同一个设备需要支持多个 Agent 场景,例如一个模型负责聊天摘要,一个模型负责工具调用,那么模型切换也需要控制频率。实际项目中通常让两个模型常驻,或者只保留高频模型常驻,低频模型按需加载。

6.4 用表格量化性能任务

一个标准的端侧性能测试任务清单可以参考下面表格:

测试项必测指标参考手段
冷启动首次推理耗时记录从进程启动到模型首 token 时间
单轮推理首 token 时延输入相同提示词,测量多次取 P50/P95
多轮 Agent工具循环总耗时模拟完整任务,统计每次推理间隔
内存峰值PSS / RSS使用 Android Profiler 观察峰值
电池平均功耗连续执行任务,观察温度和电流变化

性能优化要有数据支撑,不要凭感觉调参。每次改动(模型版本、量化方案、上下文裁剪策略)都要跑同一组测试样本,记录指标,然后再进行比较。

7. 常见问题与排查思路

端侧 Agentic AI 的开发过程中,问题往往同时来自模型层、运行时层和编排层。下面列出一些高频问题。

问题现象常见原因解决思路
ExecuTorch 模型导出失败PyTorch 与 ExecuTorch 版本不匹配确认两者版本匹配,参考官方 requirements
.pte文件加载后崩溃算子后端未注册检查算子是否包含在导出配置中
模型推理速度很慢未量化或提示词过长使用 INT8/INT4 量化,压缩上下文
Agent 循环陷入死循环工具返回未改变模型决策设置最大循环次数,改进提示词和工具描述
工具参数解析失败模型输出 JSON 格式不稳定使用多级解析、正则、模板化输出约束
内存持续增长历史记录和 KV Cache 无限制累积增加上下文裁剪、定期摘要旧对话
工具调用后回复与任务无关工具返回结果过深或过长将工具结果精简成结构化要点
模型不调用任何工具工具描述不清晰或提示词缺少引导在系统提示词中加入使用工具的示例

下面是几个典型问题的详细排查过程。

问题一:模型一直不调用工具。

首先确认模型本身是否支持 Function Calling 格式。如果模型是在普通对话数据集上微调的,它可能根本不理解工具调用协议。建议在提示词中加入一个完整示例,例如:

用户:查询我明天的日程。 助手:{"name": "query_calendar", "arguments": {"start_time": "2025-06-21 00:00", "end_time": "2025-06-21 23:59"}}

问题二:工具执行结果回传后,模型仍然重复调用同一个工具。

这种情况多数是因为工具结果不够明确。例如查询日历返回了一堆原始字段,模型无法判断自己已经拿到了数据。解决方法是把工具结果转换成更适合模型的总结摘要,例如:“你已成功获取明天的 3 个会议事件,接下来你可以基于这些事件生成摘要。”

问题三:模型多轮调用后出现幻觉信息。

在端侧设备上,Agent 任务和长对话一样可能出现越靠后推理越不稳定。这可能是因为上下文太长、模型注意力分散,也可能是因为工具返回格式被截断。解决方案是尽量缩短每轮回传给模型的内容,只保留当前步骤需要的最小信息。

8. 最佳实践与工程建议

8.1 工具描述要遵循“少而精”原则

首次上线时不要一口气注册上百个工具。每个工具都会占用模型上下文空间,并且增加模型误选工具的概率。应该从最核心的少量工具开始,验证 Agent 循环稳定后,再逐步增加工具数量。工具描述要简洁明确,使用动词开头的描述通常更友好,例如“查询日历事件”比“一个可以读取日历中已有数据的工具接口方法”更容易让模型理解。

8.2 重视模型输出的解析与校验

Agentic AI 的核心风险之一是模型输出不可控。不要假设模型每次都会输出规范 JSON。解析层要做多级回退,参数校验要覆盖缺失、类型错误、范围不合理等情况。如果模型连续多次输出无法解析的内容,可以返回一条固定提示,让模型重新作答,而不是直接把原始文本透传给用户。

8.3 上下文管理必须提前设计

端侧模型的上下文窗口有限,Agent 循环的多轮工具结果会快速消耗上下文。最佳实践是:

  • 每一轮工具返回前,先进行摘要和截断。
  • 只把当前任务相关的信息保留给模型。
  • 在整个 Agent 开始前,先对用户长文本做一次摘要化预处理。

8.4 安全与授权边界要明确

Agent 能调用工具,意味着它具有影响设备数据的能力。在设计工具时,涉及隐私和敏感操作的调用(读取通讯录、发送消息、删除文件、访问地理位置)必须增加用户确认。建议在框架层提供“敏感操作拦截”机制:工具声明自己的敏感等级,高危工具执行前强制要求 UI 层弹窗确认。

同时,本地模型从远程下载或更新时,一定要校验模型文件的哈希值或数字签名,防止模型被篡改。

8.5 日志与可观测性

On-Device Agent 排错的难度比云侧更大,因为很难直接看到模型内部状态。建议在本地记录一份结构化日志,至少包含以下字段:

  • 时间戳
  • 用户请求
  • 第几步 Agent 循环
  • 传入模型的上下文长度
  • 模型原始输出
  • 解析后的动作
  • 工具执行结果
  • 一次推理耗时

这份日志不需要上传到云端,但可以作为开发者模式下的排错工具。当用户反馈 Agent 表现异常时,可以通过导出日志快速定位是模型规划出错、工具返回出错还是解析层出错。

8.6 最大循环保护与超时控制

任何 Agent 系统都必须有循环边界。建议在框架设计阶段就加入三个保护参数:

  • 最大推理轮数:默认 5 到 8 轮。
  • 单次推理超时时间:例如 10 秒。
  • 单次工具执行超时时间:例如 5 秒。

超时后应返回一个兜底回复,例如“当前操作比较耗时,我未能完成,请稍后重试”,而不是让用户一直等待空白界面。

8.7 从 MVP 到产品化的节奏建议

不要一开始就追求完整的自动化多步 Agent。更稳妥的路径是:

  • 第一版:只做单工具调用,例如用户说“帮我定 10 分钟后提醒”,Agent 只调用一个提醒工具。
  • 第二版:做顺序工具链,例如先读日历再写备忘录,但任务结构比较固定。
  • 第三版:引入开放式规划,让模型自主决定工具选择和数据流向。

第一阶段先跑通 ExecuTorch 模型部署和基础推理性能,第二阶段引入 Agent 循环并验证工具调用稳定性,第三阶段再增加更复杂的前后依赖关系。大多数端侧 Agent 项目真正困难的地方,不是模型推理本身,而是模型与工具配合的可靠性。

9. 总结与学习路线

这篇笔记从 ExecuTorch 和 Muse Glimmer 的组合切入,梳理了 On-Device Agentic AI 的核心架构和实战要点。前半部分解释了 Agentic AI 为什么要运行在设备端,以及 ExecuTorch 与 Muse Glimmer 各自在架构中的位置;中间部分围绕模型导出量化、Android 端加载、Agent 循环实现展开;后半部分重点讲了性能优化、常见问题排查和工程最佳实践。

回到最初的主题:Fast, on-device Agentic AI。这个目标能不能实现,取决于两层是否闭环。第一层是 ExecuTorch 这条推理链路是否足够快,模型量化是否做得到位,KV Cache 缓存和历史上下文管理是否合理;第二层是 Agent 编排层是否足够稳定,工具描述是否清晰,解析层是否可靠,循环是否受控。

如果是从零开始学习这个方向,推荐按下面的路径推进:

  • 第一步:下载 ExecuTorch 官方示例,先把一个图像分类或简单文本模型跑通端侧推理。
  • 第二步:尝试把一个小语言模型量化并部署到 Android 设备,测一次推理耗时和内存占用。
  • 第三步:在本地实现一个最小 Agent 循环,先用模拟模型输出验证工具调用解析逻辑。
  • 第四步:把真实模型接入 Agent 循环,逐步优化提示词、上下文长度和工具返回格式。
  • 第五步:做性能测试与日志采集,定位瓶颈,迭代优化。

端侧 Agentic AI 现在还处于方案快速演进阶段,ExecuTorch 版本更新频繁,Muse Glimmer 这类的编排层也会不断调整接口。我的经验是,不要追着每个新版本跑,而是先把“模型导出→端侧加载→Agent 循环→工具调用”这条主线吃透。当主线稳定下来后,即使底层框架升级,需要改动的也只是接入层代码,核心的 Agent 架构设计可以长期复用。

建议收藏本文,在动手搭建端侧 Agent 工程时对照排错。也欢迎在评论区聊聊你在 ExecuTorch 部署或工具调用环节中遇到的坑,我会尽力结合自己的实践给出排查方向。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询