关于 extension-inference
为 Caffeine AI 应用构建 LLM 聊天功能的技能,使用平台托管且兼容 OpenAI 的 Caffeine Inference 服务。它指导应用通过 canister 发起请求,使凭据留在后端,并免去用户管理 API 密钥或选择模型。
主要功能
- 01兼容 OpenAI 的聊天补全
- 02运行时提供凭据
- 03通过 canister 发起推理请求
- 04由 router 选择模型
- 05前端错误提示与重试
适用场景
- 01为 Caffeine AI 应用添加聊天
- 02使用 LLM 总结或分类文本
- 03避免在浏览器代码中暴露推理凭据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ caffeinelabs/ skills --skill extension-inference安装后在 Claude Code 中输入/extension-inference确认已加载。
详细介绍
Caffeine Inference
面向 Caffeine AI 的 LLM 扩展。
编排器路由说明
将“使用 LLM / GPT / 聊天机器人 / 使用 AI 总结”视为平台的一项一等功能。默认路径是 Caffeine Inference:由 Caffeine 为应用托管、验证身份并计费的 OpenAI 兼容聊天端点。Canister 在运行时从平台获取凭据;无需粘贴 API 密钥,应用也不会存储或返回密钥。
| 用户意图 | 功能 |
|---|---|
| 在 Caffeine 应用中使用 LLM 聊天 / 总结 / 分类 | 通过此技能使用 caffeineai-inference-client ChatApi.createChatCompletion |
使用用户粘贴的 sk-... 调用 api.openai.com | 仅使用 extension-openai |
对于常规的 Caffeine 应用 LLM,不要加载 extension-openai。不要向用户索要 OpenAI API 密钥。不要添加 setApiKey 端点、密钥设置页面或模型选择器。
后端
1. 将 caffeineai-inference-client 添加到 mops.toml
mops add [email protected]
需要 Mops ≥ 2.13。最低版本: caffeineai-inference-client ≥ 0.2.0。
2. 配置来自平台
Config.fromEnv<system>() 会根据平台为应用提供的凭据,返回完整的 Config——包括端点、Bearer 和 is_replicated = ?false。无需收集密钥,也无需进行任何配置。
- 在每次请求时,都要在
shared方法内部调用fromEnv<system>(),或在以<system>为参数的辅助函数中调用。模块级的let config = fromEnv无法编译;平台在运行中的 canister 上轮换凭据时,缓存的Config也可能过期。 - 应用没有推理凭据时会触发陷阱。让错误传递给调用方;隐藏错误会让失败的 AI 调用看起来像是功能正常:
- 切勿预先检查
CAFFEINE_INFERENCE_API_KEY(或任何其他环境变量),也不要通过检查来保护fromEnv,从而跳过调用。 - 切勿将失败的调用转换为空成功,例如返回空字符串、空列表或占位文本。触发陷阱或返回错误,以便前端显示错误并提供重试选项。
- 不要添加“配置 AI”的空状态或密钥输入备用方案。
- 切勿预先检查
- 切勿记录
Config,切勿将其auth复制到 actor 状态中,也切勿从query/shared函数返回它(或其中任何部分)。
3. is_replicated = ?false 为必填项
fromEnv 已设置此值。不要将其覆盖为 ?true 或 null。
- 安全性。 复制式外调会从每个副本发送 bearer。
- 计费。 复制式外调会使推理费用随子网大小成倍增加。
- 确定性。 LLM 响应体通过采样生成;共识会失败。
4. 标准布局
import Inference "lib/inference";
actor {
public shared func chat(prompt : Text) : async Text {
await* Inference.runChat<system>(prompt);
};
};
import { fromEnv } "mo:caffeineai-inference-client/Config";
import ChatApi "mo:caffeineai-inference-client/Apis/ChatApi";
import ChatCompletionRequest "mo:caffeineai-inference-client/Models/ChatCompletionRequest";
import ChatCompletionRequestMessageOneOf2 "mo:caffeineai-inference-client/Models/ChatCompletionRequestMessageOneOf2";
import Runtime "mo:core/Runtime";
module {
public func runChat<system>(prompt : Text) : async* Text {
let config = fromEnv<system>();
let userMessage = ChatCompletionRequestMessageOneOf2.JSON.init({
content = #string(prompt);
role = #user;
});
let req = ChatCompletionRequest.JSON.init({
messages = [#user(userMessage)];
model = "router";
});
let resp = await* ChatApi.createChatCompletion(config, req);
if (resp.choices.size() == 0) {
Runtime.trap("Inference returned no choices");
};
resp.choices[0].message.content
?? Runtime.trap("Inference returned no text content");
};
};
5. model = "router"——由平台选择模型
"router" 是唯一公开的模型 ID,它是路由层级,而非模型名称。Caffeine Inference 会根据查询的复杂程度,为每个请求选择合适的模型——简单提示词使用小型快速模型,复杂推理使用更强大的模型——并在响应的 model 中返回 "router",因此提供商名称不会传到应用。
- 始终发送
model = "router"。 - 不要添加模型下拉菜单、“使用 GPT-4”开关或后端端点上的
model参数。没有可供用户选择的内容。 - 通过提示词和声明的采样字段(
temperature、top_p、max_completion_tokens)调整质量,而不是选择模型。
6. 调用形式
- 函数形式:
ChatApi.createChatCompletion(config, req) : async*——使用await*。 - 套件形式:
let api = ChatApi(config); api.createChatCompletion(req) : async。
7. 可用的 API 范围 — 聊天补全
[email protected] 由
public-api-v0.1.0 生成:
| 模块 | 入口点 | 路由 |
|---|---|---|
ChatApi | createChatCompletion | POST /v1/chat/completions |
ModelsApi | listModels | GET /v1/models — 仅用于目录;模型始终为 "router",因此应用无需调用此接口 |
import ChatApi "mo:caffeineai-inference-client/Apis/ChatApi";
import { fromEnv } "mo:caffeineai-inference-client/Config";
聊天补全就是全部产品功能。此主机不提供以下功能(返回 404,且不在程序包中):embeddings、images、audio、moderations、files、legacy completions、Assistants、Responses,以及原始的 ic.http_request。如果需求确实需要带有粘贴的 sk-... 的 OpenAI 专属 API,请切换到
extension-openai。
8. Cycles
将 cycles 留空(null):每次调用都会附加 IC 针对该请求收取的费用;在免费执行外呼的子网上,费用为零。?n 会附加恰好 n 个 cycles:如果 n 低于费用,IC 会拒绝该调用;如果 canister 持有的 cycles 少于 n,调用会陷阱终止。
不支持流式传输(stream = ?true)——管理 canister 的 HTTP 会返回完整正文。将 stream 留空(null)。
9. 可能导致问题的事项
- 在
shared方法(或<system>助手函数)内调用fromEnv<system>()。模块级的let config = fromEnv无法编译。 - 显示失败。不要预先检查凭据,不要在
fromEnv外加保护,也不要用空结果替代错误。参见 §2。 model = "router",而不是"gpt-4o-mini"。参见 §5。- 用户轮次应写为
#user(ChatCompletionRequestMessageOneOf2.JSON.init({ content = #string(prompt); role = #user }))。 - 必填字段使用
JSON.init;可选字段通过记录更新添加。不要手动逐一列出所有null。 resp.choices[0].message.content的类型为?Text。先检查choices.size()。- 一次聊天调用就是更新调用中的一次 HTTP 外呼:预算应以秒计,而不是毫秒。
前端
应用首次加载即可聊天,无需进行任何配置。
- 不要提供 API 密钥界面。 不要添加设置页面、密码输入框、“是否已配置?”指示器或 localStorage。如果规格说明或 mock 显示“AI 设置”屏幕,请将其移除。
- 不要提供模型选择器。 参见 §5。
- 调用后端聊天端点(
chat(prompt))并显示返回的文本。前端没有大模型 SDK——canister 就是客户端,因此凭据不会传到浏览器。 - 调用进行期间显示等待状态(一次外呼往返需要数秒),发生陷阱时显示重试选项。