news 2026/10/28 7:35:34

将本地AI管家装进iPhone主屏:PWA+SSE+nginx反向代理实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
将本地AI管家装进iPhone主屏:PWA+SSE+nginx反向代理实战

1. 从桌面到口袋:为什么我要给 AI 管家做手机版

我本地跑着一套 AI 管家系统,平时在电脑浏览器里用得挺顺手,查资料、整理笔记、跑一些自动化任务都靠它。但问题也很明显:人不可能一直坐在电脑前。很多时候我人在外面,突然想到一个点子,或者需要让它帮我查个东西、记一笔,掏出手机却只能干瞪眼。这种割裂感用久了实在难受,于是我就动了念头——把这套东西搬到手机上,最好能像原生 App 一样,直接装进 iPhone 主屏,点开就用。

这个项目的核心目标其实就一句话:让原本只在桌面浏览器里跑的 AI 管家,变成一个可以装进 iPhone 主屏、随时点开就能用的移动端入口。听起来简单,但真做起来,涉及的东西一点不少。前端要适配手机屏幕和 iOS 的交互习惯,后端要处理流式输出,中间还隔着一层反向代理,网络请求、跨域、证书、超时,每一个环节都可能出问题。

我先把整体思路理一遍。AI 管家本身是一个 Web 服务,跑在我本地的一台机器上,对外提供 REST 接口和 SSE 流式接口。SSE 这个东西很关键,因为 AI 回复是一段一段吐出来的,不是一次性返回,用普通的请求-响应模式体验会很差,必须用流式推送才能做到"边生成边显示"。而 iPhone 上要访问这个服务,最直接的方式就是用 Safari 打开网页,然后通过"添加到主屏幕"把它变成一个类似 App 的图标。这样不用上架 App Store,不用折腾签名,成本最低,效果也够用。

那为什么不用原生开发或者套壳打包?我算过一笔账。原生开发要单独写一套 iOS 代码,维护成本翻倍;套壳打包虽然能上架,但要走审核流程,而且我的服务是跑在本地内网的,打包出来的 App 根本连不上。相比之下,PWA(渐进式 Web 应用)加"添加到主屏幕"这条路,是投入产出比最高的方案。它本质上还是一个网页,但通过一些配置,可以让它在 iPhone 上表现得接近原生应用:有独立图标、全屏显示、没有浏览器地址栏干扰。

适合谁来参考这篇内容?我觉得有三类人。第一类是自己折腾过本地 AI 服务、想把它扩展到手机上的开发者;第二类是对 PWA、SSE、反向代理这些技术点感兴趣,想找个完整案例练手的同学;第三类是做移动端 Web 开发,想了解 iOS 上"添加到主屏幕"这套机制到底怎么玩的人。不管你基础如何,只要跟着思路走,核心环节我都会把参数和踩坑点讲清楚。

2. 整体架构设计与技术选型拆解

2.1 为什么是"网页加主屏图标"而不是原生 App

先说说方案选型这件事,因为这是整个项目的地基,选错了后面全是坑。我一开始也纠结过要不要做个真正的 iOS App,毕竟"装进主屏"听起来就该是 App 的活儿。但仔细一想,我的 AI 管家服务是跑在本地网络里的,没有公网服务器,也没有备案域名。这种情况下,原生 App 打包出来,要么连不上服务,要么得把服务暴露到公网,安全性和成本都不划算。

而网页方案的优势就很明显了。iPhone 的 Safari 支持"添加到主屏幕",添加之后,这个网页会以一个独立图标的形式出现在桌面上,点开之后是全屏运行,没有地址栏,看起来和原生 App 几乎没区别。更关键的是,它访问的还是同一个 URL,我的本地服务不需要做任何改动就能被访问到。这就好比你在家里装了一部内线电话,手机连上同一个网络就能拨通,不需要去电信局申请一条外线。

