# 聊天记录与消息拼接(规格) > 状态:**已实现(v0.8.0,2026-10-19;v0.8.1 修正日志元信息与空文本消息;v0.9.0 增加媒体落盘引用; > v0.15.0 增加戳一戳记录,见 [poke.md](./poke.md);v0.16.0 增加群文件记录,见 [media.md](./media.md) §5.2)**; > 本文既是规格也是现状记录。媒体下载细节见 [media.md](./media.md)。 > 聊天记录落在「群绑定的项目」隐藏目录里、DSH 会话可读;发给 DSH 的 prompt 由 > `lib/model/prompt.js` 统一拼接。真机验收待用户重启 GUI 后确认。 > > 用户决策(2026-10-19): > 1. **每个群都可以有独立项目目录,在配置页自行配置**; > 2. 隐藏目录名用**短的** `.qq-relay/`; > 3. **不做跨机同步**(不进 git、不同步网盘); > 4. 聊天记录**默认不自动删除**,清理功能在配置页「记录」里(保留天数 + 立即清理); > 5. agent **直接 read 文件**即可,暂不提供 `qq_history` 工具。 ## 1. 目标 - 每个群绑定一个项目目录;聊天记录写进该项目的隐藏目录 `.qq-relay/`, DSH 会话(agent)与人都能直接读。 - 发给 DSH 的 prompt 带上必要元信息:群名 / 群号 / 时间 / 条数 / @ 与引用标记 / 历史指针, 修掉现在「只丢一句裸文本」的问题。 - 旧的全局聊天日志一次性迁移;目录不可写等故障不影响消息主流程。 ## 2. 现状(v0.7.1)与缺口 入向链路: ``` OneBot 事件 → codec.toInternalEvent(segments → text,丢 at / reply 结构) ├─ 白名单 / 自己 / 去重 ├─ chatLog.append → ~/.dsh/storages/dsh-qq-relay/chat/<群>/<日期>.jsonl ├─ 入向拦截器(审批可能吃掉这条) ├─ 直通(@ / 引用 / 命令)→ prompt = "[群名片或昵称] 正文" └─ 普通消息 → 闸门 pending 批次 └─ jev 判定接话 → prompt = 每行 "[昵称] 正文" └─ sessionController.prompt(同一群固定一个持久会话) ``` 缺口: | 层 | 现状 | 问题 | |---|---|---| | 条内 | 只留文本段 | @ 了谁、引用了谁、图片/表情全丢;直通时 agent 甚至不知道「它被 @」 | | 批内 | `[昵称] 正文` 每行一条 | 没有时间、群名、「这是新消息」的边界;直通用 `sender.card`、闸门只用 `nickname`,两处不一致 | | 上下文 | jev 读聊天日志最近 60 条 | 只喂分类器,agent 看不到 | | 持久 | 全局 storage 下的聊天日志 | **在会话 workspace 之外,agent 的 read/grep 读不到**;`/clear`、压缩、重建会话后断片 | ## 3. 目录契约 ``` / └── .qq-relay/ ├── .gitignore # 内容只有一行 `*`:自我忽略,永不进用户仓库 ├── chat/ │ └── / │ └── YYYY-MM-DD.jsonl # 本地时区按天分文件,append-only └── media/ └── /<日期>/-<序号>. # 图片 / 表情文件(v0.9.0,见 media.md) ``` - 一个项目一个 `.qq-relay/`;同项目多群时按 `chat//` 分目录。 - 项目目录见 §4;目录不存在时 `mkdir -p`(与 `workspaceRegistry.create` 行为一致)。 - `.gitignore` 只在缺失时写入,不覆盖用户已有文件;写失败只记 warn。 - 不生成 `meta.json` / digest / `inbox/` 快照——先用文件本身,有需求再加(见 §12)。 ### 3.1 聊天日志行格式 ```json {"time":1792380107000,"groupId":"10002","messageId":"12345","userId":"10003","nickname":"tester","card":"tester","role":"member","text":"测试","mentionsSelf":true,"replyToId":null,"raw":"[CQ:at,qq=10001] 测试"} ``` - 在 v0.7.1 字段(`time / messageId / userId / nickname / role / text / raw`)之上新增 `groupId`、`card`、`mentionsSelf`、`replyToId`。 - 读取端对缺字段补默认值(`''` / `false` / `null`),旧行照常可读。 - 纯图片 / 表情等没有文本的消息只写日志(`text` 为空),**不进闸门、不调分类器**;`text` 写入前 trim。 - 图片 / 表情消息新增可选 `media` 数组(`kind` / `status` / `path` / `bytes` / `contentType`, 失败带 `reason`),`path` 是相对项目根的路径;格式与下载规则见 [media.md](./media.md) §4。 - `raw` 仍是 OneBot `raw_message`(含 CQ 码),调试 / 回放用。 - 戳一戳(v0.15.0)也写日志:`text` 为「戳了你一下 / 戳了 `<目标>` 一下」,`raw: "poke"`, `messageId` 用合成键 `poke: