# @alexkroman1/aai 中文文档

规范地址：https://npmcn.com/package/%40alexkroman1/aai/

版本：0\.18\.2
npm 元数据更新时间：2026-10-29T21:59:33.019Z
中文译文更新时间：2026-11-08T03:58:03.166Z
软件包许可：MIT

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

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

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

- 包体积（解压后）：4 MB，502 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24
- 运行依赖：12 个
- peer 依赖：zod ^4\.0\.0，vitest \>=3
- 首次发布：2026-03-17T22:10:26.878Z
- 最新版本发布：2026-10-29T21:59:32.737Z
- 近 30 天下载量：1897（2026-10-06 至 2026-11-04，npm 官方下载统计）
- 已发布版本数：163（近 12 个月 163 个）

## 中文简介

Assembly Associate SDK:一切都是一个 `agent.ts` 文件导入。 可自行托管的运行时间 CLI 和管理的平台运行为 `@alexkroman1/aai-runtime`。 。 。 .

## 安装

```sh
npm install @alexkroman1/aai
```

也可以使用：`pnpm add @alexkroman1/aai`、`yarn add @alexkroman1/aai`、`bun add @alexkroman1/aai`

## README

# @alexkroman1/aai (英语)

Assembly Associate SDK:一切都是一个 `agent.ts` 文件导入。 这个 自控运行时间 CLI 和管理的平台运行为 `@alexkroman1/aai-runtime`。 。 。 .

```sh
npm i @alexkroman1/aai zod
```