当然,网页方案也有它的局限。比如它没法调用一些深度的系统能力,推送通知在 iOS 上的支持也比较晚才完善。但对于我的需求——查看 AI 回复、发送指令、看流式输出——这些网页完全能胜任。所以最终我确定了这个方向:用 Web 技术实现,通过 Safari 添加到主屏幕,做成一个 PWA 形态的移动端入口。

2.2 反向代理这一层为什么绕不开

接下来说说 nginx 这一层。很多人会问,我的服务直接跑在某个端口上,手机直接访问这个端口不就行了,为什么要加个 nginx?这个问题我一开始也想过,但实际做下来发现,nginx 这一层几乎是绕不开的。

第一个原因是端口管理。我的 AI 管家可能不止一个服务,前端一个端口,后端 API 一个端口,可能还有别的辅助服务。如果每个都暴露一个端口,手机上要记一堆地址,很乱。用 nginx 做反向代理,可以把所有服务统一到一个入口,比如都用 80 或者 443 端口,通过不同的路径来区分。这就像一栋楼只有一个大门,里面各个房间怎么分,进门之后看指示牌就行。

第二个原因是 HTTPS。iOS 上很多能力,包括"添加到主屏幕"之后的某些行为、以及 SSE 的稳定性,都对 HTTPS 有要求。虽然本地网络用 HTTP 也能跑,但为了体验一致,我倾向于配上 HTTPS。nginx 处理证书、做 TLS 终止,比在每个后端服务里单独配要方便得多。

第三个原因是 SSE 的特殊性。SSE 是长连接,数据是一段一段推过来的。nginx 默认会对响应做缓冲,这会导致 SSE 的数据被攒着一起发,流式效果就没了。所以必须在 nginx 里针对 SSE 的路径关掉缓冲,这个配置后面我会详细讲。如果没有 nginx 这一层,直接连后端,反而没这个问题,但一旦加了代理,就必须处理。

2.3 SSE 流式接口在整个链路里的位置

SSE,全称 Server-Sent Events,是这套系统里技术含量最高的部分。普通的 HTTP 请求是"我问一句,你答一句",答完连接就断了。但 AI 生成回复是逐字逐句出来的,如果等全部生成完再返回,用户要盯着空白屏幕等好几秒,体验很差。SSE 解决的就是这个问题:服务器和客户端建立一个长连接,服务器每生成一小段内容,就立刻推给客户端,客户端收到就显示,做到"边生成边看"。

这个机制在桌面浏览器上跑得好好的,但搬到手机上,尤其是经过 nginx 代理之后,就容易出问题。最常见的就是超时。SSE 连接如果一段时间没有数据推送,中间的任何一层——浏览器、nginx、后端框架——都可能认为连接空闲了,然后把它掐断。我在热搜词里看到有人遇到 "stream disconnected before completion: idle timeout waiting for sse" 这个报错,这就是典型的空闲超时问题。

所以整个链路是这样的:iPhone 上的 Safari 发起请求,经过 nginx 反向代理,转发到后端的 AI 管家服务,后端通过 SSE 把生成的内容流式推回来,nginx 再转发给 Safari,Safari 实时渲染。这条链路上每一环都要配置正确,尤其是 nginx 的超时和缓冲设置,以及后端的心跳机制,缺一不可。

3. 核心环节实操:从服务配置到手机访问

3.1 nginx 反向代理配置的完整写法

nginx 的配置是整个项目里最容易出错的地方,我把它单独拎出来讲。假设我的 AI 管家后端跑在127.0.0.1:8000,前端静态页面放在/var/www/ai-butler,我要通过 nginx 统一对外提供服务,同时保证 SSE 正常工作。

先看完整的配置结构:

