Tree:
7ce56f693d
along
along-test
main
memory-center-impl-v1
memory-design-v0.2
${ noResults }
2 Commits (7ce56f693dd3f7c78ac5b64e1f15158a704afaa4)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
811a008545 |
services: 语音纪要→拾忆→MCP 链路审计落地;修两个「什么都没发生」的既有 bug
审计与落地计划见 docs/语音纪要-拾忆-MCP链路审计与落地计划.md,已在阿龙测试环境 逐条验收通过(记录见该文档 §5.1)。 ## 安全 echomeet 八个按 id 操作的接口**一处都没有比对 record.Uid 与会话 uid**,任何登录用户 改一个 id 就能读别人纪要全文、删改别人的记录、给别人的记录发起总结(扣自己算力, 但覆盖对方 summary 并触发对方的拾忆抽取)。统一改走带 uid 的查询: - 「不存在」与「不是你的」回同一个错误码同一句话——分开回等于给出一个探测他人 id 的接口;用的还是改动前记录真不存在时的 DBError,合法用户行为无任何变化; - 批量接口按交集处理、**不报错**:客户端轮询队列里本就可能留着已被别的设备删掉的 id, 报错会把整个轮询循环掀掉;delrecords 响应回实际删掉的那批; - starttask 的校验排在扣算力之前,否则拒绝了还先把钱扣了; - 回调接口是第三方来的、没有 session,不在此列。 - TestApisUseUidScopedQueries 扫源码守着:最容易复发的不是有人改回去, 而是新加接口时照旧写法抄一遍,那样既不报错也看不出来。 ## gorm 的 default: 标签吞零值 mysql.Insert = db.Table().Create(model),gorm 对带 default: 的字段**一律把零值换成 默认值**。DBMemoryItem 的 date_certain(default:true) / remind_ahead(default:5) 因此 永远写不进 false/0:测试库 25 条记录全部落成 1/5,「待定日期」分组恒为空, 会议待办还会在 08:55 响提醒——与「会议待办默认不提醒」正好相反。 ⚠️ 加 Select("*") **救不了**:替换在 ConvertToCreateValues 的 reflect.Struct 分支里 无条件做,只看值是不是零值,与 Select/Omit 无关(DryRun 实测两种写法生成的 VALUES 一模一样)。改成插入后把这两列按结构体现值补写回去,并把真值还给调用方—— gorm 连结构体上的字段一起改了,不还原的话 memory_add 回给客户端的也是错的。 存量由 migrate_defaults.go 幂等修正(只碰 source=meeting 且 user_edited=0)。 ## 内容门槛 6 秒的测试通话、20 秒的单人自述照样被抽成「继续进行进一步的功能测试」这类废话待办, 再靠条数凑够周报阈值又触发一次 LLM 调用。门槛卡**转写正文**不卡纪要——内容太薄时 模型按模板兜底规则照样能写出六百多字。只拦抽取与 MCP 检索,**纪要照出**: 短语音备忘是合法用法,也不判成 SummarizFail(客户端把那个码显示成「请稍后重试」, 用户会一直点)。数据缺失时按通过处理,不因上游漏传一个字段就静默关掉抽取。 ## 其余 - 报告补偿:原先的 requeueIfStale 由 memory_getreport 驱动,而客户端调无参版本 → 走 latestUnconfirmedReport,那条 SQL 只查 state=Done,pending/processing/failed 永远查不出来也就永远补不了。新增 ensureRecent 挂在 memory_today:漏建的补建 (过与 cron 同一道条数闸门)、卡住的复用 requeueIfStale、失败的至多重试 3 次 (计数寄在 error_msg 的 retry=N; 前缀,fail() 必须保住它,否则上限形同虚设)。 加 enqueueOnce 判重与按报告 id 的 SETNX 生成锁,cron 与 ensure 撞上只跑一次 LLM。 - 说话人标签:finishSyncTranscribe 拼的是「Speaker 0」(空格)、其余是「Speaker_0」, 抽出来的负责人就成了 Speaker2、[Speaker_0] 各种样。四条写入路径统一走 normalizeSpeakers, 顺带修掉没开说话人分离时拼出「Speaker_」空尾巴。 - 时区:LoadMemoryLocation 现在认 IANA / 固定偏移 / CST(按 Asia/Shanghai 解释, 兜住已落库那批),认不出来仍退服务器本地时区但**打 warn**——原来是全静默的。 - 删纪要连带删自动待办(只删 user_edited=0),查询带 uid:入参来自删除请求, 光信 source_id 等于把归属校验的成果又丢一次。 - 回调幂等:两个回调既不看状态也不抢锁,与轮询撞车就各翻译一遍、各入队一次。 失败分支同样要过闸门——一条已 Completed 的记录被迟到的失败回调打回 TranscribeFail, 用户看到的就是「纪要好端端地变成了转写失败」。挡下来仍回 SUCCESS,不让第三方重投。 SubmitAITask 保持不判重(用户点的「重新生成」悄悄跳过等于按钮失灵), 自动路径改走 submitAITaskOnce。 - 重复项统计:只展开闹钟。花销展开等于虚构金额,待办的 done/undone 按次拆不开。 ## 顺带修的两个既有 bug(部署验收时挖出来的) happen_date / period_start 都是 type:date 列 + DSN parseTime=True,读进 Go 的 string 是「2026-09-07T00:00:00+08:00」。SQL 比较靠 MySQL 转换还是对的,所以这个问题 **在任何日志任何报错里都看不见**,只在 Go 侧解析它的地方发作: 1. remind.go 的 expandItem 解析不了 → **memory_upcoming 恒返回 0 个提醒时刻, 整个拾忆提醒在服务端一直空转**(真机实测:建一条每天 08:00 的闹钟,slots 为 []); 2. 下发给客户端的 happen_date 一直是 RFC3339 而非协议约定的 YYYY-MM-DD, 客户端 _localToday() 的字符串相等比较恒不匹配、_mergeRange 在区间端点会重复。 这也是审计里「会议待办会在 08:55 响通知」只能是推演的原因:实际一条都不会响, 两个 bug 互相遮蔽。修法两层:ParseMemoryDate 容忍带时间的形式(安全网), 读路径出口统一归一(管对外格式),缺任一层都会留坑。 ## 列表骨架化(同批) echomeet_getallrecords 只回列表要用的骨架字段,original/translate/summary 占每条 97% 的字节(实测 59 条 ≈ 500KB,列表用得上的 15KB)。 ⚠️ 骨架记录**绝不能写回库**:mysql.Save 是整行 UPDATE,三列会被清空。 StartTask 允许对已完成/已阅/失败的记录「重新转写」,那期间 state=Transcribing 而旧 纪要还在库里,用户切一下列表页旧转写和旧总结就没了。轮询前重新取整条再操作, TestListEndpointNeverSavesBriefRecord 守着不许回退。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
3 weeks ago |
|
|
0b0ac16b3c |
记忆中心后端:memory 模块 + 会议待办抽取 + 周报月报
按设计文档一~三期的后端部分落地。全量 go build / go vet 通过,新增 22 个测试。 memory 模块(modules/memory,由 home 装载,路径 /api/home/memory_*) 11 个接口:list / today / add / update / del / complete / upcoming / stats / getreport / confirmreport / listreports。 表由 proto + gorm tag 生成(不是手写 DDL——本仓所有业务表都走 mysql.CreateTable + AutoMigrate)。protobuf 没有 DECIMAL/DATE/DATETIME/null, 逐个落成:金额 int64 存分(与 pay/结算同口径,绝不用 double)、happen_date string + gorm type:date、next_remind_at int64 unix 秒(0=无)。 几处刻意的选择 - 建表失败只 Errorln 不外传。lego.Run 里 Init 返回非 nil 就 Panicf,而五个服务 同镜像、entrypoint 见任一 pid 退出就全杀,一次建表失败等于全站 502。 allhelp/model.go 那段用命名返回值 err,第二张表失败时会漏出去,是反例。 - client_key 服务端保证非空。它参与 (uid, client_key) 唯一索引,而 MySQL 的唯一 索引不像放行 NULL 那样放行空串,留空会让同一用户的第二条记录直接插不进去。 用 crypto/rand 生成:这个值是去重依据,可预测就可被抢占。 - 重复项的提醒改成「就地展开」。设计文档 v0.1 写的「remind_at 服务端算好客户端 直接用」对重复项不成立——一个字段表达不了「每天」,且没人在它过去后往前推。 现在 memory_upcoming 按项自己的 tz 展开成绝对时刻列表返回,不落库。 单条项按自己的 tz 算而不跟着请求方走:「今天有哪些事」是视图问题可以跟着人走, 「明早 8 点响」是承诺问题不能。 - 花销按币种分行统计,不做汇率换算。把 CNY 和 JPY 加成一个数是错的。 - 统计全部走 SQL,LLM 只把 stat_json 组织成一段适合朗读的话。明细截断到 20 条 (重度用户一周两百条未完成,全塞进 prompt 会顶爆上下文,播报也没人听得完)。 - 查询/删除/更新一律带 uid 作条件,报告确认校验 uid 归属。 - 更新字段走白名单而不是黑名单,漏挡一个 uid/id 就是越权改别人的数据。 选路走编排,不再写死服务商 新增 comm.IEchomeet.ChatLLM,由 echomeet 用它的 PickLLM/GetSummarizer 实现。 memory 的报告生成与会议抽取都经它选模型。allhelp/summary.go 直接 import sys/doubao 是历史债:绕过后台编排,海外应用照样调国内豆包、后台换模型也换不掉。 新模块不复制这个写法。 会议纪要 → 待办抽取(第三路) 挂在 AIProcess 写完 summary、状态置 Completed、出队之后,另起 goroutine。 prompt 内置代码不放 echomeet_template(1189 条模板改不齐,且输出要被程序 parse)。 输入用已生成的 summary 而非转写全文。注入 <meeting_date>,服务端二次校验 due_date:解析不出或早于会议当天的一律只留 due_raw、落会议当天并置 date_certain=false。抽取失败只记日志,绝不回头改 rec.State。 - gen_round 由 memory 按 source_id 现有最大值 +1 推出,不由调用方传。 echomeet 没有地方存轮次计数器,硬编码成 1 会让第二次「重新生成」的清理条件 gen_round < 1 一条都匹配不上,旧待办从此永远留在库里。 - 会议归属日期只能用 creationtime(上传时间)——DBEchoMeetRecord 上没有「会议实际 发生日期」字段,也没有客户端时区。补录历史录音会算错,是已知偏差,注释已记。 周报月报 cron 建单 → Redis 队列 → N worker。锁与队列 key 都过 redissys.RKey 加应用前缀: 测试机多应用共用一个 Redis,锁 key 不带前缀会让 A 应用挡住 B 应用整周不生成报告, 而且安安静静什么都不报。 补偿比防重更要紧:当前 compose 是单副本,多副本重复是前瞻性风险;真会发生的是 00:30 跑到一半容器重启、cron 不补跑、报告永远停在 pending。所以 memory_getreport 里做超时重新入队(同 echomeet 用 PollTranscribe 兜「转写回调丢了」的思路)。 文案生成失败不算整体失败——统计是完整的,宁可少一句话不可让报告整个没有。 顺带修的既有缺陷 - user 模块 cancel()(账号注销)补上 memory_item / memory_report 两张表, 以及既有遗漏的 useridverify(身份证掩码 + 加盐指纹)。这个清单是硬编码的, 漏了不报错,只是个人数据静静留在库里。 - errorcode.proto 新开 5201-5206 段。复用 ReqParameterError 会让客户端无法分流, 「报告还没生成好」该转圈重试而不是弹错。 测试 comm/memory_test.go 8 个:周日在 Go 里是 0、Dart 里是 7 的换算(错了不报错, 只会让每周日的闹钟在周一响)、ISO 跨年周、闰年二月、非法时区必须回退 time.Local 而不是 UTC(否则静默偏移 8 小时)、client_key 非空且不重复。 modules/memory 14 个:一次性/每天/每周展开、锚点在一年前仍能算出未来时刻、 过期项不排、灵感花销不提醒、全天项按 9:00 不落 0:00、展开结果必须升序 (客户端要按 iOS 64 条上限截断,顺序错了会把明天的排掉留下一周后的)、 项按自己时区算、LLM 输出容错(代码块/前后解释文字)与真失败必须报错。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
1 month ago |