Arthur-Ficial/apfel

GitHub: Arthur-Ficial/apfel

apfel 是一个将 Apple Silicon Mac 内置 LLM 封装为命令行工具和 OpenAI 兼容服务器的本地 AI 推理项目,实现无需 API 密钥的完全设备端运行。

Stars: 5784 | Forks: 216

# apfel ### 你的 Mac 上自带的免费 AI。 [![版本 1.5.5](https://img.shields.io/badge/version-1.5.5-blue)](https://github.com/Arthur-Ficial/apfel) [![Swift 6.3+](https://img.shields.io/badge/Swift-6.3%2B-F05138?logo=swift&logoColor=white)](https://swift.org) [![macOS 26 Tahoe+](https://img.shields.io/badge/macOS-26%20Tahoe%2B-000000?logo=apple&logoColor=white)](https://developer.apple.com/macos/) [![无需 Xcode](https://img.shields.io/badge/Xcode-not%20required-orange)](https://developer.apple.com/xcode/resources/) [![许可证: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![100% 本地设备运行](https://img.shields.io/badge/inference-100%25%20on--device-green)](https://developer.apple.com/documentation/foundationmodels) [![网站](https://img.shields.io/badge/web-apfel.franzai.com-16A34A)](https://apfel.franzai.com) [![#欢迎AI代理](https://img.shields.io/badge/%23agentswelcome-PRs%20welcome-0066cc?style=for-the-badge&labelColor=0d1117&logo=probot&logoColor=white)](#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 上下文。 ![apfel CLI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/4b2b4eae95122034.png) ## 环境要求与安装 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绕过, 苹果生态