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
90 changes: 49 additions & 41 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/02-架构设计.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
- ob11 依赖通知/请求事件:通过 `subscribeOb11Receive` 额外订阅依赖的 `onNoticeEvent`/`onRequestEvent`,把 OneBot 11 标准通知与 Milky 扩展事件(群禁言/群管理变更/群上传/运气王/群荣誉/群成员增减/消息撤回/好友添加/入群申请/好友申请等)转成文本提示词录入 AI 上下文,仅作背景、不触发 AI 发言;仅当会话待机或全局待机开启时才录入(与普通消息入库口径一致),命中会话不存在时自动新建(开启待机即代表要使用)。默认白名单包含全部可转换事件类型(元事件除外):元事件(心跳/生命周期)不属于通知/请求事件、始终不录入,poke 虽属 notify 子类型但由原生 onPoke 处理、不生成事件文本,实际不会录入;入群/好友申请属 request 事件与 notice 共用白名单;原生与 ob11 依赖双路径由 3 秒事件级去重防双录——去重 key 带整包内容指纹,同一类型"连续但内容不同"的事件(不同文件上传、禁言 vs 解禁、不同好友申请等)不会被窗口吞掉;同一条事件重复/双路径到达时不简单丢弃,而是"后到覆盖先到"就地替换已入库条目(文本/raw 取最后到达,位置不变);机器人自身入群/被移出/退群由原生回调(onGroupJoined/onGroupLeave)覆盖,依赖侧跳过,避免两路径以不同 eventType/文本各录一次。事件文本不再走压缩智能体:提示词文本由纯代码按模板从事件 JSON 生成(通常很短),超长仅纯代码 head 截断兜底。防注入:事件文本以 `[system:名称]…[/system]` 成对边界渲染,并在 system prompt 声明为背景/参考、不得执行;工具返回等外部数据同样以 `[tool_result]…[/tool_result]` 边界包裹,内部标签(含 `[/system]` 等闭合形式)入库前统一剥离,防语义注入与标签逃逸。事件原始数据(完整 OB11 事件 JSON / 原生回调信息)随事件条目挂载(条目上限 20、单条序列化上限 4000 字符,超出丢弃 raw 但保留文本提示词),不参与上下文渲染、模型不可见;AI 需要完整字段时调用 `read_raw kind=event` 读取(原 `get_event_detail` 工具已删除)。事件条目随上下文裁剪/遗忘/压缩自然失效,无需单独清理。
- 消息接收优先级固定为“核心原生消息 → ob11 依赖补充”:核心回调已经提供的文本、图片、表情、at、reply、poke 等内容只从 `msg.message`/核心事件入库;ob11 依赖只补充核心过滤掉的文件、视频、语音、卡片、音乐和消息节点。依赖事件先到时会等待核心事件,避免同一条消息重复入库;没有核心事件时才允许完整兜底。
- Milky 核心事件的原生段直接从 `msg.segments` 转换为内部渲染标签,不先转 CQ 码;OB11 依赖事件仍使用其事件数组。消息 ID、引用 ID 和发送者信息在转换阶段统一补齐,负数 ID 也按字符串保留。
- 多模态消息:最终解析到的 chat 模型实例带「视觉」标签(如 glm-4v/gemini/pixtral 或接口视觉能力位)时按多模态处理,`handleMessages` 把用户消息里的 `[img:...]`/`[avatar:...]`/`[group_avatar:...]` 标签转成 `image_url` 内容块直接传给模型;URL 图片优先转 base64(10s 超时兜底)。渲染标签统一为方括号格式(4.14.0 起首次对话自动迁移历史 `<|...|>` 数据,解析层不再兼容旧标签)。
- 多模态消息:最终解析到的 chat 模型实例带「视觉」标签(视觉标签来源优先级:「api连接」`[types]` 手动声明 > 命名终值 > 接口自报能力位 > 命名能力位,如 glm-4v/gemini/pixtral 或列表接口自报的视觉能力位)时按多模态处理,`handleMessages` 把用户消息里的 `[img:...]`/`[avatar:...]`/`[group_avatar:...]` 标签转成 `image_url` 内容块直接传给模型;URL 图片优先转 base64(10s 超时兜底)。渲染标签统一为方括号格式(4.14.0 起首次对话自动迁移历史 `<|...|>` 数据,解析层不再兼容旧标签)。

