关于 local-ai-app-integration
local-ai-app-integration 指导开发者把 Embeddable Lemonade 嵌入现有应用,让应用通过本机子进程提供本地 AI 模式。适用于已调用云端 AI HTTP API、希望随应用打包 AI 引擎的项目。
主要功能
- 01调查现有 AI API 调用
- 02按主要模态选择模型与后端
- 03将 Lemonade 包放入应用目录
- 04启动并监控 lemond 子进程
- 05重定向客户端并处理错误
适用场景
- 01为现有应用添加本地 AI 模式
- 02将聊天客户端连接至本地 lemond
- 03把模型与后端随应用打包
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ amd/ skills --skill local-ai-app-integration安装后在 Claude Code 中输入/local-ai-app-integration确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.federated.jsonreference.mdskill-card.mdevals/evals.jsonevals/files/apikey-guard/main.pyevals/files/openai-stub/main.py详细介绍
本地 AI 应用集成(可嵌入式 Lemonade)
为现有应用添加本地 AI 模式。该应用已能调用云端 AI API(OpenAI、Anthropic 或兼容 Ollama 的 API)。应用会将可嵌入式 Lemonade 二进制文件 lemond 作为私有子进程启动,现有客户端则通过 http://localhost:PORT/api/v1 与其通信。用户无需单独安装,即可获得本地、私密且针对硬件优化的推理能力(CPU、AMD iGPU/dGPU、XDNA2 NPU)。
**最终成果:**新增一个启动器模块(约 30 行),对现有 HTTP 客户端进行三项必要修改(base_url、api_key 和 120 秒的 HTTP 超时),并在 vendor/lemonade/ 下放置一个随应用分发的二进制文件。
适用场景
以下条件全部满足时,请使用此技能:
- 应用已通过 HTTP 调用云端 AI 服务(OpenAI Chat Completions、Anthropic Messages 或 Ollama)。
- 用户希望 AI 在终端用户的电脑上运行,且 AI 引擎随应用打包,而不是由用户单独安装。
- 目标平台为 Windows x64 或 Linux x64(macOS 可嵌入版本目前处于 Beta 阶段)。
如果用户需要的是系统范围内的 Lemonade Server(安装一次,供多个应用共享),请勿使用此技能;请引导他们访问 https://lemonade-server.ai/install_options.html,并使用标准 OpenAI 基础 URL http://localhost:13305/api/v1。
固定实施流程
此技能遵循一套固定流程。除非有明确理由,否则不要更改顺序。
[ ] 1. Survey the app's current AI integration
[ ] 2. Pick a model + backend profile
[ ] 3. Place Embeddable Lemonade in the app's tree (full package, not just the binary)
[ ] 4. Add a `lemond` launcher (subprocess + API key + port + per-stage logging)
[ ] 5. Re-point the existing client at lemond (base_url, api_key, 120s timeout — all three required)
[ ] 6. Wait for /api/v1/health, install backend, then PULL the model before first use
[ ] 7. Wire shutdown and error recovery
请根据以下清单跟踪进度。只有在每一步都验证通过后,才能继续。
记录每个阶段。 本地集成有许多不易察觉的故障点——启动进程、健康检查、后端安装、模型下载、首次推理。若没有在每次转换时记录日志,“什么都没发生”和“在第 3 阶段出错”就无法区分。开发时请为每个阶段输出一条清晰的日志(参见步骤 4);此集成中最常见的卡点是结果为空且没有错误,没有日志就无法发现。
步骤 1:检查应用
找出应用当前调用云端 AI API 的所有位置。在代码仓库中搜索:
openai、OpenAI(、chat.completions、responses.createanthropic、Anthropic(、messages.createapi.openai.com、api.anthropic.com、localhost:11434(Ollama)OPENAI_API_KEY、ANTHROPIC_API_KEY
继续之前,请记录以下信息:
- 客户端库和语言(例如:
openai-python、openai-node、@anthropic-ai/sdk、go-openai、原始fetch)。 - **使用的模态:**文本聊天、工具调用、嵌入、图像生成、转录、TTS。这将决定步骤 2 中的模型和后端选择。
- **构造基础 URL 和 API 密钥的唯一位置。**如果不存在这样的集中位置,请先重构。切换本地模式必须只需修改一个配置对象。
- 所有 API 密钥门槛,即在输入密钥前阻止应用运行的机制(例如,入门流程限制、拒绝空密钥的验证器、密钥不存在时禁用 AI 的启动检查)。请逐一记录——步骤 5 会在本地模式下绕过这些限制。
第 2 步:选择模型和后端配置
根据应用的主要模态选择一个默认配置。不要提供一大堆选项,只提供一个合适的默认配置,并说明用户如何覆盖它。
| 应用的主要需求 | 默认模型 | 配方 | 原因 |
|---|---|---|---|
| 通用聊天 / 助手 | Qwen3-4B-GGUF | llamacpp | 体积小、速度快、工具调用能力好,适用于 8GB 系统 |
| 编码助手 | Qwen2.5-Coder-7B-Instruct-GGUF | llamacpp | 代码能力强,可在 iGPU 上运行 |
| 视觉 / 多模态聊天 | Gemma-4-E2B-it-GGUF | llamacpp | 小巧的多模态默认选项 |
| Ryzen AI 上优先使用 NPU | Llama-3.2-3B-Instruct-Hybrid | ryzenai-llm | Windows 上的 XDNA2 NPU |
| 语音转文本(Windows) | Whisper-Large-v3-Turbo | whispercpp | 使用单个模型;探测会自动按 NPU → iGPU/dGPU → CPU 的顺序选择 |
| 语音转文本(Linux NPU) | whisper-v3-turbo-FLM | flm | Linux NPU 路径;不使用 NPU 时回退到 whispercpp iGPU/CPU |
| 文本转语音 | kokoro-v1 | kokoro | 仅使用 CPU,延迟低 |
| 图像生成 | SDXL-Turbo | sd-cpp | 单步生成 |
对于大模型后端,默认使用 llamacpp,并让 lemond 自动按 rocm → vulkan → cpu 的顺序选择后端,方法是将 llamacpp_backend 留空。只有当应用对硬件有明确要求时才覆盖此设置。
范围:此技能会在集成时于开发者的机器上选择一次后端。 根据最终用户硬件进行运行时回退不在范围内。将 vulkan 作为通用回退方案打包,以确保应用能在任何机器上运行。如果开发机器有 NPU 且所选配方支持 NPU,技能将使用 NPU 后端;否则回退到 vulkan。
注意: 有 NPU 不代表每种配方都支持 NPU。提交采用前,请通过
GET /api/v1/system-info确认配方和后端组合为installed或installable。各配方的决策规则请参见 reference.md。
更多选项及其权衡请参见 reference.md。
第 3 步:将 Embeddable Lemonade 放入应用目录树并安装后端
从最新的 Lemonade 发布版本获取可嵌入制品:
https://github.com/lemonade-sdk/lemonade/releases/latest
下载与你的目标操作系统匹配的文件:
- Windows:
lemonade-embeddable-{VERSION}-windows-x64.zip - Linux:
lemonade-embeddable-{VERSION}-ubuntu-x64.tar.gz
不要根据标签手动拼接下载 URL。 Git 标签带有前导
v(例如v10.8.0),但制品文件名会去掉它(lemonade-embeddable-10.8.0-...),因此直接使用标签会导致 404。请通过 GitHub API 按稳定的名称模式查询制品,并使用其返回的 URL,如下所示——这样在版本和命名更改时仍然有效。
首先,创建目标目录——新克隆的仓库中尚不存在该目录:
# Windows
New-Item -ItemType Directory -Force vendor\lemonade
# Linux
mkdir -p vendor/lemonade
然后在 Windows 上下载并解压(PowerShell):
$rel = Invoke-RestMethod https://api.github.com/repos/lemonade-sdk/lemonade/releases/latest
$asset = $rel.assets | Where-Object { $_.name -like "lemonade-embeddable-*-windows-x64.zip" } | Select-Object -First 1
Invoke-WebRequest $asset.browser_download_url -OutFile lemond.zip
Expand-Archive lemond.zip -DestinationPath "$env:TEMP\lemond-unpack"
$folder = $asset.name -replace '\.zip$','' # unpacked dir = asset name without .zip
Copy-Item -Recurse "$env:TEMP\lemond-unpack\$folder\*" vendor\lemonade\
# Sanity check: resources/ must be nested under vendor\lemonade\ (not flattened)
if (-not (Test-Path vendor\lemonade\resources\*.json)) { throw "resources/ missing — re-extract and copy again" }
在 Linux 上使用 bash:
URL=$(curl -s https://api.github.com/repos/lemonade-sdk/lemonade/releases/latest \
| grep browser_download_url | grep ubuntu-x64.tar.gz | cut -d'"' -f4)
curl -L "$URL" | tar -xz --strip-components=1 -C vendor/lemonade
复制完整的软件包,而不只是二进制文件。 压缩包包含
lemond[.exe]、lemonade[.exe]、LICENSE和resources/。resources/目录是必需的——如果没有它,lemond 虽然能启动并通过健康检查,但每个模型和后端请求都会失败。只复制二进制文件会得到一个看似正常、实则无法运行的服务。lemond与lemonade命令行工具:lemond是随应用提供的内嵌服务二进制文件。lemonade命令行工具是独立的打包工具,仅在开发/构建时用于安装后端。上文解压的同一嵌入式压缩包中,lemond[.exe]旁边已经包含相匹配的lemonade[.exe],因此两者版本一致。不要通过pip install lemonade-sdk获取它:PyPI 软件包属于单独的较旧发布系列,其端口、模型名称和安装 API 与此处捆绑的lemond不匹配,混用已知会导致版本不一致却不报错。请确保本步骤下载的同一版本中的lemonade命令行工具、lemond和后端保持一致。
设置完成后(首次运行并安装后端)的预期目录结构。刚解压的软件包仅包含 lemond[.exe]、lemonade[.exe]、LICENSE 和 resources/——如下方注释所述,以下项目会在之后创建:
vendor/lemonade/
lemond[.exe] # the only binary the app ships
LICENSE
config.json # generated on first run; commit a seed copy
resources/
server_models.json # do not edit; use GET /api/v1/models at runtime
backend_versions.json
bin/ # backends bundled at packaging time
llamacpp/vulkan/llama-server[.exe]
models/ # pre-bundled model weights (optional)
models--unsloth--Qwen3-4B-GGUF/
server_models.json: 请勿编辑或依赖此文件,它可能已过时。唯一权威的模型列表,是已安装后端的运行中lemond实例上的GET /api/v1/models。
捆绑方案:谨慎选择
- 后端: 打包时捆绑
llamacpp:vulkan(适用于所有 GPU)。在受支持的 AMD 系统上,首次运行时通过检查GET /api/v1/system-info,再使用POST /api/v1/install安装llamacpp:rocm。切勿捆绑所有后端,否则会导致安装包过大。 - 模型: 可以将默认模型捆绑在
models/下(可离线安装,但安装包更大),也可以在首次运行时通过POST /api/v1/pull拉取(安装包更小,但需要网络)。选择一种方案并记录说明。 models_dir: 在config.json中设为./models,让权重仅供应用使用。只有在用户明确希望与其他应用共享权重时,才保留为auto。
后端安装时机——两种不同路径:
打包时(开发者的计算机上,在打包前)。使用
vendor/lemonade/中随软件提供的 lemonade 命令行工具,以确保其版本与捆绑的lemond一致(在前面加上./或使用完整路径):vendor/lemonade/lemonade backends install llamacpp:vulkan vendor/lemonade/lemonade backends install flm:npu # 仅限 Windows NPU 路径这会在应用发布前,将后端二进制文件打包到
vendor/lemonade/bin/中。无需运行lemond。请使用版本与捆绑的lemond相匹配的现代版lemonade命令行工具(解压的压缩包中的副本即可);不要通过pip install lemonade-sdk获取它。首次运行/运行时(用户的计算机上,在
lemond运行后):POST /api/v1/install {"recipe": "llamacpp", "backend": "rocm"}此方式用于无法通用捆绑的硬件专用后端(例如
llamacpp:rocm)。lemond必须已在运行(第 4 步已完成)。
步骤 4:添加 lemond 启动器
将启动器编写为一个名为 lemond_launcher.py 的新模块(或者使用应用所用语言对应的 lemond_launcher.<ext>)。它是一个轻量级进程监控器,只负责以下工作:
- 生成一个全新的随机 API 密钥:
key = secrets.token_urlsafe(32) - 选择一个空闲的 localhost 端口:将
socket绑定到端口 0,读取分配的端口,然后关闭套接字。 - 通过
subprocess启动 lemond:subprocess.Popen([LEMOND_BIN, LEMOND_DIR, "--port", str(port)], env={**os.environ, "LEMONADE_API_KEY": key}) - 循环轮询
GET /api/v1/health,并使用Authorization: Bearer {key},直到收到 HTTP 200——这是唯一正确的就绪检查方式。 - 将选定的
port和key提供给应用的其他部分。
每个生命周期阶段记录一行日志。 从一开始就加入日志记录, 不要等到出问题时才把它当作补充。每个无声的状态转换都需要一个 可见标记,这样发生故障时就能准确定位阶段。建议如下:
[lemond] Starting on port <port> [lemond] Healthy on port <port> [lemond] <recipe>:<backend> installed (or: already installed / install failed) [lemond] Pulling model <name>... then: Model <name> ready (or: pull returned <status>) [local] <modality> result: <value> (first inference output — empty string here = unpulled model)逐字记录首次推理结果,就能让静默空结果故障(步骤 6)从耗时数小时的 疑难问题变成一行日志即可定位的问题。通过应用的常规日志记录机制输出这些日志, 以便在发布版本中关闭。
开发模式文件监视器: 如果应用使用文件监视器(Tauri、 Electron、Next.js、Vite 等)监视源代码树,请确保监视路径中排除了
vendor/lemonade/。Lemond 会在运行时写入配置和缓存文件;如果文件监视器 检测到这些文件,就会重启应用、终止 lemond 子进程,并在新端口上启动一个新进程, 从而悄无声息地中断正在进行的转录。测试前,请将vendor/(或对应目录) 添加到文件监视器的忽略列表中。
请直接使用 reference.md § Reference launchers 中的参考实现——原样复制,只调整 LEMOND_DIR 路径。不要从头编写启动器。Python 参考启动器使用 secrets(生成 API 密钥)、socket(探测空闲端口)和 subprocess(启动 lemond);Node.js 启动器则使用对应的标准库模块。两者都能正确处理端口竞争重试和健康检查轮询。
就绪状态始终通过轮询确切端点
GET http://127.0.0.1:<port>/api/v1/health 并检查是否收到 HTTP 200 来确定——绝不要
通过读取 lemond 的 stdout 或 stderr 来判断。任何编写的健康检查辅助函数都必须访问
/api/v1/health 路径。
步骤 5:将现有客户端重新指向 lemond
对应用现有的客户端构造进行三项更改——三项都必须完成,不能省略:
- 将
base_url设置为http://127.0.0.1:{port}/api/v1 - 将
api_key设置为启动器密钥 - 将 HTTP 超时设置为 120 秒——这是强制要求,不能省略
120 秒超时不是调优建议。大多数 HTTP 客户端的默认超时为 30 秒,短于 lemond 在真实硬件上的首次模型加载时间。如果不设置,请求会静默超时,界面也不会显示任何内容,看起来就像集成出了问题。
Python(openai)——需要进行的确切更改:
import httpx
from openai import OpenAI
proc, key, port = start_lemond()
client = OpenAI(
base_url=f"http://127.0.0.1:{port}/api/v1",
api_key=key,
http_client=httpx.Client(timeout=120), # required: 120s for first-run model load
)
对于其他客户端:
| 现有客户端 | 新的 base_url | 新的身份验证 | 超时 |
|---|---|---|---|
openai-python | http://127.0.0.1:{port}/api/v1 | api_key=key | httpx.Client(timeout=120) |
openai-node | http://127.0.0.1:{port}/api/v1 | apiKey: key | timeout: 120000 |
@anthropic-ai/sdk | http://127.0.0.1:{port}/api/v1 | apiKey: key | timeout: 120000 |
原始 fetch / requests | 相同 | Authorization: Bearer {key} | 按请求设置 |
| 兼容 Ollama 的代码 | http://127.0.0.1:{port}/api/v0 | 仍然传入密钥 | 120 秒 |
请求中的模型标识符应继续使用 Lemonade 模型名称(例如
Qwen3-4B-GGUF),而不是云端名称。
本地模式完全不需要云 API 密钥。 这是本地模式的基本特性,不是什么边缘情况:没有需要进行身份验证的云服务,因此绝不应要求用户提供密钥。任何要求提供密钥的引导界面、验证器或启动检查都不得阻止本地模式用户。具体来说:
- 在本地模式下跳过密钥输入界面,或自动通过该界面。
- 在所有验证路径中,将本地模式视为已获授权——当当前模式为本地模式时,空密钥检查必须直接返回“有效”,绝不能抛出“未配置 API 密钥”。
- 仅在云模式下重新启用此门槛。
步骤 4 中的 lemond 密钥由启动器在内部生成,仅用于本地环回连接,因此用户永远不会看到或输入该密钥;任何 UI 占位符(例如 "local")都可以。切换到本地模式后,绝不能让用户卡在密钥输入界面。
步骤 6:先检查健康状态,再安装后端,然后拉取模型——在首次推理之前
GET /api/v1/health 返回 200 表示服务器已启动。这并不意味着推理可以正常工作。在首次真实请求成功之前,还必须满足另外三个条件:已安装适用于相应模态的后端、模型权重已下载到磁盘,并且(首次调用时)模型已加载到内存中。将 health=200 视为“已就绪”,是集成看起来无法正常工作的最主要原因。
不要在启动时调用 POST /api/v1/load。 Lemond 会在首次推理请求时将模型惰性加载到内存,并自行处理这一步。在不同 lemond 版本中,预加载并不可靠(/load 请求体的格式在不同版本间有过变化),格式错误的调用还可能在用户执行任何操作前导致服务器崩溃或运行不稳定。加载是唯一应交由 lemond 惰性处理的步骤——拉取不是。
拉取模型,确保其已保存到磁盘
延迟加载只会加载已下载的权重。如果模型从未拉取过,首次推理不会报错——lemond 会返回空结果/空白内容,HTTP 状态码为 200。因此,健康检查通过且后端安装完成后,请主动拉取模型:
POST /api/v1/pull
{"model": "Whisper-Large-v3-Turbo"}
此操作具有幂等性——如果权重已存在,则不执行任何操作;如果不存在,则会下载。请在设置期间运行一次(安装后端之后、首次由用户触发推理之前),并记录结果。
- 默认模型(在第 2 步中选择的模型):按上述方式通过名称拉取。
- 自定义/用户覆盖的模型:不要假设模型已存在。先通过
GET /api/v1/models确认它是一个真实的 Lemonade 模型(这是唯一可信的目录,参见 reference.md),然后再用相同方式拉取。模型出现在目录中并不能证明其权重已下载;只有成功拉取才能证明。
**静默返回空结果几乎总是因为模型尚未拉取。**如果推理返回空字符串/空白内容,且没有 HTTP 错误,说明模型尚未下载。先检查拉取步骤,再排查其他问题——这是最耗费时间的故障模式。请记录拉取结果和首次推理结果(参见第 4 步),这样就能从控制台诊断问题,而不必靠猜测。
显示整个设置过程,而不只是模型加载
首次运行时的冷启动不只是加载模型。完整流程如下:
server spawn → health 200 → backend install → model download → model load → first result
在全新机器上,安装后端和下载模型分别可能耗时数十秒到数分钟(通过网络下载数 GB 的权重)。仅模型加载就需要 10–30 秒。如果应用在此期间没有任何显示,看起来就会像卡住了一样。
最低要求:从设置开始到收到首个响应为止,显示加载指示器或状态消息(“正在设置本地 AI…”),覆盖上述整个流程,而不只是最后的加载步骤。最简单的实现方式是在设置/首次请求开始时设置一个标志,并在收到首个响应时清除该标志。模型拉取并加载一次后,后续运行都会很快;长时间等待只会发生在首次运行时。
第 7 步:生命周期与恢复
只需处理以下故障模式。不要过度设计。
| 症状 | 原因 | 恢复方法 |
|---|---|---|
| 推理返回空结果/空白内容,HTTP 状态码为 200,且没有错误 | 模型从未拉取:后端已安装,但权重不存在,因此延迟加载没有内容可加载 | 使用 {"model":"..."} 调用 POST /api/v1/pull,等待成功后重试。记录拉取结果和首次推理结果。这是最常见的静默故障——参见第 6 步 |
POST /api/v1/load 返回 404/找不到模型 | 模型尚未拉取(与上方空结果行的根本原因相同) | 使用 {"model": "..."} 调用 POST /api/v1/pull,然后重试 /api/v1/load |
POST /api/v1/load 返回 500,并显示后端错误 | 尚未为此硬件安装后端 | 调用 GET /api/v1/system-info,选择受支持的后端,使用 {"recipe": "...", "backend": "..."} 调用 POST /api/v1/install,然后重试 |
| 子进程立即退出 | 端口竞争:在 freePort() 和 lemond 绑定端口之间,另一个进程抢占了该端口 | 参考启动器会自动使用新端口重试(最多 3 次) |
/api/v1/health 始终未返回 200 | 首次运行时,后端在冷磁盘上的解压速度较慢 | 首次启动时将超时时间延长至 90 秒,之后设为 30 秒 |
| 每个请求都返回 HTTP 401 | 忘记添加 Authorization: Bearer 标头 | 检查客户端配置,因为设置了 LEMONADE_API_KEY 时,Lemonade 会拒绝未经身份验证的调用 |
**关闭:**应用退出时,调用 proc.terminate()(Unix)或 proc.kill()(Windows)。lemond 会刷新配置,并在几秒内正常退出。始终等待进程结束;切勿让其成为孤儿进程。
不要解析 lemond 的标准输出来检测是否已就绪;请使用 HTTP /api/v1/health 探测。标准输出格式不是稳定的契约。
验证清单
只有以下所有条件都满足时,集成才算完成:
-
vendor/lemonade/包含完整软件包:lemond[.exe]、lemonade[.exe]、LICENSE和resources/,而不仅仅是二进制文件。 - 每次启动时,
lemond都会以子进程的形式启动,并使用新的 API 密钥。 -
GET /api/v1/health在超时时间内返回 200。 - 首次推理前,默认模型已下载(或已随软件包提供);对于
自定义/覆盖的模型,先通过
GET /api/v1/models确认,再下载。 如果结果为空且没有错误,说明跳过了此步骤。 - 每个生命周期阶段都会记录清晰的日志(启动、健康检查、后端安装、 模型下载、首次结果),以便在出现故障时通过控制台诊断。
- 将基础 URL 和密钥替换后,现有客户端的聊天/图像/语音调用会返回 有效响应,且没有更改其他代码。
- 首次运行延迟会明确显示:从发送首次推理请求到收到响应期间,界面 一直显示加载状态。
- HTTP 客户端超时设置为 120 秒。
- 在本地模式下,应用无需云 API 密钥:没有新手引导拦截、 验证器或启动检查会阻止用户;当当前模式为本地模式时,任何代码路径 都不会抛出“未配置 API 密钥”。
- 如果应用使用开发模式文件监视器,则将
vendor/lemonade/排除在 监视路径之外,以免lemond的运行时写入触发重启。 - 终止父进程后,不会遗留
lemond子进程。 - 在没有最佳后端的全新机器上,应用仍可通过
bin/中随附的 Vulkan 回退方案运行。
如果有任何一项未勾选,请勿宣布任务完成。
参考
有关详细的模型目录、后端选择矩阵、完整端点参考、配置键以及针对各模型的
recipe_options.json 调优,请参阅
reference.md。