> ## Documentation Index
> Fetch the complete documentation index at: https://dev.marginnote.com.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用 MN 内置 AI

MN 内置了 AI 服务。插件无须自行接入模型，也无须自行保管密钥；发送的内容范围、消耗的额度以及用户是否知情，均由 MN 处理。

# 声明 AI 任务

任务声明在模板中，增加一条任务只需改动模板，MN 本身无须改动。`mnaddon.json` 不必为此增加字段。

```json template.json theme={null}
{
  "id": "u-hello",
  "name": "你好世界",
  "aiTasks": [
    {
      "id": "explain",
      "title": "解释一下",
      "trigger": "user",
      "scope": "word_sentence",
      "tier": 1,
      "output": { "reply": "string" },
      "required": ["reply"],
      "prompt": "用一句中文解释给定的词或短语在学习笔记语境下的意思。reply 写那一句话，不要罗列义项、不要加原文里没有的信息。"
    }
  ]
}
```

任务可用的字段如下：

* `id`：任务的标识，依据该字段指定要调用的任务。
* `title`：显示给用户的任务名称。
* `trigger`：说明该任务的发起方式。默认值 `"user"`。
  * `"user"` 表示仅在用户触发之后发出
  * `"auto"` 表示允许插件自行发起。需要先取得一次同意。
* `scope`：发送出去的材料的范围。
  * `word_sentence` 允许插件提供一个词语和该词语所在的句子。词语不超过 80 个字符，句子不超过 600 个字符。
  * `card_text`  MN 从卡片中取出标题与正文，插件传入的内容不予采用。
  * `attempt_ink` 用于读取卡上书写内容的场合：手写、文本框与公式框合并为一张图片发送，交由识别手写的模型处理，卡上没有书写内容时该次调用会被拒绝。
* `prompt`：提示词。
* `output`：返回值的 JSON Schema，取值只能是 `"string"`、`"number"` 与 `"bool"`。
* `required`：`output` 中必须有值的字段。
* `tier`：所用的模型级别，`0` 最快，`2` 最强，默认值 `0`。

# 调用 AI

页面以 `mn.ai(卡片id, 任务id, 参数)` 调用一条已经声明过的任务。

`参数`随 `scope` 而定：

* `word_sentence` 的材料由插件放进参数，需提供 `word` 与 `sentence`。
* `card_text` 的材料由 MN 从卡片上取出，参数不会发送出去，`参数` 中填写的内容不予采用。
* `attempt_ink` 的图片由 MN 从卡上合成，图片之外的材料由插件放进参数，例如题干与参考答案。

下面是一个对上文定义的 AI 任务的调用例子：

```javascript index.html theme={null}
// word_sentence
var r = await mn.ai(noteid, "explain", { word: word, sentence: sentence });
if (r && r.ok) {
  show(r.json.reply);
}
// card_text
var r = await mn.ai(noteid, "make_summary", {});
// attempt_ink
// TODO: 补充示例
```

返回结果的形式如下：

* 成功时返回 `{ ok: true, text, json }`。
  * `json`：按任务的 `output` 解析出来的对象
  * `text`：模型返回的原文。
* 失败时返回 `{ ok: false, error }`，`error` 包括：
  * `bad_args` 表示材料不符合要求，或该任务的声明不合格。
  * `bad_output` 表示模型返回的字段结构不对，卡片上没有写入任何内容。
  * `task_used` 表示同一张卡上的该任务 MN 只发送一次，重复请求会被拒绝。
  * `declined` 表示用户在弹出的提示中选择了拒绝。
  * `consent_required` 表示自动发起的任务尚未取得同意。
  * `max_required` 表示该任务需要 MAX 订阅。
  * `no_service` 表示 App 中尚未配置 AI 服务。
  * `rate_limited` 表示请求过于频繁。
  * `no_ticket` 表示该卡没有交给本插件的 AI。
  * `not_owner` 表示该卡上的 AI 交给了别的插件。
  * `network` 表示请求没有送达。
  * `no_ink` 表示作答区域中没有可以取用的内容。

<Tip>
  AI 的处理需要数秒。等待期间用户可能已经切换到别的卡片，返回的结果应当写入发起时记录的卡片，不要用当前聚焦的卡片进行处理。
</Tip>

<Note>
  MN 在扣除额度之前会弹出提示征求用户同意。提示尚未关闭时再次发送请求，会被视为拒绝。因此发起期间应当禁用按钮，避免重复点击。
</Note>

# 指定 AI 交给的插件

模板的摘录工具与划线快捷项各自带有 `ai_addon` 字段，取值为插件的 id，指明用该工具摘下来的卡片交由该插件处理。划线快捷项指在文档中选中一段文字时，菜单条上出现的一排快捷按钮。

在 `编辑场景模板→ 卡片与摘录菜单` 中，添加划线快捷项时可配置`AI 交给：……`。

```json template.json theme={null}
{
  "excerptShortcuts": [
    {
      "title": "收进生词本",
      "hashtag": "#Pending",
      "ai_addon": "mn.hello"
    }
  ]
}
```

用该摘录工具摘录出的卡片，只有 `ai_addon` 字段指向的插件能对其调用 AI。卡片已经交给别的插件之后，本插件在该卡上调用任务会被拒绝，收到 `not_owner`；该工具没有启用 AI 时收到 `no_ticket`。

# 从模板读取任务列表

页面可以读取当前模板声明的任务，并依据声明生成按钮，这样再往模板中增加任务时不必改动插件：

```javascript index.html theme={null}
var t = await mn.api("/immersive/template");
var tasks = (t && t.aiTasks) || [];
tasks.forEach(function (task) {
  addButton(task.title || task.id, function () { runAI(task.id); });
});
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.