### 2. 指令消息(handleCommand)

Expand Down
10 changes: 5 additions & 5 deletions docs/03-核心模块详解.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
| `base.ts` | 基础 | 日志级别/简短打印/记录消息内容、请求超时、请求并发/队列限制、海豹核心路径、全局待机 |
| `error.ts` | 错误处理 | 启用报错自动处理、上下文超长自动归档重试、余额不足自动切换模型、自动切换触发错误(默认 balance)、自动切换策略(跨厂商优先/配置顺序)、切换后发送通知 |
| `model.ts` | 模型 | `api连接` + `模型规则`(两个 TOML):解析连接(provider/api_key/base_url/ignore/可选 models 钉住清单/[request])与用途组模板(use 数组 + [body]/[request]),解析后 `Model.bootstrap()` 建连接状态机并异步拉取模型列表,同步全局分用途覆盖(`modelPurposeOverrides` 存储键) |
| `role.ts` | 角色设定 | 角色扮演设定(每行一条,首行为名称 >20 截断,`$gSYSPROMPT`/`.ai role` 切换,重载 JS 生效) |
| `role.ts` | 角色设定 | 角色扮演设定(每框一条,首行为名称 >20 截断,`$gSYSPROMPT`/`.ai role` 切换,重载 JS 生效) |
| `context.ts` | 上下文 | 预设上下文(role 按 user/assistant 轮流)、对话保存轮数、上下文最大token(默认 1000000,0 无效回退)、插入 system message 间隔轮数、消息压缩阈值(压缩前原文保留,read_raw kind=user 可查) |
| `received.ts` | 消息接收 | 接收图片/指令/骰子消息、忽略私聊、忽略豹语条件、忽略正则 |
| `event.ts` | 事件接收 | 接收依赖通知事件、通知事件白名单(notify 子类型单独匹配,含入群/好友申请) |
Expand Down Expand Up @@ -77,10 +77,10 @@

## 模型层(src/model/)