server { listen 443 ssl; server_name ai-butler.local; ssl_certificate /etc/nginx/certs/ai-butler.crt; ssl_certificate_key /etc/nginx/certs/ai-butler.key; # 前端静态资源 location / { root /var/www/ai-butler; index index.html; try_files $uri $uri/ /index.html; } # 普通 REST 接口 location /api/ { proxy_pass http://127.0.0.1:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # SSE 流式接口,单独配置 location /api/stream/ { proxy_pass http://127.0.0.1:8000/stream/; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 关键:关闭缓冲 proxy_buffering off; proxy_cache off; chunked_transfer_encoding on; # 关键:延长超时 proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_connect_timeout 60s; } }

这里有几个点必须解释清楚,不然照抄也可能出问题。

proxy_buffering off是 SSE 的命门。nginx 默认会把后端的响应先缓冲起来,攒到一定大小再发给客户端。对于普通请求这没问题,甚至能提升性能。但对 SSE 来说,这就等于把"流式"变成了"批量",用户看到的还是一大坨内容突然出现,失去了逐字显示的效果。关掉缓冲之后,后端推一段,nginx 就转一段,实时性才能保证。

proxy_read_timeout 3600s是防超时的。默认值只有 60 秒,如果 AI 思考时间比较长,或者两次推送之间间隔超过 60 秒,nginx 就会主动断开连接。设成 3600 秒,基本能覆盖绝大多数场景。当然,光靠延长超时还不够,后端最好还要有心跳机制,这个后面讲。

proxy_http_version 1.1和proxy_set_header Connection ""这两行是配套的。SSE 依赖 HTTP/1.1 的长连接特性,而默认的代理可能用的是短连接。把 Connection 头清空,是为了让 nginx 使用长连接和后端通信,避免每次推送都重新建连。

注意:如果你的 nginx 版本较老,chunked_transfer_encoding这个指令可能不支持,可以去掉,不影响核心功能。另外,证书路径要换成你自己的,本地测试可以用自签名证书,但 iOS 对自签名证书比较严格,可能需要在手机上手动信任,这个后面会讲。

3.2 后端 SSE 接口的心跳与超时处理

nginx 配好了,后端也不能掉链子。SSE 连接最怕的就是"沉默",一旦一段时间没有数据推送,链路上的任何一环都可能把它判定为空闲然后断开。解决办法就是心跳机制:即使没有实际内容要推,也定期发一个空消息或者注释行,告诉链路"我还活着"。

以 Python 后端为例,一个典型的心跳实现是这样的:

import time import json def sse_stream(): last_heartbeat = time.time() while True: # 检查是否有新内容 chunk = get_next_chunk() if chunk: yield f"data: {json.dumps(chunk)}\n\n" last_heartbeat = time.time() else: # 超过 15 秒没有内容,发心跳 if time.time() - last_heartbeat > 15: yield ": heartbeat\n\n" last_heartbeat = time.time() time.sleep(0.1)

这里的心跳用的是 SSE 的注释语法,也就是以冒号开头的行。这种行会被客户端忽略,不显示给用户,但能起到保活的作用。心跳间隔我设的是 15 秒,比 nginx 的 60 秒默认超时短很多,留足了余量。即使网络有波动,也不至于被误判为空闲。

除了心跳,后端还要注意响应头。SSE 的标准响应头包括Content-Type: text/event-stream、Cache-Control: no-cache、Connection: keep-alive。这几个头缺一不可,尤其是Content-Type,如果写错了,浏览器根本不会按 SSE 来解析。

return Response( sse_stream(), mimetype="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" } )

那个X-Accel-Buffering: no是给 nginx 看的,相当于在响应头里再强调一遍"别缓冲"。虽然我们在 nginx 配置里已经关了缓冲,但加上这个头相当于双保险,某些场景下 nginx 会优先看这个头。

3.3 前端适配 iOS 的关键细节

前端这块,核心是两件事:一是让页面在 iPhone 上看起来正常,二是让"添加到主屏幕"之后的行为符合预期。

先说视口。iPhone 的屏幕尺寸五花八门,从 SE 的小屏到 Pro Max 的大屏,必须用响应式布局。基础的 viewport meta 标签是必须的:

<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">

viewport-fit=cover这个属性是为了适配 iPhone 的刘海屏和底部横条。没有它的话,页面内容可能会被刘海挡住,或者底部留出奇怪的白边。配合 CSS 里的env(safe-area-inset-*)变量,可以把内容安全地放在可视区域内。

