Siri AI背后的端云架构:本地大模型Agent与Server推理如何协同?

Siri AI背后的端云架构:本地大模型Agent与Server推理如何协同? WWDC 上 Siri AI 最吸引人的地方不是语音识别更快了也不是回答更自然了而是 Siri 开始像 Agent 一样去理解上下文、查找资料、调动应用内能力。要做到这一点真正核心的不是某一个参数更大的模型而是把 Server 推理、分布式调度和端侧大模型组合成一条完整链路。这条链路正是本地大模型 Agent 落地时绕不开的工程骨架。本文不打算复述发布会现场而是从工程视角拆解为什么 Siri 的 AI 离不开 Server 推理分布式推理在其中解决什么问题以及当我们自己要做“本地大模型 Agent”时该如何复刻这套端云协作的架构。适用读者包括移动端 AI 工程师、AI Infra 工程师以及正在做端侧模型接入的客户端开发。读完这篇文章你会得到一条清晰的架构主线什么时候用本地模型什么时候把请求放到 Server怎样设计路由和降级以及怎样用最小代码验证整个链路是否符合预期。1. Siri AI 的端云边界先厘清“谁在回答问题”1.1 Agent 化让 Siri 的输入输出都变复杂了传统语音助手的工作流程很像“命令解析”。用户说“设置明天早上八点的闹钟”系统做三件事语音识别、槽位提取、调用系统接口。整个过程中模型承担的角色很轻更多是规则和分类器的工作。当 Siri 走向 AI Agent 形态后输入不再是固定指令而是带有个人语境的自然语言。用户可能说“帮我把上周收到的航班行程整理一下顺便提醒我提前两小时到机场”。这句话至少包含两层意图查找邮件或聊天记录中的航班信息理解时间概念创建提醒。只靠一个端侧小模型完成这件事难度在于上下文检索和推理能力都受限只靠服务端大模型完成又无法读取本地应用数据也会带来隐私和延迟问题。所以Agent 化的 Siri 不可能由一个孤立模型支撑而是一组模型协作。端侧模型负责轻量判断、个人信息索引、动作执行服务端模型负责复杂生成、知识问答、长文本总结。我们需要在架构上把“谁在回答问题”拆开。1.2 本地模型和 Server 模型各承担什么任务本地大模型 Agent 通常部署在手机、PC 或边缘设备上。它的第一层价值是保护隐私因为原始文本和本地应用数据不需要离开设备第二层价值是降低延迟因为不是每个请求都要跨网络等待第三层价值是离线可用在无网环境下仍能完成简单任务。但本地模型的参数规模受限。手机内存有限芯片功耗有限模型太大就无法实时推理。Server 模型则没有这个问题它可以承载几千亿参数的稠密模型也可以接入外部知识库和工具集。代价是网络延迟、服务成本和隐私风险。在实际系统中判断权必须动态分配。一个简单直观的分配原则是高频、短路径、隐私敏感的操作优先留在本地。低频、复杂推理、需要最新知识、需要大模型长上下文处理的任务放到 Server。本地模型无法判断时可交给 Server 做兜底但要把用户明确授权的上下文一起发送。1.3 WWDC 场景里的推理链路可以如何拆分从 WWDC 展示的 Siri AI 行为来看一个看起来一气呵成的能力通常可以拆成如下原子任务用户输入 - 端侧语音识别 - 端侧意图判断 - 本地上下文检索邮件、消息、日历、应用状态 - 请求路由 - Server 大模型复杂推理 / 工具调用 - 返回结构化结果 - 端侧执行动作这个链路里的每一步都可能由不同进程或不同设备完成。端侧不是把整段原始语音直接发给服务端而是先经过本地模型做初步加工Server 也不是把所有能力都包揽而是只负责本地处理不了的那一段推理。把“谁在回答问题”想清楚之后接下来的问题才真正有价值一次请求如何在多台计算节点之间协作这就要进入分布式推理的范畴。2. 分布式推理的真正作用是把“一次智能”变成“一组可靠任务”2.1 从一次 Agent 请求看分布式推理“分布式推理”听起来像是要把一个大模型分到多张 GPU 上做并行计算。这确实是其中一个方向但放在 Siri 和本地 Agent 的语境里它更常见的是另一种含义把一次推理任务按阶段、按负载、按上下文来源分发给不同计算节点处理。例如用户问“这篇 20 页文档的结论是什么帮我列成三条要点然后存到备忘录里。”如果全部用 Server 模型处理就必须先把 20 页文档传到服务端。此时网络传输时间长隐私数据暴露面大服务端上下文也可能被撑满。合理的做法是端侧先对文档做分页和压缩提取关键段落。端侧用本地模型判断任务属于“长文本总结 创建备忘录”。将压缩后的关键段落以及任务模板发送给 Server。Server 执行总结并返回三条要点。端侧再把要点写入备忘录。在这个过程中不是所有请求都经过同一台服务器也不是所有数据都原样上传。请求被拆分成了多个子任务每个子任务由最合适的计算节点执行这就是分布式推理在 Agent 场景里的直接体现。2.2 分布式推理与 API 调用的差异很多开发者在做本地 Agent 时最容易犯的错误是只把 Server 模型当作一个“更聪明的 API”。代码大概长这样设备端拿到用户问题后直接把整段文本 POST 给一个远程模型接口取回结果再拼装成 UI 文案。这种做法的问题在于没有上下文分级本地明明已有的日历、通讯录数据也要重新编码发给服务端。没有路由策略所有请求都走大模型延迟和成本不可控。没有降级链路Server 出问题时整个 Agent 直接不可用。没有可观测性请求失败后很难定位是网络、模型还是工具调用的问题。分布式推理框架要解决的是可靠性、可调度性和资源利用率。它要求我们区分“哪个模型处理这个任务”“任务需要哪些上下文”“失败之后如何降级”“结果如何返回端侧”。这套机制是普通 API 调用不包含的。为了更直观地说明差异可以把三种常见方案放在一起对比方案请求路径优势短板中心化单体推理端侧 - 服务端大模型 - 返回实现简单模型能力强延迟高数据容易过度暴露成本不可控纯端侧推理本地模型完成所有任务隐私好延迟低离线可用复杂任务效果有限设备资源压力大端云分布式推理端侧识别 - 路由 - 端侧/服务端协作隐私、效果、成本可平衡系统复杂度最高需要链路设计和监控WWDC 所展示的 Siri AI 能力本质上更接近第三种方案。这种方案不是把 Server 当作一个孤立接口而是把 Server 推理变成整个 Agent 调度系统中的一个可编排服务。2.3 请求路由与降级的切入时机要落地端云分布式推理第一步不是训练模型而是设计“请求路由”。路由层决定一个问题应该交给本地、远端还是两者协作。路由不一定是复杂的机器学习模型。初期可以设计成关键词规则涉及操作、提醒、打开应用等固定意图 - 走本地 Agent。涉及总结、对比、分析、生成文本等高级语义 - 走 Server。涉及个人资料且需要保护隐私 - 先在本地做匿名化和摘要再决定是否请求 Server。路由决策之后才是降级策略。假设 Server 服务超时系统是否能返回“当前网络不稳定请稍后再试”是不够的更理想的机制是先尝试本地模型生成一个保守回答再提示联网能力受限。再往下是任务编排。例如要完成“总结邮件并提醒我开会”系统需要先调用邮件检索工具再把检索结果作为上下文传给模型。这种多次调用在分布式系统中必须带上同一个 trace ID才能拼出完整执行链路。3. 搭一个最小“端侧 Agent Server 推理”可运行示例理论讲完下面用一个最小可运行示例演示端侧 Agent 与 Server 推理如何协作。示例不追求复现 Siri 的完整能力而是把一个可扩展的骨架拿出来本地意图识别、远程大模型调用、工具执行、降级处理。3.1 环境准备示例使用 Python 3.10 和两个模型入口本地模型使用 Ollama 启动例如qwen2.5:1.5b或llama3.2:1b。这类小模型足够做意图分类和简单回答。Server 模型使用一个兼容 OpenAI 接口的服务可以是 vLLM、Ollama 的远程部署也可以是企业内部模型网关。为了演示先把它指向http://127.0.0.1:8000/v1。如果暂时没有远程模型服务也可以通过本地 FastAPI 写一个 mock 接口甚至直接把远程配置指向第二个 Ollama 实例。生产环境则必须替换成正式推理服务。依赖安装pip install openai uvicorn fastapi3.2 目录结构与核心配置项目结构可以按下面这种方式组织agent_demo/ ├── config.py ├── router.py ├── agent.py ├── tools.py ├── server_mock.py └── main.pyconfig.py统一管理本地模型和 Server 模型的地址、超时时间、模型名。这样后面调整环境时不需要在业务代码里到处改 URL。# config.py import os AGENT_CONFIG { local: { base_url: os.getenv(LOCAL_LLM_BASE_URL, http://127.0.0.1:11434/v1), api_key: os.getenv(LOCAL_LLM_API_KEY, ollama), model: os.getenv(LOCAL_LLM_MODEL, qwen2.5:1.5b), timeout: 5, }, remote: { base_url: os.getenv(REMOTE_LLM_BASE_URL, http://127.0.0.1:8000/v1), api_key: os.getenv(REMOTE_LLM_API_KEY, EMPTY), model: os.getenv(REMOTE_LLM_MODEL, qwen2.5:72b-instruct), timeout: 20, }, }关键点在于本地和远端都使用 OpenAI 客户端调用但 base_url 指向不同推理服务。模型名称、超时时间和密钥都通过环境变量覆盖便于部署到不同环境。3.3 本地意图识别与路由代码路由层是端云协作的第一个入口。演示代码先使用关键词判断实际项目里可以替换成一个小型意图分类模型。下面是一个可运行的router.py# router.py LOCAL_ACTIONS [提醒我, 创建提醒, 倒计时, 打开, 记录] REMOTE_ACTIONS [总结, 概括, 分析和对比, 写邮件, 翻译, 推荐] def route_by_user_message(user_message: str) - str: if any(action in user_message for action in LOCAL_ACTIONS): return local if any(action in user_message for action in REMOTE_ACTIONS): return remote # 无法明确判断时默认交给远端强模型兜底 return remote这里的意图判断为什么不适合用真正的 Agent 完成因为在最终架构里“本地模型负责意图判断”本身也是一种推理任务。如果系统已经部署了一个足够好的端侧小模型完全可以直接让本地模型完成分类并输出 JSON。演示中使用关键词是为了让人一眼看懂路由逻辑。3.4 远端推理客户端与工具执行agent.py负责真正编排一次请求。它先做本地路由如果目标是 local则调用本地动作工具如果目标是 remote则把用户问题、本地检索到的上下文和工具定义一起发给 Server 模型。# agent.py from openai import OpenAI import config import tools import router def build_client(node_type: str) - OpenAI: cfg config.AGENT_CONFIG[node_type] return OpenAI( base_urlcfg[base_url], api_keycfg[api_key], timeoutcfg[timeout], ) def run_local_action(user_message: str): if 提醒 in user_message: return tools.create_reminder(user_message) if 记录 in user_message: return tools.add_note(user_message) return {status: unknown_local_action, message: user_message} def run_remote_agent(user_message: str, local_context: str) - str: client build_client(remote) cfg config.AGENT_CONFIG[remote] current_tools tools.get_tool_schemas() response client.chat.completions.create( modelcfg[model], messages[ {role: system, content: 你是一个任务型 Agent只在必要时调用工具。}, {role: system, content: f下面是端侧检索到的本地上下文{local_context}}, {role: user, content: user_message}, ], toolscurrent_tools, tool_choiceauto, ) return response.choices[0].message这里调用工具的方式不是让模型自由发挥而是通过tools参数把可选操作告诉模型。模型可以选择调用工具也可以直接生成文本。tools.py中定义的工具结构需要保持固定否则模型无法稳定输出。# tools.py def add_note(content: str): return {action: add_note, content: content} def create_reminder(content: str): return {action: create_reminder, content: content} def get_tool_schemas(): return [ { type: function, function: { name: add_note, description: 创建一条系统备忘录, parameters: { type: object, properties: { content: {type: string, description: 备忘录内容} }, required: [content], }, }, }, { type: function, function: { name: create_reminder, description: 创建一条提醒, parameters: { type: object, properties: { content: {type: string, description: 提醒内容} }, required: [content], }, }, }, ]3.5 在本地快速跑通启动 Ollama 并加载本地模型ollama serve ollama run qwen2.5:1.5b如果远程模型服务还没有起来可以先启动server_mock.py作为演示接口# server_mock.py from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): model: str messages: list app.post(/v1/chat/completions) def chat_completion(req: ChatRequest): text req.messages[-1][content] if req.messages else return { choices: [ { message: { role: assistant, content: f[mock] 已经收到复杂任务{text[:50]}, } } ] }启动 mock 服务uvicorn server_mock:app --host 0.0.0.0 --port 8000然后在项目根目录写一个入口文件main.py# main.py from agent import run_local_action, run_remote_agent import router if __name__ __main__: user_message input(输入: ) decision router.route_by_user_message(user_message) print(f[路由] - {decision}) if decision local: result run_local_action(user_message) print([本地结果], result) else: context 日历今天下午 3 点有产品评审会消息客户希望提前一天交付。 result run_remote_agent(user_message, local_contextcontext) print([服务端结果], result.content if hasattr(result, content) else result)运行python main.py输入“提醒我明天下午四点开会”路由会选择 local结果为创建提醒。输入“总结这周的会议记录并生成行动项”路由会选择 remote调用 mock 服务返回结果。4. 验证这个架构是否正确不只看“能不能回答”本地 Demo 跑通后很多人会立刻进入“效果调优”阶段反复换提示词。更值得做的是先验证链路本身是否稳定、可观测、可降级。4.1 给每次请求绑定 ID 并观察链路在演示代码里路由结果直接打印出来已经能大致判断请求走向。生产级验证需要给每次请求绑定一个trace_id并在端侧、路由、Service 模型、工具调用四个环节输出结构化日志。import uuid request_id str(uuid.uuid4()) print(frequest_id{request_id} actionroute target{decision}) print(frequest_id{request_id} actionremote_llm statusstart) # 调用远程模型 print(frequest_id{request_id} actionremote_llm statusdone elapsed_ms2300)这样一旦某个请求延迟很高就能直接看到时间是耗在远端模型还是耗在本地工具调用。没有这个链路信息分布式推理就只剩“分布”没有“推理”。4.2 功能用例与预期可以用一张表整理最小验证用例输入路由预期执行预期验证点提醒我明天上午十点开会local创建提醒验证关键词规则是否正确命中本地动作总结这份文档并输出三条要点remote调用服务端模型验证远端模型工具调用和上下传递我下周去深圳出差帮我查当地天气remote模型选择查询天气工具验证工具 schema 是否能被模型正确理解服务器返回 500remote触发降级逻辑验证远端不可用时能否给出提示或使用本地模型兜底这些用例中第四类最容易被忽略。不要在真实生产环境里让用户等待一个必然失败的请求建议给远程调用设置断路器或超时兜底。4.3 网络断开时观察降级行为为了验证降级可以手动停掉server_mock然后输入一个 remote 意图。当前演示代码会因为连接失败直接抛异常。实际项目需要捕获远程调用异常try: result run_remote_agent(user_message, local_contextcontext) except Exception as exc: fallback_text f网络不可用已切换到本地问答。异常{exc} local_client build_client(local) fallback local_client.chat.completions.create( modelconfig.AGENT_CONFIG[local][model], messages[{role: user, content: user_message}], ) print([降级结果], fallback.choices[0].message.content)这样至少能保证在无网环境下仍然返回一个本地模型生成的结果。至于结果质量能不能满足用户是第二个问题第一个问题是不让链路直接崩溃。5. 常见问题与排查路径在端云协作的 Agent 系统里问题往往不是单一的模型效果差而是链路中的某一环节出现故障。下面整理一些高频问题。5.1 频繁超时请求发到 Server 后迟迟没有响应现象是用户感觉“Siri 转圈时间很长”日志显示 remote_llm 调用耗时超过预设阈值。常见原因包括远端模型输入过长排队时间变长。网络连接不稳定或经过代理转发后延迟放大。Server 端模型配置了较长的 max_tokens再加上工具调用循环导致总时间被放大。检查顺序建议先看服务端日志确认请求是否到达再看模型服务是否存在排队然后看返回结果的 token 长度最后检查是否因为工具调用进入多次循环。解决方式是在路由前先压缩端侧上下文限制工具调用轮数并缩短 Server 模型超时时间。生产环境还要给推理服务设置排队上限和负载告警。5.2 本地模型总是被路由选中但回答质量太差现象是一些明显需要复杂推理的问题被关键词规则错误归类到 local导致用户得到很短的模板化回答。这种情况不是模型本身问题而是路由规则设计得太粗糙。真实系统不应该只依赖少数关键词而应该使用本地语义分类模型并给路由结果附带置信度。当置信度低时默认走 remote而不是强行本地回答。5.3 模型没有调用工具直接输出一段文字现象是明明给模型传了tools但模型回答“我不能访问你的备忘录”而不是调用工具。可能原因是模型版本过老或者模型没有真正看到工具描述。检查请求体里的tools和tool_choice是否生效检查模型服务是否支持 function calling检查工具 JSON Schema 是否过长被截断。5.4 上下文过大导致服务端请求失败现象是用户粘贴了一整篇长文请求直接被 Server 拒绝或报 context length exceeded。核心原因是端侧没有做压缩。长文本先在端侧按段落切分使用本地小模型抽取关键信息只把摘要和必要原文发送到 Server。原始长文本不要直接进网络。针对这些问题的排查路径可以整理成一张表问题现象常见原因检查方式处理建议请求到 Server 后超时输入过长或模型排队看模型服务日志、请求耗时分布增加超时、压缩上游上下文、限流本地模型回答质量差路由规则把复杂任务分到了本地打印路由命中原因和置信度使用语义路由低置信度交给 Server模型不调用工具模型版本不支持或工具 schema 错误打印请求体确认 tools 参数更换支持 function calling 的模型Server 返回 401 / 403API key 配置错误或网关鉴权失败查看网关侧记录将凭据移入环境变量或密钥管理服务同一问题反复请求不同模型路由结果不稳定增加 trace 日志基于用户历史行为做会话级路由缓存6. 从 Demo 走向生产还需要补齐哪些能力演示代码证明了“端侧 Agent Server 推理”是可运行的但它离 Siri 级别的稳定性还有很长距离。从 Demo 到生产至少要补齐以下几块。6.1 配置外置化与密钥管理不要继续在config.py里写死模型地址。生产环境建议使用环境变量、配置中心或密钥管理服务把 API key、base_url、模型名按环境分开。发布时也不要把本地测试配置带入生产镜像。6.2 会话管理和记忆要有边界Siri 要理解“你刚才提到的那封邮件”必须跨请求保存会话状态。这个状态可以存在端侧也可以存在服务端但必须明确规定哪些数据能进入记忆哪些数据只允许在单次请求内存在。隐私规则要落在代码里而不是靠开发者的自觉。推荐方案是端侧维护一个结构化记忆库只把“经过用户同意且需要外部推理”的上下文发送给 Server。Server 侧不保留原始会话只处理一次请求。6.3 请求路由的可观测性分布式推理系统的故障定位依赖 trace。建议在路由、模型调用、工具调用、结果返回四个关键节点都输出结构化事件。字段最少包括request_id、node_name、model_name、start_time、end_time、status、error_code。有了这些数据才能回答三个问题一次请求平均耗时多少各环节耗时占比如何Server 模型调用失败率是否超过阈值6.4 模型更新与灰度发布Server 端模型会频繁升级端侧模型也会因为包体更新而需要灰度替换。系统要支持按用户比例把请求路由到不同版本模型并比较回答质量、延迟和兜底命中率。不要在一个版本上“一把梭”。6.5 把 Agent 能力当成可插拔模块从长线看把意图识别、路由、工具调用、远端模型解耦成独立模块比把所有逻辑塞进一个 Agent 类里更可维护。每个模块只做一件事模块之间通过标准输入输出协作。这样即使 WWDC 上出现新的 Siri 能力底层架构框架也可以保留复用。对新手最有价值的练习不是急着优化某一个提示词而是先用最小链路把“本地识别 - Server 推理 - 工具执行 - 降级”完整跑一遍再把网络异常、模型变更、上下文超限这些问题逐个加进来。只有把异常链路也当成功能来设计本地大模型 Agent 才能真正从演示走向实用。