BrainBank

01 - 系统架构

2026/7/30 20:26:18 · 更新于 2026/7/30 22:41:06

AI 翻译于 2026/7/30 22:45:07 · 使用 Qwen3.6 35B (fast, default)

#system-architecture#best-practices#rag#agent-harness#local-ai#ollama#vector-search#chromadb#hardware-sizing

本地 Mac 端到端 AI 基础设施参考:Ollama 模型服务、双网关 API、具备 RAG 索引的三级知识库、自研 Agent 管道、Tailscale 网络、备份策略,以及将它们统一连接起来的架构原则。

本文件提供了 llmpowerhouse 系统架构的完全梳理——涵盖每一个组件、它们如何相互连接,以及驱动这些结构选择的底层设计理念。它详细覆盖了从硬件到备份在内的七个架构层,外加网络支持、辅助应用,以及关键决策背后的考量依据。

概述

整个系统运行在一台 Mac Studio 上,模型权重、文档、代码、日志和备份全部存储在外挂卷 (/Volumes/AI_DATA) 的一个分区中。这种高度便携化的设计意味着,即便未来 Mac 主机本身被替换掉,整套环境也可以从零开始完整重建。该架构刻意将两个前端入口分离开来——一个供人类聊天交互使用,另一个用于程序化 API 访问——同时让两者共享同一个知识库,以支撑检索增强生成 (RAG)。

第一层 —— 硬件与模型引擎

Mac Studio (Apple M3 Ultra, 12核CPU/28核GPU, 96 GB 统一内存)
  └── /Volumes/AI_DATA  (1.8 TB APFS 专用卷)
        └── models/ollama/   (102 GB 的模型权重)
              └── Ollama (localhost:11434)  — 推理引擎,提供兼容 OpenAI 的 API

Ollama 共加载了三个聊天模型和一个嵌入 (Embedding) 模型。它们的大小和功能定位划分如下:

ModelSizeRole
qwen3.6:35b-a3b22.3 GiB默认主力模型。 Mixture-of-Experts(混合专家架构),每处理一个 token 仅激活约 30 亿参数,是三者中速度最快的(实测约 72 tok/s)。
qwen3-vl:30b18.2 GiB视觉语言模型。 唯一支持图像输入识别的模型。
gpt-oss:120b60.9 GiB参数量最大、推理最深。 无法与其他任何模型共享显存 (VRAM) —— 详见下文限制说明。
nomic-embed-text0.3 GiB非聊天模型,专门负责生成支撑检索搜索的向量 (vectors)。

显存 (VRAM) 的限制

该 Mac 仅有 77.8 GiB 的可用显存,并被配置为最多同时驻留 2 个模型 (OLLAMA_MAX_LOADED_MODELS=2)。qwen3.6 + qwen3-vl 组合仅占 40.5 GiB,能够完美共存并保持常驻状态 (stay warm side by side)。但无论其中哪一个与庞大的 gpt-oss:120b 搭配,都会超出承载预算——一旦选定加载这个大模型,其他所有模型都会被强制驱逐;而当切换回原先的模型时,大模型又会再次被驱逐。这种来回切换意味着每次都要承受长达数分钟的冷启动加载惩罚

在实际运行中,这带来了以下影响:

  • qwen3.6 承担几乎所有的交互流量。
  • qwen3-vl 仅在有图像输入时才会激活。
  • gpt-oss:120b 被预留起来专门处理偶尔遇到的极难问题(前提是其加载带来的延迟成本在可接受范围内)。

第二层 —— 刻意设计的双前端入口

                     ┌─────────────────────────────┐
                     │   Ollama :11434 (私有)      │
                     │   从不暴露在外网              │
                     └───────────▲─────────────────┘
                                 │
              ┌──────────────────┴───────────────────┐
              │                                       │
   ┌──────────┴──────────┐                ┌───────────┴────────────┐
   │  Open WebUI :8080   │                │  agent-server :8788    │
   │  (用户聊天界面)      │                │  (应用/工具的 API)     │
   │  + 内置 RAG         │                │  + ChromaDB RAG        │
   │  无记忆,无工具调用  │                │  + 用户级记忆          │
   └─────────────────────┘                │  + MCP 工具调用        │
                                          └───────────┬────────────┘
                                                      │
                                         ┌────────────┴─────────────┐
                                         │  gateway.py :8787 (遗留)   │
                                         │  简单代理,用于 ~20 个旧应用│
                                         └───────────────────────────┘