然后是"添加到主屏幕"的配置。iOS 的 Safari 识别几个特定的 meta 标签,来决定添加到主屏后的表现:

<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-title" content="AI管家"> <link rel="apple-touch-icon" href="/icons/icon-180.png">

apple-mobile-web-app-capable设为 yes,是让添加后的应用以全屏模式运行,不显示 Safari 的地址栏和工具栏。apple-mobile-web-app-status-bar-style控制顶部状态栏的样式,black-translucent会让状态栏半透明,内容可以延伸到状态栏下面,视觉效果更沉浸。apple-mobile-web-app-title是主屏图标下面显示的名字,不设的话会默认用网页标题,可能太长被截断。apple-touch-icon是图标,iOS 要求至少 180x180 像素,不然会显示一个模糊的截图。

实操心得:iOS 对apple-touch-icon的缓存非常顽固。如果你换了图标但手机上还是旧的,别怀疑人生,先把主屏上的图标删掉,清一下 Safari 缓存,再重新添加。我在这上面浪费了快半小时,一度以为配置写错了。

前端处理 SSE 用的是EventSourceAPI,这个在 Safari 上是支持的。但要注意,EventSource默认不支持自定义请求头,如果你的接口需要鉴权,得把 token 放在 URL 参数里,或者用 fetch 加 ReadableStream 的方式自己实现。我用的是后者,因为更灵活,也能更好地处理错误和重连。

async function streamChat(message) { const response = await fetch('/api/stream/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n\n'); buffer = lines.pop(); for (const line of lines) { if (line.startsWith('data: ')) { const data = JSON.parse(line.slice(6)); appendToChat(data.content); } } } }

这段代码的关键在于buffer的处理。SSE 的消息是以两个换行符分隔的,但网络传输是流式的,一次read()拿到的数据可能包含半条消息。所以要用一个缓冲区把不完整的部分存起来,等下次数据到了再拼。这个细节如果处理不好,会出现消息解析错误或者内容丢失。

4. 手机端访问与主屏安装的完整流程

4.1 让 iPhone 能访问到本地服务

服务配好了,接下来就是让 iPhone 能连上。这里有个前提:iPhone 和跑服务的机器必须在同一个局域网里。我的 AI 管家跑在一台常开的机器上,iPhone 连的是家里的 Wi-Fi,两者在同一个网段,这是最基础的条件。

怎么确认能连通?先在电脑上查一下本机的局域网 IP。Linux 或 macOS 上用ifconfig或者ip addr,Windows 上用ipconfig。找到类似192.168.1.100这样的地址。然后在 iPhone 的 Safari 里输入https://192.168.1.100,看能不能打开页面。

这里大概率会遇到第一个坑:证书警告。因为我用的是自签名证书,Safari 会提示"此连接非私人连接"。这时候不能直接点"继续",因为 iOS 对自签名证书的处理比较特殊。正确的做法是:先在 Safari 里访问一次,看到警告后点"显示详细信息",然后选择"访问此网站",系统会提示你是否信任这个证书。但光这样还不够,还需要去"设置 - 通用 - 关于本机 - 证书信任设置"里,手动把刚才那个证书的开关打开。这一步不做的话,SSE 连接会因为证书问题被拒绝。

注意:如果你觉得自签名证书太麻烦,本地测试阶段也可以先用 HTTP。但要注意,iOS 上某些 PWA 能力在 HTTP 下会受限,而且"添加到主屏幕"之后,如果服务地址变了,图标点开可能会白屏。所以长期用的话,还是建议把 HTTPS 配好。

4.2 添加到主屏幕的具体操作与验证

能正常访问之后,就可以添加到主屏幕了。操作本身很简单:在 Safari 里打开页面,点底部的分享按钮,在弹出的菜单里往下滑,找到"添加到主屏幕",点进去可以改名字,然后点"添加"。桌面上就会出现一个图标。

