跳转至

第 12 章:App、测试与扩展边界——从运行时到产品

一、桌面 App 不是另一个推理引擎

app/ 包含 macOS/Windows 相关的启动、托盘、更新、store、webview、认证和打包资源。它的关键职责是:

  • 为用户安装/升级和启动 Ollama。
  • 在需要时拉起本地服务。
  • 将桌面生命周期映射到 CLI/server 生命周期。
  • 提供平台相关的 UI/权限/日志路径。

推理仍然通过 api.Clientserver 完成。这样的分离使 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。