这是一项刻意为之的设计选择,而非未完成的迁移。这两扇“门户”服务于具备不同能力的受众群体。 gateway.py(端口 8787)是那个原始、简单的桥接器——它将请求转发给 Ollama,除此之外不再做任何处理。它目前仍在为大约 20 个现有系统提供支持,且明确不会被弃用。 agent-server(端口 8788)是目前积极开发的版本。它新增以下功能:

  • 知识库检索
  • 用户级记忆
  • MCP 工具调用
  • 实时流式传输
  • 支持作用域和速率限制的每应用 API 密钥

为一个服务器签发的密钥在另一个服务器上无法使用——两者拥有完全独立的密钥库。从外部来看,唯一的可见区别是 :8443:443 的后缀差异(详见网络配置部分)。 Open WebUI 直接与 Ollama 通信,而非通过 agent-server —— 因此它仅具备自身更简易的检索机制,完全不具备 agent-server 的记忆或工具调用功能。


第三层——知识体系:三个层级

knowledge-bank/
├── DOD-FM-Knowledge-Bank/     (财务管理文档, 288 MB)
├── K12-Knowledge-Bank/        (教育标准/课程大纲, 1.8 GB)
├── Wiki/                      (精炼的概念笔记,单页一页, 257 页)
├── _inbox/                    (新文件提交筐)
└── _index/
      └── chromadb/            (722 MB —— 向量搜索索引)

第一层——向量 RAG(检索增强生成)

基础层:文档被切块(800 个字符,重叠 100 个字符),通过 nomic-embed-text 模型进行向量化嵌入,并存储在 ChromaDB 中。查询请求也以相同方式进行向量化,并通过余弦相似度进行匹配。该项目自身的文档将其评价为 “90% 是文献整理功夫,10% 是技术手段” ——文件夹分类逻辑、统一的命名规范以及清理陈旧重复项,其重要性远超任何算法策略。

二级 —— LLM Wiki

apps/llm-wiki/llm_wiki.py 监控知识库,并为每个概念自动起草一篇精简的 Markdown 页面,经人工审核后方可发布。百科页面本身会被一级架构索引,因此精心编排的百科内容在检索中往往会优于原始源文本。

三级 —— Graph RAG

已设计但尚未部署——专用于应对一、二级在明确的跨步式“什么依赖什么”查询上失效的场景,采用 LightRAG 而非自研图谱数据库。

源权重权威重排

通过 knowledge-bank/source_authority.json 文件引导搜索结果向更高可信赖度的文档倾斜(例如原始法规而非其摘要),但不会完全压倒相关性匹配。


第四层 —— 代理服务器请求管线

这是系统中最具架构趣味性的部分,一个手工编写的代理执行器,而非现成框架。完整细节见 03_Agent_Harness_Deep_Dive.md;整体结构如下:

调用方(应用、curl、VS Code)
  │  POST /v1/chat/completions  +  Bearer API 密钥
  ▼
auth.py            —— 验证密钥哈希、作用域、速率限制、配额
  ▼
agent_loop.py       —— 组合系统提示词(AGENT.md + 匹配的技能 + 记忆)
  │                    检查:是否为自指涉问题?(若是则跳过工具调用)
  ▼
graph.py             —— 基于波次的执行器:运行规划器,发散分发所有工具调用
  │                    随后收束结果,循环直至完成或预算耗尽
  ├──► tools/registry.py  (手工编写的工具:search_knowledge_base, get_study_plan)
  ├──► mcp_client.py      (外部工具:文件系统、网络搜索、PDF 阅读器、规划)
  └──► hooks/              (前置钩子:拦截禁止的写入操作;后置钩子:触发重新同步)
  ▼
Ollama :11434         —— 实际的模型调用
  ▼
memory_pipeline.py   —— 记录查询与检索结果(发后即忘式,不阻塞响应)
  ▼
返回响应给调用方(流式或完整返回)