但添加完之后,有几件事要验证。第一,点开图标,看是不是全屏运行,有没有地址栏。如果还有地址栏,说明apple-mobile-web-app-capable没生效,检查一下 meta 标签是不是写在了<head>里,有没有拼写错误。第二,看图标是不是你设置的那个,如果显示的是网页截图,说明apple-touch-icon没被识别,检查图片尺寸和路径。第三,测试一下 SSE 流式输出,发一条消息,看内容是不是逐字出现的,而不是等半天突然全出来。

我实测下来,最容易出问题的是图标和全屏这两个。图标的问题通常是缓存,全屏的问题通常是 meta 标签的位置或者拼写。这两个都是小问题,但不知道的话能卡很久。

4.3 多服务场景下的路径规划

我的 AI 管家其实不止一个服务,除了主对话接口,还有一个文件管理的小工具,以及一个定时任务的查看页面。如果每个都单独配一个端口,手机上要记好几个地址,很麻烦。所以我在 nginx 里做了路径规划,统一到一个入口。

服务路径后端端口说明
主对话界面/静态文件前端页面
对话 API/api/chat8000普通 REST
流式对话/api/stream/chat8000SSE 长连接
文件管理/files/8001独立服务
定时任务/tasks/8002独立服务

这样手机上只需要记住一个地址,比如https://192.168.1.100,然后通过不同的路径访问不同功能。添加到主屏幕的时候,我可以给主对话界面加一个图标,给文件管理也加一个,各自独立,互不干扰。这就像一栋楼有多个入口,每个入口通向不同的区域,但都在同一个地址下。

配置多服务的时候,要注意每个 location 的proxy_pass路径匹配规则。nginx 的proxy_pass带不带斜杠,行为是不一样的。比如proxy_pass http://127.0.0.1:8001/带斜杠,会把 location 匹配到的路径部分替换掉;不带斜杠则是直接拼接。这个细节如果搞错,会出现 404 或者路径重复的问题。我一般建议统一带上斜杠,然后在 location 里把路径写完整,这样逻辑最清晰。

5. 常见问题排查与避坑经验实录

5.1 SSE 连接中断的排查思路

SSE 断连是这套系统里最高频的问题,没有之一。表现就是:AI 回复到一半突然停了,或者干脆一开始就连不上。排查的时候,我一般按这个顺序来。

先看浏览器控制台。Safari 的开发者工具虽然没有 Chrome 那么方便,但也能看网络请求。打开"开发 - 显示 Web 检查器",在手机上操作,电脑上就能看到请求详情。重点看 SSE 那个请求的状态,是 pending 还是 failed,有没有报错信息。

如果请求是 pending 然后突然变成 failed,大概率是超时。这时候要检查三个地方的超时设置:nginx 的proxy_read_timeout、后端框架的超时、以及客户端有没有设置超时。三个地方任何一个短了,都会导致断连。我的经验是,nginx 设 3600 秒,后端不主动断,客户端不设超时,让连接自然保持。

如果请求直接 failed,连 pending 都没有,那可能是证书问题或者跨域问题。证书问题前面讲过了,跨域的话,要确保 nginx 配置了正确的 CORS 头,或者前后端同源。同源是最省事的,所以我建议前端和后端都通过同一个 nginx 入口访问,避免跨域。

还有一个隐蔽的坑是中间网络设备的空闲回收。有些路由器或者防火墙会对长时间空闲的连接做回收,即使 nginx 和后端都设了长超时,中间设备也可能把连接掐了。这种情况心跳机制就特别重要,定期发数据让连接保持活跃,中间设备就不会认为它空闲。

5.2 主屏图标不更新与白屏问题

主屏图标的问题我遇到过好几次,总结下来就两类:图标不更新,和点开白屏。

图标不更新,前面提过,是 iOS 的缓存机制。解决办法就是删掉图标重新添加,必要时清 Safari 缓存。但还有一种情况是图标路径写错了,或者图片格式不对。iOS 对apple-touch-icon的要求是 PNG 格式,尺寸建议 180x180,不支持 SVG。如果你用的是 SVG,它不会报错,但会显示默认图标或者截图。

