跳转至

第 1 章:开篇 —— Jan 为什么值得拆解

Jan 的核心问题不是“怎样显示一个聊天气泡”,而是“怎样把本地模型、云端模型、工具和隐私边界装进一个跨平台产品”。

一句话定义

Jan 是一个本地优先、可接入云端 Provider、支持桌面与移动端的开源 AI 聊天应用。它同时承担三种身份:

  1. 产品:聊天、助手、模型管理、设置、项目和附件。
  2. 运行时:启动/停止推理后端,维护 MCP 服务,暴露本地 OpenAI 兼容 API。
  3. 平台:通过 Core 类型、Extension 抽象和 Tauri plugin guest API 让能力可以替换。

用户看到的能力,对应什么源码

产品能力 主要实现
聊天和流式输出 web-app/src/routes/threads/$threadId.tsxuse-chat.tscustom-chat-transport.ts
模型选择与参数 model-factory.tsuseModelProviderpredefinedParams.ts
本地 GGUF 模型 extensions/llamacpp-extension + src-tauri/plugins/tauri-plugin-llamacpp
Apple Silicon MLX extensions/mlx-extension + macOS 条件编译的 MLX plugin
云端 Provider TypeScript Provider SDK + Rust server/proxy.rs 与 converters
助手 extensions/assistant-extension,磁盘上的 assistants/*/assistant.json
历史对话 extensions/conversational-extension → Tauri threads commands
MCP 工具 src-tauri/src/core/mcp + web-app/src/lib/mcp-orchestrator
RAG/向量检索 rag-extensionvector-db-extension + 两个 Tauri plugin
外部客户端接入 Rust API server,默认产品文档宣传 localhost:1337

一张图看懂四个层次

flowchart TB
  subgraph Product[产品层]
    UI[React + TanStack Router]
    State[Zustand stores + hooks]
    Chat[AI SDK Chat + CustomChatTransport]
  end
  subgraph SDK[共享协议层]
    Core[TypeScript Core]
    Types[Thread / Message / Model / Extension types]
    Ext[ExtensionManager + AIEngine]
  end
  subgraph Host[Rust 宿主层]
    Cmd[Tauri commands]
    State2[AppState]
    Services[threads / server / mcp / downloads]
  end
  subgraph Runtime[执行时层]
    Local[llama.cpp / MLX]
    Remote[remote providers]
    Tools[MCP / RAG / Web Search]
    Store[files / SQLite / keyring]
  end
  UI --> State --> Chat --> Core --> Cmd --> State2
  Types -. shared shapes .- Core
  Ext --> Local
  State2 --> Services
  Services --> Local
  Services --> Remote
  Services --> Tools
  Services --> Store

最重要的架构事实

1. WebView 不是“薄 UI”

WebView 里有大量业务编排:模型工厂、参数过滤、上下文裁剪、工具 schema 修复、会话级 chat 对象、分支消息和附件处理都在 TypeScript 中。这让 UI 响应快、迭代快,也意味着 Web 层承担了相当多的 AI 运行时知识。

2. Rust 不是“后台 API”那么简单

Rust 负责系统边界:进程、文件、密钥、端口、MCP 子进程、跨平台条件编译、退出清理和本地服务器。AppState 是这些长生命周期资源的共享容器。

3. Extension 是运行时的“插座”

@janhq/core 定义 BaseExtensionAIEngineConversationalExtension 等协议;具体能力在 extensions/* 实现。它不是微服务,也不是完全动态的插件沙箱,而是“可注册的同进程模块 + Tauri plugin 支持的系统能力”。

这套设计的代价

  • 同一个概念可能同时出现在 TypeScript 类型、前端 store、Tauri command 参数、Rust serde struct 和 plugin guest API 中。
  • 一个请求要跨 WebView、IPC、Rust、子进程或网络,错误信息必须经过多层翻译。
  • “本地优先”和“云端兼容”导致参数能力矩阵很大:top_k 对 llama.cpp 有意义,对 OpenAI 可能是非法字段。
  • 为了保持产品体验,系统有大量迁移、回退、重连、清理和兼容代码;这些才是 Jan 的工程核心。