# @broberg/ai-sdk 中文文档

规范地址：https://npmcn.com/package/%40broberg/ai-sdk/

版本：0\.69\.4
npm 元数据更新时间：2026-10-31T16:16:49.920Z
中文译文更新时间：2026-10-31T17:32:36.311Z
软件包许可：FSL-1\.1-Apache-2\.0

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40broberg/ai-sdk
原文对照：https://npmcn.com/package/%40broberg/ai-sdk/?lang=original
项目主页：https://github.com/broberg-ai/ai-sdk#readme
源码仓库：https://github.com/broberg-ai/ai-sdk

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.4 MB，18 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：1 个
- 首次发布：2026-06-02T08:09:43.990Z
- 最新版本发布：2026-10-31T16:16:48.248Z
- 已发布版本数：124（近 12 个月 124 个）

## 中文简介

统一的AI/LLM SDK -一个门面，所有提供商，所有功能，每次调用的一流成本控制。

## 安装

```sh
npm install @broberg/ai-sdk
```

也可以使用：`pnpm add @broberg/ai-sdk`、`yarn add @broberg/ai-sdk`、`bun add @broberg/ai-sdk`

## README

# @broberg / ai-sdk

一个AI/LLM SDK -一个门面，所有的提供者，所有的功能 **一流的 对每个电话进行成本控制**.

与提供者无关的外观：您的代码调用 `ai.chat()`， `ai.vision()`， `ai.image()` -永远不要直接提供SDK。通过改变一个层来交换提供商； 不是你的电话亭。每次调用都会返回 `Usage` (令牌，成本，延迟， 运输, **data-residency地区**)，并将其扩展到任何成本池。

```bash
bun add @broberg/ai-sdk   # or: npm i @broberg/ai-sdk
```

## 数据到哪里去了？- - - - - - `usage.region`

每个回应都带有 **实际应答的端点的数据驻留**：

```ts
const { text, usage } = await ai.chat({ prompt, tier: "smart" });
if (usage.region !== "eu") throw new Error(`personal data would have left the EU (${usage.region})`);
```

**只有 `"eu"` 是一个积极的主张。** `"unknown"` 意思是我们不能这么说 `region !== "us"` 是 **不** 欧盟检查——它通过了每一个OpenRouter调用。

`region` 是由 **主人** 使用的请求，而不是来自提供程序的名称- `vertex`， `azure` 和 `bfl` 默认是EU，但每个都需要重写，所以a 基于名称的表将报告 `eu` 因为有人指着电话 `us-central1`.

**三件事决定你的防御是否有效：**

|  |  |
| --- | --- |
| **电话结束后** | `usage.region` - - - - - - `"eu"` | `"us"` | `"cn"` | `"unknown"` |
| **打电话之前** | `regionOfHost(urlOrHostname)` ——这是拒绝的唯一方式 _之前_ 字节离开 |
| **从来没有** | `regionOfProvider(name)` -见下文 |

```ts
import { regionOfHost } from "@broberg/ai-sdk";
if (regionOfHost("https://api.mistral.ai/v1") !== "eu") throw new Error("not EU");
```

**不建立常驻警卫吗 `regionOfProvider`.** 一个名字不能回答 托管时，提供商采取 `baseUrl`,所以 `regionOfProvider("mistral")` 是 `"unknown"` ——还有一个守卫，写着 `regionOfProvider(p) === "eu"` 因此拒绝了 **米斯特拉尔，欧盟唯一的航线。** 故障关闭和无用：它过滤掉 这是它存在的目的。

容许清单 `(provider, model)` 对的另一边有相同的孔：a 米斯特拉尔门前的大门还没有动 `"mistral"`/`"mistral-large-latest"`，两者都匹配 田野和传球。 `regionOfHost` 答案 `"unknown"` 这是最诚实的回答。

## 快速启动

```ts
import { createAI } from "@broberg/ai-sdk";

const ai = createAI(); // real adapters, keys from env (ANTHROPIC_API_KEY, …)

const { text, usage } = await ai.chat({ prompt: "Say hi in Danish" });
console.log(text, usage.costUsd);

const v = await ai.vision({ image: "https://…/photo.png", prompt: "Describe" });
const img = await ai.image({ prompt: "a sunlit beach in Blokhus" });
const da = await ai.translate({ text: "hello", to: "Danish" });
const emb = await ai.embedding({ text: ["a", "b"] });
```

