#029 Agent 的嵌入式部署:在 Raspberry Pi、Jetson 上运行轻量 Agent

#029 Agent 的嵌入式部署:在 Raspberry Pi、Jetson 上运行轻量 Agent 一、从一次“跑不起来”的教训说起去年冬天我在一块Raspberry Pi 4上部署一个基于LangChain的Agent模型用的是Llama 2 7B的量化版。板子插着散热风扇室温15度我满怀信心地敲下python agent.py。结果——内存直接爆掉OOM Killer把进程杀了系统日志里留下一行冷冰冰的“Out of memory: Killed process”。风扇呼呼转屏幕黑着我盯着串口输出心想这玩意儿真能在嵌入式上跑后来我换了Jetson Orin Nano显存8GB心想总该够了吧。结果Agent的推理延迟飙到单次调用8秒用户问一句“今天天气怎么样”Agent先调用工具查天气再调用LLM总结一来一回快20秒。这哪是Agent这是“龟速助手”。从那以后我明白嵌入式部署Agent不是把PC上的代码搬过来就完事。你得重新思考——哪些组件能砍哪些推理能省哪些内存能复用。这篇文章就记录我在这条路上踩过的坑和最终跑通的方案。二、嵌入式Agent的“不可能三角”在PC上Agent可以随意调用大模型、加载知识库、跑多轮对话。但在嵌入式设备上你面对的是内存天花板Raspberry Pi 4只有4GB RAMJetson Orin Nano虽然有8GB但显存和系统内存共享实际可用更少。算力瓶颈CPU推理7B模型单token生成时间在500ms以上Agent的ReAct循环会让延迟指数级增长。功耗限制Jetson Orin Nano满血模式功耗25WRaspberry Pi 4只有5W但散热和供电都是硬约束。我总结了一个经验公式Agent响应时间 ≈ (模型推理时间 × 工具调用次数) (上下文拼接时间 × 轮次)。在嵌入式上这个公式里的每一项都要优化到极致。三、选型不是所有板子都适合跑Agent3.1 Raspberry Pi 54GB/8GB适合场景轻量Agent模型参数不超过3B工具调用次数控制在2次以内。我实测过Raspberry Pi 5上跑Qwen2.5-1.5B-Q4_K_M推理速度约15 tokens/s勉强能接受。但一旦Agent需要调用外部API比如查数据库、发HTTP请求Python的GIL和网络延迟会让整体体验变差。别这样写直接用transformers库加载原始模型。Raspberry Pi的CPU扛不住FP16推理必须用llama.cpp或mlx的量化版本。3.2 Jetson Orin Nano8GB/16GB适合场景中等Agent模型参数3B-7B支持多工具调用和简单记忆。Jetson的优势在于GPUAmpere架构1024 CUDA核心。我试过用llama.cpp的CUDA后端跑Qwen2.5-7B-Q4_K_M推理速度约40 tokens/s比Raspberry Pi快3倍。但注意Jetson的显存和系统内存共享加载模型时如果显存不够会自动占用系统内存导致其他进程被挤爆。这里踩过坑我一开始用nvidia-smi看显存占用发现模型只占了4GB以为还有4GB空闲。但实际上Jetson的显存是动态分配的系统内存被占用了2GB用于缓存。最终我通过jetson_clocks固定GPU频率并设置llama.cpp的--no-mmap参数才稳定运行。3.3 其他选择Orange Pi 5RK3588芯片NPU算力6 TOPS适合跑端侧推理但生态不如Jetson成熟。Mac Mini M4虽然不算嵌入式但功耗低、性能强适合做边缘Agent服务器。我试过用mlx框架跑Qwen2.5-7B推理速度80 tokens/s延迟极低。四、模型选择量化是唯一出路在嵌入式上模型量化不是可选项是必选项。我试过几种量化方案4.1 GGUF llama.cpp这是最成熟的方案。GGUF格式支持多种量化级别Q4_K_M、Q5_K_M、Q8_0。我推荐Q4_K_M它在精度和速度之间平衡最好。代码示例别直接复制要根据你的板子调整# 这里踩过坑不要用默认的llama.cpp Python绑定它会在CPU上跑# 正确做法编译带CUDA或Metal后端的版本fromllama_cppimportLlama# 别这样写llm Llama(model_pathmodel.gguf) # 默认CPU推理慢到哭# 正确写法llmLlama(model_pathqwen2.5-1.5b-q4_k_m.gguf,n_gpu_layers-1,# 全部层放到GPUJetson上必须n_ctx2048,# 上下文长度别太大否则内存爆n_threads4,# Raspberry Pi上建议4线程Jetson上可以8线程verboseFalse# 关掉日志省点CPU)# 推理时注意别用默认的max_tokens512Agent场景下50-100就够responsellm(用户问今天天气怎么样,max_tokens100,temperature0.7,stop[\n,用户,助手]# 自定义停止词防止Agent无限生成)Raspberry Pi上的特殊处理编译llama.cpp时加上-DLLAMA_BLASON -DLLAMA_BLAS_VENDOROpenBLAS利用CPU的SIMD指令加速。实测推理速度提升30%。4.2 MLX仅限Apple Silicon如果你用Mac Mini M4做边缘部署MLX是首选。它原生支持Apple Silicon的GPU和NPU推理速度比llama.cpp快2倍。# 别这样写用transformers加载内存占用高# 正确写法importmlx.coreasmxfrommlx_lmimportload,generate model,tokenizerload(mlx-community/Qwen2.5-1.5B-4bit)# 这里踩过坑MLX默认使用float16但4bit量化后精度足够responsegenerate(model,tokenizer,prompt用户问今天天气怎么样,max_tokens100,temp0.7)4.3 ONNX Runtime 量化适合需要跨平台部署的场景。我试过将Qwen2.5-1.5B导出为ONNX再用INT8量化在Jetson上推理速度约30 tokens/s。但ONNX的算子支持不如llama.cpp全面某些Agent需要的特殊token如|im_start|需要手动处理。五、Agent框架裁剪别把PC上的那一套搬过来PC上的Agent框架LangChain、AutoGPT功能丰富但依赖太多。在嵌入式上我选择自己写一个极简的ReAct循环。5.1 极简Agent核心# 别这样写from langchain.agents import AgentExecutor # 依赖太多内存占用高# 正确做法手写ReAct循环classLightweightAgent:def__init__(self,llm,tools):self.llmllm self.tools{t.name:tfortintools}self.max_steps3# 这里踩过坑步数太多延迟爆炸defrun(self,user_input):# 这里踩过坑不要用完整的System Prompt太长会占上下文system_prompt你是一个助手。你可以使用工具{tool_names}。回答要简短。messages[{role:system,content:system_prompt.format(tool_names, .join(self.tools.keys()))},{role:user,content:user_input}]forstepinrange(self.max_steps):# 推理时注意限制输出长度防止Agent生成长篇大论responseself.llm(messages,max_tokens50)# 解析Action这里用简单的正则别用复杂的JSON解析ifAction:inresponse:actionself._parse_action(response)ifactionandaction[name]inself.tools:resultself.tools[action[name]].run(action[args])messages.append({role:assistant,content:response})messages.append({role:observation,content:result})continueelse:# 没有Action直接返回returnresponsereturn抱歉我无法处理这个请求。5.2 工具设计原则工具要轻量不要用requests库调用外部API改用urllib或aiohttp。我试过用requests查天气每次调用耗时1.5秒换成urllib后降到0.3秒。工具返回值要短Agent会把工具返回的内容拼接到上下文中如果返回一个JSON数组上下文会迅速膨胀。我强制工具返回不超过200字符的摘要。工具数量要少别超过5个。每多一个工具Agent的推理时间增加10%-20%。六、内存优化每一MB都要精打细算6.1 上下文窗口管理这是最大的内存杀手。Agent每轮对话都会把历史记录拼接到上下文中如果不加控制几轮对话后上下文长度就超过2048 tokens。这里踩过坑我一开始用滑动窗口保留最近N轮对话。但Agent会丢失早期信息导致重复调用工具。后来我改用“摘要最近对话”策略classMemoryManager:def__init__(self,max_tokens1024):self.max_tokensmax_tokens self.summaryself.recent_messages[]defadd_message(self,message):self.recent_messages.append(message)# 这里踩过坑不要每次添加都计算token数太慢# 改为每5条消息检查一次iflen(self.recent_messages)5:self._compress()def_compress(self):# 用LLM生成摘要别用大模型用小模型summary_promptf请用一句话总结以下对话{self.recent_messages}self.summaryself.llm(summary_prompt,max_tokens50)self.recent_messagesself.recent_messages[-2:]# 保留最近2条6.2 模型加载优化使用内存映射llama.cpp的--no-mmap会禁用内存映射导致模型全部加载到RAM。在Raspberry Pi上必须启用内存映射默认开启让操作系统按需加载。共享内存如果同时运行多个Agent实例比如一个处理用户输入一个处理后台任务可以用mmap共享模型权重。我试过在Jetson上同时跑两个Agent内存只增加了200MB。卸载不用的层llama.cpp支持--tensor-split参数可以把部分层卸载到CPU。在Jetson上如果显存不够可以把前几层放到CPU后几层放到GPU。七、性能调优从8秒到1.5秒的实战以Jetson Orin Nano上运行Qwen2.5-7B-Q4_K_M为例我做了以下优化7.1 推理加速批处理Agent的ReAct循环中每次推理的prompt长度不同。我改用llama.cpp的batch模式把多个prompt打包成一批利用GPU并行计算。实测吞吐量提升3倍。KV Cache复用Agent的多轮对话中前几轮的KV Cache可以复用。我修改了llama.cpp的Python绑定支持手动管理KV Cache。第一次推理后保存Cache后续推理直接加载省去重新计算的时间。量化注意力llama.cpp的Q4_K_M量化只量化了权重注意力计算还是FP16。我试过用--attention-typeflash_attn在Jetson上推理速度提升20%。7.2 工具调用优化预编译工具把工具函数编译成C扩展用ctypes调用。比如查天气的API我用C语言写了一个HTTP客户端编译成.so文件Python调用时延迟从0.3秒降到0.05秒。异步工具调用Agent需要调用多个工具时用asyncio并发执行。比如查天气和查日历可以同时进行总耗时从2秒降到1秒。7.3 系统级优化CPU亲和性在Raspberry Pi上把Agent进程绑定到特定CPU核心避免上下文切换。用taskset -c 0,1 python agent.py推理速度提升15%。关闭交换分区嵌入式设备的SD卡或eMMC写入速度慢交换分区会导致严重卡顿。我直接关闭交换用swapoff -a内存不够时让OOM Killer杀掉非关键进程。使用tmpfs把模型文件放到内存文件系统/dev/shm减少I/O延迟。注意Raspberry Pi的/dev/shm默认只有一半内存需要手动调整。八、实际部署案例一个能用的天气助手最终我在Jetson Orin Nano上部署了一个轻量Agent功能是查询天气和设置提醒。配置如下模型Qwen2.5-1.5B-Q4_K_MGGUF格式框架自写ReAct循环 llama.cpp工具查天气本地缓存、设置提醒SQLite上下文最大1024 tokens摘要最近2轮对话实测数据首次推理延迟1.2秒包括模型加载单轮对话延迟0.8秒包括工具调用连续5轮对话延迟3.5秒包括上下文压缩内存占用1.8GB模型1.2GB 运行时0.6GB这个延迟虽然比不上PC上的Agent但已经能用了。用户问“今天天气怎么样”Agent先查本地缓存如果缓存有直接返回没有则调用API然后生成回答整个过程在2秒内完成。九、个人经验性建议别追求大模型在嵌入式上1.5B模型比7B模型实用得多。精度差距可以通过更好的prompt设计弥补但延迟差距是硬伤。我见过有人用7B模型跑Agent结果用户等得不耐烦直接关掉了应用。工具调用是瓶颈很多人只优化模型推理忽略了工具调用的延迟。在嵌入式上一次HTTP请求可能比一次模型推理还慢。尽量用本地工具SQLite、文件系统少用外部API。上下文管理是核心Agent的“记忆”能力在嵌入式上是个伪命题。别想着让Agent记住所有历史用摘要最近对话的策略既省内存又省推理时间。监控是必须的部署后一定要加监控。我遇到过Raspberry Pi的SD卡写入速度下降导致模型加载变慢也遇到过Jetson的散热风扇停转导致GPU降频。用htop、nvidia-smi、iostat定期检查别等用户投诉才发现问题。接受“不完美”嵌入式Agent不可能像ChatGPT那样流畅。用户问复杂问题时直接告诉“我无法处理”比让Agent瞎猜要好。我设计了一个fallback机制如果Agent的推理时间超过5秒直接返回“请稍后再试”避免用户等待。最后嵌入式Agent的部署是一个不断妥协的过程。你需要在模型大小、推理速度、工具能力之间找到平衡点。别想着一步到位先跑通一个最小可行版本再逐步优化。毕竟能跑起来的Agent比跑不起来的完美方案强一百倍。