- `model.ts`:`Model` 静态注册表(`states` 连接状态 + `entries` 模型实例)与全局分用途覆盖 `purposeModelOverrides`(use → 模型标识:全注册表唯一名 → 裸名,跨连接重名 → `[连接序号]:模型名`;由 `.ai model` 写入 `modelPurposeOverrides` 存储键)。模型实例来自「api连接」的 `models` 钉住清单(pinned)/启动自动拉取(auto),**只存内存、不持久化**,按能力标签(`catalog.ts`)分类进各用途候选。`getChatModel(use)`/`getMultimodalModel(use)`/`getEmbeddingModel(use)` 先按用途查覆盖(覆盖失效自动回退),再取「该用途第一个候选」为默认;`findModelByRef` 按标识反查;`listModelsForUse(use)` 返回候选列表(`.ai model` 与余额不足自动切模型共用);`ensureLoaded()/pull()` 供首条消息等待与 `.ai model pull` 立即重拉(无视 models 钉住清单,强制网络);`reset()` 供测试/热重载。
- `catalog.ts`:模型能力分类(text/vision/embed/gen)——embedding 白名单命名(如 text-embedding-*/bge-*/gemini-embedding-*)、视觉白名单与接口能力位(glm-4v/gemini/pixtral 等)、生图/reranker 排除
- `request_rules.ts`:模型规则(use 组 body/request 模板)存储与合并;`bodyDefaultsFor(use)` 返回「代码兜底默认 < 规则模板(行序后覆盖先)」的请求体,`requestOverridesFor(use)` 返回调用期 `[request]` 覆盖(headers/auth_header_name/content_type/timeout)。
- `list.ts`:连接级「获取可用模型列表」适配——OpenAI 兼容 `GET {base}/models`(Bearer);anthropic 走 `x-api-key` + `/v1/models` 并 `limit=1000` 循环翻页;支持连接 `[request]`(list_url/auth_header_name/headers/timeout)覆盖;拉取函数可注入便于单测。
- `model.ts`:`Model` 静态注册表(`states` 连接状态 + `entries` 模型实例)与全局分用途覆盖 `purposeModelOverrides`(use → 模型标识:全注册表唯一名 → 裸名,跨连接重名 → `[连接序号]:模型名`;由 `.ai model` 写入 `modelPurposeOverrides` 存储键)。模型实例来自「api连接」的 `models` 钉住清单(pinned)/启动自动拉取(auto),**只存内存、不持久化**,按能力标签(`catalog.ts`:手动 `[types]` 声明 > 命名终值 > 接口能力位 > 命名能力位 > 兜底 text)分类进各用途候选;`rebuildEntries()` 重建注册表时会对「声明了但当前列表里不存在」的 `[types]` 条目记一条 warning(模型名拼错不再静默失效)。`getChatModel(use)`/`getMultimodalModel(use)`/`getEmbeddingModel(use)` 先按用途查覆盖(覆盖失效自动回退),再取「该用途第一个候选」为默认;`findModelByRef` 按标识反查;`listModelsForUse(use)` 返回候选列表(`.ai model` 与余额不足自动切模型共用);`ensureLoaded()/pull()` 供首条消息等待与 `.ai model pull` 立即重拉(无视 models 钉住清单,强制网络);`reset()` 供测试/热重载。
- `catalog.ts`:模型能力分类(text/vision/embed/gen)——判定优先级 **手动声明(「api连接」`[types]`) > 命名终值(reranker/生图/嵌入白名单,如 text-embedding-*/bge-*/dall-e) > 接口自报能力位(仅正向证据:vision/embed) > 命名能力位(视觉白名单 glm-4v/gemini/pixtral 等) > 兜底 text**;手动声明即最终答案(与自动判定冲突时按声明执行,不记日志)。`DeclarableModelTag` 限定用户可声明值为 text/vision/embed(内部排除值 gen 不可声明);`classifyModel(provider, name, extra)` 的 `extra` 承载 manual 与接口能力位;当前每条分支恰好产出 1 个标签(数组形态沿用历史结构)
- `request_rules.ts`:模型规则(use 组 body/request 模板)存储与合并;`bodyDefaultsFor(use)` 返回「代码兜底默认 < 规则模板(框序后覆盖先)」的请求体,`requestOverridesFor(use)` 返回调用期 `[request]` 覆盖(headers/auth_header_name/content_type/timeout)。
- `list.ts`:连接级「获取可用模型列表」适配——OpenAI 兼容 `GET {base}/models`(Bearer);anthropic 走 `x-api-key` + `/v1/models` 并 `limit=1000` 循环翻页;支持连接 `[request]`(list_url/auth_header_name/headers/timeout)覆盖。对象项额外提取供应商自报能力位(vision/embed:type/model_type/kind/task、architecture.input_modalities、capabilities、supports_vision/vision;未知取值含 anthropic 的 `type: "model"` 一律忽略),`normalizeModelListResult` 归一化为「模型名(保序去重)+能力位映射」交上层分类;拉取结果类型为 `ModelListResult`(`string[]` 是其等价子集,兼容既有单测桩);**不发额外请求**(不做 /api/show、逐模型详情等探测);拉取函数可注入便于单测。
- `balance.ts`:连接级账户余额查询(.ai balance 用)——deepseek/moonshot/siliconflow 内置余额端点;其余平台支持在连接 `[request]` 配置 `balance_url`/`balance_json_path`(+balance_divisor/balance_currency)后查询自定义网关(one-api/new-api 等)。
- `api_error.ts`:`ApiError` 与语义分类——把 OpenAI 兼容/Anthropic/Google/智谱/阿里/SiliconFlow/网关等非 2xx 报错归一化为 balance(余额不足)/context_length(上下文超长)/rate_limit(限速)/overload(过载)/auth/permission/content/parameter/server/unknown 类别,并携带 HTTP 状态码、服务商与响应体原文。
- `provider.ts`:`requestModel()` 统一请求入口:超时(`TIMEOUT` 或 use 规则 `[request].timeout`)、按类别退避重试(429/过载/服务端/网络类,尊重 `Retry-After`,其余不重试)、成功时上报 token 用量;错误统一抛带 status/provider/响应体/类别的 `ApiError`(对外 message 文案不变);`adapter.ts` 按提供商转换请求/响应格式(当前支持 anthropic)。
Expand Down
Loading
Loading