- dasha-unify: fix Ashtottari birth balance (BUG-1344) - rect-scoring-perf: server-side candidate cap 241 (BUG-1345) - node-runtime-resources: enable user-side session cookie cache, 300 s (BUG-1346) - consult-prep-parallel: classifier/reserve concurrency declined - answer-effort-ab: model key supplied by product at acceptance - drop stricter-than-test line-count red lines (AGENTS.md section 6) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Ad9rqAB8pXXvyzh7f4X8v
28 KiB
TASK · 普通对话发问前的准备步骤改为并行(issue #待定)
- 日期:2026-10-13
- 来源:「2026-10-13 全仓屎山与性能排查」P2 · 咨询前置并行 / 多领域并发 / 报告章节并发
- 执行方:Grok(Windows + Docker),分支
codex/consult-prep-parallel-20261013,只推这个分支 - 验收:Claude(Linux)
- BUG 编号:BUG-1337~1339(开工时核对
docs/BUG_HISTORY.md最大号;写作时最大号 BUG-1317,1335/1336 已被其它单占用)(另:TASK-upstream-sync7-20261014占 BUG-1318~1322,本批号段整体从 BUG-1323 起) - 开工前置:无(第一批)。本单之后还有两单改同一批文件,必须串行:
answer-effort-ab(改consultation-tools.ts)→consult-route-split(搬route.ts)。本单不要顺手搬route.ts的其它段落。
1. 基线
- 基线 commit:
origin/staging77057a819 - 涉及文件:
frontend/src/app/api/consult/route.ts::POST(1,840 行文件,POST从第 296 行起)frontend/src/lib/model-catalog.ts::readVersion/resolveSessionLanguageModel/invalidateLanguageModelCatalogfrontend/src/lib/moderation/index.ts::moderatefrontend/src/mastra/consultation-tools.ts::createConsultationTools(多领域循环)frontend/src/lib/personal-report-generation.ts::generateSectionedPersonalReport(只核实,不改,见 §4 决策 4)
2. 事故实证
2.1 开流前的串行等待(route.ts::POST)
开流(streamFirstResponse,第 1821 行)之前,下面每一步都是 await 完上一步才开始:
| 顺序 | 行(77057a819) |
步骤 | 依赖 |
|---|---|---|---|
| 1 | 301–316 | createServerSupabaseClient + supabase.auth.getUser() |
— |
| 2 | 318 | refuseWhenAccountDeletionPending(user.id)(一次 service client RPC) |
只要 user.id |
| 3 | 321 | request.json() + zod |
无 |
| 4 | 331 | 读 chat_sessions(含整段 messages) |
只要 user.id + 请求体的 sessionId |
| 5 | 365 | resolveSessionLanguageModel(model_id, model_config_version) |
第 4 步 |
| 6 | 424 | moderate(visibleQuestion, "input"):本地词表恒跑;MODERATION_PROVIDER=aliyun 时再远程,上限 MODERATION_REMOTE_TIMEOUT_MS = 1_500 |
第 4、5 步通过 |
第 2 步与第 3~5 步互不依赖;第 6 步排在首帧之前,开了阿里云时首帧最多晚 1.5 s(BUG-1074 的「先建流」只把第 444 行以后搬进了流里)。
流内(runTurn)还有两处串行:
- 第 674 行
classifyConsultationTurn(SMALLTALK_TIMEOUT_MS = 3_000)排在prepareConsultationRoute(资料/时区/候选区间读取 +reserve_consultation_usage)与append_consultation_question之后。 - 第 741 行分类完成后才再读一次
chat_sessions.title(只读,不依赖分类结果)。
2.2 readVersion 每轮查库 + 解密 + DNS(model-catalog.ts)
resolveSessionLanguageModel 在会话绑定了版本号时(普通对话都绑定)走 readVersion → queryCatalog(一次联表 SQL)→ resolveRow:decryptModelProviderApiKey + 对 openai-compatible 供应商 assertAllowedModelProviderUrl(dns.lookup 全部地址并逐个判公网)。没有缓存;同文件的 loadLanguageModelCatalog 对同样的解析结果(含同一个 DNS 校验)已经缓存 15 s(cacheTtlMs = 15_000)。
核实「版本不可变」:只对了一半。
- 不可变:已发布/已退役版本所用供应商的
code、provider_type、base_url(触发器prevent_published_model_provider_runtime_mutation,frontend/supabase/migrations/20260808010000_model_provider_emergency_disable.sql;管理端报model_provider_runtime_immutable)。 - 可变:
model_providers.enabled(紧急停用)、model_config_versions.enabled、供应商密钥(重新启用时可换 key)。readVersion的 where 条件里有v.enabled and p.enabled,所以缓存必须有 TTL 且能被管理端操作清掉。
核实 SSRF(AGENTS.md §8.3):assertAllowedModelProviderUrl 是解析模型配置时的公网地址校验;之后真正发模型请求的是 Mastra 的 createOpenAICompatible(baseURL 是主机名,fetch 会重新解析 DNS,不是 pinnedAddressLookup 固定地址)。所以今天这道 DNS 校验也不是逐请求绑定,目录路径已经把它缓存 15 s。结论:按(模型, 版本)缓存、TTL 与目录相同(15 s)、管理端变更时一并清除,不削弱现有防护;TTL 更长或缓存失败结果则会削弱,不做。
2.3 多领域逐个 await(consultation-tools.ts::createConsultationTools)
第 1230–1280 行附近 for (let index = 0; index < domains.length; …) 逐个 await runConsultationWorkflow(POST /api/consultation_workflow)。MAX_CONSULTATION_DOMAINS = floor((110_000 − 45_000) / 31_000) = 2。/api/consultation_workflow 在 scripts/api_heavy_compute_gate.py::HEAVY_COMPUTE_PATHS 里,JYOTISH_HEAVY_COMPUTE_CONCURRENCY 默认 2,满了直接 429;前端 consultation-workflow.ts::httpFailureCode 把 429 记成 workflow_rate_limited,不退避重试,整次计算失败。
本机实测(起草时,77057a819,系统 python3.13,无 vedastro SDK,本地起 ThreadingHTTPServer,Steve Jobs 公开 AA 盘,事业 + 婚恋两领域,每组 3 次取中位数):
| 情形 | 串行 | 两路并发 |
|---|---|---|
| 盘已缓存 | 0.47 s | 0.60 s(更慢) |
| 盘未缓存(每轮换出生分钟) | 2.25 s(第一域约 2.0 s,第二域吃到排盘缓存 0.29 s) | 2.03 s(两路各自冷算约 2.0 s) |
复现:scratchpad/bench-prep/bench2.py、bench3.py(起草会话临时目录,执行方按 §6 T5 重写进 frontend/scripts/research/)。
解读:Python 服务是 ThreadingHTTPServer,排盘吃 GIL;串行时第二个领域复用第一个领域的排盘缓存,并发时两路各算一遍。本机并发只省约 10%,CPU 翻倍、一次占满两个重计算名额。 生产容器装了 vedastro SDK,每域约 1.66 s 里有 1.33 s 是 BUG-301 保留的前台网络等待(consult-latency-audit 10-05 记录),网络等待会释放 GIL,所以生产上的收益可能比本机大——要靠 A/B 实测,不能推断。
另:同一盘、同一分钟,「串行时第二个领域(命中缓存)」与「冷启动单算该领域」的响应逐字段比对,除计时/缓存字段(consult_card_facts.timing.*、chart.runtime_cache.*)外,还有 module_execution_audit[*].module_id 与 consultation_native_layers.d9_summary.vargottama 两处列表顺序不同(集合遍历顺序,ERR-111:需 PYTHONHASHSEED=0)。所以 §6 T5 的差分脚本必须固定 PYTHONHASHSEED=0 并列出忽略字段,不能直接 cmp。
2.4 报告章节并发:目标代码已退役
generateSectionedPersonalReport(personal-report-generation.ts 第 3510 行)确实逐章串行,但它只在 PERSONAL_REPORT_WRITER_ENABLED 为真时被 worker 调用;frontend/src/lib/personal-report-writer-flag.ts 写死 false(注释:「Writer narrative pipeline is retired for new reports. Keep the code; do not call it.」,cfcd369d4)。新报告走 generateLongformReport(Python 完整数据版模板渲染,不逐章调模型)。/api/reports 的 resolveReportCreate 有 jobs 时只入队。改它对线上零影响,见 §4 决策 4。
3. 根因
- 2.1:不是回归。各步按当时目的串行写成;BUG-1074 只把「会等模型或数据库的大头」搬进流里,开流前那几步与流内的读标题没有人回头看能否并行;输入审核 09-30 加在开流前。
- 2.2:从未设计缓存。目录缓存(15 s)只服务「没绑版本」的旧路径,绑版本的
readVersion是后加的。 - 2.3:BUG-257 的防复发明确写了「任何领域级并行提议必须先证明后端能承接并发,判据是 Python 侧的进程模型、GIL 约束与有界队列」。本单只做开关和证明,不默认开启。
关联记录(均已读完并核对防复发仍在):
| BUG | 与本单的关系 | 必须保留的防线 |
|---|---|---|
| BUG-1074 | 先建流、首帧是确定性进度句 | 开流前只留鉴权/请求体/会话/会话模型(consult-first-frame-and-pacing-20260928.test.tsx 两张清单);流内拒绝必须能表达成 request_rejected |
| BUG-1075 | 放字节奏 | 不碰 |
| BUG-976 / 977 | 寒暄分类 | 「reserve 仍在分类前,零点无订阅仍被拦」——分类器不得早于预留 |
| BUG-1142 | 答题钟 300 s / 工具钟 110 s / 领域预算 65 s、2 个 | 领域上限公式不变,并发不得改 MAX_CONSULTATION_DOMAINS |
| BUG-257 / 945 | 领域上限由时钟推导;计划长度与执行长度两个常数 | 并发仍按 executableDomainPlan 截断、仍披露 omitted_domains、仍降级 |
| BUG-723 | 引擎 429 单独成档 | 并发版本对 429 不得压平;并发导致的 429 必须在 A/B 里计数 |
| BUG-1231 / 1232 | 分段计时(modelSteps、answer.reasoning_ms、分类耗时进用量) |
不删字段;本单新增的准备阶段计时走同一个 logAgentObservability |
| BUG-148 | DNS 固定地址请求 | requestAllowedModelProvider 不动 |
| BUG-496 | 报告分章失败不得中止其余章 | 本单不改报告 |
4. 决策记录
产品授权:2026-10-13 产品负责人同意按排查结论全部立单交 Grok 执行;P1/P2 改动必须做 A/B、做成开关,默认值按 A/B 定。
- 开流前的独立读取并行(删号检查 ∥ 读请求体 + 读会话 → 解析模型):做。
- 输入审核:仍在任何模型调用和预留之前生效,但不再挡首帧——开流后与资料读取并行,在
reserve之前等它的结论;被拦时仍不扣点、不进模型;报错优先级与今天一致(见 T2)。不推翻 BUG-1074 的开流前清单(审核本来就不在那张清单里)。 readVersion缓存:做,TTL 与目录缓存相同(15 s),不缓存失败和null,管理端模型/供应商变更时清除。不延长 TTL。- 报告章节并发:本单不做。目标函数已退役(§2.4),改了不会让任何用户的报告变快;排查报告里这一条作废。若产品以后重新启用 writer 管线,另立单。
- 分类器与预留并发:产品负责人 2026-10-14 决定不做。 它会让零点用户的问题在预留失败前先送进模型(违反 BUG-976 边界「reserve 仍在分类前」),且分类调用产生供应商成本。可选折中「预留成功后,分类与
append_consultation_question并行;append 失败则丢弃分类结果并取消」也涉及计费顺序,同样留待拍板。 - 多领域并发:做成开关,默认关(串行),A/B 达标才改默认。不改领域上限、不改时钟。
- 不推翻任何既有红线。
5. 硬红线
- 被审核拦下的问题:不扣点(不得调用
reserve_consultation_usage)、不进任何模型(分类器、Agent、标题生成都不得先于审核结论)。 - 开流前的 HTTP 状态集合与先后顺序不变:401 → 423 → 400(请求体)→ 503(读会话失败)→ 404 → 409(
session_not_consultation)→ 409(会话模型已变)→ 503(模型不可用)→ 409(旧校正入口)→ 400(blocksPromptExtraction)。并行后按这个顺序判定返回,不得因为谁先回来就先报谁。 - 删号冻结(423)必须在任何扣点、写库之前判定(
account-deletion.test.tsx的if (frozen) return frozen;)。 readVersion缓存:只缓存成功解析的结果;TTL ≤ 15 s;invalidateLanguageModelCatalog()同时清它;不缓存 DNS 失败或非公网地址的结果;不改assertAllowedModelProviderUrl与requestAllowedModelProvider。- 多领域并发开关打开时:
executions顺序必须等于计划顺序(不得按完成先后);任一领域失败的处理与串行一致(整次计算失败、错误码不变);429 不得被吞或改码;MAX_CONSULTATION_DOMAINS、domainFitsRunBudget的公式与常量不变。 - 不动表结构、不加迁移;不改
scripts/jyotish_api_server.py;不碰frontend/src/app/(app)/page.tsx;不改.gitea/workflows/**;不升级依赖;不顺手修 warning。 - 改既有断言写「原值 / 新值 / 原因」三栏;不得为让测试过而弱化断言。开工先 grep(AGENTS.md §7.8):
已知会受影响:
grep -rn "await moderate(visibleQuestion\|await resolveSessionLanguageModel\|return await readVersion\|refuseWhenAccountDeletionPending\|const runTurn = async\|for (let index = 0; index < domains.length" frontend/tests testsfrontend/tests/content-moderation.test.ts(约 242–243 行:await moderate(visibleQuestion, "input"存在且在reserve_consultation_usage之前)、frontend/tests/model-catalog.test.ts(约 48–49 行:return await readVersion(...)、await resolveSessionLanguageModel()、frontend/tests/application-billing-contract.test.ts(约 160 行)、frontend/tests/consult-first-frame-and-pacing-20260928.test.tsx(256–289 行两张清单)、frontend/tests/account-deletion.test.tsx(约 111–122 行)、frontend/tests/consultation-agentic-runtime.test.ts(领域循环相关用例)。 - 输出逐字节一致:开关全关时,
/api/consult的 NDJSON 事件序列与77057a819相同;开关全开时,除新增的consultation-prep-v1可观测日志外,响应体与落库内容与全关相同(T7 差分验证)。 - 公开仓库:A/B 记录、PROGRESS 只写虚构盘或公开名人盘;不写真实用户资料、key、日志原文。
6. 任务分解
所有新开关在 frontend/src/lib/consult/prep-flags.ts(新建目录 frontend/src/lib/consult/,后续 consult-route-split 单沿用)集中读取,每个请求读一次 process.env,非法值按默认处理并 console.warn 一次。
| 开关 | 取值 | 本单代码默认 | A/B 后的默认 |
|---|---|---|---|
CONSULT_PREP_PARALLEL |
on / off |
off |
按 T7 判定 |
MODEL_VERSION_CACHE_TTL_MS |
0~15000 的整数,0 关 |
0 |
按 T7 判定(开则 15000) |
CONSULT_DOMAIN_CONCURRENCY |
1 / 2 |
1 |
按 T7 判定 |
默认值的改动(若 A/B 达标)只改
prep-flags.ts里的常量,单独一个提交,便于回滚。
T1 · 开流前并行(BUG-1337 的一半)(Grok 自跑 / Claude 验收)
- 新建
frontend/src/lib/consult/turn-prep.ts,导出loadConsultTurnHead(deps):输入为注入的依赖(getUser、readPendingDeletion、readBody、readSession、resolveModel),返回{ ok: false, response }或{ ok: true, user, parsed, chatSession, sessionModel }。route.ts::POST用它替换第 301–377 行的判定段,NextResponse.json(...)的文案、状态码、code原样搬过去(逐字)。 CONSULT_PREP_PARALLEL=on:拿到user后同时启动「删号检查」与「读请求体 → 读会话 → 解析模型」两条链;然后按 §5.2 的顺序依次检查结果再返回。off:保持今天的串行顺序(同一个函数里两条代码路径,便于 A/B)。- 注意:并行时请求体非法、会话不存在等情形下仍会多跑一次删号检查(只读),可接受;但不得在冻结账户上调用任何写操作。
- 验收:新增
frontend/tests/consult-turn-prep-20261013.test.ts:用假依赖(每个依赖带可控延迟和返回值)覆盖 §5.2 每一种返回,并断言两种开关下状态码与 body 逐字段相同;on时总耗时 ≈ max(删号, 会话 + 模型) 而非相加(用假时钟或延迟 50/80/80 ms 断言 < 230 ms);冻结账户 + 非法请求体时返回 423。 - 命令:
./node_modules/.bin/tsx --test tests/consult-turn-prep-20261013.test.ts
T2 · 输入审核不挡首帧,仍在预留前生效(BUG-1337 的另一半)(Grok 自跑 / Claude 验收)
CONSULT_PREP_PARALLEL=on:在第 424 行原位置(所有开流前检查都通过之后)启动moderate(...)但不await,得到inputModerationPending;进入runTurn后:prepareConsultationRoute的beforeReserve回调第一行await inputModerationPending,非pass时recordModerationEvent(只记一次),block时抛专用错误ConsultationInputBlockedError;prepareConsultationRoute的catch里先看审核结论:若为block,无论 prepare 抛的是什么,都返回今天那个 400(code: "content_blocked"、inputBlockedCopy(...))——保持「审核拦截优先于资料错误」的旧优先级;- legacy 运行时(
shouldUseAgenticRuntime为假)同样走这段,仍是直接 HTTP 400。
off:维持第 424 行await原样。- 首帧之后被拦的请求,客户端收到
run.failed/request_rejected/status: 400/reason: "content_blocked",use-consultation-run.ts的 4xx 回滚已有,不改客户端。自伤类拦截文案(MODERATION_SELF_HARM_COPY)必须原样到达用户(断言事件message)。 - 验收:
- 新测试:审核挂起 1.5 s 时首帧已写出(参照
consult-first-frame-and-pacing-20260928.test.tsx的onFirstFrame用例);审核block时reserve假函数调用 0 次、模型假函数调用 0 次;审核block+ 资料读取抛ConsultationProfileTruthError时返回content_blocked;自伤文案逐字到达。 content-moderation.test.ts约 242–243 行按三栏改:原值「源码含await moderate(visibleQuestion, "input"且位置早于reserve_consultation_usage」→ 新值「beforeReserve内await inputModerationPending早于input.reserve()(consultation-route-service.ts里beforeReserve先于reserve已有保证)+ 行为测试:block 时 reserve 0 次」。原因:审核从开流前挪到流内、预留前。新值必须比原值更强(有行为断言)。
- 新测试:审核挂起 1.5 s 时首帧已写出(参照
T3 · readVersion 缓存(BUG-1338)(Grok 自跑 / Claude 验收)
model-catalog.ts:新增模块级Map<"modelId:version", { expiresAt, model }>(挂globalThis,同jyotishaModelCatalogCache写法)。resolveSessionLanguageModel绑版本分支:TTL > 0 且命中未过期 → 直接返回;否则readVersion,只有非 null 成功结果才写缓存。invalidateLanguageModelCatalog()同时清空它。- 不缓存:
readVersion抛错(return null路径)、resolveRow返回 null(解不开 key)、assertAllowedModelProviderUrl失败。 - 验收:
frontend/tests/model-catalog.test.ts加行为测试(注入假queryCatalog与假 DNS):TTL 内第二次不查库不解析 DNS;过期后重查;invalidateLanguageModelCatalog后重查;失败不缓存;TTL=0 每次都查。约 48 行return await readVersion(modelId, configVersion)按三栏改(原值 / 新值readVersion结果经缓存写入 / 原因)。管理端model-mutation-handler.ts的invalidateCatalog调用点(保存供应商、发布、回滚)不变,断言它们仍在。 - 说明 SSRF 结论(§2.2)写进 PROGRESS 与 BUG-1338「防复发」:TTL 不得超过目录缓存。
T4 · 流内读标题与分类并行(BUG-1337)(Grok 自跑 / Claude 验收)
- 第 741 行读
chat_sessions.title改为在append_consultation_question成功后立即启动,与classifyConsultationTurn并行,在原使用点await。分类本身的位置不动(仍在预留和 append 之后,§4 决策 5)。 - 验收:源码断言读标题在 append 之后启动;行为不变(
session-title-persist-contract.test.ts、consultation-smalltalk.test.ts全绿)。
T5 · 多领域并发开关(BUG-1339)(Grok 自跑 / Claude 验收)
createConsultationTools的领域循环:CONSULT_DOMAIN_CONCURRENCY=2时,对executableDomainPlan截断后的领域同时发起runWorkflow,用Promise.all收齐后按计划顺序组装executions;chart-calculation活动句按今天的chartCalculationProgressLabel(index + 1, domains.length)在发起时依次写出。任一失败 → 与串行一样整次失败、错误码不变。=1时代码路径与今天逐字相同。- 并发时
domainFitsRunBudget不再逐个判断(所有领域同时开始),但领域数仍受MAX_CONSULTATION_DOMAINS截断;在 PROGRESS 里写明这一点。 - 验收:
consultation-agentic-runtime.test.ts加用例:假runWorkflow让第二个领域先完成,executions/ 证据卡 /workflowReceipt与串行逐字段相同;一个领域 429 时错误码为workflow_rate_limited(与串行同);开关为 1 时调用顺序严格串行。 - 写
frontend/scripts/research/consult-domain-concurrency-ab.py(或.mts):本地起 Python API(PYTHONHASHSEED=0,带 vedastro SDK 的 Docker 镜像一组、不带一组),公开 AA 盘 3 张 × 领域对 2 组(career,marriage、parents,health)× 冷/热缓存,串行 vs 两路并发,每格 3 次取中位数;另跑「两个用户同时问两领域」的争用场景,统计 429 次数。差分:同盘同分钟两种方式的领域响应,忽略字段清单 =consult_card_facts.timing.*、chart.runtime_cache.*,并对module_execution_audit、vargottama等集合派生列表排序后比较,要求其余逐字节一致。
T6 · 准备阶段计时(Grok 自跑 / Claude 验收)
- 在
route.ts现有logAgentObservability体系里加一个事件agentVersion: "consultation-prep-v1",contractPhases记:prep.auth、prep.deletion、prep.session、prep.model、prep.moderation、prep.subject(prepare 到 beforeReserve)、prep.reserve、prep.append、prep.classify、prep.title,以及开关取值(写进phase名或已有可用字段,不新增 schema 字段)。只记毫秒和状态,不含问题原文、出生资料、用户标识以外的任何字段(用户标识沿用现有规则)。 - 验收:测试断言事件通过
agentObservabilityEventSchema,且植入的问题原文不出现在日志里(参照 BUG-1232 的写法)。
T7 · A/B 验证(Grok 跑能跑的;需要 staging 的由 Claude)
记录写 docs/testing/consult-prep-parallel-ab-20261013.md。
| 项 | A | B | 输入 | 度量 | 判定门槛(默认开启的条件) |
|---|---|---|---|---|---|
开流前并行 + 审核挪后(CONSULT_PREP_PARALLEL) |
off |
on |
进程内基准:用 T1/T2 的假依赖,延迟取真实中位数(Grok 在本机 Docker 里对本地 Postgres 量 getUser/删号 RPC/读会话/readVersion 各 30 次取中位数;阿里云审核无 key 时按 300 ms 与 1,500 ms 两档模拟);每组 30 次 |
首帧毫秒(onFirstFrame)、准备总毫秒、各段毫秒 |
首帧中位数降 ≥ 20% 且 §5.2 全部返回逐字段一致、被拦请求 reserve 0 次 → 默认 on;否则保持 off |
readVersion 缓存 |
TTL 0 | TTL 15000 | 同上进程内基准 + 本地 Postgres 真查;每组 30 次 | prep.model 毫秒、SQL 次数、DNS 解析次数 |
prep.model 中位数降 ≥ 50%、TTL 内 SQL 与 DNS 次数为 0、失效/停用 15 s 内生效(测试证明)→ 默认 15000 |
多领域并发(CONSULT_DOMAIN_CONCURRENCY) |
1 | 2 | T5 的脚本 | 两领域墙钟中位数、Python 进程 CPU 秒、峰值内存、争用场景 429 次数、差分结果 | 带 vedastro SDK 的镜像里墙钟中位数降 ≥ 25%,且争用场景 429 次数不多于 A,且差分一致 → 默认 2;任何一条不满足保持 1。本机起草实测只降约 10%(§2.3),预计不达标,如实记录即可 |
| staging 实测(Claude 验收时) | 部署前 7 天 consultation-stream-first-v1 first_byte 中位数 |
部署后同口径 + consultation-prep-v1 各段 |
真实流量或产品负责人用测试账号 10 轮 | first_byte、prep.* |
只做核对,不改默认;数字写进同一份记录 |
每组至少重复 3 次取中位数(上表已写更多次的按表);正确性部分全部要求差分一致,不是「看起来一样」。
T8 · 记录(Grok)
docs/BUG_HISTORY.md:- BUG-1337:普通对话开流前与流内的准备步骤无依赖却串行(删号检查、读会话、解析模型、开了阿里云时审核挡首帧最多 1.5 s、读标题)。
- BUG-1338:会话模型按版本解析每轮查库 + 解密 + DNS,无缓存(目录路径早有 15 s 缓存)。写清「版本不可变只对了一半」与 SSRF 结论。
- BUG-1339:多领域串行;并发开关 + A/B 结论(若不达标,状态写
closed_by_design或investigating并附数字,不写 resolved)。
docs/tasks/PROGRESS-consult-prep-parallel-20261013.md:每个 T 的结果、三栏改断言表、A/B 数字、开关最终默认值及依据。CHANGELOG.md:若有默认开启的开关,写一句「发问后更快出现第一行」之类用户可感知变化;全部默认关则不写。frontend/DESIGN.md:不改(无 UI 变化)。BLOCKED.md:缺阿里云 key、缺 staging 实测时各记一条。
7. 让步顺序
时间不够时按此顺序砍(先砍最后的):T5(多领域并发,预计不达标)→ T4 → T6 的细分段(保留 prep.moderation 与 prep.model)。T1、T2、T3、T7 中对应 A/B 与 T8 不可砍。
8. 验收命令分工
| 谁 | 命令 |
|---|---|
| Grok 自跑 | cd frontend && ./node_modules/.bin/tsc --noEmit && npm run lint;单文件:./node_modules/.bin/tsx --test tests/consult-turn-prep-20261013.test.ts tests/content-moderation.test.ts tests/model-catalog.test.ts tests/application-billing-contract.test.ts tests/consult-first-frame-and-pacing-20260928.test.tsx tests/account-deletion.test.tsx tests/consultation-agentic-runtime.test.ts tests/session-title-persist-contract.test.ts tests/consultation-smalltalk.test.ts(看 # fail 与 # cancelled 都为 0、退出码 0);T7 进程内基准与 T5 脚本。前端全量、Python 快速门、整站构建不跑 |
| Claude 验收 | 上述全部 + 前端全量 npm test 与基线逐名比对 + next build(/ 仍 Static,首屏 gzip ±2%)+ 复跑 T7 基准与 T5 脚本 + staging 部署后 first_byte 对比 |
test:db |
不需要(不动表、不改数据库访问层查询语句);若 T3 改了 queryCatalog 的 SQL 文本则必须跑并按 tools/agent-loop/windows-known-failures.txt 比对 |
9. 开工前置命令
git fetch origin --prune
git worktree add -b codex/consult-prep-parallel-20261013 .worktrees/consult-prep-parallel-20261013 origin/staging
cd .worktrees/consult-prep-parallel-20261013
git log -1 --format=%h # 应为 77057a819 或其后代
grep -n "^## BUG-" docs/BUG_HISTORY.md | tail -3 # 核对最大号,本单用 1337~1339
cat frontend/src/lib/personal-report-writer-flag.ts # 确认仍为 false(决策 4 的前提)
grep -rn "await moderate(visibleQuestion\|await resolveSessionLanguageModel\|return await readVersion\|refuseWhenAccountDeletionPending\|const runTurn = async\|for (let index = 0; index < domains.length" frontend/tests tests
10. BUG 编号起点
BUG-1337(写作时最大号 BUG-1317;1335、1336 已分配给其它单)。开工时若 1337~1339 已被占用,顺延并在 PROGRESS 写明。
11. 交付物清单
- 代码:
frontend/src/lib/consult/prep-flags.ts、frontend/src/lib/consult/turn-prep.ts(新)、route.ts、model-catalog.ts、consultation-tools.ts - 测试:
frontend/tests/consult-turn-prep-20261013.test.ts(新)及 §5.7 列出文件的三栏修改 - 脚本:
frontend/scripts/research/consult-domain-concurrency-ab.*、T7 进程内基准脚本(同目录) - 文档:
docs/testing/consult-prep-parallel-ab-20261013.md、docs/tasks/PROGRESS-consult-prep-parallel-20261013.md、docs/BUG_HISTORY.md(BUG-1337~1339)、视结果CHANGELOG.md、BLOCKED.md - 不改:
frontend/DESIGN.md、数据库迁移、scripts/jyotish_api_server.py
12. 环境缺口(预告)
- 阿里云审核真实延迟需 key,无 key 用 300 / 1,500 ms 模拟,写进
BLOCKED.md。 - staging 前后对比只能在部署后由 Claude 做。
- 多领域并发的生产收益取决于 vedastro SDK 的网络等待,Grok 的 Docker 镜像要与生产同版本才算数。