Afterglow Chat v1 格式
Afterglow v1 是给第三方中间件使用的稳定、平台无关中间格式,目前只支持私聊。可以使用 AI 编写 一次性中间件,将任意来源的聊天记录转换为该格式,以快速复用 Afterglow 的导入流水线。
转换中间件适合验证和个人迁移;长期维护、公开使用或需要完整保留源平台语义时,仍建议实现独立 ingestion plugin 并提交 PR。plugin 可以把格式识别、身份嗅探、消息解析和图片引用提取封装在同一处, 不会因中间格式转换损失信息。
JSON 格式:
{
"afterglow": {"format": "afterglow-chat", "version": "1.0"},
"conversation": {"id": "conv-001", "type": "private", "title": "可选"},
"participants": [
{"uid": "me", "name": "我", "role": "self"},
{"uid": "friend", "name": "TA", "role": "friend"}
],
"messages": [
{
"id": "m1",
"seq": 1,
"timestamp_ms": 1783625485000,
"sender_uid": "friend",
"sender_name": "TA",
"sender_role": "friend",
"kind": "text",
"raw_type": "text",
"text": "今天怎么样"
}
]
}消息字段:
timestamp_ms必须是毫秒时间戳。sender_role可为self/friend/system/other;缺失时按participants和.envUID 兜底。kind复用text/reply/placeholder/recalled/system/unknown。- 图片消息在普通文本导入中只保留占位和引用,不调用视觉模型:
{
"id": "m2",
"seq": 2,
"timestamp_ms": 1783625493000,
"sender_uid": "friend",
"sender_role": "friend",
"kind": "placeholder",
"raw_type": "image",
"text": "[图片: a.jpg]",
"placeholders": ["[图片]"],
"attachments": [{"type": "image", "name": "a.jpg"}]
}图片应放在导出目录的 resources/images/ 下:
export-dir/
chat.json
resources/
images/
a.jpg文本导入完成后,手动运行图片导入:
uv run python -m xuwen.ingestion.cli import-images export-dir --plugin afterglow_v1 --language zh-CNJSONL 同时支持两种形式。推荐使用 _type=header/participant/message 的 typed records:
{"_type":"header","afterglow":{"format":"afterglow-chat","version":"1.0"},"conversation":{"id":"conv-001","type":"private"}}
{"_type":"participant","uid":"me","name":"我","role":"self"}
{"_type":"participant","uid":"friend","name":"TA","role":"friend"}
{"_type":"message","id":"m1","seq":1,"timestamp_ms":1783625485000,"sender_uid":"friend","sender_name":"TA","kind":"text","text":"今天怎么样"}也可以每行直接放一个 message 对象。裸 message JSONL 的每一行都必须包含 sender_uid(或 senderUid)以及 timestamp_ms(或 timestamp);由于没有 participants,身份必须由消息的 sender_role、.env 中的 UID,或配置向导中的身份分配确定。
一次导入可以传入多个 Afterglow JSONL:
uv run python -m xuwen.ingestion.cli import \
chunk-001.jsonl chunk-002.jsonl \
--plugin afterglow_v1 --language zh-CN多文件导入当前是批量逐文件处理,而不是先拼接为一个逻辑 JSONL 流:
- 每个 typed JSONL 文件必须自包含一个
header,并且只能在其后包含participant和message;建议每个分片重复完整的participants。 - bare message 分片可以独立导入,但必须满足上述逐行必填字段和身份配置要求。
message.id应在整个导出包中稳定且全局唯一。不要依赖 loader 按文件名和行号生成的兜底 ID,否则文件改名、重新分片或 WebUI 重新上传后无法保证幂等去重。- 文件边界也是会话切分边界;窗口和 response pair 不会跨文件生成,
conversation.id当前不会触发分片合并。 - WebUI 会合并本次任务的全部文件生成人格、风格和作息画像;CLI 批量导入也会基于本次传入的全部文件重建作息画像。主动开聊画像会在批量导入完成后基于全部已入库窗口重建。
如果多个 chunk 属于同一段连续对话,并且需要保留跨 chunk 的上下文、窗口和问答关系,应由转换中间件先按时间排序、去重并合并成一个自包含的 typed JSONL,再交给 Afterglow 导入。
上述限制针对文本向量入库。独立的 persona 聚合器会先跨文件去重,再按 ingestion plugin 分组, 在每组内部按 (timestamp_ms, seq, message_id, sender_uid) 排序并重新切分会话。因此同格式 chunk 边界两侧的消息可以形成画像回复样本;不同 plugin 的消息会共同参与词频和作息统计,但不会组成 跨平台 response pair。调用方式:
uv run python scripts/analyze_persona.py \
chunk-001.jsonl chunk-002.jsonlscripts/analyze_persona.py 和 scripts/check_proactive_quality.py 是独立辅助脚本,目前没有 --language 参数,部分控制台提示仍为中文。不要给它们追加该参数;需要选择语言的导入或分析任务请使用主 CLI 的 --language zh-CN / --language en。
所有画像输入必须属于同一个目标人物。跨平台或多账号时,应在 .env 的 SELF_UID / FRIEND_UID 中配置该人物对应的全部 UID;不同人物必须分开生成画像。
import-images 会按图片 bytes 计算 SHA-256 去重,把原图保存到 .data/images/<sha>.<ext>, 对每个唯一 SHA 只调用一次 VISION_MODEL 生成摘要,再把“图片摘要 + 消息时间/发送者/上下文 + image_sha”写入 history_images 表。召回时默认使用已生成的图片摘要,不会在聊天请求里重复把历史 原图发给主模型或 VLM。