Files
TencentDB-Agent-Memory/scripts/seed-v2
2026-05-29 17:51:28 +08:00
..
2026-05-29 17:51:28 +08:00
2026-05-29 17:51:28 +08:00
2026-05-29 17:51:28 +08:00
2026-05-29 17:51:28 +08:00
2026-05-29 17:51:28 +08:00
2026-05-29 17:51:28 +08:00
2026-05-29 17:51:28 +08:00

seed-v2

通过 v2 API 把历史对话灌入 memory-tencentdb gateway。

是什么

seed-v2 是一个纯 HTTP 客户端,把一批历史对话喂给已经在跑的 gateway:

fixture.json
   │
   │ HTTP POST /v2/conversation/add  (一轮一次)
   ▼
gateway (standalone or service)
   │
   │ 自动调度 L0 → L1 → L2 → L3
   ▼
本地 vectors.db / records / scene_blocks / persona.md

客户端不 import 任何 plugin 内部模块,只用 fetch + /v2/conversation/add + /v2/pipeline/status

跟老版 src/cli/commands/seed.tsv1import 核心 runtime 直跑)相比:

  • 同一份代码同时适用于 standalone 和 service 模式
  • 不会和已经在跑的 gateway 抢同一份 SQLite / state
  • 通过 status 接口节流,串行写入 → 等抽取完 → 再写下一批
  • 调度状态由 gateway 侧 StatefulPipelineManager 统一管,跟生产路径完全一致

调用方式

方式 1:通过 npm bin(推荐)

# 1. 把 gateway 起起来(standalone 用法)
cd extensions/memory-tencentdb/__tests__/standalone && ./start.sh

# 2. 灌数据
cd ../..
npm run seed-v2 -- --input ./scripts/seed-v2/fixtures/minimal.json

# 3. 完事停 gateway
./__tests__/standalone/stop.sh

方式 2:直接命令

node ./bin/seed-v2.mjs --input fixture.json --endpoint http://127.0.0.1:18420

方式 3:一键 wrapper(含起停 + 落盘断言)

./scripts/seed-v2/seed-v2.sh                         # 默认 fixture
./scripts/seed-v2/seed-v2.sh path/to/my-fixture.json # 自定 fixture
./scripts/seed-v2/seed-v2.sh --keep                  # 跑完不停 gateway
./scripts/seed-v2/seed-v2.sh --no-start              # gateway 已在跑

CLI 参数

参数 env 默认 说明
--input <file> -i 必填 fixture JSON 路径
--endpoint <url> -e SEED_ENDPOINT http://127.0.0.1:18420 gateway 地址
--api-key <key> SEED_API_KEY standalone-e2e Bearer key
--service-id <id> -s SEED_SERVICE_ID default x-tdai-service-id
--every-n <n> -n SEED_EVERY_N 5 每 N 轮等一次 L1 idle
--poll-ms <ms> SEED_POLL_MS 500 status 轮询间隔
--stable-rounds <n> SEED_STABLE_ROUNDS 2 连续 N 次 idle 才认为 stable
--max-wait-ms <ms> SEED_MAX_WAIT_MS 600000 每批 waitL1 only)超时(默认 10 分钟)
--final-max-wait-ms <ms> SEED_FINAL_MAX_WAIT_MS 600000 最终 waitL1+L2+L3 全 idle)超时(默认 10 分钟)
--no-final-wait false 写完最后一批不等 cascade
--dry-run false 只打印计划不实际请求
--session-key <key> SEED_FALLBACK_SESSION_KEY fallback 给缺 sessionKey 的 session
--strict-round-role SEED_STRICT_ROUND_ROLE=1 false 每 round 必须含 user+assistant
--no-auto-fill-timestamps SEED_AUTO_FILL_TIMESTAMPS=0 false 缺 ts 时不自动 fill
--quiet -q SEED_VERBOSE=0 false 静默
--help -h 帮助

Fixture 校验(与 v1 seed 完全对齐)

启动时会做 6 层校验(任一失败 → exit 2,不会浪费时间灌一半才挂):

检查
1. file 存在 + 非空 + 合法 JSON
2. top-level Format A { sessions: [...] } 或 Format B [...]
3. session sessionKey 必须非空字符串 + conversations 必须 2D array
4. round 必须非空 array--strict-round-role 时还要求 user + assistant 同时存在
5. message role/content 非空字符串 + timestamp 类型校验(int 或 ISO string
6. timestamp consistency 全有 / 全无 / mixed 三态:mixed 拒绝;全无时按 --no-auto-fill-timestamps 决定行为

错误输出示例(mixed timestamps):

$ seed-v2 --input mixed.json
[seed-v2] Seed input validation failed (1 error(s)):
  [timestamp_consistency] Timestamp consistency check failed: some messages have timestamps while others do not. ...
exit code: 2

Timestamp 三态

状态 行为
全有 保留原值,hasTimestamps=true
全无 + auto-fill 开(默认) fillTimestamps() 给全局递增 epoch msDate.now() 起,每条 +100ms)—— 跨 session 也单调,避免 L0 capture cursor 误过滤
全无 + --no-auto-fill-timestamps timestamp=0;写入时 client 用 Date.now() 兜底
mixed exit 2,明确报错

Fixture 格式

兼容老 seed Format A{ sessions: [...] })和 Format B(顶层数组):

{
  "sessions": [
    {
      "sessionKey": "user-001",
      "sessionId": "user-001",          // 可选,缺省 = sessionKey
      "conversations": [                // rounds
        [                                // round 0:一组消息
          { "role": "user",      "content": "..." },
          { "role": "assistant", "content": "..." }
        ],
        [ /* round 1 ... */ ]
      ]
    }
  ]
}

参考最小示例:fixtures/minimal.json2 session × 6 round = 24 messages)。

阻塞节奏(重要)

完全对齐老 src/core/seed/seed-runtime.ts:executeSeed

for session, round:
  POST /v2/conversation/add { session_id, messages }   # 立刻返回
  if (round + 1) % every_n == 0:
    waitForL1Idle  (stable 2 polls)                    # 只等 L1L2/L3 后台异步跑
end-of-session: waitForL1Idle                          # 末尾再等一次
final: waitForL1Idle                                   # 写完所有 round 后再等 L1 drain

/v2/pipeline/status 按 L 分桶返回 { l1: {idle,…}, l2: {…}, l3: {…} }。seed-v2 只看 data.l1.idle

  • l1.idle = (l1.queued === 0 && l1.running === 0) — 跟老 v1 seed-runtime.waitForL1Idle 完全对齐
  • L2/L3 抽取慢(cascade 一次 LLM 工具链可能 3-5 分钟)→ 不会阻塞 seed 的下一批 dispatch
  • L2 LLM timeout / lock-conflict drop 等异常情况 → 不会l1.idle 误判 false(之前用单标量 busy 时会被这两个 bug 污染)

详见 docs/plans/server/14-pipeline-status-api.md

Pipeline 调参(服务端 yaml

灌入速度由两侧共同决定:seed 客户端的 wait 节奏 + gateway 流水线的触发节奏。客户端参数已在上面 CLI 参数 表里,下面是服务端对应的 tdai-gateway.standalone.yaml 配置项(标星号的最常调):

memory:
  capture:
    enabled: true                    # L0 capture 开关,必须 true 否则没数据进流水线

  extraction:
    enabled: true
    enableDedup: true                # L1 抽取后去重(vector recall topK=5);关掉提速但可能产生重复 record
    maxMemoriesPerSession: 20      # ★ 单 session L1 抽取的 atomic memory 数上限

  persona:
    triggerEveryN: 50              # ★ 每抽取 N 条 L1 record 触发 1 次 L3 persona-gen
    maxScenes: 15                   # L2 scene 数上限(达到后停止 CREATE 新 scene

  pipeline:
    everyNConversations: 5         # ★★★ 每 N 轮对话触发 1 次 L1 抽取(必须跟 seed 客户端 --every-n 对齐)
    enableWarmup: true              # 冷启动前几次更快触发 L1(让 persona.md 早点形成骨架)
    l1IdleTimeoutSeconds: 600      # ★ session 空闲 N 秒后强制触发 L1(兜底,防 conversation < everyN 时 L1 永不抽)
    l2DelayAfterL1Seconds: 90      # ★★ L1 完成后延迟 N 秒触发 L2(避免每条 L1 都触发一次 L2,太碎)
    l2MinIntervalSeconds: 900      # ★★ 同 session 两次 L2 之间最小间隔(节流,防 L2 LLM 调用过频)
    l2MaxIntervalSeconds: 3600     # ★ 同 session L2 最大间隔(兜底,无论间隔多久必跑一次)

怎么调:常见场景

场景 怎么调
seed 灌入想最快(接受 L1 粒度粗) everyNConversations: 20(每 20 轮才抽一次 L1+ seed --every-n 20 对齐
seed 灌入想 L1 立即抽(每轮都抽,最细粒度但贵) everyNConversations: 1 + seed --every-n 1
fixture 是短对话< everyN 轮)触发不到 L1 l1IdleTimeoutSeconds: 601分钟无新消息就触发兜底)
L2 cascade 太频繁cost 高) 拉大 l2MinIntervalSeconds: 180030 分钟才一次)
L2 cascade 太稀疏 l2DelayAfterL1Seconds: 30 + l2MinIntervalSeconds: 300
测试/调试想看快速反馈 triggerEveryN: 10 + everyNConversations: 2 + l1IdleTimeoutSeconds: 30

seed 客户端 vs 服务端的对应

seed 参数 服务端 yaml 对应 是否需对齐
--every-n (SEED_EVERY_N) pipeline.everyNConversations ⚠️ 建议一致(客户端 wait 间隔 = 服务端触发间隔,灌入流畅)
--max-wait-ms pipeline.l1IdleTimeoutSeconds wait 时长应 ≥ idle timeout(否则会 max-wait-reach 而不是 stable-idle
--final-max-wait-ms 综合 L2 慢 task 时长 ≥ p95(L2 LLM duration) × maxScenes/(同时抽的 scene 数)(默认 10 分钟够大多数场景)
--stable-rounds 客户端独有,控制连续多少次 idle 才认定真稳定

调参对 seed 总耗时的影响(粗估)

配置 persona-010 sessions × 366 msgs)耗时
默认(everyNConversations=5l2DelayAfterL1=90sl2MinInterval=900s ~90 min(实测 persona-0
降本:everyNConversations=20l2MinInterval=3600s ~30-50 minL1 抽得粗,L2 抽得少)
极速:--no-final-wait + 仅 L0 模式(extraction.enabled=false ~1-2 min(只灌 L0,不抽取)

⚠️ 改 yaml 后必须重启 gatewayscripts/seed-v2/seed-v2.sh 自动起停;如果用 start.sh 手动起的需 stop.sh && start.sh)。

退出码

code 含义
0 全部成功
1 seed 失败(写入或 wait 异常)
2 前置条件不满足(fixture 缺失 / gateway 不通 / 配置错误)

构建 + 发布

npm run build:seed-v2     # tsc 编译到 scripts/seed-v2/dist/
npm run build:scripts     # 构建所有 scripts(含 seed-v2

bin/seed-v2.mjs 是薄启动器:dist 存在就跑 dist,否则 fallback 到 tsx 跑源码。开发期不需要先 build。