第 12 章:App、测试与扩展边界——从运行时到产品¶
一、桌面 App 不是另一个推理引擎¶
app/ 包含 macOS/Windows 相关的启动、托盘、更新、store、webview、认证和打包资源。它的关键职责是:
- 为用户安装/升级和启动 Ollama。
- 在需要时拉起本地服务。
- 将桌面生命周期映射到 CLI/server 生命周期。
- 提供平台相关的 UI/权限/日志路径。
推理仍然通过 api.Client 和 server 完成。这样的分离使 Linux headless、Docker、CLI 和桌面版本共享同一服务核心。
二、测试布局揭示架构¶
快照的测试不是只有单元测试:
| 测试区域 | 主要验证 |
|---|---|
api/*_test.go |
请求类型、client、JSON/TypeScript 兼容 |
server/*_test.go |
route、模型、cloud proxy、scheduler、流式行为 |
agent/*_test.go |
tool loop、审批、compaction、skills、取消 |
convert/*_test.go |
family converter、tokenizer、tensor 变换 |
integration/ |
真实模型/服务的端到端行为 |
app/、cmd/ |
CLI、TUI、平台与 launch 集成 |
读测试的最好方式是把它们当作“契约文档”。例如 Agent 测试直接告诉你取消后需要保留 partial stream,Scheduler 测试告诉你显式 context 与自动 context 的降级语义不同。
三、integration/ 是运行时的外部证据¶
集成测试覆盖 chat、embed、vision、thinking、tools、quantization、concurrency、max queue、create 等场景。它们的价值在于验证跨包组合:
API JSON
→ server route
→ manifest/model
→ scheduler
→ native runner
→ stream
单元测试可以 fake client 或 fake runner,但只有集成测试能暴露 template、模型 metadata、GPU/runner 和真实流式协议之间的组合问题。
四、扩展点地图¶
如果要在 Ollama 上做二次开发,优先从这些边界切入:
| 目标 | 推荐边界 |
|---|---|
| 新客户端 | 使用 api.Client 或实现 HTTP client |
| 新协议 | 新增 middleware/adapter,复用 handler |
| 新模型家族 | model/ + convert/,补 metadata/tensor/test |
| 新 native 后端 | 实现 llm.LlamaServer 或独立 runner engine |
| 新 Agent 工具 | 实现 agent.Tool,注册到 Registry |
| 新技能 | 按 agent/skills.go 约定提供 skill 目录 |
| 新 GPU | discover/、ml/ 与 CMake/native bundle |
| 新桌面行为 | app/,不要把平台逻辑塞进 server |
五、哪些地方不适合直接扩展¶
- 不应在每个 handler 里直接启动 runner。
- 不应在 OpenAI middleware 中复制 prompt/模型加载逻辑。
- 不应让工具实现访问 Scheduler 私有状态。
- 不应绕过 manifest 直接把任意文件当成模型。
- 不应把 cloud URL 直接交给用户请求控制。
这些做法短期能跑,长期会破坏资源所有权和安全边界。
六、从源码到贡献的验证顺序¶
静态检查
→ 包级单元测试
→ server/agent 组合测试
→ integration(需要模型/runner)
→ 平台构建(CMake + native backend)
→ 手工 curl/CLI 验证
完整构建入口由 AGENTS.md 给出:CMake 构建 native payload,再运行 ./ollama serve;如果已有 native payload,也可以先做 Go-only iteration。
七、最终心智模型¶
产品层:CLI / App / SDK
协议层:Native / OpenAI / Anthropic
编排层:Handler / Agent / Scheduler
资源层:Model / Manifest / Blob / GPU
计算层:llama-server / MLX / native backend
每层都有自己的不变量和测试。Ollama 的可维护性不来自“代码很少”,而来自这些层之间的契约足够明确:请求不拥有 runner,manifest 不等于可运行 Model,兼容协议不复制推理主线,Agent 不等于 HTTP server。