## 功能

`chat` · `vision` · `translate` · `image` (歧视。ai default / OpenRouter `embedding` · `transcribe` (耳语)优先 **提示合同** 具有结构化输出：

```ts
import { z } from "zod";
const { data } = await ai.contracts.extract({
  text: "Sanne is 40 and lives in Blokhus",
  schema: z.object({ name: z.string(), age: z.number(), city: z.string() }),
});
// also: ai.contracts.{ mockup, design, classify, rerank }
```

**`ai.judge` - Jev (TypeSafe), F066** 不是聊天模式：输入是/否，选择和 对一段内容的问题进行评分，每个答案都有一个校准的概率 和一个 `confidence` 你可以登机了。每百万输入令牌$0.042，输出免费。 **美国托管-不用于个人数据。** 看到文档/ API。md§4。

## 大声朗读：实际上所说的不是你的 `text`

`ai.tts({ pronunciations })` 重写您的文本之前，提供者看到它- `broberg.ai` 变成四个口语单词。所以 **`text` 是音频的复述吗**，和 文字荧光笔建立在 `text` 孤独会漂向每一个重写的字。

有两个领域弥补了这一差距：

```ts
const { audio, wordTimings, ssml } = await ai.tts({
  text, voice, wordTimings: true, pronunciations,
  override: { provider: "azure" },
});
wordTimings.words      // [{ text, startMs, endMs, sourceStart, sourceEnd }]
wordTimings.unaligned  // spoken words we could NOT place — never a guessed offset
ssml                   // the EXACT markup we sent, verbatim — your ground truth
```

`sourceStart`/`sourceEnd` 指数 **你的原文**，因为Azure会报告音频时间 只有和不携带任何文本偏移量；手稿的链接在这里。 `ssml` 存在，因此您可以检查派生而不是信任它-它是字符串 我们发送了，而不是重建（由无法验证所说内容的消费者提交，F055.4）。 它是 `undefined` 在没有建立标记的路由上；空字符串则是另一种说法。

**两个让人惊讶的匹配规则**，两者均以 `pronunciations`：

-   **不区分大小写的。** `ai`， `Ai` 和 `AI` 所有匹配规则为 `AI`.
-   **碰到连字符的单词保持其拼写。** `broberg.ai-drevet` 不会被重写 除非这个条目 `matchInCompounds: true` -保持的规则 `mail` 的 `e-mail`． 一个消费者得出了他们自己的期望单词列表，得到了这两个 字段存在之前出现错误。

**Word计时需要Azure的BATCH路由**，不同的呼叫形式（提交→投票→ZIP）， 该路由需要资源的自定义子域-区域主机回答401 使用有效的密钥，责备密钥。集 `AZURE_SPEECH_RESOURCE`;没有它 `wordTimings` 在调用之前失败，命名修复。

## 供应商和层级

适配器: **人为** (HTTP + `claude -p` 子流程), **OpenAI**， **谷歌 双子座**， **DeepInfra**， **OpenRouter** (包括极大极小), **fal.ai** (图片)。

通过命名调用路由 **层** - - - - - - `fast · smart · powerful · cheap · vision · embedding` -每个解析为a `(provider, model, transport)` 三, 每次调用可重写：

```ts
await ai.chat({ prompt: "…", tier: "powerful" });
await ai.chat({ prompt: "…", override: { provider: "openrouter", model: "minimax/minimax-m2.7" } });
```

> **图像-光栅vs.矢量。** `ai.image()` 默认为false。ai（栅格PNG）。为 **向量/ SVG** 输出（标识），覆盖到OpenRouter Recraft - slug是一个 **OpenRouter** 模型，而不是一个失败的应用程序id：
> 
> ```ts
> await ai.image({ prompt: "…", override: { provider: "openrouter", model: "recraft/recraft-v4.1-vector" } });
> // → data:image/svg+xml;base64,… with ground-truth cost
> ```
> 
> `recraft/recraft-v4.1-pro-vector` 为复杂向量提供了更高的细节（~$0.30） 每张图片vs ~0.08美元); `recraft/recraft-v4.1-pro` 光栅等效(~0.21美元)。

