Skip to content

message 服务(契约包速查) ​

这不是一个 DI 服务。 @aalis/schema-message 是纯契约包——只导出消息载体类型、WellKnownRole/WellKnownKinds、prepareLLMMessages/toLLMRole、附件占位符文法、发送者标识工具,外加经 declaration merging 注入的几个事件。没有任何插件用 provide.use(message, …) 注册运行时服务,也没有 caps.message.use(已 grep 全仓确认:provides/provide/getService 均无 'message' 命中)。

包元数据也证明这一点:packages/schema-message/package.json 没有 aalis.service 字段,keyword 是 aalis-schema(不是 aalis-plugin,不会被当作插件加载)。第三方作者不是"实现/消费 message 服务",而是 import 这些类型/函数来写自己的 LLM provider / 适配器 / 读历史的插件。

本页用途 ​

完整语义(role × kind 正交模型、强制出口 prepareLLMMessages、附件 [图片 | ref:…] 文法、<at id="X"> @提及约定、流式分段、actor 授权身份、边界与注意事项)均在概念文档中完整阐述:

→ docs/concepts/message-llm-pipeline.md(写 LLM provider 或写"会发消息/会读历史"插件前必读)

本页仅提供导出符号速查表,按文件归档;每项标注真实 file:line,语义细节见概念文档对应小节。


导出速查(@aalis/schema-message) ​

LLM 协议层类型 — src/index.ts ​

符号类别file:line说明
Messageinterfaceindex.tsLLM 对话上下文消息主体:role/content/toolCalls/name/kind/segments/images/audios/metadata 等
ToolCallinterfaceindex.tsassistant 消息的 tool_calls 载荷(OpenAI 协议字段:id/type:'function'/function{name,arguments})
ContentSegmenttype(三联合)index.tsassistant 输出的有序时间线:text / reasoning_text / tool_call(后者带 startTime/endTime 与这次调用的 callId)
WellKnownRoletypeindex.ts'system' | 'user' | 'assistant' | 'tool'——chat 协议直接接受的四种
MessageRoletypeindex.tsWellKnownRole | (string & {})——四种标准 + 任意扩展 role(如 'notice')

平台适配层类型 — src/index.ts ​

符号类别file:line说明
IncomingMessageinterfaceindex.ts从适配器流入的原始消息:会话上下文 + attachments + triggerType + actor(授权身份)等
OutgoingMessageinterfaceindex.ts发往平台的回复:content/attachments/source/modelInfo
StreamChunkMessageinterfaceindex.ts流式片段(经 'outbound:stream' 事件发往前端):turnId(所在回合)/contentDelta/reasoningDelta/toolCallProgress/done/toolLimitReached
MessageAttachmentinterfaceindex.tsv2 多模态附件统一载体:kind/data/mimeType/description/ref 等

LLM 出口工具(值导出)— src/index.ts ​

符号类别file:line说明
WellKnownKindsconst 对象index.ts约定 kind 常量:EventMarker/CrossSessionDelegation/OutboundImage/OutboundAudio/OutboundVideo/OutboundFile/HostNotice/IdlePrompt/OutboundDeliveryFailed/TurnStopped
WellKnownKindtypeindex.tsWellKnownKinds 值的联合
WellKnownMetadataKeysconst 对象index.ts跨插件约定的 Message.metadata 键,写方与读方都引用它:可见正文、发言人写的话、附件清单与随附的转发、发送者前缀的标签与被 @ 的人、说话人、说话所在的群与会话类型、平台侧的消息 id、来源、宿主通知的种类、id 与给界面的事实、回合与工具调用的 id、回复的模型与 token、发出附件原样的地址与名字、投递失败的原因;各键谁写谁读见 schema-message 的「存档元数据的键」
ArchivedAttachmentinterfaceindex.ts存档里提问带的一个附件(WellKnownMetadataKeys.Attachments 的一项):kind/name/ref(存储地址或网址)
ForwardedChatinterfaceindex.ts随入站消息转发来的一段聊天记录(IncomingMessage.forwards 的一项):id、给模型的展开 content、给界面的事实 facts
ForwardFactsinterfaceindex.ts一段转发给界面的事实:count/participants(前 8 个)/unlisted/text(摘要或可能截断的原文)/cutOff
ArchivedForwardtypeindex.ts存档记下的一段转发(WellKnownMetadataKeys.Forwards 的一项):ForwardedChat 去掉 content
CONTROL_KINDSconst 数组index.ts控制类 kind(当前仅 EventMarker)——消费方拼装历史时须自行过滤(并非 prepareLLMMessages 的职责)
DIRECTIVE_KINDSconst 数组index.ts指令类 kind(CrossSessionDelegation、HostNotice、IdlePrompt):宿主或系统撰写、不是任何人发言。归档消息属于它时抽取与记忆扩窗跳过;请求期 metadata.injector 属于它时是本轮指令块(见 schema-message)
toLLMRole(role)functionindex.ts自定义 role → WellKnownRole;未知一律回落 'system';notice → system
prepareLLMMessages(messages)functionindex.tsLLM provider 出口必调:归一 role + 加前缀;不改原对象,返回浅拷贝;幂等
outboundMediaMessage(kind, ref, { desc, source }?)functionindex.ts发出的图片、音频、视频与文件在会话历史里的记法:kind 为 OutboundImage/OutboundAudio/OutboundVideo/OutboundFile,正文是附件引用占位符(文件的描述位记文件名);source 是 agent 的是她发的(role assistant),其余是插件发的(role notice,进模型时是系统通知);时间戳与 metadata 由调用方补上。出站附件的归档一律用它,memory_recall 与会话服务回看历史才能按同一格式命中、还原
outboundMarkerMessage(msg)functionindex.ts不是她回复的出站文字(source 不是 agent)在会话历史里的记法:一条事件标记,元数据记 Text(有才记);她的回复与空文字回 undefined,时间戳与出站的 Source、TurnId、CallId 由调用方补上。不是她回复的出站文字的归档一律用它,会话服务回看历史才能还原成同样的通知