白屏问题更麻烦一点。点开图标之后一片白,什么都不显示。这种情况通常是添加图标时的 URL 和实际访问的 URL 不一致导致的。比如你添加的时候用的是https://192.168.1.100,但后来服务地址变成了https://192.168.1.101,图标点开还是访问旧地址,自然白屏。解决办法是删掉重新添加,或者确保服务地址固定不变。

还有一种白屏是 JavaScript 报错导致的。PWA 模式下没有地址栏,看不到报错信息,排查起来比较麻烦。我的做法是在页面里加一个全局的错误捕获,把错误信息显示在页面上,这样即使没有控制台也能看到。

window.addEventListener('error', (e) => { document.body.innerHTML = `<pre>Error: ${e.message}\n${e.filename}:${e.lineno}</pre>`; });

这段代码在开发阶段特别有用,能快速定位问题。上线之后可以去掉,或者改成上报到后端。

5.3 常见问题速查表

问题现象可能原因排查方法解决方案
SSE 输出不流式,一次性出现nginx 缓冲未关闭检查proxy_buffering设为 off,加X-Accel-Buffering: no
连接几十秒后断开超时设置过短查看 nginx 和后端超时延长proxy_read_timeout,加心跳
主屏图标显示截图图标未识别检查尺寸和格式用 180x180 PNG
点开图标白屏URL 变更或 JS 报错检查地址一致性,加错误捕获重新添加,修复 JS
证书警告无法消除自签名证书未信任检查证书信任设置手动信任或换正式证书
手机无法访问不在同一网络ping 测试连通性确认同一局域网
消息解析错乱缓冲区处理不当检查前端解析逻辑用 buffer 拼接完整消息

这张表是我踩坑之后整理的,基本上覆盖了 90% 的问题。遇到问题的时候先对照这张表,能省不少时间。

5.4 几个容易被忽略的细节

最后分享几个我在实操中发现的、文档里不太会提的细节。

第一个是iOS 的省电模式会影响 SSE。iPhone 进入低电量模式后,Safari 对后台连接的管理会更激进,SSE 连接可能被挂起。这个没法完全避免,但可以通过心跳稍微缓解。如果用户反馈"用一会儿就断了",先问问是不是开了省电模式。

第二个是Safari 的标签页数量限制。iOS 的 Safari 对同时打开的标签页有数量限制,超过之后旧标签会被回收。如果你的 PWA 是以标签页形式打开的(没有添加到主屏),被回收后连接就断了。添加到主屏可以避免这个问题,因为它是独立运行的。

第三个是nginx 的worker_connections限制。SSE 是长连接,每个连接都占用一个 worker 连接数。如果并发用户多,可能会遇到连接数不够的问题。默认值通常是 512 或 1024,对于个人使用足够了,但如果多人共用,需要适当调大。

第四个是后端框架的流式响应支持。不是所有框架都原生支持 SSE,有些需要额外的配置或者中间件。比如 Flask 需要stream_with_context,FastAPI 用StreamingResponse,Django 可能需要 Channels。选框架的时候要把这一点考虑进去,不然写到一半发现不支持,就得换方案。

6. 后续可以继续折腾的方向

这套东西跑通之后,我又陆续加了一些小功能。比如在 PWA 里加了一个"快捷指令"入口,配合 iOS 的快捷指令 App,可以实现"嘿 Siri,问 AI 管家一个问题"这种语音交互。原理就是快捷指令发一个 HTTP 请求到我的接口,然后把结果读出来。这个不需要改后端,纯前端配置就能搞定。

还有一个方向是离线缓存。PWA 可以用 Service Worker 缓存静态资源,这样即使网络不好,页面框架也能快速加载,只是数据请求会失败。对于我这种主要在内网使用的场景,离线缓存的意义不大,但如果你有外网访问的需求,这个就很有用。