`cheap` 默认为最便宜的云模型 **据悉,小** （欧盟/巴黎托管，gdp安全，~ 0.10美元/ 0.30美元）-所以成本层呼叫是安全的 默认的个人资料；覆盖每个呼叫，以获得更便宜的非个人路线。 ( `claude -p` 子进程传输仍然可以通过显式传输 `override: { transport: "subprocess" }`，但不再是默认路由。)

## 这将走向何方？- - - - - - `wouldRouteTo` （预测，不是事实）

`usage.region` 答案 **电话去了哪里**． 它无法回答 _我可以寄这个吗？_,因为 它只在字节剩余时存在。 `wouldRouteTo` 回答那一半：

```ts
import { wouldRouteTo } from "@broberg/ai-sdk";

const f = wouldRouteTo("smart");
// { kind: "forecast", provider: "mistral", host: "https://api.mistral.ai/v1",
//   wouldRouteTo: "eu", onlyIf: [ …the assumptions… ] }
```

**它的形状故意不像 `usage.region`.** 没有所谓的领域 `region` 在 这是由一个考验来执行的：一个可能是错误的预测不能穿衣服 事实不能。 `onlyIf` 回来 **与** 而不是生活在这些 文档-只有当你推翻“否”时，答案才有效 `baseUrl` 没有退路，因为 **退路是一条路线，而这条路线决定了居住权**.

**`"depends-on-config"` 是一个答案，而不是一个失败。** `azure`， `vertex`， `deepl`， `requesty` 和 `fal` 根据您提供的值构建它们的主机。 `requesty` 是这个国家存在的原因吗 有 _两个_ 欧盟和非欧盟的东道主，所以我们在那里命名的任何地区都会有居住权 由表而不是由路由决定的。

**它什么都不拒绝。** 它永远不会抛出和阻止调用-即使是无意义的 提供者。居住权政策属于每个消费者，而不属于这个包裹；这是 使决策成为可能的信息，这是门的反义词。

**为什么它会说 `"eu"` 哪里 `regionOfProvider` 说 `"unknown"`：** 它要求 _主人_ 这个 SDK将实际使用，而不是提供商的 _名字_. `regionOfProvider("mistral")` 是 `"unknown"` 根据设计，适配器需要一个 `baseUrl`． 由一位消费者提出的 手动复制我们的层表到他们自己的仓库来回答这个问题；现在可以删除该副本 而不是维护。

## 成本，预算和水槽

```ts
import { createAI, upmetricsSink, sqliteSink, multiSink } from "@broberg/ai-sdk";

const ai = createAI({
  budget: { perCallUsd: 0.05, rollingUsd: 5 }, // pre-flight guard (throws BudgetExceededError)
  costSink: multiSink([
    upmetricsSink({ baseUrl: "https://upmetrics.org", apiKey: process.env.UPMETRICS_API_KEY!, agentName: "my-app" }),
    sqliteSink({ dbPath: "./ai-cost.db" }), // Bun only — throws on Node, see below
  ]),
});
```

汇: `upmetricsSink` (规范), `discordSink`， `sqliteSink`， `multiSink`， `noopSink`． 失败的水槽 _在_ 一个调用永远不会使该调用崩溃。

> **`sqliteSink` 和 `getCostSummary` 是Bun-only** (在v0.48.0更改，已发布 2026年9月22日)。他们的背后是 `bun:sqlite`， Node根本无法导入。在Node上 **扔到哪里 构建他们** -故意的，这是一个行为改变：直到v0.47你 有个水槽砸在每个 `record()`，客户端吞下每个调用 由于设计上的失误，成本跟踪悄无声息地消失了。空成本数据集 和一个正常工作的在报告里看起来一模一样。 **关于Node的使用 `upmetricsSink`.** `sqliteBudgetStore` 也是出于同样的原因；它总是彻底失败 在那里，因为预算错误没有被吞下。

### 成本跟踪是默认开启的（v0.24+）

