Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
- 让你的骰娘活起来

![License](https://img.shields.io/badge/License-MIT-blue)
![Version](https://img.shields.io/badge/Version-4.15.1-green)
![Version](https://img.shields.io/badge/Version-4.15.2-green)

## 快速开始

Expand Down Expand Up @@ -201,7 +201,7 @@ max_tokens = 2048

| 设置项 | 说明 |
|:---:|:---|
| 接收图片 | 是否接收并识别图片消息,关闭后不处理任何图片消息 |
| 接收图片 | 是否接收图片消息并将图片 URL 记录到上下文;是否自动识别由图片识别条件和图片模型配置决定 |
| 接收指令消息 | 是否将指令消息计入上下文(指令仍会执行) |
| 接收骰子发送的消息 | 是否处理机器人自己发送的消息 |
| 忽略私聊消息 | 开启后私聊消息不触发 AI |
Expand Down Expand Up @@ -237,6 +237,7 @@ max_tokens = 2048
| 禁止调用的函数 | 每行一个,设置后将不被允许开启 |
| 默认关闭的函数 | 每行一个,AI 在新会话中默认无法调用,需 `.ai tool on <函数名>` 开启 |
| 提供给AI的牌堆名称 | 每行一个牌堆名,用于 `draw_deck` 工具;没有的话建议把 `draw_deck` 加入禁止调用 |
| 是否启用MCP | MCP 功能总开关,默认关闭;开启后才会解析并连接下方的 MCP 服务器,未安装对应 MCP 后端时建议保持关闭 |
| MCP服务器配置 | 仅支持标准 `mcpServers` JSON 格式(Claude/Cursor/.mcp.json 可直接粘贴);stdio 服务器会跳过;配置增删自动生效。默认包含三个服务器:mcp-files-exec(文件执行)、web-read(网页读取)、md-html-render(Markdown/HTML 渲染)。格式定义见 [MCP 官方规范](https://modelcontextprotocol.io/specification/latest) |
| 技能配置 | 仅支持标准 SKILL.md 格式(frontmatter 的 name/description 自动解析,正文为技能内容),可直接粘贴其他 agent 的技能文件。格式定义见 [agentskills.io 规范](https://agentskills.io/specification) |
| ai语音使用的音色 | 预设音色需要支持 AI 语音的协议端,自定义音色需要生成音频依赖(tts)和 ffmpeg |
Expand Down
4 changes: 2 additions & 2 deletions docs/01-项目概览.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ AI 骰娘4(`aiplugin4`)是运行在 SealDice 上的 JavaScript 插件,为骰娘

## 版本信息

- `src/config/static_config/meta.ts` 中 `VERSION = "4.15.1"`,作者 `baiyu&错误`,插件名 `aiplugin4`。
- `src/config/static_config/meta.ts` 中 `VERSION = "4.15.2"`,作者 `baiyu&错误`,插件名 `aiplugin4`。
- 开发遵循「分支 + squash PR」流程(见 [07-开发指南](07-开发指南.md));近期工作围绕:模型配置 TOML 化、Provider 统一请求(超时/重试/用量上报)、智能体编排重构、MCP/技能、日志治理、消息管线、配套后端独立仓库化等。
- README 顶部徽章与 `VERSION` 保持一致(当前 4.15.1)(详见 [09-注意事项与常见问题](09-注意事项与常见问题.md))。
- README 顶部徽章与 `VERSION` 保持一致(当前 4.15.2)(详见 [09-注意事项与常见问题](09-注意事项与常见问题.md))。

## 环境要求

Expand Down
2 changes: 2 additions & 0 deletions docs/02-架构设计.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
```

- ob11 额外接收:核心原生(milky)路径会丢弃卡片/视频/文件/语音/合并转发等消息段;安装 ob11 网络连接依赖后,`subscribeOb11Receive` 订阅其事件分发(`net.getEventDispatcher`),只处理含这些额外段的消息,构造 ctx/msg 后走同一套 `handleNonCommand`(黑名单/忽略/触发/待机全部生效),并按段类型过滤避免与核心回调重复处理;合并转发经 `get_forward_msg` 展开为可读文本,过长走压缩。
- 消息接收优先级固定为“核心原生消息 → ob11 依赖补充”:核心回调已经提供的文本、图片、表情、at、reply、poke 等内容只从 `msg.message`/核心事件入库;ob11 依赖只补充核心过滤掉的文件、视频、语音、卡片、音乐和消息节点。依赖事件先到时会等待核心事件,避免同一条消息重复入库;没有核心事件时才允许完整兜底。
- Milky 核心事件的原生段直接从 `msg.segments` 转换为内部渲染标签,不先转 CQ 码;OB11 依赖事件仍使用其事件数组。消息 ID、引用 ID 和发送者信息在转换阶段统一补齐,负数 ID 也按字符串保留。
- 多模态消息:对话模型命中「图片模型」列表(或本身就是 use=chat 的图片模型条目)时按多模态处理,`handleMessages` 把用户消息里的 `[img:...]`/`[avatar:...]`/`[group_avatar:...]` 标签转成 `image_url` 内容块直接传给模型;URL 图片优先转 base64(10s 超时兜底)。渲染标签统一为方括号格式(4.14.0 起首次对话自动迁移历史 `<|...|>` 数据,解析层不再兼容旧标签)。

### 2. 指令消息(handleCommand)
Expand Down
4 changes: 3 additions & 1 deletion docs/03-核心模块详解.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

### static_config/(静态常量)

- `VERSION`(当前 4.15.1)、`AUTHOR`、`NAME`、`STORAGE_VERSION`、`CONFIG_CACHE_TTL`、`CQ_TYPES_ALLOW`。
- `VERSION`(当前 4.15.2)、`AUTHOR`、`NAME`、`STORAGE_VERSION`、`CONFIG_CACHE_TTL`、`CQ_TYPES_ALLOW`。
- `PRIVILEGE_LEVEL_MAP`:master=100 / whitelist=70 / owner=60 / admin=50 / inviter=40 / user=0 / blacklist=-30。
- `HELP_MAP`、`ALIAS_MAP`(命令别名表)、`FACE_MAP`(QQ 表情编号 → 名称)。
- `PROVIDER_MAP`:10 家厂商的 OpenAI 兼容 base_url。
Expand Down Expand Up @@ -99,6 +99,8 @@

- `types.ts`:消息类型定义:`UserMessageItem`、`SystemUserMessageItem`、`AssistantMessageItem`、`ToolCallsMessage`、`ToolCallbackMessage`、`RequestMessage`。
- `message.ts`:`Message` 静态方法判断消息类型;`handleMessages` 把内部消息转为 API 请求格式,元数据(时间/用户/消息ID/系统来源)存在消息项字段中,多模态模式下把 `[img:...]` 等图片标签转成 `image_url` 内容块直接传给模型。
- `pipeline.ts` 的接收适配遵循核心优先、依赖补充原则:核心 `msg.message`/Milky `msg.segments` 直接进入统一转换器;ob11 依赖只补充核心不接收的 file/video/record/json/music/node 等段,并通过唯一消息 ID 去重。转换器输出统一的 `[from:...]`、`[time:...]`、`[msg_id:...]`、`[img:...]`、`[at:...]`、`[reply:...]`、`[face:...]`、`[poke:...]` 标签。
- `handleMessages` 在组装 system、示例、历史和工具定义后进行整包 token 估算;system 与示例不裁剪,历史从最早消息开始裁剪。长对话中按 `INSERT_COUNT` 插入短提醒,不会重复复制完整 system prompt。
- `context.ts`:`Context` 类:
- 添加用户消息(自动改名、超长消息交给 `compress_agent` 压缩)、助手消息(触发记忆访问与总结检查)、系统用户消息、工具调用/回调消息。
- `limitMessages()` 按 `MAX_ROUNDS`(出现一次 user 视作一轮)裁剪;`handleMessages` 另有 token 预算裁剪。
Expand Down
1 change: 1 addition & 0 deletions docs/04-工具系统.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
- 协议:Streamable HTTP 上的 JSON-RPC(`initialize` → `notifications/initialized` → `tools/list` → `tools/call`),带 `Mcp-Session-Id` 会话头,兼容 SSE 响应。
- 工具以 `<服务器名>_<工具名>` 注册为敏感工具,参数透传 `inputSchema`。
- 工具列表有 60 秒 TTL 缓存;会话失效(`会话不存在/已失效/session`)时重新 initialize 并重试一次。
- MCP 配置修改后会按服务器名重建会话并同步工具差集;删除服务器或服务器返回的工具会从注册表移除。MCP 总开关关闭时不建立连接,system prompt 也不会展示不可调用的 MCP/技能入口。
- 错误信息把 JSON-RPC 错误码映射为可读中文。

## 技能(Skills,src/tool/skills.ts)
Expand Down
4 changes: 2 additions & 2 deletions docs/05-命令与配置.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@
| 基础 | 日志级别、请求超时时限、请求并发上限、请求队列上限、是否开启全局待机 | 全局开关 |
| 模型 | 对话模型/图片模型/嵌入模型、是否开启图片模型、是否开启嵌入模型 | TOML 每行一个;`name`/`api_key`/`use` 必填,`use` 可选项:`chat`/`compression`/`summarization`(对话)、`image-understanding`(图片)、`text-embedding`(嵌入);`provider`/`base_url` 可自动识别;默认对话模型取列表第一项;可加 `[body]` 覆盖请求参数;嵌入模型输出维度取 `[body] dimensions`(默认 1024);各平台模型示例见 [MODELS-chat](MODELS-chat.md)/[MODELS-image](MODELS-image.md)/[MODELS-embedding](MODELS-embedding.md) |
| 对话 | 角色扮演设定、示例对话、对话保存轮数、上下文最大token、消息压缩阈值 | 每条设定第一行为角色设定名称(>20 截断),其余为设定;`$gSYSPROMPT` 选择 |
| 消息接收 | 接收图片/指令消息/骰子发送的消息、忽略消息豹语条件、忽略消息正则表达式 | |
| 消息接收 | 接收图片/指令消息/骰子发送的消息、忽略消息豹语条件、忽略消息正则表达式 | 接收图片只负责记录图片 URL;自动识别仍由图片识别条件和图片模型配置决定 |
| 消息触发 | 触发正则表达式、触发需要满足的条件、触发次数上限、触发次数补充间隔 | 令牌桶配额 |
| 工具 | 开启调用函数功能、切换为提示词工程、允许连续调用函数次数、工具响应压缩触发字数、禁止/默认关闭的函数、MCP服务器配置、技能配置、ai语音使用的音色 | MCP 仅支持标准 mcpServers JSON(默认含 mcp-files-exec/web-read/md-html-render,格式见 modelcontextprotocol.io/specification/latest),技能仅支持标准 SKILL.md(格式见 agentskills.io/specification) |
| 工具 | 开启调用函数功能、切换为提示词工程、允许连续调用函数次数、工具响应压缩触发字数、禁止/默认关闭的函数、是否启用MCP、MCP服务器配置、技能配置、ai语音使用的音色 | 「是否启用MCP」默认关闭,开启后才会解析并连接 MCP 服务器;MCP 仅支持标准 mcpServers JSON(默认含 mcp-files-exec/web-read/md-html-render,格式见 modelcontextprotocol.io/specification/latest),技能仅支持标准 SKILL.md(格式见 agentskills.io/specification) |
| 记忆 | 启用长期/总结/知识库记忆、长期记忆上限/展示数量、总结记忆上限/间隔/参与轮数、短期记忆上限、知识库注入阈值(字符)、知识库(Markdown 模板,每条一份完整文档) | 知识库只读,AI 通过 kb 工具/指令检索;修改后自动生效(缓存最多 1 分钟);Markdown 语法见 commonmark.org/help/ |
| 回复 | 回复引用、回复最大字数、分段发送延时/基础延时/含图额外延时、禁止回复复读 | 回复过滤规则已内置硬编码 |
| 后端 | 流式输出、图片转base64、联网搜索、用量图表、论坛地址/API Token/签名密钥 | 可自建,见 [08-相关后端项目](08-相关后端项目.md);网页读取与 md/html 渲染(MCP)在「工具 → MCP服务器配置」 |
Expand Down
2 changes: 1 addition & 1 deletion docs/09-注意事项与常见问题.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

## 代码中的已知不一致

- `src/config/static_config/meta.ts` 的 `VERSION` 与 `src/update.ts` 的 `updateInfo` 最新条目需保持一致(当前 4.15.1);`checkUpdate` 按 `VERSION` 比较输出对应更新日志。
- `src/config/static_config/meta.ts` 的 `VERSION` 与 `src/update.ts` 的 `updateInfo` 最新条目需保持一致(当前 4.15.2);`checkUpdate` 按 `VERSION` 比较输出对应更新日志。
- `package.json` 的 `main`/`typings` 指向 `build/index.js`/`build/index.d.ts`,但实际构建产物是 `dist/aiplugin4.js`(esbuild),`build/` 目录是陈旧的 tsc 输出,与构建流程无关。
- `engines.node >= 10` 是模板遗留,实际构建依赖 esbuild,建议使用较新 Node。
- `sub_cmd/sample.ts` 与 `agent/agents/samples.ts` 是开发示例,未注册到运行时。
Expand Down
2 changes: 1 addition & 1 deletion header.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ==UserScript==
// @name AI骰娘4
// @author 错误、白鱼
// @version 4.15.1
// @version 4.15.2
// @description 适用于大部分OpenAI API兼容格式AI的模型插件,测试环境为 Deepseek AI (https://platform.deepseek.com/),用于与 AI 进行对话,并根据特定关键词触发回复。使用.ai help查看使用方法。具体配置查看插件配置项。\nopenai标准下的function calling功能已进行适配,选用模型若不支持该功能,可以开启迁移到提示词工程的开关,即可使用调用函数功能。\n交流答疑QQ群:143412516
// @timestamp 1733387279
// 2024-12-05 16:27:59
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aiplugin4",
"version": "4.15.1",
"version": "4.15.2",
"description": "ai plugin for sealdice",
"main": "build/index.js",
"typings": "build/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion sealpack/info.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ format_version = "1.0.0"
[package]
id = "error2913/aiplugin4"
name = "AI骰娘4"
version = "4.15.1"
version = "4.15.2"
authors = ["错误、白鱼"]
license = "MIT"
description = "适用于大部分 OpenAI API 兼容格式 AI 的 Sealdice 模型插件,支持对话、记忆、工具调用、MCP/Skills 等。"
Expand Down
2 changes: 1 addition & 1 deletion src/config/configs/received.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getRegexConfig } from "../config";

export default class ReceivedConfig {
static register() {
seal.ext.registerBoolConfig(ext, "接收图片", true, "开启后接收图片消息;图片识别需要额外配置并开启图片模型", "消息接收");
seal.ext.registerBoolConfig(ext, "接收图片", true, "开启后接收图片消息并将图片 URL 记录到上下文;是否自动识别图片由图片识别条件和图片模型配置决定", "消息接收");
seal.ext.registerBoolConfig(ext, "接收指令消息", false, "开启后指令消息也会计入上下文(指令仍会执行)", "消息接收");
seal.ext.registerBoolConfig(ext, "接收骰子发送的消息", false, "开启后记录非本插件发送的机器人回复(要计入上下文需同时开启待机)", "消息接收");
seal.ext.registerBoolConfig(ext, "忽略私聊消息", false, "开启后私聊消息不触发 AI", "消息接收");
Expand Down
2 changes: 1 addition & 1 deletion src/config/static_config/meta.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// 静态常量:版本/作者/模型映射表/权限等级/别名/表情表等
export const VERSION = "4.15.1";
export const VERSION = "4.15.2";
export const AUTHOR = "baiyu&错误";
export const NAME = "aiplugin4";

Expand Down
14 changes: 10 additions & 4 deletions src/memory/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { MEMORY_TEMPLATE } from "../prompt/templates";
import Image from "../resource/image";
import { Session } from "../session/session";
import { GroupInfo, SessionInfo, UserInfo } from "../session/types";
import { stripInternalTags } from "../utils/string";
import { stripInternalTags, truncateText } from "../utils/string";
import { generateId, getCommonItem, revive, TypeDescriptor } from "../utils/utils";

import MemoryItem from "./memory_item";
import { bumpMemoryRevision } from "./revision";
import { MemorySource, searchOptions } from "./types";

const MEMORY_RENDER_LIMIT = 1000;

export default class MemoryService {
static validKeysMap: { [key in keyof MemoryService]?: TypeDescriptor<MemoryService[key]> } = {
memoryMap: { array: MemoryItem },
Expand Down Expand Up @@ -166,7 +168,7 @@ export default class MemoryService {
this.limitMemories(1);
}

buildMemory(si: SessionInfo, ml: MemoryItem[]): string {
buildMemory(si: SessionInfo, ml: Array<{ id: string, content: string }>): string {
if (ml.length === 0) return '';
const listText = ml.map((m, i) =>
(i + 1) + '. [' + m.id + '] ' + m.content
Expand Down Expand Up @@ -231,17 +233,21 @@ export default class MemoryService {
if (this.persona && this.persona !== '无') {
s += `${ctx.isPrivate ? '个人设定' : '群聊设定'}: ${this.persona}\n`;
}
const promptMemories = memories.map(memory => ({
id: memory.id,
content: truncateText(memory.content, MEMORY_RENDER_LIMIT)
}));
s += ctx.isPrivate
? this.buildMemory({
isPrivate: true,
id: ctx.endPoint.userId,
name: seal.formatTmpl(ctx, '核心:骰子名字')
}, memories)
}, promptMemories)
: this.buildMemory({
isPrivate: false,
id: ctx.group!.groupId,
name: ctx.group!.groupName
}, memories);
}, promptMemories);
return s;
}

Expand Down
6 changes: 4 additions & 2 deletions src/memory/session_memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import { Session } from "../session/session";
import { GroupInfo, UserInfo } from "../session/types";
import User from "../session/user";
import { buildContent } from "../utils/message";
import { stripInternalTags } from "../utils/string";
import { stripInternalTags, truncateText } from "../utils/string";
import { TypeDescriptor } from "../utils/utils";

import MemoryService from "./memory";
import MemoryItem from "./memory_item";
import { bumpSummaryRevision } from "./revision";

const MEMORY_RENDER_LIMIT = 1000;

export default class SessionMemoryService extends MemoryService {
static validKeysMap: { [key in keyof SessionMemoryService]?: TypeDescriptor<SessionMemoryService[key]> } = {
memoryMap: { array: MemoryItem },
Expand Down Expand Up @@ -220,7 +222,7 @@ export default class SessionMemoryService extends MemoryService {
const { SUMMARY } = Config.memory;
return SUMMARY_TEMPLATE({
"SUMMARY": SUMMARY,
"summaries": this.summaries
"summaries": this.summaries.map(summary => truncateText(summary, MEMORY_RENDER_LIMIT))
});
}
}
Loading
Loading