另外,我还试过把主屏图标做成不同颜色的版本,对应不同的功能入口。比如蓝色图标是主对话,绿色图标是文件管理,橙色图标是定时任务。这样一眼就能区分,不用点进去才知道是哪个。实现方式就是不同的 HTML 入口,各自配置不同的apple-touch-icon。

最后再分享一个小技巧:如果你在调试阶段频繁修改前端代码,每次都要删图标重新添加很麻烦。可以在页面里加一个版本号参数,比如?v=2,这样 iOS 会认为是新的页面,重新加载资源。调试完之后再去掉参数,用干净的地址添加图标。这个技巧帮我省了很多重复操作的时间。

整套方案从想法到落地,前后大概花了一个周末。中间踩的坑主要集中在 nginx 配置和 iOS 的缓存机制上,但一旦跑通,稳定性还是不错的。现在我在外面掏出手机,点开主屏上的图标,就能直接和我的 AI 管家对话,流式输出也很流畅,基本达到了我最初"想在手机上用"的预期。如果你也在折腾类似的东西,希望这些经验能帮你少走点弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 7:35:15

跨模型自动扩缩容:共享LLM服务如何把GPU利用率拉到90%

读这篇论文之前&#xff0c;我正好在为一个多模型推理集群头疼&#xff1a;三个LLM服务各自守着独立的GPU池&#xff0c;白天A模型流量爆炸&#xff0c;池子占满&#xff0c;B和C却闲着&#xff1b;到了晚上反过来。说白了就是三份独立的峰值冗余叠在一起&#xff0c;实测GPU利…

作者头像 李华
网站建设 2026/10/28 7:34:39

DenseNet架构解析与PyTorch实现指南

1. 稠密连接网络(DenseNet)的设计哲学稠密连接网络(DenseNet)作为卷积神经网络架构的重要突破&#xff0c;其核心创新在于彻底重构了传统网络层间的连接方式。2017年CVPR会议上提出的这种架构&#xff0c;通过密集跨层连接(dense connectivity)机制&#xff0c;在ImageNet和CIF…

作者头像 李华
网站建设 2026/10/28 7:34:28

共享储能优化配置:MATLAB实现新能源微网经济性建模

简介&#xff1a;本资源是一份面向能源系统优化研究者与电力领域Matlab实践者的学术复现资料&#xff0c;聚焦微电网中新能源经济消纳与共享储能协同配置这一核心问题。资源完整复现了《考虑微网新能源经济消纳的共享储能优化配置》一文提出的双层优化模型&#xff1a;外层以储…

作者头像 李华
网站建设 2026/10/28 7:33:32

异常点检测算法解析与应用实践

1. 异常点检测算法概述异常点检测&#xff08;Anomaly Detection&#xff09;是数据分析领域的一项重要技术&#xff0c;主要用于识别数据集中与大多数数据显著不同的观测值。这类算法在金融欺诈检测、工业设备故障预警、网络安全监控等领域有着广泛应用。我曾在多个工业项目中…

作者头像 李华
网站建设 2026/10/28 7:33:10

Java排序算法原理、实现与性能优化指南

1. Java数据结构中的排序算法概述排序是计算机科学中最基础也最重要的算法类别之一&#xff0c;在Java开发中几乎无处不在。从简单的数组排序到复杂业务数据的处理&#xff0c;高效的排序算法能显著提升程序性能。Java标准库虽然提供了现成的排序方法&#xff0c;但理解各种排序…

作者头像 李华
网站建设 2026/10/28 7:32:20

GigaWorld-0:基于世界模型的具身智能数据引擎解析

1. 项目背景与核心价值GigaWorld-0这个项目名称已经透露了它的野心——构建一个以世界模型&#xff08;World Model&#xff09;为驱动的具身智能&#xff08;Embodied AI&#xff09;数据引擎。作为长期从事AI与机器人系统开发的从业者&#xff0c;我深知当前具身智能领域面临…

作者头像 李华