大多数项目不直接安装这个.. `aai init` (从) [`@alexkroman1/aai-cli`](https://www.npmjs.com/package/@alexkroman1/aai-cli)(中文(简体) ) 脚手架用它连接一个项目.

## 简单的案子就简单

SDK的一种模式是单个套接字上的快取、语音和一些密钥名词 整个特工:

```ts
import { agent, assemblyAIS2s } from "@alexkroman1/aai";

export default agent({
  name: "Support",
  // The prompt is the system-prompt.md beside this file.
  mode: "s2s",
  s2s: assemblyAIS2s({ voice: "jane", keyterms: ["AssemblyAI"] }),
});
```

下方的一切都是当您自己的代码运行在它旁边时 能得到的. 这里不需要任何工作代理.

| 能力 | 这是什么 | 何处 |
| --- | --- | --- |
| 工具 | 文件在 `tools/` 模型可以调用 | [`tool()`](https://npmcn.com/package/%40alexkroman1/aai/#readme-tools-a-tool-is-a-file) |
| 构建代码执行( D) | 可选入模式的可调用工具,直至运行真正的联署材料 | [`builtinTools`](https://npmcn.com/package/%40alexkroman1/aai/#readme-built-in-tools-and-code-execution) |
| 输入会话状态 | 已命名、 已输入状态工具的翻转读写 | [`sessionSlot()`](https://npmcn.com/package/%40alexkroman1/aai/#readme-typed-session-state) |
| 转动级耐久性 | 失传的回音记录 | [恢复](https://npmcn.com/package/%40alexkroman1/aai/#readme-every-session-can-resume) |
| 对话框 | 以代码执行的对话命令, 不在提示中建议 | [`dialog()`](https://npmcn.com/package/%40alexkroman1/aai/#readme-conversation-order-as-a-guarantee) |
| 工具内情报 | 一发LLM在工具内呼叫,返回精确的形状 | [`ctx.generate()`](https://npmcn.com/package/%40alexkroman1/aai/#readme-understanding-mid-turn) |
| 副剂 | 具有自身工具和上下文窗口的第二个工具循环 | [`ctx.delegate()`](https://npmcn.com/package/%40alexkroman1/aai/#readme-subagents) |
| 长期工作流程 | 日记运行超过呼叫 | [`workflow()`](https://npmcn.com/package/%40alexkroman1/aai/#readme-work-that-outlives-the-call) |
| 定制产品表面 | 会话上的网络用户界面,仅显示您的预测 | [`syncState`](https://npmcn.com/package/%40alexkroman1/aai/#readme-voice-in-screen-out) |
| 运输 | 浏览器 WebSocket、Twilio 和 Telnyx 上的同一代理 | [电话](https://npmcn.com/package/%40alexkroman1/aai/#readme-one-agent-every-line) |
| 工程寿命周期 | 孤立工具测试、构建时间检查、部署或自主机 | [测试](https://npmcn.com/package/%40alexkroman1/aai/#readme-testing-a-tool) |
| 行为影响 | 开一个真实的会话 并断言经纪人做了什么 | [`describeEval`](https://npmcn.com/package/%40alexkroman1/aai/#readme-behavioral-evals) |

## 定义代理人

`agent.ts` ——定义,加上拥有会话状态的槽:

```ts
import { agent, sessionSlot } from "@alexkroman1/aai";

export const notesSlot = sessionSlot("notes", () => ({ items: [] as string[] }), {
  view: (notes) => ({ count: notes.items.length }),
  sync: true,
});

// `sync: true` on the slot is what shows it to the browser client (read there
// with `useAgentState`); the prompt is the system-prompt.md beside this file.
export default agent({ name: "Notes" });
```

-   `agent()` ——代理定义;每个字段和默认记录在 [`AgentDef`](https://alexkroman.github.io/agent/)。没有提供者字段 运行默认的 Assembly STT → LLM → TTS 管道,计费为 1 `ASSEMBLYAI_API_KEY`· ; `voice: "aai-michael"` 选它TTS的声音.
-   `tool()` - 无国籍人案件输入的工具:Zod `inputSchema` 还有一个 `execute(args, ctx)` 运行服务器侧 `ctx.env` (秘诀), `ctx.signal` (驳船时的破坏), `ctx.generate` (一发LLM呼叫), `ctx.delegate` (水手们), `ctx.workflows` (启动并找到可持久运行)和 `ctx.send` (向浏览器客户端推出事件).
-   `sessionSlot()` ——一个被打出名的槽,拥有会话状态. `slot.tool()` 读取(数值被深深地冻结)和 `slot.updateTool()` 写出来 同步进行. 没有 `ctx.state` 没有 `ctx.db`。 。 。 .
-   `dialog()` 编号 : `procedure()` - 车型被门挡住 车型自有 密码驱动器.
-   `speaker()` 编号 : `roster()` 第2个声音:下放到线下 (单位:千美元)`ctx.delegate`),或发出电话(`speaks: true`) (中文(简体) ).
-   `workflow()` 编号 : `workflowApp()` - 耐用跑道, 和形式形状的代理人 前门是工作流程而不是麦克风.
-   `assemblyAIPipeline()` ——与明确分布相同的默认管道 (单位:千美元)`...assemblyAIPipeline({ region: "eu" })`),因为当你想要三个 在配置或跨STT和网关的欧盟区域可见.

`agent()` 取一个物体. `AgentDef` 是每个字段的参考文献 E. 方法——每个字段和默认都记载在那里 `AgentParams` 这是 以法律为组合的参考。 更全面的配置,包括 字段,上面的例子除外:

```ts
import { agent, sessionSlot } from "@alexkroman1/aai";
import { assemblyAITts } from "@alexkroman1/aai/tts";

// `sync: true` is what the browser client renders with `useAgentState`.
const cart = sessionSlot("cart", () => ({ items: [] as string[] }), {
  view: (c) => ({ count: c.items.length }),
  sync: true,
});

export default agent({
  name: "Storefront",
  greeting: "Storefront here — what are you after?",
  tts: assemblyAITts({ voice: "aai-michael" }),
  // Server-side helpers the model may call, on top of your own tool files.
  builtinTools: ["calculate"],
  // Tool-calling steps per reply, and how long a pause ends the caller's turn.
  maxSteps: 6,
  turnTaking: { minSilenceMs: 1200 },
  // Observe-only hooks over the session event stream.
  events: {
    "tool.called": (event) => {
      console.log("called", event.toolName);
    },
  },
});
```

## 工具:一个工具是 FILE

一个特工的价值是它要采取的行动, 他们每个人都生活在一个 只有您的代码才能到达系统 。 工具是一个文件在 `tools/`,以其名称命名 文件名 - `agent()` 采取不 `tools` 字段,没有记录:

```ts
// file: agent.ts
import { agent, sessionSlot } from "@alexkroman1/aai";

export const notesSlot = sessionSlot("notes", () => ({ items: [] as string[] }));

export default agent({ name: "Notes" });

// file: tools/add_note.ts
import { z } from "zod";
import { notesSlot } from "../agent.ts";

export default notesSlot.updateTool({
  description: "Save a note for the caller",
  inputSchema: z.object({ text: z.string() }),
  // `updateTool` hands the body a mutable draft, stored when it returns.
  execute: ({ text }, notes) => {
    notes.items.push(text);
    return { saved: notes.items.length };
  },
});
```

秘密不在代码之外 飞行中的工作就停止了 打断.. **通过 `ctx.signal` 任何缓慢的事物**数字 :

```ts
import { requireEnv, tool } from "@alexkroman1/aai";
import { z } from "zod";

export default tool({
  description: "Get current weather for a city",
  inputSchema: z.object({ city: z.string() }),
  execute: async ({ city }, ctx) => {
    const key = requireEnv(ctx, "WEATHER_KEY");
    const resp = await fetch(`https://api.example.com/weather?q=${city}&key=${key}`, {
      // Aborts on barge-in, reset, session stop, or this call's timeout.
      signal: ctx.signal,
    });
    return await resp.json();
  },
});
```

## 内置工具和代码执行

`builtinTools` 将模型选择为服务器侧助手, 它可以调用 - `web_search`, (中文(简体) ), `visit_webpage`, (中文(简体) ), `get_page_design`, (中文(简体) ), `fetch_json`, (中文(简体) ), `think`, (中文(简体) ), `remember`编号 :`recall`, (中文(简体) ), `calculate`,以及 `run_code`,模型写 JavaScript并运行在代理的沙盒中. 模型在精神上是不可靠的 算术; 真正的执行是使拨打的号码正确 打电话者要采取行动了.

```ts
import { agent } from "@alexkroman1/aai";

// system-prompt.md: "Use run_code for ANY math, counting, or data processing."
export default agent({
  name: "Analyst",
  builtinTools: ["web_search", "run_code"],
});
```

选择 `builtinTools` 让他们都无法生存 网络化的内建 模型控制的 URL,所以它们在容器和盖外被 SSRF 筛选 回来的是什么; `run_code` 需要一个沙盒——平台,或 零允许 `AAI_RUN_CODE=deno` - 并拒绝一个外.

## 输入会话状态

长时间的通话积累了事实——订单号,什么已经核实,什么在 车。 没有结构化的状态 探员从记录中重新找到他们 每转一圈. `sessionSlot()` 在一个共享模块中声明,唯一的 如何在会话工具中保持状态 :

```ts
import { sessionSlot } from "@alexkroman1/aai";
import { z } from "zod";

export const cartSlot = sessionSlot("cart", () => ({ items: [] as string[] }));

// `slot.tool()` READS — what the body is handed is frozen.
export const listCart = cartSlot.tool({
  description: "List what's in the cart",
  execute: (_args, cart) => ({ items: cart.items }),
});

// `slot.updateTool()` WRITES — mutate the draft; it is stored when the body
// returns, so a write is SYNCHRONOUS and may not `await`.
export const addItem = cartSlot.updateTool({
  description: "Add an item to the cart",
  inputSchema: z.object({ item: z.string() }),
  execute: ({ item }, cart) => {
    cart.items.push(item);
    return { count: cart.items.length };
  },
});
```

槽持有纯数据(a) `Map`, (中文(简体) ), `Set`, (中文(简体) ), `Date` 或类案例被拒绝 字段名称)。 在平台上,他们被储存 为你 - 一个坠机或一个 调用不再失去它们,也没有任何可用的. 下 `aai dev` 他们生活在记忆中 除非你设置 `DATABASE_URL`代码是相同的 就是这样.

## 每届会议可以复会

连接下降, 五分钟的身份验证加半建 命令不应该重新启动。 客户端重新连接 `?sessionId=<id>` 重新登记 同一会话:事件日志被恢复,槽值被还水 问候被压制,因为呼叫者已经听到了. 简历 没有东西被当做新会议 而不是坐着问候 相通而无声. 背景工作按键再次找到——稍后, 或者在下一个电话:

```ts
import { tool, workflow } from "@alexkroman1/aai";
import { z } from "zod";

export const research = workflow({
  description: "Research a topic",
  input: z.object({ topic: z.string() }),
  run: async (input) => ({ topic: input.topic, findings: [] as string[] }),
});

export default tool({
  description: "Check on research from earlier",
  execute: async (_args, ctx) => {
    // The run outlived the call — find it by the key it was started with.
    const [run] = await ctx.workflows.find(research, ctx.sessionId);
    if (!run) return { status: "none started" };
    return run.status === "completed" ? run.output : { status: run.status };
  },
});
```

## 作为保证的谈话令

在系统提示中“在披露前验证”是建议. `dialog()` 让它成为一个 规则: 以 `when` 根本就没有在这些国家之外运行 模型会回来的 `ToolFailure` 命名必须先发生的事情。 这个 位置是下面的一个插槽,所以它能活过再连接.

```ts
import { dialog } from "@alexkroman1/aai";
import { z } from "zod";

export const claim = dialog("claim", {
  initial: "verifying",
  states: {
    verifying: {
      instruction: "Verify the caller's policy number before quoting anything.",
      on: { VERIFIED: "quoting" },
    },
    quoting: {
      instruction: "Quote the excess. Do not re-verify.",
      on: { QUOTED: "done" },
    },
    done: { final: true },
  },
});

// Cannot run before the caller is verified; advances the dialog when it does.
export const quoteClaim = claim.tool({
  when: "quoting",
  send: { type: "QUOTED" },
  description: "Quote the premium for an excess",
  inputSchema: z.object({ excess: z.number() }),
  execute: ({ excess }) => ({ premium: excess * 2 }),
});
```

州和事件是从谱中推断出来的 所以拼错了 `send` 是一个 编译错误 . `procedure()` 是无型号驱动的流体兄弟 循环中的 X 状态机器,有分支、重试和有限制的预算,运行 与 `await procedure(machine).run(input)`。 。 。 .

## 理解中转

特工们不断需要小的谅解行为: 从乱伦的句子中,对意图进行分类,对检索到的文件进行分级. `ctx.generate()` 一个LLM呼叫,主机侧, 保证形状回:

```ts
import { tool } from "@alexkroman1/aai";
import { z } from "zod";

export default tool({
  description: "Start an order from what the caller just said",
  inputSchema: z.object({ utterance: z.string() }),
  execute: async ({ utterance }, ctx) => {
    const parsed = await ctx.generate({
      prompt: `Extract the order from: "${utterance}"`,
      schema: z.object({ sku: z.string(), qty: z.number() }),
    });
    // `object` is typed by the schema: { sku: string; qty: number }.
    return parsed.object;
  },
});
```

它默认为代理自己的 `llm`; 通过一个 `llm` 描述符或模型代号 字符串用于使用密钥在代理机密中的任何提供者——这也是 s2S代理如何到达模型.

## 副剂

复杂请求需要未知数量的查询 呼叫者不需要 听着 排出队来,他们低声说话,每转一转就慢一点; 外出时,腿部工作会从侧面进行——如果有的话,则平行进行 数个角度——呼叫只带答案. 副剂是第二 工具环路, 带有自己的系统即时、模型、工具和上下文窗口:

```ts
import { speaker, tool } from "@alexkroman1/aai";
import { z } from "zod";

const researcher = speaker({
  name: "researcher",
  systemPrompt:
    "Research the task with the tools you have. IMPORTANT: your final message " +
    "is the only thing the caller sees — end with a self-contained summary.",
  builtinTools: ["web_search", "visit_webpage"],
  maxSteps: 6,
});

export default tool({
  description: "Research a question in depth",
  inputSchema: z.object({ question: z.string() }),
  execute: async ({ question }, ctx) => {
    const { text, toolCalls } = await ctx.delegate(researcher, { task: question });
    return { answer: text, lookups: toolCalls.length };
  },
});
```

你收到它的最后信息,它的背景是孤立的,因此请它 总结和写 `task` 作为完整的简介。 代表团是一个深层.

## 活在电话里的工作

报告生成时,没有呼叫者持有4分钟。 工具开始持久 运行并回答转弯; 运行是日记的, 因此它能通过重启, a 调用和闲置的沙盒。 与 `notify` 经纪人说结果的时候 lands——普通可中断的转会,只有在呼叫者仍然在线上. 如果他们挂了,运行完成 反正下一个电话找到它 `key`。 。 。 .

```ts
import { tool, workflow, type WorkflowContext } from "@alexkroman1/aai";
import { z } from "zod";

// workflows/research.ts — an ordinary async function of its input and a ctx.
export async function researchFlow({ topic }: { topic: string }, ctx: WorkflowContext) {
  // `ctx.step` runs once and journals what it returned; a replay never re-runs
  // it. The body itself makes no undurable decision — no Date.now(), no fetch.
  const findings = await ctx.step("gather", () => gather(topic));
  // Suspended, not blocked: the container may exit here and resume when due.
  // Ten minutes and six hours work the same way.
  await ctx.sleep("settle", 10 * 60 * 1000);
  return findings;
}

declare function gather(topic: string): Promise<{ topic: string; points: string[] }>;

export const research = workflow({
  description: "Research a topic and report back",
  input: z.object({ topic: z.string() }),
  run: researchFlow,
});

// tools/research_topic.ts — promise made, and kept.
export default tool({
  description: "Research a topic in the background",
  inputSchema: z.object({ topic: z.string() }),
  execute: async ({ topic }, ctx) => {
    await ctx.workflows.start(research, { topic }, {
      key: ctx.sessionId,
      notify: "Tell the caller what it found.",
    });
    return { started: true };
  },
});
```

宣布代理工作流程(`agent({ workflows: { research } })`和 `ctx.workflows` 能够 `start`, (中文(简体) ), `find`, (中文(简体) ), `get`, (中文(简体) ), `wakeUp` 财务报告和审定财务报表 `stream` 它的运行。 何时 产品是形式而不是呼唤, `workflowApp()` 其代理人 前门是一个工作流程——没有会话,没有LLM回路,每个语音节点 a 编译错误 .

## 说话,屏出

调用者在屏幕上的手推车、表格或行程更新时进行交谈 输入,屏幕确认。 这样一来,命令就不用读了 十六位数大声点. `syncState` 取一个proJECTION,而不是一面旗帜,所以你选择 允许浏览器查看的内容 :

```ts
import { agent, sessionSlot } from "@alexkroman1/aai";

// shared.ts — declare the view on the slot and import it at both ends.
// `staffPin` is not in the view, so it never reaches the browser.
export const cartSlot = sessionSlot("cart", () => ({ cart: [] as string[], staffPin: "" }), {
  view: (s) => ({ cart: s.cart }),
  sync: true,
});

export default agent({ name: "Storefront" }); // the `sync: true` slot is collected
```

```tsx
import { sessionSlot } from "@alexkroman1/aai";
import { mountClient, useAgentState } from "@alexkroman1/aai-ui";

// client.tsx — in a project this is `import { cartSlot } from "./shared.ts"`,
// the same slot the agent declared, so the two ends cannot name different views.
const cartProjection = sessionSlot("cart", () => ({ cart: [] as string[] }), {
  view: (s) => ({ cart: s.cart }),
}).projected;

function App() {
  // The projection types the state AND supplies the frame rendered before the
  // first push — no type argument, no `?? EMPTY`.
  const view = useAgentState(cartProjection);
  return (
    <ul>
      {view.cart.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}

mountClient({ component: App });
```

投影在每次工具调用后运行, 只有在结果出来时才发出 换了. `ctx.send(event, data)` {\\fn黑体\\fs20\\shad2\\2ah82\\3ah20\\4ah33\\fscx95\\3ch592001\\be1}是一切不真实的推道 状态; `@alexkroman1/aai-ui` 有钩子和部件.

## 每行各一名探员

说出你的号码所在的载体, 和特工的电话 会话以 :

```ts
import { agent } from "@alexkroman1/aai";

export default agent({
  name: "Support",
  // `true` admits every carrier this build decodes; omit it and the phone
  // route is not served at all.
  telephony: ["twilio"],
});
```

然后把数字的语音网钩对准经纪人 用媒体回答 流线 :

```text
wss://<your-agent-url>/phone?carrier=twilio
wss://<your-agent-url>/phone?carrier=telnyx
```

Twilio和Telnyx是这些建筑解码器的载体;它们都说出8千赫的m-law, 以两面相接而行, 驳船,工具和槽像浏览器一样在呼叫中运行. 一个电话是 运输,不是一种模式——你在网络演示中测试的是呼叫者得到的.

## 会话模式和提供者

**管道模式** (默认) 串流 STT 部分进入服务器侧 LLM 循环和 通过 TTS 供应商来表达答案 。 把任何舞台都换成工厂 提供者子路径 - 设置任意子集 `stt`, (中文(简体) ), `llm`, (中文(简体) ), `tts`; 未设置的相位保留 assemblyAI 默认:

| 子路径 | 工厂 |
| --- | --- |
| `@alexkroman1/aai/stt` | `assemblyAIStt`, (中文(简体) ), `deepgramStt`, (中文(简体) ), `elevenLabsStt`, (中文(简体) ), `sonioxStt` |
| `@alexkroman1/aai/llm` | `llm({ provider, model })` - 一个工厂; `provider` 实值 `"assemblyai"`, (中文(简体) ), `"anthropic"`, (中文(简体) ), `"openai"`, (中文(简体) ), `"google"`, (中文(简体) ), `"mistral"`, (中文(简体) ), `"xai"`, (中文(简体) ), `"groq"`, (中文(简体) ), `"cerebras"`, (中文(简体) ), `"openrouter"`, (中文(简体) ), `"gateway"`,或任何以下列方式命名的 OpenAI 兼容端点: `baseUrl` |
| `@alexkroman1/aai/tts` | `assemblyAITts`, (中文(简体) ), `cartesiaTts`, (中文(简体) ), `rimeTts` |

工厂返回纯描述符——可序列数据,而不是SDK客户端. 证书由代理服务器端解决(每个工厂的文件) 所以没有提供商SDK 或秘密进入代理包. `llm` 也接受一个模型化字符串 : `"creator/model"` 途经 Vercel AI Gateway,一个通过AssemblyAI LLM Gateway的赤裸的身份证—— `agent({ name: "...", llm: "claude-sonnet-4-6" })` 换掉的只是模型.

**S2S 模式** 是语音对语音服务的明确选择,其中STT, lLM 环路和 TTS 都在一个套接字上运行: `s2s: assemblyAIS2s()` (源导出)或 `openAIS2s()` 从 `@alexkroman1/aai/s2s`。 。 。 .

## 测试工具

一个复杂的代理是一个活代码库——数十个工具,一个不断发展的即时, 有几个人编辑它。 一个工具是文件,所以 `agent.ts`默认导出 没有携带工具; `deployedAgent` 给您定义一个部署的代理 运行,然后每个工具都可以单独调用,没有会话、模型和 无网络 :

```ts
// file: agent.ts
import { agent, sessionSlot } from "@alexkroman1/aai";

export const notesSlot = sessionSlot("notes", () => ({ items: [] as string[] }));

export default agent({ name: "Notes" });

// file: tools/add_note.ts
import { z } from "zod";
import { notesSlot } from "../agent.ts";

export default notesSlot.updateTool({
  description: "Save a note for the caller",
  inputSchema: z.object({ text: z.string() }),
  execute: ({ text }, notes) => {
    notes.items.push(text);
    return { saved: notes.items.length };
  },
});

// file: agent.test.ts
import { createToolContext, deployedAgent, toolRunner } from "@alexkroman1/aai-runtime/testing";
import { expect, test } from "vitest";
import authored from "./agent.ts";
import addNote from "./tools/add_note.ts";

const agentDef = deployedAgent(authored, {
  tools: import.meta.glob("./tools/*.ts", { eager: true }),
});
const run = toolRunner(agentDef);

test("saves a note", async () => {
  // Typed by the tool's own def: `saved` is a number, no cast.
  const { saved } = await run(addNote, { text: "milk" }, createToolContext());
  expect(saved).toBe(1);
});
```

`createToolContext()` 建立一个完整的 `ToolContext` 有惰性默认和 a 录音 `ctx.send`· ; `stubGenerate`, (中文(简体) ), `stubDelegate`, (中文(简体) ), `stubGateway` 财务报告和审定财务报表 `stubUploads` 驱动工具合作者的答案. `aai build` 运行 在项目捆绑之前先检查套件和类型.

## 行为影响

快速的调子可以悄悄地降低工具纪律 没有人用手测试每部编辑的40种情景. `describeEval` (从) `@alexkroman1/aai-runtime/testing/vitest`运行 `aai eval`驱动一个真实的 并声称代理人做了什么 是什么工具 它叫什么顺序, 和它所说的:

```ts
import { agent } from "@alexkroman1/aai";
import { describeEval } from "@alexkroman1/aai-runtime/testing/vitest";
import { expect } from "vitest";

const agentDef = agent({ name: "Support" }); // the prompt is system-prompt.md

describeEval(agentDef, (test) => {
  test(
    "looks the order up first",
    async ({ session }) => {
      const turn = await session.say("where is order W1234?");
      expect(turn.toolCalls.map((c) => c.name)).toContain("look_up");
      expect(turn.text).toMatch(/shipped/i);
    },
    // What a scripted model answers when there is no provider key — the eval
    // still proves the agent boots, tools resolve, and a reply comes back.
    { stubReply: "Order W1234 shipped yesterday." },
  );
});
```

使用提供者密钥, 同一文件驱动一个活模式; 没有一个则运行 脚本,所以它在CI中是自由的和决定性的.

## 其他子路径

每个子路径都由WHO 读取它命名——当右侧一栏到达 描述你在做什么.

| 子路径 | 找到它的时候 |
| --- | --- |
| `/testing`, (中文(简体) ), `/testing/vitest` | 测试助手被宣布的地方 – 一个测试文件导入到 `@alexkroman1/aai-runtime/testing` 财务报告和审定财务报表 `/testing/vitest` |
| `/stt`, (中文(简体) ), `/llm`, (中文(简体) ), `/tts`, (中文(简体) ), `/s2s` | 为管道阶段挑选供应商(上表) |
| `/step`, (中文(简体) ), `/step-errors`, (中文(简体) ), `/step-files` | 在工作流程中写一个步骤—— `stepFetch`, (中文(简体) ), `stepEnv`, (中文(简体) ), `mapConcurrent`, (中文(简体) ), `stepGenerate`, (中文(简体) ), `readUploadToFile` |
| `/workflow-api` | 从网页、脚本或简历中调用已部署的代理人—— `createAgentClient` |
| `/tools` | 电话 `fetchJson`, (中文(简体) ), `visitWebpage` 或者说 `webSearch` 从您自己的工具代码 |
| `/channels` | 发布一个结果某处 - `slackChannel`, (中文(简体) ), `sendToChannel` |
| `/html` | 读取页面或种子.. `htmlToText`, (中文(简体) ), `pageMetadata`, (中文(简体) ), `parseFeed` |
| `/utils` | 在工具体内写作的小助手—— `toolFailure`, (中文(简体) ), `errorMessage`, (中文(简体) ), `pushCapped`, (中文(简体) ), `withLock` |
| `/ffmpeg` | 从一个步骤运行 ffmpeg -- `runFfmpeg`, (中文(简体) ), `probeMedia`, (中文(简体) ), `transcodeToWav` |
| `/protocol`, (中文(简体) ), `/manifest`, (中文(简体) ), `/slugify`, (中文(简体) ), `/workspace-files`, (中文(简体) ), `/internal` | cLI 和平台所使用的框架内部; 不是一个公共的 API , 并且没有被 semver 覆盖 |

## 文档

完整 API 参考 : [https://alexkroman.github.io/ agent/ 页面存档备份,存于互联网档案馆](https://alexkroman.github.io/agent/)

完整的编写指南 `agent()` 字段、工作流程规则、UI 钩子 语音即时规则——本套装中的船为: `AGENT_GUIDE.md`, (中文(简体) ), 除了这个文件。 (直接不是相对链接: TypeDoc 复制链接) 文件输入了制成的 Docs 树,这个是生成的100 kB 文本 已经住在两个地方。 )

## 常见问题

### @alexkroman1/aai 是什么？

Assembly Associate SDK:一切都是一个 agent\.ts 文件导入。 可自行托管的运行时间 CLI 和管理的平台运行为 @alexkroman1/aai-runtime。 。 。 \.

### 如何安装 @alexkroman1/aai？

在项目目录运行 npm install @alexkroman1/aai，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：zod（^4\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @alexkroman1/aai 支持 TypeScript 吗？

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

### @alexkroman1/aai 支持 ESM 还是 CommonJS？

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

### @alexkroman1/aai 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @alexkroman1/aai 可以商用吗？

@alexkroman1/aai 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @alexkroman1/aai 的下载量有多少？

根据 npm 官方下载统计，@alexkroman1/aai 在 2026/10/06 – 2026/11/04 这 30 天内被下载 1,897 次，日均约 63 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @alexkroman1/aai 还在维护吗？

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