prepareLLMMessages 签名:<T extends Pick<Message, 'role' \| 'content' \| 'kind'>>(messages: T[]): T[](index.ts)。语义、为何必调、为何不剔 event-marker——见概念文档 §3 + §10。

附件占位符文法 — src/attachment-ref.ts(经 index.ts 转出) ​

符号类别file:line说明
AttachmentRefKindconst 对象 + typeattachment-ref.ts中文显示名:图片/音频/视频/文件
ATTACHMENT_REF_KINDSconst 对象attachment-ref.ts附件种类(MessageAttachment['kind'])→ AttachmentRefKind,写引用与按种类认引用的各方共用这一份
AttachmentRefinterfaceattachment-ref.ts{ kind, desc?, ref }
formatAttachmentRef(r)functionattachment-ref.ts→ '[图片: desc | ref:xxx]'(desc 空则省冒号段;desc 自动单行化,| → 丨、] → ])
parseAttachmentRefs(text)functionattachment-ref.ts扫描全部 [<kind>(: <desc>)? | ref:<ref>] 占位符
buildAttachmentRefMatcher(kind, ref)functionattachment-ref.ts构造匹配「指定 kind + 指定 ref」全部占位符的正则

契约约束:byte-for-byte 兼容历史;写入方须保证 desc/ref 不含 ]/|。不要手动拼接字符串,见概念文档 §4。

发送者标识工具 — src/identity.ts(经 index.ts 转出) ​

符号file:line说明
getSenderLabel(nickname?, userId?)identity.ts两者都有 → 昵称(ID);否则取其一;都无 → undefined
prefixSender(content, nickname?, userId?)identity.ts有标签 → [label]: content,否则原样
stripSenderPrefix(content, label)identity.tscontent 以 [label]: 开头 → 去掉这一段,否则原样;label 取存档记下的 SenderLabel,没有时原样
getMessageName(userId?)identity.ts给 Message.name / OpenAI name 字段用的稳定标识符(用 userId 不用 nickname)

入站消息的归档文本 — src/incoming-content.ts(经 index.ts 转出) ​

符号file:line说明
buildIncomingContent(incoming, speaker?)incoming-content.ts入站消息 → 归档文本:转发的展开换回正文里占位的位置,再加发送者前缀(给了说话人才加,说话人按 api-message-archive 的 speakerOf)+ 引用回复 + _attachmentDescriptions。plugin-message-archive 落库与触发判定共用,保证判定时看到的当前消息与归档逐字一致;只拼接,不触发识别
forwardPlaceholder(id)incoming-content.ts正文里一段转发所在位置的占位(<forward id="…">[合并转发消息]</forward>),适配器写进 content
withForwards(content, forwards)incoming-content.ts正文里的转发占位换成对应的展开;没有对应展开的占位原样留着

事件(declaration merging 注入 @aalis/core 的 AalisEvents)— src/index.ts ​

事件名payload说明
'inbound:message'[IncomingMessage]适配器流入的入站消息
'inbound:message:archived'[{ sessionId, incoming: IncomingMessage, archivedMessage: Message }]入站已落库(无论是否触发回复都发)
'outbound:message'[OutgoingMessage]发往平台的回复
'outbound:delivered'[OutgoingMessage]送达之后的消息快照,给存档与旁观者用:content 只在文字送达时有,attachments 只含成功的附件。认领回执的适配器在送达后对每条消息都发(只有文字的也发),存档据此调 recordOutbound 记进会话历史;送达本身就是写进会话历史的适配器(WebUI)不发,改为直接调 recordOutbound
'outbound:stream'[StreamChunkMessage]流式片段(发往 WebUI 等前端)

该包仅以 import type {} from '@aalis/core' 锚定模块身份做增强(index.ts),运行时无副作用。listen/emit 这些事件的方式见 docs/core/context.md / docs/concepts/service-model.md。


谁 import 它(典型消费点,全部 file:line 实测) ​

不是"消费 message 服务",而是 import 上面这些符号。代表性站点:

  • LLM provider 出口必调 prepareLLMMessages:plugin-llm-deepseek/src/index.ts、plugin-llm-ollama/src/index.ts、plugin-llm-openai(同模式,概念文档 §3.1)。
  • CONTROL_KINDS 过滤(消费方职责):plugin-agent/src/index.ts。
  • 附件占位符:产出于 plugin-adapter-onebot/src/index.ts、plugin-image-sender/src/index.ts;重写/解析于 plugin-media/src/tools.ts。
  • 发送者标识:plugin-agent/src/index.ts、plugin-message-archive/src/index.ts、plugin-memory-vector/src/index.ts、plugin-adapter-onebot/src/index.ts。
  • 全仓有 ~40 个包 import @aalis/schema-message(消息是跨层公共载体)。

相关文档 ​

权威源码:packages/schema-message/src/index.ts、packages/schema-message/src/attachment-ref.ts、packages/schema-message/src/identity.ts、packages/schema-message/src/incoming-content.ts。

文档对应 dev@c4e046e41,core 0.21.0,构建于 2026-11-02
基于 MIT 许可发布