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)
本地 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) 模型。它们的大小和功能定位划分如下:
| Model | Size | Role |
|---|---|---|
qwen3.6:35b-a3b | 22.3 GiB | 默认主力模型。 Mixture-of-Experts(混合专家架构),每处理一个 token 仅激活约 30 亿参数,是三者中速度最快的(实测约 72 tok/s)。 |
qwen3-vl:30b | 18.2 GiB | 视觉语言模型。 唯一支持图像输入识别的模型。 |
gpt-oss:120b | 60.9 GiB | 参数量最大、推理最深。 无法与其他任何模型共享显存 (VRAM) —— 详见下文限制说明。 |
nomic-embed-text | 0.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。
为何如此架构
每一项重大结构决策都归结于两条约束:
- 96 GB 内存上限——迫使我们采用"常态用主力模型、难题上大型模型"的模型策略,以及双服务器而非单一服务器的暴露架构。
- 将廉价的确定性机制层叠在昂贵的模糊推理之前的设计哲学:
- 正则在前、嵌入向量在中、完整模型调用在后
- 向量 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.
动手实践——分步指南
-
Provision a macOS machine with at least 32 GB RAM (96 GB recommended) and format an external volume as APFS.
-
Install Ollama (
brew install ollamaor download the installer), confirm it's running on localhost:11434, setOLLAMA_MAX_LOADED_MODELS=2. Pull three models:ollama pull qwen3:35bollama pull qwen3-vl:30bollama pull gpt-oss:120bPlus the embedding model:
ollama pull nomic-embed-text. Verify each viaollama listand a quick query likecurl http://localhost:11434/api/generate. -
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. -
Build the vector index with ChromaDB:
- Install
chromadband usenomic-embed-textvia Ollama's/api/embeddingsendpoint or a compatible HuggingFace pipeline. - Store each chunk's embedding + metadata (source, page, section) in a Chroma collection named
kb_tier1.
- Install
-
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) -
Add authority re-ranking: create
knowledge-bank/source_authority.jsonmapping 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. -
Deploy Open WebUI:
docker run -d -p 8080:8080 --name webui ghcr.io/open-webui/open-webuiSet 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. -
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 throughtools/registry.pyfor built-in tools andmcp_client.pyfor MCP tool servers (use MCP SDK to discover endpoints). - Log every API call & tool result to
logs/agents/*.jsonl. Use python-fire-and-forgetasyncio.create_task()or a background queue so memory recording doesn't block the response.
- Create
-
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 11434that the port is bound only to 127.0.0.1.
-
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/. -
Set up backups:
- Short backup (
Backup-AI.command): rsynckeys.db,memory.db,AGENT.md,skills/to a timestampedbackups/<date>/directory on the same volume. - Long backup (
Backup-AI.command full): additionally rsync the ChromaDB index (~722 MB).
- Short backup (
-
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.
三大推荐资源
- 1Ollama 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
- 2ChromaDB 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
- 3Model 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 推荐——使用前建议快速核实。