你不用给水槽装电线。如果 `UPMETRICS_API_KEY` 是在羡慕，一个赤裸 `createAI()` 自动附加upmetrics接收器-这存在是因为大多数 呼叫地点没有经过水槽，留下约91%的西北风花费不可见。没有钥匙 环境→没有沉没，没有坠毁（船暗）。

**每个客户端都需要一个名称（0.63+,F034.4）。** `createAI()` 启动时抛出，除非 `UPMETRICS_AGENT_NAME` 准备好了还是你过去了 `agentName` -即使没有钥匙，所以 您的测试在生产之前捕获它。业主订单2026-10-15：«Ingen morv ære 匿名»-在30天内，8个回购者发送了他们的支出 `unknown`． 不需要 有一个明确的 `costSink` 或者 `costSink: null`.

```ts
createAI({ agentName: "my-app" }); // key in env → tracked; no key → nothing happens
createAI();                      // same, if UPMETRICS_AGENT_NAME is set — else throws
createAI({ costSink: mySink });  // explicit sink wins
createAI({ costSink: null });    // explicit OPT-OUT — see below
```

| Env var | 效果 |
| --- | --- |
| `UPMETRICS_API_KEY` | **唯一的开关。** Present→tracking on。 |
| `UPMETRICS_AGENT_NAME` | 行标签, **要求** (或 `agentName`) -在生产环境和测试环境中设置它。到0.62时，回退为 `npm_package_name`，空在npm/bun脚本外面，所以 `node dist/…` 和Docker服务日志为 `unknown`. |
| `UPMETRICS_BASE_URL` | 摄取宿主。默认为 `https://upmetrics.org`. |
| `UPMETRICS_COMPLIANCE` | `1` 设置合规模式。 `Usage` 两种方式都不携带提示/响应文本。 |

**在repo中采用它-首先要做两件事，否则采用会破坏 它本来要透露的数字是：**

1.  **在你已经申报了自己的费用的地方选择退出。** 通过 `costSink: null`． 否则SDK会添加一个 _第二个_ 上面的报告路径 你和每一通电话都算数 **两次**，在生产中，没有错误 任何地方。(发现的 `buddy`，谁聚集到一起 `cli_usage` 每小时推一次。)
    
2.  **不要让钥匙进入你的测试。** 测试运行器自动加载 `.env` (包 做;Vitest与dotenv设置)，所以任何套件达到a `createAI()` 将 **制造用于生产遥测**． 请注意 触发因素是 _进口_ 构建客户端的模块——模块级 `createAI()` 在没有任何命令的情况下武装它。去掉整个前缀：
    
    ```ts
    // test-setup.ts  — bunfig.toml: [test] preload = ["./test-setup.ts"]
    for (const k of Object.keys(process.env)) {
      if (k.startsWith("UPMETRICS_")) delete process.env[k];
    }
    ```
    
    去掉 **前缀**不是名单，而是你必须记住的守卫 更新是一种无声腐烂的东西。并断言 _效果_ (没有 `UPMETRICS_*` 对测试可见)有一个控制，证明探针可以变红；一个测试 检查“密钥未定义”通过，同时不保护机器上的任何内容 他从来没有钥匙。
    

采用 **每个回购，选择加入** -没有舰队范围的推进。打开它 回购值得关注，因为双方都有防范措施。

## 许可证

目前2.0 - 1.1 - apache -

## 常见问题

### @broberg/ai-sdk 是什么？

统一的AI/LLM SDK -一个门面，所有提供商，所有功能，每次调用的一流成本控制。

### 如何安装 @broberg/ai-sdk？

在项目目录运行 npm install @broberg/ai-sdk，或使用 pnpm add / yarn add / bun add。

### @broberg/ai-sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @broberg/ai-sdk 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @broberg/ai-sdk 可以商用吗？

@broberg/ai-sdk 使用 FSL-1\.1-Apache-2\.0 许可。声明的许可为 FSL-1\.1-Apache-2\.0，请阅读许可原文确认使用条件。以上为概括，不构成法律意见，请以许可原文为准。

### @broberg/ai-sdk 还在维护吗？

最新版本 0\.69\.4 发布于 2026/11/01（2 天前）；至今共发布 124 个版本，其中过去 12 个月发布了 124 个；首次发布于 2026/06/02。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

