跳转至内容
  • 版块
  • 最新
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • 浅色
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • 深色
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(不使用皮肤)
  • 不使用皮肤
折叠
品牌标识

抡锤者

  1. 主页
  2. LLM讨论区
  3. Mac 丐版 低配篇。大众基本都是这个版本的用户。敬请参考!

Mac 丐版 低配篇。大众基本都是这个版本的用户。敬请参考!

已定时 已固定 已锁定 已移动 LLM讨论区
13 帖子 6 发布者 165 浏览
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • 怪 离线
    怪 离线
    怪叔叔
    编写于 最后由 编辑
    #3

    我用m1 max 64G的macbook pro跑omlx Qwen3.5-35b-a3b-4bit-mlx 好像也是30几tokens/s

    1 条回复 最后回复
    0
    • Tony WangT 离线
      Tony WangT 离线
      Tony Wang
      编写于 最后由 编辑
      #4

      我的M5pro 64G, 35A3 能跑到 80-90, 随着上下文变长, 会降到 50-60. 不过跑hermes, 64K上下文, 几轮对话下来, 内存就会到50G, 我一般同时开十几个网页, 微信, ChatGPT, telegram... 差不多是这个情况. 所以32G的Mac, 跑Hermes+35A3, 我感觉是不行的.

      terryT 1 条回复 最后回复
      0
      • Tony WangT Tony Wang

        我的M5pro 64G, 35A3 能跑到 80-90, 随着上下文变长, 会降到 50-60. 不过跑hermes, 64K上下文, 几轮对话下来, 内存就会到50G, 我一般同时开十几个网页, 微信, ChatGPT, telegram... 差不多是这个情况. 所以32G的Mac, 跑Hermes+35A3, 我感觉是不行的.

        terryT 离线
        terryT 离线
        terry
        编写于 最后由 编辑
        #5

        @Tony-Wang你有Pro发几个帖子啊老哥,论坛缺苹果的测试数据,以后我做视频,也好云的有依据。

        1 条回复 最后回复
        0
        • Tony WangT 离线
          Tony WangT 离线
          Tony Wang
          编写于 最后由 编辑
          #6

          好, 稍后弄一下. 不过Mac的环境很难弄干净, 我尽量将用不到的进程都干掉 再测试一下.

          1 条回复 最后回复
          0
          • terryT terry

            卧槽,你确定是丐版?这么快?32G 不算事丐版了,我的24G,丐版是16G。当然CPU就是丐版了。

            williamlouisW 离线
            williamlouisW 离线
            williamlouis
            编写于 最后由 编辑
            #7

            @terry pro都不带,不是丐版是啥?论坛里的炫机佬都是啥。你品品。

            个人主页:xlkj.org Telegram https://t.me/xinlinlu

            1 条回复 最后回复
            0
            • williamlouisW williamlouis

              1.内存(模型权重占用)=参数规模✖️每个参数的位深÷8。比如我们32B模型,量化方式是4-bit,所以需要的内存就是:
              32x4÷8=16G
              01cdb4bd-0392-492b-ba37-14aaf7eb6cc0-image.jpeg
              核对自己的配置选用即可。
              2.KV Cache .在实际运行中,KV Cache(上下文缓存)会随对话增长额外吃掉数GB内存,这是32B成为"临界点"的真正原因。就是不要顶住你的配置是跑。一定会越跑越慢。
              3.推理速度(Tokens/s)=内存带宽÷模型运行的实际大小

              结尾:用OMLX配合Qwen3.5-35b-a3b-4bit-mlx,是可以稳定跑出30~35tokens/s。如果是多线程,OMLX的基准测试是可以到90tokens/s。
              重点1:选对推理引擎,OMLX当下非常好
              重点2:选对模型,要点:moe+mlx。像我的32G的M4 Macmini,跑24b-A2b模型,是可以跑到50~60tokens/s。另外,不是带着thinking/reasoning标签的模型都适合所有任务的,想要体验好,按需求选模型太重要了。

              参考。心情好就回答。没回答就是心情不好。
              可能很多人问。16G的版本。我直接给个结论省的提问的人太多。
              答:可以跑。适合折腾。深度的学习很重要。我提供了重要的线索。剩下的动动脑子就行了。

              williamlouisW 离线
              williamlouisW 离线
              williamlouis
              编写于 最后由 编辑
              #8

              主要是希望能帮大家选对要折腾的目标。深度理解公式。选一个你可以折腾的搞。参数可以计算了。不合理硬上蛇了。会浪费你的生命。再有很多是 单mac 拥有者。一个小知识。单台搞得太过活是不能恢复系统的。

              个人主页:xlkj.org Telegram https://t.me/xinlinlu

              1 条回复 最后回复
              0
              • xiaopbroX 离线
                xiaopbroX 离线
                xiaopbro
                编写于 最后由 编辑
                #9

                32g还算丐版的话,那我16g算什么?丐中丐?o(╥﹏╥)o

                E 1 条回复 最后回复
                0
                • williamlouisW 离线
                  williamlouisW 离线
                  williamlouis
                  编写于 最后由 编辑
                  #10

                  在 Hermes 和 OMLX 之间插一个本地代理,自动数对话轮数,第 15 轮时后台调用 35B 自身做静默总结,把前 14 轮压成一段系统记忆,KV Cache 瞬间从 15 轮降到 2 轮。Hermes 完全无感知。


                  1. 保存代理脚本

                  cat > ~/omlx_compress_proxy.py << 'EOF'
                  #!/usr/bin/env python3
                  import json, time, copy
                  from http.server import HTTPServer, BaseHTTPRequestHandler
                  from urllib.request import Request, urlopen
                  from urllib.error import HTTPError
                  
                  OMLX_BASE = "http://127.0.0.1:8201"   # OMLX 新地址
                  PROXY_PORT = 8200                     # 接管 Hermes 原来的端口
                  COMPRESS_EVERY = 15
                  KEEP_RECENT = 1
                  SUMMARY_MAX_TOKENS = 400
                  SUMMARY_TEMP = 0.1
                  
                  SUMMARY_SYSTEM = (
                      "你是后台上下文压缩器。请用中文极度精简地总结以下对话,"
                      "保留所有关键决策、代码、数据、未完成任务。只输出总结内容,"
                      "不要任何解释、问候、格式标记。"
                  )
                  
                  class Store:
                      def __init__(self):
                          self.sessions = {}
                      def get(self, body):
                          key = body.get("conversation_id") or body.get("session_id") or body.get("user") or "default"
                          return self.sessions.get(key, []), key
                      def set(self, key, messages):
                          self.sessions[key] = messages
                      def count(self, messages):
                          return sum(1 for m in messages if m.get("role") in ("user", "assistant"))
                  
                  store = Store()
                  
                  def call_omlx(messages, max_tokens=None, temperature=None):
                      payload = {"model": "Qwen3.5-35b-a3b-4bit-mlx", "messages": messages, "stream": False}
                      if max_tokens: payload["max_tokens"] = max_tokens
                      if temperature is not None: payload["temperature"] = temperature
                      req = Request(f"{OMLX_BASE}/v1/chat/completions",
                                    data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"}, method="POST")
                      return json.loads(urlopen(req, timeout=300).read().decode())
                  
                  def compress(messages):
                      sys_msgs = [m for m in messages if m.get("role") == "system"]
                      non_sys = [m for m in messages if m.get("role") != "system"]
                      if len(non_sys) <= KEEP_RECENT: return messages
                      to_compress = non_sys[:-KEEP_RECENT]
                      keep = non_sys[-KEEP_RECENT:]
                      prompt = []
                      if not sys_msgs: prompt.append({"role": "system", "content": SUMMARY_SYSTEM})
                      prompt.append({"role": "user", "content": "\n\n".join(f"[{m['role']}] {m['content'][:2000]}" for m in to_compress)})
                      summary = call_omlx(prompt, max_tokens=SUMMARY_MAX_TOKENS, temperature=SUMMARY_TEMP)["choices"][0]["message"]["content"].strip()
                      new_msgs = list(sys_msgs)
                      new_msgs.append({"role": "system", "content": f"[历史压缩] {summary}"})
                      new_msgs.extend(keep)
                      return new_msgs
                  
                  class H(BaseHTTPRequestHandler):
                      def log_message(self, *args): pass
                      def do_POST(self):
                          if self.path != "/v1/chat/completions": return self._proxy()
                          body = json.loads(self.rfile.read(int(self.headers.get("Content-Length", 0))).decode())
                          history, key = store.get(body)
                          history = body.get("messages", [])
                          if store.count(history) >= COMPRESS_EVERY:
                              t0 = time.time()
                              history = compress(history)
                              print(f"[Compress] {key}: done in {time.time()-t0:.1f}s")
                          store.set(key, history)
                          omlx_body = copy.deepcopy(body)
                          omlx_body["messages"] = history
                          for k in ["conversation_id", "session_id", "user"]: omlx_body.pop(k, None)
                          req = Request(f"{OMLX_BASE}/v1/chat/completions",
                                        data=json.dumps(omlx_body).encode(), headers={"Content-Type": "application/json"}, method="POST")
                          try:
                              with urlopen(req, timeout=600) as r:
                                  data = r.read()
                                  self.send_response(r.status)
                                  for k, v in r.headers.items():
                                      if k.lower() != "transfer-encoding": self.send_header(k, v)
                                  self.end_headers(); self.wfile.write(data)
                                  try:
                                      msg = json.loads(data.decode())["choices"][0]["message"]
                                      history.append(msg); store.set(key, history)
                                  except: pass
                          except HTTPError as e:
                              self.send_response(e.code); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(e.read())
                      def _proxy(self):
                          body = self.rfile.read(int(self.headers.get("Content-Length", 0))) if int(self.headers.get("Content-Length", 0)) else b""
                          req = Request(f"{OMLX_BASE}{self.path}", data=body, headers={k:v for k,v in self.headers.items()}, method="POST" if body else "GET")
                          try:
                              with urlopen(req) as r: self.send_response(r.status); [self.send_header(k,v) for k,v in r.headers.items()]; self.end_headers(); self.wfile.write(r.read())
                          except HTTPError as e: self.send_response(e.code); self.end_headers(); self.wfile.write(e.read())
                  
                  if __name__ == "__main__":
                      print(f"Proxy http://127.0.0.1:{PROXY_PORT}/v1 -> {OMLX_BASE}")
                      print(f"Compress every {COMPRESS_EVERY} turns")
                      HTTPServer(("127.0.0.1", PROXY_PORT), H).serve_forever()
                  EOF
                  chmod +x ~/omlx_compress_proxy.py
                  

                  2. 调整端口(核心)

                  假设你之前 Hermes 连的是 127.0.0.1:8200:

                  # 先停掉 OMLX
                  pkill -f "omlx serve"
                  
                  # OMLX 改绑到 8201(让出 8200 给代理)
                  omlx serve --port 8201 --model Qwen3.5-35b-a3b-4bit-mlx
                  

                  3. 启动代理(占原端口 8200)

                  另开一个终端:

                  python3 ~/omlx_compress_proxy.py
                  

                  日志应显示:

                  Proxy http://127.0.0.1:8200/v1 -> http://127.0.0.1:8201/v1
                  Compress every 15 turns
                  

                  4. Hermes 配置(保持不变)

                  你在 Hermes SSH 终端界面里不要改任何端口,保持原来的 127.0.0.1:8200。因为代理已经占了 8200,Hermes 的请求会先经过代理,代理再转发给 OMLX 的 8201。

                  如果你之前在 Hermes 里配的是其他端口(比如 11434 或 5001),把上面脚本里的 PROXY_PORT 和 OMLX_BASE 对应改一下即可。


                  5. 验证压缩触发

                  正常聊天到第 15 轮,代理终端会打印:

                  [Compress] default: done in 1.8s
                  

                  此时 KV Cache 从 15 轮瞬间降到 2 轮,内存压力解除。Hermes 前端看不到任何异常。


                  关键提醒

                  项目 说明
                  第 15 轮延迟 35B 总结前 14 轮需 1-3 秒,这是物理限制(OMLX 单线程)。建议 COMPRESS_EVERY 设 12~15,别设太小。
                  总结质量 SUMMARY_TEMP=0.1 已锁死,防止总结时模型发散。
                  内存效果 压缩后 KV Cache 从 ~15 轮降到 ~2 轮,按 35B 每轮 300MB 算,瞬间释放 3-4GB。
                  失败回退 如果代理挂了,Hermes 直接连不上;OMLX 本身不受影响,重启代理即可。

                  按这个顺序部署:先改 OMLX 端口 → 启动代理 → Hermes 保持原配置继续用。
                  16和24 需要改小总结轮数测试。测试机型32G M4。
                  请勿直接使用。

                  个人主页:xlkj.org Telegram https://t.me/xinlinlu

                  1 条回复 最后回复
                  1
                  • xiaopbroX xiaopbro

                    32g还算丐版的话,那我16g算什么?丐中丐?o(╥﹏╥)o

                    E 离线
                    E 离线
                    eddie-hk
                    编写于 最后由 编辑
                    #11

                    @xiaopbro 我到現在都後悔為何當時買 M4 24g 512g ssd,不把錢用在 ram 上。因為自行更換了 2TB ssd,現在的 512g ssd 放在櫃桶裏吃灰。

                    terryT williamlouisW 2 条回复 最后回复
                    0
                    • E eddie-hk

                      @xiaopbro 我到現在都後悔為何當時買 M4 24g 512g ssd,不把錢用在 ram 上。因為自行更換了 2TB ssd,現在的 512g ssd 放在櫃桶裏吃灰。

                      terryT 离线
                      terryT 离线
                      terry
                      编写于 最后由 编辑
                      #12

                      @eddie-hk 买都买了,有啥后悔的,我买的也是24G版本,你买32G它也干不了活,跑AI还是一样受罪。还得买显卡。

                      1 条回复 最后回复
                      0
                      • E eddie-hk

                        @xiaopbro 我到現在都後悔為何當時買 M4 24g 512g ssd,不把錢用在 ram 上。因為自行更換了 2TB ssd,現在的 512g ssd 放在櫃桶裏吃灰。

                        williamlouisW 离线
                        williamlouisW 离线
                        williamlouis
                        编写于 最后由 williamlouis 编辑
                        #13

                        @eddie-hk 硬盘可以咸鱼,不过得你过保的。不过。别人插了。用不了。反正是有收的。

                        个人主页:xlkj.org Telegram https://t.me/xinlinlu

                        1 条回复 最后回复
                        0

                        你好!看起来您对这段对话很感兴趣,但您还没有一个账号。

                        厌倦了每次访问都刷到同样的帖子?您注册账号后,您每次返回时都能精准定位到您上次浏览的位置,并可选择接收新回复通知(通过邮件或推送通知)。您还能收藏书签、为帖子顶,向社区成员表达您的欣赏。

                        有了你的建议,这篇帖子会更精彩哦 💗

                        注册 登录
                        回复
                        • 在新帖中回复
                        登录后回复
                        • 从旧到新
                        • 从新到旧
                        • 最多赞同


                        • 登录

                        • 没有帐号? 注册

                        • 登录或注册以进行搜索。
                        • 第一个帖子
                          最后一个帖子
                        0
                        • 版块
                        • 最新
                        • 标签
                        • 热门
                        • 用户
                        • 群组