【免费下载链接】apfel
The free AI already on your Mac. CLI tool, OpenAI-compatible server, and interactive chat — all on-device via Apple Intelligence. No API keys, no cloud, no downloads.
apfel 的核心产品是apfelCLI 与apfel --serveOpenAI 兼容服务器,而ApfelCore是仓库内一个刻意不依赖 FoundationModels 的纯 Swift 库产品,面向需要复用「OpenAI 兼容类型、请求校验、MCP 协议、Schema 解析、重试分类、上下文裁剪策略」的 Swift 下游开发者。读完本文你将掌握:如何从 Package.swift 引入ApfelCore、各能力域的代表性类型与调用方式、它与可执行目标之间的架构边界,以及1.1.0起稳定的 API 承诺。
定位:为什么需要一个独立的纯 Swift 库
在 Package.swift 中可以看到整个仓库被拆成四个相互独立的目标:
ApfelCore:纯逻辑库,零依赖(dependencies: []),路径为 Sources/Core;ApfelCLI:CLI 参数解析层,依赖ApfelCore(例如复用ContextStrategy);apfel可执行目标:组合ApfelCore+ApfelCLI+ Hummingbird(HTTP 服务器)+ FoundationModels(推理)+ Lesbar(文件提取);apfel-tests:纯 Swift 测试运行器,同样只依赖ApfelCore与ApfelCLI。
这种分层是刻意的:库本身不触碰 FoundationModels 和 Hummingbird,因此可以单元测试、交叉编译,也可以被那些自己发起 FoundationModels 调用的应用直接嵌入。apfel 可执行文件只是把这条纯逻辑层与 Apple 的推理框架、Hummingbird 服务器组合在一起的装配件。
什么场景适合依赖 ApfelCore
关联文档明确给出了适用边界:
- 你的 Swift 应用直接调用 FoundationModels,但希望在线缆上使用 OpenAI 形状的 JSON(请求/响应模型与 OpenAI API 对齐);
- 你想复用 apfel 的上下文裁剪策略、工具调用解析或 MCP 协议类型,而不想引入整个 CLI 二进制;
- 你希望获得与 apfel 自身完全一致的错误分类与重试逻辑。
什么场景不应该用它
- 想在 shell 里跑 prompt → 用
apfelCLI; - 想要本地 OpenAI 兼容服务器 → 用
apfel --serve; - 想要 FoundationModels 本身 → 直接依赖 Apple 框架。
ApfelCore按设计不包含它。
安装:从 1.1.0 开始依赖 ApfelCore
包含ApfelCore的首个打标签版本是1.1.0。在Package.swift中直接依赖该产品:
dependencies: [ .package(url: "https://github.com/Arthur-Ficial/apfel.git", from: "1.1.0") ], targets: [ .executableTarget( name: "MyTool", dependencies: [ .product(name: "ApfelCore", package: "apfel") ] ) ]仓库中的五个示例目标就是按这一模式声明的(见 Package.swift),例如apfelcore-openai-types-example、apfelcore-tool-calling-example、apfelcore-error-handling-example、apfelcore-mcp-protocol-example、apfelcore-context-strategies-example,它们都只依赖ApfelCore,是开箱即用的最小可运行示例。
快速上手:构造一个 OpenAI 兼容请求并校验
引入后,你可以直接使用与 apfel 内部完全相同的请求、消息与校验类型:
import ApfelCore let request = ChatCompletionRequest( model: "apple-foundationmodel", messages: [OpenAIMessage(role: "user", content: .text("Hello"))] ) if let failure = ChatRequestValidator.validate(request) { print(failure.message) }从源码看(Sources/Core/OpenAIModels.swift),ChatCompletionRequest是Decodable, Sendable, Equatable, Hashable的值类型,字段几乎完整覆盖 OpenAI chat completions 线缆格式:
| 字段 | 含义 |
|---|---|
model/messages | 请求的模型名(ApfelCore接受apple-foundationmodel)与对话转写 |
stream/stream_options | 是否请求流式分块及流式配置 |
temperature/top_p | 采样温度、核采样(top-p)覆盖 |
max_tokens/max_completion_tokens | 新旧两代最大补全 token 字段 |
seed | 可选确定性随机种子 |
tools/tool_choice/parallel_tool_calls | 工具定义、工具选择策略、是否允许多工具调用 |
response_format/logprobs/n | 响应格式契约、logprobs 标志、请求补全数量 |
stop/presence_penalty/frequency_penalty/user | 停止序列、惩罚项、终端用户标识 |
x_context_strategy/x_context_max_turns/x_context_output_reserve | apfel 扩展的上下文裁剪策略覆盖项 |
值得注意的细节:effectiveMaxTokens属性在同时给出两个 token 字段时以max_completion_tokens ?? max_tokens解析;服务器路径上的校验器会拒绝冲突值,因此该优先级只对直接使用库的调用方有意义(Sources/Core/OpenAIModels.swift)。初始化器还保留了两个向后兼容重载(v1.11.0 之前与 v1.11.0 之后),供不同版本迁移期使用。
ChatRequestValidator负责拒绝不支持的特性——例如 embeddings、logprobs、n > 1——这正是关联文档「Validation」一栏所指的请求校验能力。
API 面总览
关联文档给出的高层 API 一览,结合源码可确认如下代表性类型:
| 领域 | 代表性类型 | 仓库位置 |
|---|---|---|
| OpenAI 类型 | ChatCompletionRequest、OpenAIMessage、MessageContent、OpenAITool、ToolChoice、ResponseFormat | Sources/Core/OpenAIModels.swift |
| 校验 | 针对不支持特性的请求校验器(embeddings、logprobs、n>1) | Sources/Core/ChatRequestValidator.swift |
| 上下文策略 | ContextStrategy的 5 种裁剪策略 | Sources/Core/ContextStrategy.swift |
| 工具调用 | ToolCallHandler、JSON 工具调用检测、Schema 转换 | Sources/Core/ToolCallHandler.swift |
| MCP 协议 | 消息类型 + 传输无关的客户端原语 | Sources/Core/MCPProtocol.swift、Sources/Core/MCPToolRegistry.swift |
| 错误处理 | ApfelError带类型化错误分类 | Sources/Core/ApfelError.swift |
| 重试逻辑 | withRetry、isRetryableError | Sources/Core/Retry.swift |
完整 API 参考位于 DocC 目录 Sources/Core/ApfelCore.docc,其入口文档 Sources/Core/ApfelCore.docc/ApfelCore.md 描述了库的定位与 Topics 索引。
上下文裁剪策略:ContextStrategy 的 5 种策略
ContextStrategy与ContextConfig描述调用方在把对话交给固定上下文窗口的模型之前,应如何裁剪或保留对话历史(Sources/Core/ContextStrategy.swift):
public enum ContextStrategy: String, Codable, Sendable, CaseIterable, Hashable { case newestFirst = "newest-first" case oldestFirst = "oldest-first" case slidingWindow = "sliding-window" case summarize = "summarize" case strict = "strict" }它同时是CustomStringConvertible与CustomDebugStringConvertible,description直接返回 rawValue。ContextConfig承载四项配置(Sources/Core/ContextStrategy.swift):
| 参数 | 类型与默认值 | 含义 |
|---|---|---|
strategy | ContextStrategy = .newestFirst | 裁剪策略 |
maxTurns | Int? = nil | 裁剪后保留的最大对话轮数 |
outputReserve | Int = 512 | 为模型输出预留的 token 数 |
permissive | Bool = false | 是否允许宽松(不裁剪)行为 |
ContextConfig.defaults等价于默认构造,对应可执行文件的默认行为。需要偏向「最近优先」「严格」或「摘要」时,自行构造即可:
import ApfelCore let config = ContextConfig( strategy: .slidingWindow, maxTurns: 8, outputReserve: 512, permissive: false ) print("strategy=\(config.strategy.rawValue)") print("max_turns=\(config.maxTurns ?? 0)") print("output_reserve=\(config.outputReserve)")这段代码与示例 Examples/ContextStrategies/main.swift 完全一致,可直接运行验证。需要强调的是:ApfelCore本身不做任何 FoundationModels 调用,它只提供可套用在你自己转写/提示构建逻辑之上的策略类型。
工具调用:ToolCallHandler 与 Schema 转换
工具调用域提供ToolCallHandler、JSON 工具调用检测与 Schema 转换。示例 Examples/ToolCalling/main.swift 展示了最小的使用方式:
import ApfelCore let tool = ToolDef( name: "add", description: "Adds two numbers", parametersJSON: #"{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"]}"# ) print(ToolCallHandler.buildOutputFormatInstructions(toolNames: [tool.name])) print(ToolCallHandler.buildFallbackPrompt(tools: [tool]))ToolDef用一段 JSON Schema 字符串描述参数(这里要求a、b两个 number 字段),ToolCallHandler据此生成输出格式指令与回退提示。与之配套的还有SchemaParser/SchemaIR(Sources/Core/SchemaParser.swift、Sources/Core/SchemaIR.swift),负责把 JSON Schema 转换为可传递给模型的中间表示。工具来源解析器ToolResolution的取舍逻辑在测试中可验证(Tests/apfelTests/ToolResolutionTests.swift):客户端自带工具优先,客户端无工具时注入 MCP 工具,两者皆无则返回nil。
MCP 协议:传输无关的客户端原语
ApfelCore提供 MCP 的消息类型与传输无关的客户端原语(Sources/Core/MCPProtocol.swift),并与MCPToolRegistry(Sources/Core/MCPToolRegistry.swift)配合管理 MCP 注入的工具。最小示例(Examples/MCPProtocol/main.swift):
import ApfelCore print(MCPProtocol.initializeRequest(id: 1)) print(MCPProtocol.toolsListRequest(id: 2))initializeRequest与toolsListRequest直接生成带 JSON-RPC id 的协议帧,MCP 相关测试见 Tests/apfelTests/MCPClientTests.swift 与 Tests/apfelTests/MCPToolRegistryTests.swift。
错误处理与重试:与 apfel 完全一致的类型化分类
ApfelError(Sources/Core/ApfelError.swift)是Error, Equatable, Hashable, Sendable的枚举,覆盖典型的 on-device 推理失败模式:
public enum ApfelError: Error, Equatable, Hashable, Sendable { case guardrailViolation case refusal(String) case contextOverflow case contextWindowExceeded(tokenCount: Int, contextSize: Int) case rateLimited case rateLimitedUntil(retryAfterSeconds: Int) case concurrentRequest case assetsUnavailable case unsupportedGuide case decodingFailure(String) case unsupportedLanguage(String) case toolExecution(String) case invalidImageInput(String) case unknown(String) }classify(_:)(Sources/Core/ApfelError.swift)先把已是ApfelError的错误原样返回,再匹配 FoundationModels 的GenerationError,最后回退到字符串匹配本地化描述。示例 Examples/ErrorHandling/main.swift 演示了组合使用:
import ApfelCore let errors: [ApfelError] = [ .rateLimited, .contextOverflow, .unsupportedLanguage("tlh"), ] for error in errors { print("\(error.cliLabel) \(error.localizedDescription)") }重试逻辑withRetry(Sources/Core/Retry.swift)实现指数退避:
public func withRetry<T: Sendable>( maxRetries: Int = 3, delays: [Double] = [0.1, 0.5, 2.0], operation: @Sendable () async throws -> T ) async throws -> T行为要点:maxRetries = 0时是纯透传(不重试);重试前用isRetryableError判断,非可重试错误立即抛出;延迟序列默认[0.1, 0.5, 2.0],超出序列后复用最后一个值;退避横幅写入 stderr 时采用容忍式写入(writeTolerantly),即使 stderr 已关闭也不会中断运行或改变退出码。重试相关的单元测试见 Tests/apfelTests/RetryTests.swift。
稳定性契约:semver 保护的公共 API
ApfelCore与 apfel 主项目共享同一套版本号,没有独立的库版本线(STABILITY.md)。核心承诺(STABILITY.md):
- 破坏性变更必须主版本升级,CI 通过
swift package diagnose-api-breaking-changes把关; - 公共枚举非冻结(non-frozen):MINOR 版本可能新增 case(例如新的校验失败或错误类型),因此消费方不要对它们做穷尽式 switch,始终保留
default分支——CI 的 API 破坏门禁恰好只拦截删除与签名变更,允许新增枚举 case; - 弃用策略:先以
@available(*, deprecated, ...)落地一个发布版本,下一个兼容版本线内仍可用,删除只发生在主版本; - 补丁版本可携带仅服务于 bug 修复的纯类型 API(先例:v1.7.1 为 #315 修复发布了
TokenCountFallback); - 公共表面的变更必须在 CHANGELOG.md 中说明。
对应地,apfel 的 1.0 稳定范围包括 CLI 标志、退出码、输出格式、OpenAI 兼容端点与响应 Schema(/v1/chat/completions、/v1/models、/health)、MCP 工具调用接口、brew services集成、环境变量配置以及ApfelCore公共 Swift API;而模型输出质量、模型可用性、性能特征与--debug调试输出格式则不在稳定承诺内。
可运行的示例入口
关联文档整理了五个主题目录,全部在 Examples 下,且都对应 Package.swift 中声明的可执行目标,可直接swift run:
| 主题 | 目录 |
|---|---|
| OpenAI 请求/响应形状 | Examples/OpenAITypes |
| 工具调用端到端 | Examples/ToolCalling |
| MCP 协议原语 | Examples/MCPProtocol |
| 错误处理 + 重试 | Examples/ErrorHandling |
| 上下文裁剪策略 | Examples/ContextStrategies |
这些示例同时被集成测试覆盖(Tests/integration/test_apfelcore_examples.py),可作为你搭建自己项目的起点。
架构边界小结
一句话概括ApfelCore的架构:零 FoundationModels、零 Hummingbird 依赖是设计意图,而非能力缺口。apfel 可执行文件在 Package.swift 中把ApfelCore与 FoundationModels(推理)、Hummingbird(HTTP 服务器)、Lesbar(文件提取)组装起来,而库本身保持纯 Swift,因此可以被单元测试、交叉编译,并被做自有 FoundationModels 调用的应用直接嵌入。先想清楚你的需求落在哪一层——shell 里跑 prompt 用 CLI,本地 OpenAI 兼容服务用--serve,而要复用策略层,才把ApfelCore作为依赖引入。
【免费下载链接】apfel
The free AI already on your Mac. CLI tool, OpenAI-compatible server, and interactive chat — all on-device via Apple Intelligence. No API keys, no cloud, no downloads.
相关推荐
ROCm 6.4.1 支持 Radeon RX 9070:硬件架构对照表与 Linux、WSL 差异
ROCm 6.4.1 支持 Radeon RX 9070:硬件架构对照表与 Linux、WSL 差异 AMD 开源计算平台 ROCm 在 6.4.1 版本把 R
深入 Swift OPA:在 Swift 应用中原生执行 OPA 策略(IR 计划解释器)
深入 Swift OPA:在 Swift 应用中原生执行 OPA 策略(IR 计划解释器) 2025 年 5 月,Open Policy Agent 社区正式发
后端认证鉴权云原生Swift iOS开发完全指南:Awesome-Swift-Education中的移动应用编程
Swift iOS开发完全指南:Awesome Swift Education中的移动应用编程 想要快速掌握Swift iOS开发?Awesome Swift
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考