每次请求、工具调用、技能匹配和反馈信号都会记录到 logs/agents/*.jsonl —— 正是这条审计日志使得我们能够基于证据(而非猜测)诊断出七月三十日的延迟故障(详见 05_Gap_Analysis.md)。


第五层 —— 网络与暴露架构

互联网
  │
  ▼
Tailscale Funnel(加密隧道,无需路由器端口转发)
├── :443   →  gateway.py     (旧版,约 20 个应用)
└── :8443  →  agent-server   (当前)

Ollama 本身绝不对 localhost 和私有 Tailscale 网络之外的任何访问开放 —— 它自身无身份验证机制,因此这被视作一条绝对规则而非选项。唯一的公开入口仅为上述两个代理,且二者都会在流量触及 Ollama 前强制校验各自的 API 密钥。

对 Mac 本机的管理员访问权限分为层级:优先使用基于浏览器的管理面板,按需使用 SSH,仅作为最后手段时使用屏幕共享(VNC)。


第六层 —— 辅助应用

应用程序/目录用途
apps/llmpowerhouse-site/公共 Next.js 展示站点(托管于 Vercel),包含桥接至家庭服务器的实时演示以及 /server-status 健康检查页面。
apps/llm-wiki/Wiki 自动化监控器。
apps/open-webui-filters/自定义过滤器(knowledge_scope_filter.py),防止 Open WebUI 对话中跨知识库的知识泄露。
apps/mcp-servers/fetch/独立的 Python 环境,供那个需要与 agent-server 自身不同的依赖版本的 MCP 工具使用。

第 7 层 — 备份

Backup-AI.command  →  backups/<timestamp>/
    (keys.db、memory.db、AGENT.md、skills/、*.command 脚本:体积小但不可替代)
Backup-AI.command full  →  同时复制 722 MB 的 ChromaDB 索引

模型权重(104 GB)和源 PDF 文件不与此同法备份——它们可重复生成ollama pull、重新下载),而非不可替代。

已知风险: 备份当前与所保护的数据位于同一存储卷上,能覆盖误删除场景但无法覆盖硬盘故障。详见 05_Gap_Analysis.md


为何如此架构

每一项重大结构决策都归结于两条约束:

  1. 96 GB 内存上限——迫使我们采用"常态用主力模型、难题上大型模型"的模型策略,以及双服务器而非单一服务器的暴露架构。
  2. 将廉价的确定性机制层叠在昂贵的模糊推理之前的设计哲学:
    • 正则在前、嵌入向量在中、完整模型调用在后
    • 向量 RAG 优先、Wiki 次之、图谱再次
    • 手写工具与 MCP 工具共享同一注册表,使路由逻辑永远无需关心两者的身份差异

扩展系统时,这个模式值得牢记——详见 08_Extending_The_Harness_Agent.md

学习地图

Learning Map: Building from Hardware to Agent Harness

Stage 1 — Foundations (Hardware & Model Serving)

  • Understand GPU/VRAM constraints for multi-model concurrency on consumer silicon
  • Run Ollama as a local, OpenAI-compatible inference server (port 11434)
  • Load chat (qwen3.6:35b-a3b), vision (qwen3-vl:30b), and large-scale reasoning models (gpt-oss:120b)

Stage 2 — Knowledge Layer (RAG & Indexing)

  • Chunk documents (800-char / 100-char overlap) and embed them with an embedding model (nomic-embed-text)
  • Store vectors in ChromaDB and query by cosine similarity for retrieval
  • Add authority-weighted re-ranking via source_authority.json
  • Introduce Tier 2 (auto-drafted wiki pages through human curation) and learn when Tier 3 (Graph RAG / LightRAG) is warranted

Stage 3 — Dual API Gateways

  • Deploy Open WebUI (:8080) for human-facing chat with its own lightweight RAG
  • Stand up agent-server (:8788) for programmatic API access—memory, MCP tool-calling, per-app keys & rate limits
  • Keep the legacy gateway.py proxy on :8443 as a stable surface for existing integrations

Stage 4 — Agent Harness Pipeline

  • Wire authentication (auth.py), system prompt composition (agent_loop.py), and model calls to Ollama
  • Use the wave-based executor (graph.py) to fan out tool calls concurrently and converge results
  • Implement a single tool registry mixing hand-written tools (search_knowledge_base, get_study_plan) with MCP servers (filesystem, web search, PDF reader)
  • Add audit logging (logs/agents/*.jsonl), memory pipeline, and pre/post hooks

Stage 5 — Networking & Operations

  • Tunnel public access through Tailscale Funnel without router port-forwarding
  • Implement backup strategy (Backup-AI.command) covering configs vs. reproducible model weights
  • Set up monitoring via a Next.js dashboard (/server-status)

Prerequisite: Comfort with Docker / Python virtual environments and basic REST APIs.

动手实践——分步指南

  1. Provision a macOS machine with at least 32 GB RAM (96 GB recommended) and format an external volume as APFS.

  2. Install Ollama (brew install ollama or download the installer), confirm it's running on localhost:11434, set OLLAMA_MAX_LOADED_MODELS=2. Pull three models:

    ollama pull qwen3:35b ollama pull qwen3-vl:30b ollama pull gpt-oss:120b

    Plus the embedding model: ollama pull nomic-embed-text. Verify each via ollama list and a quick query like curl http://localhost:11434/api/generate.

  3. Create a knowledge-bank directory with a Tier 1 structure:

    mkdir -p knowledge-bank/{_inbox,_index/chromadb}

    Place PDFs and text docs in _inbox/. Use python-pymupdf or similar library, chunk them at 800 chars / 100 overlap.

  4. Build the vector index with ChromaDB:

    • Install chromadb and use nomic-embed-text via Ollama's /api/embeddings endpoint or a compatible HuggingFace pipeline.
    • Store each chunk's embedding + metadata (source, page, section) in a Chroma collection named kb_tier1.
  5. Write a simple retrieval function:

    import chromadb
    client = chromadb.Client()
    coll = client.get_or_create_collection("kb_tier1")
    query_emb = embed("your question here")  # same encoder as step 4
    results = coll.query(query_embeddings=query_emb, n_results=6)
    
  6. Add authority re-ranking: create knowledge-bank/source_authority.json mapping document filenames to numeric priority (e.g., {"DOD-Fmr-12-1.pdf": 10}). Post-ChromaDB retrieval multiply each score by (1 + authority_weight) before sorting.

  7. Deploy Open WebUI:

    docker run -d -p 8080:8080 --name webui ghcr.io/open-webui/open-webui

    Set the base URL to http://host.docker.internal:11434 (Ollama). Add a filter (apps/open-webui-filters/knowledge_scope_filter.py) to scope queries to specific Chroma collections so knowledge banks never leak.

  8. Build agent-server (:8788):

    • Create auth.py — validate Bearer API keys (hashed), enforce scopes and per-key quotas.
    • Create agent_loop.py — compose the system prompt from core rules (AGENT.md) + matched skills from stored skill markdowns + per-user memory snippets fetched from a local SQLite / Postgres table.
    • Wire graph.py (wave-based loop): after the LLM responds, parse tool calls; route them through tools/registry.py for built-in tools and mcp_client.py for MCP tool servers (use MCP SDK to discover endpoints).
    • Log every API call & tool result to logs/agents/*.jsonl. Use python-fire-and-forget asyncio.create_task() or a background queue so memory recording doesn't block the response.
  9. Network public exposure:

    • Install and configure Tailscale on your Mac. Enable "Funnel" for two listeners: map :443 → gateway.py (legacy HTTP proxy forwarding to port 11434) and :8443 → agent-server.
    • Never expose Ollama directly. Confirm with ss -tlnp | grep 11434 that the port is bound only to 127.0.0.1.
  10. Validate everything end-to-end:

    # Via Open WebUI — ask a question, check source documents are surfaced.
    # Via agent-server — send a Bearer key with MCP tool scope and verify a filesystem write tool is blocked by hooks/.
    
  11. Set up backups:

    • Short backup (Backup-AI.command): rsync keys.db, memory.db, AGENT.md, skills/ to a timestamped backups/<date>/ directory on the same volume.
    • Long backup (Backup-AI.command full): additionally rsync the ChromaDB index (~722 MB).
  12. Extend with Tier 3 (optional): when answer quality degrades on multi-hop queries, integrate LightRAG as a post-processing layer over ChromaDB results instead of replacing it.

三大推荐资源

  1. 1
    Ollama Documentation

    The official reference for installing, pulling models (Qwen, GPT-oss), configuring OLLAMA_MAX_LOADED_MODELS, and leveraging OpenAI-compatible endpoints.

    https://ollama.com/blog/openai-compatibility

  2. 2
    ChromaDB Docs — Embeddings & Collections

    Step-by-step guide to creating collections, embedding chunks with custom encoders (like nomic-embed-text), and querying by cosine similarity.

    https://docs.trychroma.com/docs/overview/introduction

  3. 3
    Model Context Protocol (MCP) SDK — Quickstart

    The canonical MCP specification and SDK docs for building unified tool-calling agents that bridge locally-hosted tools and external MCP servers.

    https://modelcontextprotocol.io/introduction

链接由 AI 推荐——使用前建议快速核实。