Arthur-Ficial/apfel
GitHub: Arthur-Ficial/apfel
apfel 是一个将 Apple Silicon Mac 内置 LLM 封装为命令行工具和 OpenAI 兼容服务器的本地 AI 推理项目,实现无需 API 密钥的完全设备端运行。
Stars: 5784 | Forks: 216
# apfel
### 你的 Mac 上自带的免费 AI。
[](https://github.com/Arthur-Ficial/apfel)
[](https://swift.org)
[](https://developer.apple.com/macos/)
[](https://developer.apple.com/xcode/resources/)
[](LICENSE)
[](https://developer.apple.com/documentation/foundationmodels)
[](https://apfel.franzai.com)
[](#contributing)
Apple Silicon Mac 通过 [Apple FoundationModels](https://developer.apple.com/documentation/foundationmodels) 内置了 LLM。`apfel` 将其公开为 UNIX 工具和兼容 OpenAI 的本地服务器。100% 在设备本地运行。无需 API 密钥,无需云端。
| 模式 | 命令 | 你将获得 |
|------|---------|--------------|
| UNIX 工具 | `apfel "prompt"` / `echo "text" \| apfel` | 支持管道的回复、文件附件、JSON 输出、退出码 |
| 兼容 OpenAI 的服务器 | `apfel --serve` | 可直接替换的 OpenAI SDK 本地 `http://localhost:11434/v1` 后端 |
`apfel --chat` - 交互式 REPL。
工具调用在所有上下文中均可使用。4096-token 上下文。

## 环境要求与安装
macOS 26 Tahoe+,Apple Silicon (M1+),[已启用 Apple Intelligence](https://support.apple.com/en-us/121115)。
```
brew install apfel
```
更新:
```
brew upgrade apfel
```
从源码构建(需带有 macOS 26.4 SDK / Swift 6.3 的 Command Line Tools,无需 Xcode):
```
git clone https://github.com/Arthur-Ficial/apfel.git && cd apfel && make install
```
Nix、当日同步的 tap、Mint、mise、故障排除:[docs/install.md](docs/install.md)。
## 快速开始
### UNIX 工具
在 zsh/bash 历史扩展中,使用单引号将带有 `!` 的 prompt 括起来:`apfel 'Hello, Mac!'`。
```
# 单 prompt
apfel "What is the capital of Austria?"
# Permissive mode - 减少 creative/long prompt 的 guardrail 误报
apfel --permissive "Write a dramatic opening for a thriller novel"
# Stream 输出
apfel --stream "Write a haiku about code"
# Pipe 输入
echo "Summarize: $(cat README.md)" | apfel
# 将文件内容附加到 prompt
apfel -f README.md "Summarize this project"
# 附加多个文件
apfel -f old.swift -f new.swift "What changed between these two files?"
# 将文件与 piped input 结合
git diff HEAD~1 | apfel -f CONVENTIONS.md "Review this diff against our conventions"
# 用于 scripting 的 JSON 输出
apfel -o json "Translate to German: hello" | jq .content
# System prompt
apfel -s "You are a pirate" "What is recursion?"
# 来自文件的 system prompt
apfel --system-file persona.txt "Explain TCP/IP"
# 用于 shell 脚本的 Quiet mode
result=$(apfel -q "Capital of France? One word.")
```
### 兼容 OpenAI 的服务器
```
apfel --serve # foreground
brew services start apfel # background (like Ollama)
brew services stop apfel
APFEL_TOKEN=$(uuidgen) APFEL_MCP=/path/to/tools.py brew services start apfel
```
```
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"apple-foundationmodel","messages":[{"role":"user","content":"Hello"}]}'
```
```
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused")
resp = client.chat.completions.create(
model="apple-foundationmodel",
messages=[{"role": "user", "content": "What is 1+1?"}],
)
print(resp.choices[0].message.content)
```
后台服务详情:[docs/background-service.md](docs/background-service.md)。
### 快速测试聊天
`apfel --chat` 是一个小型 REPL,用于测试 prompt 或 MCP 服务器。如需 GUI 聊天应用,请参见 [apfel-chat](https://github.com/Arthur-Ficial/apfel-chat)。
```
apfel --chat
apfel --chat -s "You are a helpful coding assistant"
apfel --chat --mcp ./mcp/calculator/server.py # chat with MCP tools
apfel --chat --debug # debug output to stderr
```
按 Ctrl-C 退出。上下文会自动修剪([docs/context-strategies.md](docs/context-strategies.md))。
## 演示
[`demo/`](./demo/) 中的 shell 脚本:
**[cmd](./demo/cmd)** - 自然语言转 shell 命令:
```
demo/cmd "find all .log files modified today"
# $ find . -name "*.log" -type f -mtime -1
demo/cmd -x "show disk usage sorted by size" # -x = execute after confirm
demo/cmd -c "list open ports" # -c = copy to clipboard
```
**Shell 函数版本** - 添加到你的 `.zshrc` 中,即可在任何地方使用 `cmd`:
```
# cmd - 自然语言到 shell 命令 (apfel)。添加到 .zshrc:
cmd(){ local x c r a; while [[ $1 == -* ]]; do case $1 in -x)x=1;shift;; -c)c=1;shift;; *)break;; esac; done; r=$(apfel -q -s 'Output only a shell command.' "$*" | sed '/^```/d;/^#/d;s/\x1b\[[0-9;]*[a-zA-Z]//g;s/^[[:space:]]*//;/^$/d' | head -1); [[ $r ]] || { echo "no command generated"; return 1; }; printf '\e[32m$\e[0m %s\n' "$r"; [[ $c ]] && printf %s "$r" | pbcopy && echo "(copied)"; [[ $x ]] && { printf 'Run? [y/N] '; read -r a; [[ $a == y ]] && eval "$r"; }; return 0; }
```
```
cmd find all swift files larger than 1MB # shows: $ find . -name "*.swift" -size +1M
cmd -c show disk usage sorted by size # shows command + copies to clipboard
cmd -x what process is using port 3000 # shows command + asks to run it
cmd list all git branches merged into main
cmd count lines of code by language
```
**[oneliner](./demo/oneliner)** - 从纯英文生成复杂的管道链:
```
demo/oneliner "sum the third column of a CSV"
# $ awk -F',' '{sum += $3} END {print sum}' file.csv
demo/oneliner "count unique IPs in access.log"
# $ awk '{print $1}' access.log | sort | uniq -c | sort -rn
```
**[mac-narrator](./demo/mac-narrator)** - 你的 Mac 的内心独白:
```
demo/mac-narrator # one-shot: what's happening right now?
demo/mac-narrator --watch # continuous narration every 60s
```
`demo/` 中还有:
- **[wtd](./demo/wtd)** - “这个目录是什么?”瞬间了解项目概况
- **[explain](./demo/explain)** - 解释命令、错误或代码片段
- **[naming](./demo/naming)** - 为函数、变量、文件提供命名建议
- **[port](./demo/port)** - 哪个进程在使用这个端口?
- **[gitsum](./demo/gitsum)** - 总结最近的 git 活动
更详细的演示:[docs/demos.md](docs/demos.md)。
## MCP 工具支持
使用 `--mcp` 附加 [Model Context Protocol](https://modelcontextprotocol.io/) 服务器。apfel 会自动发现、调用并返回结果。
```
apfel --mcp ./mcp/calculator/server.py "What is 15 times 27?"
```
```
mcp: ./mcp/calculator/server.py - add, subtract, multiply, divide, sqrt, power ← stderr
tool: multiply({"a": 15, "b": 27}) = 405 ← stderr
15 times 27 is 405. ← stdout
```
使用 `-q` 可隐藏工具信息。
```
apfel --mcp ./server_a.py --mcp ./server_b.py "Use both tools"
apfel --serve --mcp ./mcp/calculator/server.py
apfel --chat --mcp ./mcp/calculator/server.py
```
内置了一个计算器,位于 [`mcp/calculator/`](./mcp/calculator/)([docs/mcp-calculator.md](docs/mcp-calculator.md))。
**远程 MCP 服务器**(Streamable HTTP,MCP 规范 2025-03-26):
```
apfel --mcp https://mcp.example.com/v1 "what tools do you have?"
# bearer token - 优先使用 env var(flag 在 ps aux 中可见)
APFEL_MCP_TOKEN=mytoken apfel --mcp https://mcp.example.com/v1 "..."
# mixed local + remote
apfel --mcp /path/to/local.py --mcp https://remote.example.com/v1 "..."
```
## apfel-run:可选的配置层
apfel 本身没有配置文件——就像任何 UNIX 工具一样,仅使用参数和环境变量。如果你需要 TOML 配置(例如多个 MCP、配置档案、存放在 git 中的团队配置),[**apfel-run**](https://github.com/Arthur-Ficial/apfel-run) 是一个 MIT 协议的封装工具,可通过 `execve` 直接替换并添加该功能。
```
brew install Arthur-Ficial/tap/apfel-run
apfel-run config init # starter ~/.config/apfel/config.toml
alias apfel=apfel-run # optional, every apfel flag still works
```
## OpenAI API 兼容性
**Base URL:** `http://localhost:11434/v1`
| 功能 | 状态 | 备注 |
|---------|--------|-------|
| `POST /v1/chat/completions` | 支持 | 流式传输 + 非流式传输 |
| `GET /v1/models` | 支持 | 返回 `apple-foundationmodel` |
| `GET /health` | 支持 | 模型可用性、上下文窗口、语言 |
| `GET /v1/logs`, `/v1/logs/stats` | 仅限调试 | 需要 `--debug` |
| 工具调用 | 支持 | 原生 `ToolDefinition` + JSON 检测。参见 [docs/tool-calling-guide.md](docs/tool-calling-guide.md) |
| `response_format: json_object` | 支持 | 注入系统 prompt;输出中会剥离 markdown 代码块 |
| `response_format: json_schema` | 支持 | 通过 FoundationModels `DynamicGenerationSchema` 保证输出符合 schema;支持 `stream: true` |
| `temperature`, `top_p`, `max_tokens`, `seed` | 支持 | 映射至 `GenerationOptions`。`top_p` 为核采样(nucleus sampling);`temperature: 0` 映射为贪心策略(确定性)。省略 `max_tokens` 时将使用剩余的上下文窗口(兼容 OpenAI 语义)——参见[默认响应上限](#default-response-cap-max_tokens) |
| `stream: true` | 支持 | SSE;仅在设置 `stream_options: {"include_usage": true}` 时返回最终 token 使用量分块(遵循 OpenAI 规范) |
| `finish_reason` | 支持 | `stop`, `tool_calls`, `length` |
| 上下文策略 | 支持 | `x_context_strategy`, `x_context_max_turns`, `x_context_output_reserve` 扩展字段 |
| CORS | 支持 | 通过 `--cors` 启用 |
| `POST /v1/completions` | 501 | 不支持传统的文本补全 |
| `POST /v1/embeddings` | 501 | 设备本地不支持 embedding |
| `logprobs=true`, `n>1`, `stop`, `presence_penalty`, `frequency_penalty` | 400 | 明确拒绝。`n=1` 和 `logprobs=false` 虽被接受但不产生实际效果 |
| 多模态(图像) | 400 | 会以明确的错误信息拒绝 |
| `Authorization` header | 支持 | 设置 `--token` 时必需。参见 [docs/server-security.md](docs/server-security.md) |
完整的 API 规范:[openai/openai-openapi](https://github.com/openai/openai-openapi)。
## 默认响应上限 (`max_tokens`)
当省略 `max_tokens` 时,**CLI 与兼容 OpenAI 的服务器行为完全一致**:该值会作为 `nil` 传递,模型将使用 4096-token 上下文窗口中的所有剩余空间。这是标准的 OpenAI 语义——没有武断的回退常数。
设备端模型拥有 **4096-token 的上下文窗口**,输入和输出共享该空间。如果生成过程触及上限,响应将以 `finish_reason: "length"` 正常结束,并返回部分内容(服务器:HTTP 200;CLI:exit 0 并带有 stderr 警告)。当你希望有更严格的延迟预算或为客户端设定明确上限时,请显式传入 `max_tokens`。
### 示例
```
# Omitted:使用剩余 window,finish_reason:"stop" 或 "length"
curl -sS http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"apple-foundationmodel",
"messages":[{"role":"user","content":"Reply SKIP, MOVE, or RENAME."}]}'
# Explicit cap(推荐用于 tight latency budgets)
curl -sS http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"apple-foundationmodel","max_tokens":128,
"messages":[{"role":"user","content":"Summarise: ..."}]}'
```
### 数值选择建议
| 用例 | `max_tokens` |
|----------------------------------------|---------------|
| 单词 / 分类回复 | 16 - 32 |
| 单行指令 | 64 - 128 |
| 短段落 | 256 - 512 |
| 长段落 / 结构化 JSON | 1024 - 2048 |
| 尽可能使用完整上下文窗口 | 省略 |
请确保 `input_tokens + max_tokens` 小于 4096。如果 prompt 本身超出了窗口范围,生成将无法开始,且请求会因 `[context overflow]` 失败(HTTP 400 / CLI exit 4)。验证器会拒绝非正值(`max_tokens <= 0`)。
### CLI 一致性
CLI 和服务器遵循同一规则:省略即代表使用剩余窗口。不存在会导致偏移的常数。可通过 `--max-tokens N` 或 `APFEL_MAX_TOKENS=N` 覆盖。
```
apfel "Reply SKIP." # uses remaining window
apfel --max-tokens 64 "Reply SKIP." # explicit cap
APFEL_MAX_TOKENS=2048 apfel "..." # via env var
```
### 服务器的宽松护栏
`apfel --serve --permissive` 使服务器对其处理的**每一个请求**都使用 Apple 的 `.permissiveContentTransformations` 护栏。相同的参数,与 CLI 的 `--permissive` 具有相同的语义([docs/PERMISSIVE.md](docs/PERMISSIVE.md))。不支持针对单个请求进行覆盖——由服务器运营者决定整个进程的行为。
```
apfel --serve --permissive # every request uses permissive guardrails
```
## 限制
| 约束 | 详情 |
|------------|--------|
| 上下文窗口 | **4096 tokens**(输入 + 输出合并计算) |
| 平台 | macOS 26+,仅限 Apple Silicon |
| 模型 | 仅一个模型(`apple-foundationmodel`,设备端约 3B 参数),不可配置 |
| 护栏 | Apple 的安全系统可能会拦截无害的 prompt。`--permissive` 可减少误判([docs/PERMISSIVE.md](docs/PERMISSIVE.md)) |
| 速度 | 在设备本地运行,而非云端规模——每次响应需几秒钟 |
| 无 embedding / 视觉功能 | 设备本地不支持 |
| 训练数据 / 知识截止日期 | Apple 尚未公布设备端模型的精确截止日期。当被追问时,模型**在不同的样本中会捏造不同的日期**(例如 "October 2023"、"April 2023")。应将模型关于自身训练的所有自我报告视为不可靠。 |
| 无当前日期或实时感知 | 模型不知道今天的日期,也没有网络/时钟访问权限。如果被问及,它要么拒绝回答,要么编造一个日期。当计算依赖于日期时,请通过系统 prompt 注入当前日期(参见下方的变通方案)。 |
**针对依赖日期的 prompt 的变通方案** - 将当前日期作为系统消息注入:
```
apfel -s "Today is $(date '+%B %d, %Y')." "Write a one-line release note dated today."
```
```
apfel --chat -s "Today is $(date '+%B %d, %Y'). You are a helpful assistant."
```
注意:即使注入了日期,3B 模型仍可能产生幻觉(尤其是在被直接问及其自身的训练截止时间时)。注入有助于*使用*日期的生成式 prompt;它并不能覆盖模型自我报告的本能。
背景信息:[#158](https://github.com/Arthur-Ficial/apfel/issues/158)。
## 参考文档
使用 [Python](docs/guides/python.md)、[Node.js](docs/guides/nodejs.md)、[Ruby](docs/guides/ruby.md)、[PHP](docs/guides/php.md)、[Bash/curl](docs/guides/bash-curl.md)、[Zsh](docs/guides/zsh.md)、[AppleScript](docs/guides/applescript.md)、[Swift](docs/guides/swift-scripting.md)、[Perl](docs/guides/perl.md)、[AWK](docs/guides/awk.md) 调用 apfel 的指南——请见 [docs/guides/index.md](docs/guides/index.md)。已经过实测验证;可运行的证明见 [apfel-guides-lab](https://github.com/Arthur-Ficial/apfel-guides-lab)。
- [docs/install.md](docs/install.md) - 安装、故障排除以及 Apple Intelligence 设置
- [docs/cli-reference.md](docs/cli-reference.md) - 详细的参数、退出码和环境变量
- [docs/background-service.md](docs/background-service.md) - `brew services` 和 launchd 的使用
- [docs/openai-api-compatibility.md](docs/openai-api-compatibility.md) - 深入了解 `/v1/*` 支持矩阵
- [docs/server-security.md](docs/server-security.md) - 来源检查、CORS、token 以及 `--foot`
- [docs/context-strategies.md](docs/context-strategies.md) - 聊天上下文修剪策略
- [docs/mcp-calculator.md](docs/mcp-calculator.md) - 本地和远程 MCP 使用
- [docs/tool-calling-guide.md](docs/tool-calling-guide.md) - 详细的工具调用行为
- [docs/integrations.md](docs/integrations.md) - 第三方工具集成(如 opencode 等)
- [docs/local-setup-with-vs-code.md](docs/local-setup-with-vs-code.md) - 在 VS Code 中使用 apfel 和另一个编辑/应用模型进行本地代码审查
- [docs/demos.md](docs/demos.md) - shell 脚本演示的详细流程
- [docs/EXAMPLES.md](docs/EXAMPLES.md) - 50 多个真实 prompt 及其未经编辑的输出
- [docs/swift-library.md](docs/swift-library.md) - 供下游开发者使用的 `ApfelCore` Swift Package
- [docs/coreai-impact.md](docs/coreai-impact.md) - 为什么 apfel 运行在 FoundationModels 上,而不是 Apple 的新 Core AI(Core ML 的继任者)
## 架构
```
CLI (single/stream/chat) ──┐
├─→ FoundationModels.SystemLanguageModel
HTTP Server (/v1/*) ───────┘ (100% on-device, zero network)
ContextManager → Transcript API
SchemaConverter → native ToolDefinitions
TokenCounter → real token counts (SDK 26.4)
```
Swift 6.3 严格并发。包含三个 target:`ApfelCore`(纯逻辑,可进行单元测试,也可作为 Swift Package 产品使用——参见 [docs/swift-library.md](docs/swift-library.md)),`apfel`(CLI + 服务器),以及 `apfel-tests`(纯 Swift 运行器,无 XCTest)。
## 构建与测试
```
make test # release build + all unit/integration tests
make preflight # full release qualification
make install # build release + install to /usr/local/bin
make build # build release only
make version # print current version
make release # patch release
make release TYPE=minor # minor release
make release TYPE=major # major release
swift build # quick debug build (no version bump)
swift run apfel-tests # unit tests
python3 -m pytest Tests/integration/ -v # integration tests
apfel --benchmark -o json # performance report
```
`.version` 是唯一的真实来源。只有 `make release` 才会提升版本号。本地构建不会改变版本。
## apfel 相关项目树
基于 apfel 构建的项目。每个项目都作为独立的代码库 + Homebrew formula 发布。
| 项目 | 功能描述 | 安装方式 |
|---------|--------------|---------|
| [**apfel**](https://apfel.franzai.com) | 根项目。基于设备本地 FoundationModels 的 CLI + 兼容 OpenAI 的服务器。 | `brew install apfel` |
| [**apfel-chat**](https://apfel-chat.franzai.com) | macOS 聊天客户端:流式 markdown、语音输入/输出、Apple Vision 图像分析。 | `brew install Arthur-Ficial/tap/apfel-chat` |
| [**apfel-clip**](https://apfel-clip.franzai.com) | 针对剪贴板的菜单栏 AI 操作:总结、翻译、改写。 | `brew install Arthur-Ficial/tap/apfel-clip` |
| [**apfel-quick**](https://apfel-quick.franzai.com) | 即时 AI 悬浮层:按键、提问、回答、关闭。 | `brew install Arthur-Ficial/tap/apfel-quick` |
| [**apfelpad**](https://apfelpad.franzai.com) | 公式记事本 - 将设备端 AI 作为内联单元格函数使用。 | `brew install Arthur-Ficial/tap/apfelpad` |
| [**apfel-mcp**](https://apfel-mcp.franzai.com) | 针对 4096 窗口优化 token 预算的 MCP:`url-fetch`, `ddg-search`, `search-and-fetch`。 | `brew install Arthur-Ficial/tap/apfel-mcp` |
| [**apfel-gui**](https://github.com/Arthur-Ficial/apfel-gui) | SwiftUI 调试检查器:请求时间线、MCP 协议查看器、TTS/STT。 | `brew install Arthur-Ficial/tap/apfel-gui` |
| [**apfel-run**](https://github.com/Arthur-Ficial/apfel-run) | UNIX 封装工具,在 `apfel` 之上添加了持久的 MCP 注册表 + TOML 配置。 | `brew install Arthur-Ficial/tap/apfel-run` |
| [**apfel-tag**](https://github.com/Arthur-Ficial/apfel-tag) | 设备端内容标记 CLI:输入文本,输出标签/主题/情感。 | `brew install Arthur-Ficial/tap/apfel-tag` |
| [**apfel-server-kit**](https://github.com/Arthur-Ficial/apfel-server-kit) | 用于生态工具的 Swift package:发现、生成进程并从本地 `apfel --serve` 获取流。 | Swift Package |
## 贡献
欢迎在任何 `Arthur-Ficial/apfel*` 代码库中提交 Issue 和 PR。
**#agentswelcome** - 欢迎提交 AI agent 的 PR。请阅读代码库的 `CLAUDE.md`,运行测试,并在 `Co-Authored-By` 尾注中标明使用的工具。要求与人类相同:代码整洁、测试通过、如实说明限制。最适合 agent 的切入点:[apfel-mcp](https://github.com/Arthur-Ficial/apfel-mcp)([贡献规则](https://apfel-mcp.franzai.com/#contribute))。
## 许可证
[MIT](LICENSE)
标签:DLL 劫持, OpenAI兼容, 人工智能, 大语言模型, 本地部署, 用户模式Hook绕过, 苹果生态