
上周五晚上我在回家的地铁上把最后一个工具函数写完然后在 iPhone 上调出那个测试用的快捷指令入口对着麦克风说了一句“把明天下午三点的会议改到四点半并且给参会人发一封邮件说明原因。”手机安静了大约四秒钟弹出一条权限确认框我点了允许日历事件变了邮件草稿也躺在了待发列表里。那一刻我才确定一个几乎完整的 AI Agent 真的可以在 iPhone 和 iPad 上跑起来而且不是玩具。这个项目我没用什么炫技框架核心就是三块一个负责任出推理和决策的大模型、一堆暴露给 Agent 调用的系统工具、以及一个能把“对话-决策-执行-再决策”串起来的移动端外壳。模型用的是 DeepSeek 和通义千问的 API工具层覆盖日历、提醒事项、备忘录、相册、定位这类 iOS 系统能力长期记忆则落在一个本地 SQLite 文件里。如果你正在琢磨“怎么把手上的 iPhone 变成真正的智能助理”或者你是刚准备入门 AI Agent 开发、想找一个能落地到手机上的架构思路这篇文章就是为你准备的。我不会绕弯子直接讲我踩过的坑和最终跑通的完整链路。1. 为什么非要在手机上跑 Agent我的三个真实场景很多人第一反应是Agent 在电脑上跑得好好的为什么要吃力不讨好地在手机上搭一套这个问题我一开始也问自己。但连续经历了几次手边没有电脑的时刻我才意识到移动端 Agent 不是桌面端 Agent 的降级版它要解决的是一类完全不同的需求。1.1 通勤路上接到突击任务桌上没有电脑第一次催生这个想法是在地铁上被拉进一个临时会议。领导丢过来一条消息查一下某位客户的上次回访时间确认合同里有没有续签条款然后把结论整理成两句话发到群里。平时在电脑前我可以开五个网页、翻 CRM、查邮件五分钟内搞定。但在地铁上手机里只有一堆 App没有一个能把这些动作串起来。我当时用一个普通 AI 对话 App 试了试它能把我的问题理解得很清楚但只能“给建议”不能“执行”。它没法去读我的备忘录没法帮我查通讯录里的历史记录更没法一口气把结论整理好发出去。我需要的是一个能连续调用多个工具的 Agent而不是一个只会说话的聊天框。1.2 线下见客户拍照、查档、回消息要一次搞定第二个场景来自一次拜访客户。在对方会议室里我拿到一份纸质合同要现场确定几个关键日期是不是跟我们的报价单一致。我掏出 iPhone 拍了合同想让它自动提取日期、跟我日历里的日程比对再给我一个能不能签的提醒。这种需求在桌面端其实很常见OCR 识别、上传文件、写个脚本比对。但在手机上最重要的是流程不能断相机拍照、图片识别、日历查询、日历新建、生成提醒一步接一步。任何一个中间环节需要我手动跳转复制粘贴体验就崩了。它逼着我把 Agent 做成一个真正能“摸到”系统数据的程序。1.3 移动端 Agent 不是“变小了的桌面 Agent”在动手之前我还想过另一个方案把一台小服务器长期连着手机通过 SSH 在远端跑 Agent手机只当遥控器。技术上完全可行但我很快就意识到这不是想要的体验。我要的是一台装着 GPS、相机、日历、通讯录、健康数据的随身设备直接参与决策而不是把手机当成一个哑终端。这里也顺便回答一个被问了很多次的问题DeepSeek 到底是 Agent 还是模型DeepSeek、GPT、通义千问这类是 LLM属于 Agent 的“大脑”负责理解和规划而 AI Agent 是整个执行系统包括大脑、工具、记忆、权限控制、运行循环。iOS 移动端缺的从来不是大脑而是后面那一整套执行机制。所以“在 iPhone 上跑 AI Agent”这个目标真正要做的是给 LLM 装上能操作 iOS 系统能力的手脚。2. 整体方案选型全本地、纯云、还是混合编排确定了要做后面就是选型问题。我先把目前主流的三条路线摆在一起对比因为很多人一上来就想“直接在手机上跑一个大模型”结果把方向带偏了。2.1 三条技术路线的对比清单我把方案分成三类全本地运行、纯云端调用、混合编排。它们的核心差异在于“模型跑在哪里、工具执行层放在哪里、密钥和权限由谁管理”。方案模型位置工具层位置优势劣势我的判断全本地iPhone/iPad 上跑量化模型全部本地隐私最好、断网可用设备要求高、推理慢、耗电严重适合试验不适合日常用纯云调用云端 API云端开发简单、模型能力强手机拿不到系统数据很难算真正的 Agent可以当聊天工具但不是 Agent混合编排云端 API 本地小模型辅助本地 App 云函数网关兼顾能力、系统权限、扩展性架构复杂、需要维护网关我最终选的方案这里说下全本地方案。iPhone 15 Pro 和 iPad Pro 的算力确实能跑 7B 甚至 13B 的量化模型但代价是明显发热、续航下降以及每秒几个 token 的生成速度。你可以拿它做离线摘要演示但要让 Agent 连续调用五六轮工具、每轮都要重新推理体验会非常煎熬。所以我的建议是如果刚入门 AI Agent别先去折腾本地大模型先把工具调用链路跑通后面再加本地模型做补充。2.2 最终架构iOS App 云函数网关 LLM API我的最终架构分成了四层每一层职责单一出问题时能快速定位。端侧 iOS App负责收集语音、文字、照片展示 Agent 的实时状态并在确认后执行系统工具。Agent 网关部署在云函数上保存会话状态维护工具注册表循环调度 LLM并负责把 App 上报的执行结果重新喂给模型。LLM APIDeepSeek、通义千问这类模型接口负责工具选择、参数抽取和最终回复生成。本地数据层App 内嵌 SQLite保存对话历史、长期记忆、工具执行审计日志。一句话描述流程就是用户输入进入 AppApp 把消息发给网关网关带着会话历史请求 LLM如果 LLM 返回的是一个工具调用指令网关就把这个指令转给 App 执行App 执行完把结果回传给网关网关把结果追加进对话后再次请求 LLM直到模型觉得任务完成并给出最终回复。当初很多人看到我把网关放在云函数而不是放在手机本地会觉得多此一举。但实际跑下来这个选择至少省掉了三个大麻烦第一API 密钥不用塞进 App 安装包不那么容易被别人逆向拿走第二Agent 多次工具调用的中间状态由网关统一维护手机切后台导致请求中断时任务还能继续第三我可以像写后端服务一样对工具流程做版本迭代不用每次改动都重新打包发版。2.3 关键组件的选型理由模型 API 方面我同时接了两家DeepSeek 的 deepseek-chat 做通用对话和工具调用通义千问的 qwen-plus 做备用因为偶尔会遇到某个模型服务波动双备份不至于让整个 Agent 卡死。它们都支持 OpenAI 兼容的函数调用格式切换成本很低。网关我用的是云函数平台选它不是因为能力最强而是因为它支持 HTTP 触发、环境变量注入密钥、日志查询而且没什么冷启动维护成本。严格来说这里不需要任何花哨框架一个能接收 POST 请求、能调外部 API 的 Python 函数就够了。手机端我用 SwiftUI 写了原生 App同时保留了快捷指令作为轻量入口。这样做的原因是日常高频小任务用快捷指令触发最快复杂任务才需要真正的 App 界面。后面我会具体讲两条入口各自怎么实现。3. 快速跑通的第一个版本快捷指令 云端 Agent 网关如果你刚准备入门 AI Agent我强烈建议先别急着写 iOS App。先用快捷指令配合一个云端网关把最核心的闭环跑通哪怕丑一点、卡一点都无所谓。这个闭环一旦成立后面所有工作都只是把每一环做得更精致。3.1 网关里最核心的“Agent 循环”怎么写网关的核心不是某个大模型 API而是一个循环请求模型判断返回结果是要“工具调用”还是“最终回答”如果是工具调用就执行、把结果塞回消息列表再请求一次模型循环往复。这是 Agent 和普通聊天接口最大的区别也是所有框架的内核。我第一版的代码非常克制核心逻辑大概长这样def run_agent(messages, available_tools): for step in range(MAX_STEPS): response llm.chat(messagesmessages, toolsavailable_tools) msg response.choices[0].message if not msg.tool_calls: return msg.content messages.append(msg) for call in msg.tool_calls: result execute_tool(call.function.name, call.function.arguments) messages.append({ role: tool, tool_call_id: call.id, content: result }) return Reached max steps.MAX_STEPS 我第一版设为 6后来觉得日用好用的 Agent 经常会连续调用十几步比如“读日历-改时间-查联系人-发邮件-写备忘”就已经五步了所以最终调到了 15。每一轮循环都会消耗 token这就是后面 token 失控问题的根源但那是后话。3.2 快捷指令这边的四个关键步骤快捷指令的最大优势是低门槛它可以把手机上的大部分系统能力快速暴露出来。我在快捷指令里做了四个步骤接收输入支持文本和听写两种方式用户可以用 Siri 或手动输入启动HTTP 请求把输入和用户身份 ID 一起 POST 到云函数网关解析返回值用“获取字典的值”把网关返回的内容拆出来反馈输出把结果用“显示通知”弹出来或者写入备忘录。这个流程下来你对着 iPhone 说一句话三五秒后就能收到一条通知内容已经是 Agent 处理完的结果。我第一版只接了一个自定义工具“获取当前时间”但用来测试整个链路已经足够因为 Agent 循环的核心——模型理解意图、生成工具调用、执行后继续推理——已经完整跑通了。3.3 雏形版本暴露的三个问题第一版能跑但离“可用”还很远问题也很集中。一是冷启动体验差。云函数临时启动时如果碰上首次冷启动加上大模型输出耗时总等待时间很容易超过 10 秒。快捷指令的通知会在结果出现后才弹出中间那段时间用户只能盯着屏幕干等像断线了一样。解决办法是给 App 端加流式输出和状态提示单纯用快捷指令做不了这件事。二是没有真正的系统工具。快捷指令虽然可以访问通讯录、日历但参数抽取和返回结果需要手工拼装跟 LLM 自动生成的 JSON 参数有一层隔阂。后面我改用原生 App 后用 Swift 代码直接接收工具参数、调用 EventKit 和 Contacts才真正把系统能力接进来。三是缺少权限确认。快捷指令调用系统数据时会有系统弹窗但那个弹窗说的是“快捷指令想访问你的日历”用户根本不知道是 Agent 指定的哪个操作、为什么要访问。这种信任问题会限制 Agent 的使用频率必须在原生层自己解决。4. 原生 App 补上“手和眼”工具协议、权限与多模态快捷指令验证了链路接下来就是用 SwiftUI 做一个正式 App。这个阶段我把大部分时间花在三件事上工具定义协议、权限模型、多模态输入。这三件事决定了一个 Agent 在移动端到底“能用”还是“只是演示”。4.1 工具描述必须写得像“给实习生写说明书”很多做过 Agent 开发的读者应该深有体会LLM 做工具调用时完全依赖工具描述的清晰度来选择该调哪个工具。如果描述写得模棱两可模型就会在几个相似工具之间随机选甚至拒绝调用。我在 App 内置了一套工具注册表每个工具都用 OpenAI 风格的 Function Calling 格式描述。一个典型例子{ type: function, function: { name: create_calendar_event, description: 在系统日历中创建一个新事件。当用户要求安排会议、日程、提醒、约会时使用。需要同时传入标题、开始时间和结束时间。如果只知道开始时间默认时长为1小时。, parameters: { type: object, properties: { title: { type: string, description: 事件标题必须简洁 }, start_time: { type: string, description: ISO8601格式的开始时间 }, duration_minutes: { type: integer, description: 持续时间默认60 }, notes: { type: string, description: 事件备注可空 } }, required: [title, start_time] } } }这里有个很重要的经验工具描述里的“触发条件”要写得非常明确。比如这个工具描述里专门写了“当用户要求安排会议、日程、提醒、约会时使用”模型才能准确地把“明天下午三点开会”映射到这个工具。如果你只写“创建日历事件”模型可能会在需要查已有日历时也误调用它。4.2 权限模型把手机交给 Agent但不能裸奔我刚跑通原生 App 后犯过一个错为了省事首次授权后就让 Agent 直接操作日历和提醒事项。结果有次我让它“整理一下我这周的会议”它自动创建了二十多个事件差点把日历塞满。那次之后我重新设计了权限模型原则很简单Agent 可以提议但不能擅自执行高危操作。我按风险把工具分成三类低风险查询天气、获取位置、读取日历、读取相册、打开网页默认放行但写入审计日志中风险创建日历事件、修改提醒事项、写入备忘录每次执行前弹确认框高风险发送短信、发送邮件、删除日历事件、修改系统设置除了弹确认框还要在通知里显示完整参数。所谓审计日志就是每次工具调用都记录“模型要求执行了什么操作、参数是什么、用户有没有同意”。我在 App 里做了一个类似隐私报告的页面用户随时能看到 Agent 最近一小时、一天、一周都干了什么。这一步极大提升了可用性因为用户不再怕这个程序背着自己乱动数据。4.3 多模态输入照片先压缩语音先转写别一股脑给模型iOS 上最值钱的多模态能力是相机、相册和麦克风。但我实际测试后发现如果直接把手拍照片原图传给模型token 消耗会迅速失控。手机拍的照片动辄 2000 万像素一张图转成 base64 后可能占几千甚至上万个 token连续几张图一次对话的 token 成本就翻了几十倍。我的处理方案是分层图片类输入先在本地做压缩和裁剪最长边缩到 1280 像素再作为图片消息传给支持视觉输入的模型如果只是识别文字就先用系统 Vision 框架做本地 OCR把提取出的文本丢给 LLM根本不传图片。语音则不用音频直接给模型而是用 Speech 框架先实时转写再作为文本进入 Agent 循环。这样既保留多模态能力又不会让请求体膨胀。Swift 里我写了一个很小的图片压缩函数func prepareImage(_ image: UIImage) - String? { let maxSide: CGFloat 1280 let scale min(maxSide / image.size.width, maxSide / image.size.height, 1) let newSize CGSize(width: image.size.width * scale, height: image.size.height * scale) UIGraphicsBeginImageContext(newSize) image.draw(in: CGRect(origin: .zero, size: newSize)) let resized UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return resized?.jpegData(compressionQuality: 0.8)? .base64EncodedString() }注意这里 scale 最多取到 1不会把小图强行放大。压缩后图片信息量基本保留但 token 消耗大幅下降。如果你做 Agent 时遇到“怎么输入几张照片就欠费”的问题多半就是漏了这一步。5. 记忆、技能与 MCP让 Agent 长成“老员工”工具调用跑通之后Agent 已经能完成单次任务但它还像一个“没有记忆的临时工”每次对话都是白纸一张不知道你上周提过什么偏好也不知道老板的英文名怎么拼。这一章解决的就是“长期靠谱”的问题。5.1 记忆层SQLite 存会话摘要存事实移动端做记忆我听到最多的是“上一个向量数据库”。但我的观点是个人手机上的 Agent90% 的记忆需求用 SQLite 就够别一上来就引入重数据库。我这里的两层记忆结构是短期的会话历史放在网关侧按 session_id 存最近 20 条消息超过就截断长期的“事实记忆”放在 App 本地 SQLite 表里每条记忆长这样CREATE TABLE memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, fact TEXT NOT NULL, category TEXT DEFAULT general, created_at TEXT DEFAULT (datetime(now)), last_accessed_at TEXT );每个任务结束后我会让 LLM 根据整段对话生成两条“值得长期记住的事实”然后插入这个表。比如“用户习惯把会议安排在周三下午”“客户公司的合同编号是 C-2024-011”。下次用户再提到相关话题时App 会把匹配的记忆作为系统提示词注入到请求里。为什么不用向量检索因为个人 Agent 的记忆量级很小几百条事实而已前端用关键词匹配再用 SQLite 的 LIKE 查询响应在毫秒级完全秒开。向量化只有在记忆达到数万条、不能靠关键词精准召回时才值得用。对移动端来说先跑起来比做架构审美更重要。5.2 MCP 的移动端落地把网关当作客户端App 当作适配层MCPModel Context Protocol是最近 AI Agent 社区特别热的话题。很多人把它理解成“只能在电脑服务器上跑的东西”但其实 MCP 的设计目标是标准化“模型-工具”之间的通信协议。在移动端没有现成 MCP 客户端可以直接安装的情况下我把网关当 MCP ClientApp 当 MCP Server 的适配层。具体落地方式是网关保存一份 MCP 工具列表当 LLM 选中某个 MCP 工具时网关不直接执行而是把结构化请求发给 AppApp 调用对应的 iOS SDK 能力再把结果以 JSON-RPC 格式回传。这样一来iOS 系统能力变成了标准 MCP 服务端Github 上的 MCP 工具社区生态也能慢慢接进来。一次工具请求在网关侧看起来是这样的{ jsonrpc: 2.0, id: 3, method: tools/call, params: { name: 日历查询, arguments: { start_date: 2025-03-10, days: 7, include_events: true } } }App 收到后执行再返回 events 数组。这里给想参考这个架构的读者提个醒别直接照搬桌面端 MCP 的所有规范移动端要关心电量、权限弹窗和用户体验所以 App 只实现 tools/list 和 tools/call 两个核心方法就足够了。5.3 Skill 是“可复用套路”把经验沉淀下来Skill 这个概念看起来高大上其实就是“可复用的任务模板”把一类固定任务的工具序列、参数默认值和校验规则提前写成配置文件。有了 SkillAgent 就不需要每次从零理解“预订单个会议室”的完整流程。我在网关侧用 YAML 维护了一批 Skill举一个例子name: schedule_meeting_room description: 预订会议室需要日期、时间段、人数 tools: - query_calendar - find_room_by_capacity - create_calendar_event - send_group_message defaults: duration_minutes: 60 rules: - 必须确认参会人数大于0 - 如果指定时间已有会议优先推荐临近空闲时段当用户说“帮我订个周五下午的会议室五六个人”网关先把这句话对到 schedule_meeting_room 这个 Skill再按顺序调用工具序列。这样比每次都让模型随机发挥稳定得多也是从“偶尔能用”到“天天敢用”的关键一步。6. 跑通之后的踩坑记录后台、token 与老设备这个项目最折腾我的不是架构设计而是一些看起来很小的实际问题。它们不解决Agent 就只能留在“演示”阶段。我把踩过的坑集中写在这里希望你能少走几趟。6.1 后台被挂起短任务前台跑长任务回调收iOS 对后台执行有严格限制App 切到后台后URLSession 请求会被系统挂起甚至取消。我的第一个版本就栽在这里Agent 正在执行连环工具调用屏幕一锁请求断了任务什么都没留下。我最后定的策略是能快速完成的前台任务保持屏幕常亮等结果需要长时间执行的任务比如“等半个小时后提醒我”不在 App 内后台等而是生成一个 task_id由云函数网关挂定时器或者对接日历提醒App 只负责在完成后用本地通知通知用户。简单说就是让 App 别硬扛后台把长任务的外部化。6.2 token 失控一段工具结果毁掉整轮对话Agent 连续多次调用工具时最大的隐性成本是 token 失控。比如“识别合同照片”会把图片 base64 放进上下文“读取日历一周事件”会返回几十个事件对象工具结果又会被继续发给模型做下一轮推理。这些内容叠满了之后一次任务可能烧掉几万 tokens账单数字让人不敢看。我后来做了三条硬性规则第一工具返回结果默认截断到 2000 字符超过部分只保留摘要第二图片类的多模态消息只在第一轮传给模型后续循环不再携带图片原始数据而是用文字描述当前图片分析结论第三每次请求只带最近 10 条消息更早的历史用“上一轮摘要”代替。这三条规则定下来后单次任务的 token 消耗下降大约 60%而且效果几乎没变。成本方面现在跑一天日常 Agent 任务深度使用大概也就是几毛钱到一块钱人民币左右。用 DeepSeek 这类性价比高的模型做工具调度用通义千问做复杂摘要整体比订阅各种语音助手还便宜。6.3 老设备与耗电20 分钟掉电 8% 的实测我在 iPhone 12 和 iPad mini 6 上都跑过这套系统。iPhone 12 的 A14 芯片对于 Agent 的 iOS 端运行已经完全够用因为主要推理在云端本地只是 UI、权限、压缩和 SQLite负载不大。真正耗电的反而是连续的麦克风转写和相册压缩。实测连续语音对话 20 分钟用了大约 8% 电量属于可接受范围。但设备明显会温温热这个发热主要来自屏幕常亮和网络持续请求。我加了一个低电量模式检测到电量低于 20% 时自动禁用多模态输入只保留纯文本对话语音输入也改成按需转写而非连续监听。还有一个兼容性建议开发时尽量别依赖最新版本的 iOS API。我的 App 用了 EventKit、Contacts、Speech 框架这些都是多年前就稳定的框架最低支持 iOS 16。这样你在 iPad 或者旧 iPhone 上部署时就不会被系统版本卡住。7. 现在它真的在替我干活5 个固定技能与后续计划这个 Agent 从搭建到现在已经稳定运行了几周我从最开始天天手动调试到现在每天只是看一眼审计日志。这里聊几句日常真正在用的东西给你一个更具体的想象。7.1 每天都在用的 5 个 Agent 技能一会议整理拍一张白板照片Agent 自动识别内容、提取待办事项、写入提醒事项并按优先级排序。二日程调整“帮我把这周的会重新排一下避开周四下午”它会遍历日历找出时间冲突生成新方案让我确认。三拍照记账每次吃完饭拍张单据Agent 提取金额和商家写入备忘录里的账本表格。四快递跟单把短信里的快递单号交给 Agent它调用网页搜索接口返回最新物流状态。五联系人整理定期扫描通讯录找出重复联系人、缺职位信息的人生成清理建议。这些都是很朴实的任务没有一个是“炫技”但它们每天实实在在地省了我十几分钟。对一个 AI Agent 项目来说能被天天使用是比任何架构评级都重要的成功指标。7.2 下一步想做的事下一步我有三个方向想继续推进第一把本地 embedding 接进来让离线状态也能做语义搜索提升记忆召回准确率第二加入 TTS 语音回复让 Agent 从“屏幕反馈”变成“说话反馈”更适合开车场景第三把整套工具定义和 Skill 配置做成可分享模板让其他有 iPhone 的人也能直接用。其实做到这一步我已经不太在乎它是不是一个“完美的 Agent”了我更在意的是它在真实生活里到底能不能被信任、被依赖。至少现在我手机里那个 App 已经是我的第一选择。