fix(web): keep consultation conclusions across turns and surface cache hits (BUG-555, BUG-556)
Session history was silently clipped to the first 4000 characters of the last 12 messages, so follow-ups could not see timing or audit tables. Keep an append-only tail plus a checkpoint summary, retry overflow in the same request, and expose cache hit rate in admin usage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8565,4 +8565,36 @@
|
||||
- 复发自:无
|
||||
- 修复版本:`a1956deb`
|
||||
|
||||
## BUG-555 | 普通咨询历史只留每条开头 4000 字,下一轮看不到应期和审计表
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-09-06
|
||||
- 最近更新:2026-09-06
|
||||
- 影响面:`POST /api/consult`、`consultation-session-history.ts`、`session-context-summary.ts`、`chat_sessions.context_summary`
|
||||
- 用户现象:追问「你上次说的应期」时,模型像没看过上一轮。Level 2 报告的应期、综合判断和技法审计表在下一轮消失,也没有任何截断提示。上下文超限时走普通失败,没有缩窗重试。
|
||||
- 触发条件:普通咨询多轮追问;单条助手回复超过 4000 字;或累计历史超过模型窗口。
|
||||
- 根因:历史窗口写死为最近 12 条 × 每条开头 4000 字,不知道模型 `context_window`,也不保留结论。超限错误没有识别为 `context_overflow`。
|
||||
- 修复:历史改为摘要之后的 append-only 尾巴,按模型窗口算预算,超长从最旧整条丢。单条上限 12,000 字并写明省略字数。尾巴超过 16,000 字时在结算后异步做检查点摘要(不扣点、15 秒 ref 超时、乐观并发)。识别上下文溢出后同一请求内缩到「摘要 + 最后一对」重试一次。
|
||||
- 验证:`frontend/tests/consultation-session-history.test.ts`、`session-context-summary.test.ts`、`consultation-context-cache-contract.test.ts`。
|
||||
- 防复发:咨询历史不得再按固定 12 条 × 头部截断静默砍结论。摘要超时不得用 `AbortSignal.timeout()`。溢出重试不得新增用户可见等待态。
|
||||
- 相关记录:BUG-523
|
||||
- 复发自:无
|
||||
- 修复版本:待发布
|
||||
|
||||
## BUG-556 | 缓存命中已写入账本,后台看不到;Anthropic 历史没有第二断点
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-09-06
|
||||
- 最近更新:2026-09-06
|
||||
- 影响面:`api/admin/usage/aggregate`、`api/admin/usage`、定价测算页、用量列表、`cachedHistoryMessage`、`skill-binding` 共享方法段
|
||||
- 用户现象:后台用量页没有缓存命中率或缓存读 tokens。Anthropic 只缓存了系统块,历史每轮全价重算。跨领域不变的 Full-spectrum 与 Event judgment skeleton 每次跟着工具结果重发。
|
||||
- 触发条件:普通咨询跑过后打开用量聚合或用量列表;使用 Anthropic 或多领域咨询。
|
||||
- 根因:`complete_usage` 已把 `metadata.cache` 写入 `usage_ledger`,聚合与列表都不读。历史消息没有 Anthropic `cacheControl`。共享方法段放在工具结果里,不在可缓存系统块。
|
||||
- 修复:聚合按模型给出 7 天 / 30 天命中率与读写未命中;列表加「缓存读 tokens」。历史尾巴最后一条在 Anthropic 上打第二断点。共享两段搬进 `<jyotish-shared-method>`。
|
||||
- 验证:`frontend/tests/admin-usage-aggregate-contract.test.ts`、`agent-generation-settings.test.ts`、`skill-binding.test.ts`、`consultation-methodology.test.ts`。
|
||||
- 防复发:有 `metadata.cache` 的运行必须进入命中率分母;`readTokens = 0` 计未命中。无缓存数据的行不得进分母。系统块 9 段节选与 `providesSkillDiscovery: "on-demand"` 不得回退。
|
||||
- 相关记录:BUG-555
|
||||
- 复发自:无
|
||||
- 修复版本:待发布
|
||||
|
||||
|
||||
|
||||
@@ -279,3 +279,9 @@ Prevention: make the gateway request official raw evidence without relying on an
|
||||
`manman-linux` runner 以 `:host` 模式注册在一台同时运行 Nacos/MySQL/Redis/xxl-job 的跳板机上,质量门每次 push 都在该宿主机本地 build 两个按 SHA 打标的镜像且从不回收,累积 1453 个镜像后根分区 99G 用满 94G、Avail 归零。`docker compose up -d --wait postgres` 阶段 `initdb` 报 `No space left on device`,23 个数据库集成测试级联失败,`publish` 连续 5 次 skipped,`staging` 上 4 个提交无法发布。ERR-103 当初据以搬迁的「`xiaoxin` 缺少 Docker Compose v2」是误诊:真实原因是 `xiaoxin` 的 `/root/.docker/cli-plugins/docker-compose` 有一个零字节文件遮蔽了系统插件(用户级插件目录优先),Docker Engine 本身一直正常。
|
||||
|
||||
Prevention: 构建与测试 runner 不得与其他服务共用宿主文件系统;门禁必须在开跑前自行回收磁盘并在余量不足时 fail-closed 报出磁盘原因,而不是让下游 fixture 代为暴露。判定某台 runner「不支持 Docker Compose」之前,必须先看 `docker info` 的 client plugins 报错与用户级 `~/.docker/cli-plugins` 是否存在遮蔽文件;把能力缺失归因到整台机器会导致错误的迁移决策,本次代价是发布中断。
|
||||
|
||||
## ERR-106 | 本机 Docker 用户网络地址池耗尽,`test:db` compose fixture 无法建网 | observed 2026-09-06
|
||||
|
||||
`docker compose --project-name jyotisha-postgres-* up -d --wait postgres` 报 `failed to create network …_app: all predefined address pools have been fully subnetted`。Docker daemon 正常,本机同时存在着十余个遗留 `jyotisha-postgres-*-postgres-1` 与 `jyotisha-local-preview-postgres-1`。`npm run test:db --test-concurrency=1` 仍每测新建 compose 项目网络。咨询上下文单未做 `docker network prune`(会清共享宿主资源)。
|
||||
|
||||
Prevention: 需要真实库测时先清本任务自己的 fixture 网络,或在默认 `bridge` 上起一次性 Postgres 做迁移语法检查;不得把地址池耗尽写成「本机无 Docker」。新迁移至少用 `psql --set ON_ERROR_STOP=1 -f` 在可写实例上跑通 ALTER/GRANT。
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# PROGRESS · 普通对话上下文窗口与模型缓存(2026-09-06)
|
||||
|
||||
工作树:`.worktrees/consultation-context-and-cache-20260906`
|
||||
分支:`codex/consultation-context-and-cache-20260906`
|
||||
基线:任务书写 `origin/staging` @ `d8a0f615`;开工时 `origin/staging` 已到 `9ce7a374`(含 BUG-551/552/553),以当时远端为准。
|
||||
|
||||
未改 `page.tsx`、生时校正链路、Skill 正文、引擎。客户端零改动。迁移只加 `chat_sessions.context_summary`。未 bump Skill。
|
||||
|
||||
| 任务 | 状态 | BUG |
|
||||
| --- | --- | --- |
|
||||
| 5.1 摘要 + 尾巴历史 + 按模型预算 + 溢出重试 | 完成 | BUG-555 |
|
||||
| 5.2 后台缓存可见 | 完成 | BUG-556 |
|
||||
| 5.3 Anthropic 历史断点 | 完成 | BUG-556 |
|
||||
| 5.4 共享方法段搬进系统块 | 完成 | BUG-556 |
|
||||
| 5.5 BUG_HISTORY / CHANGELOG / testing | 完成 | — |
|
||||
|
||||
## 实现要点
|
||||
|
||||
- 历史 = `context_summary` 之后的全部消息。超 `historyBudgetChars` 时从最旧整条丢;单条 12,000 字截断并写省略标记。
|
||||
- 摘要在最后一条用户消息、时间行之后、问题之前。检查点在 `complete_consultation_response` 成功后异步触发,不扣点,15 秒 ref 超时,乐观并发。
|
||||
- 溢出识别后同一请求把历史缩到摘要 + 最后一对再 `stream` 一次。咨询公共事件是封闭联合,不能发 rectification 的 `attempt.reset`,所以重试留在服务端。
|
||||
- 用量聚合按 `actual_model_id` 给出 7/30 天缓存命中;列表加 `cacheReadTokens`。`costMicrousd` 计价口径未改。
|
||||
|
||||
## 系统块 / 工具结果字节
|
||||
|
||||
| 块 | 之前 | 之后 | 变化 |
|
||||
| --- | --- | --- | --- |
|
||||
| `jyotishSkillMethodBlock` | 28,877 B | 35,323 B | +6,446 B(任务预期 +6.4 KB) |
|
||||
| `<jyotish-shared-method>` | 无 | 6,396 B | Full-spectrum + Event judgment skeleton |
|
||||
| 单领域 career `methodology.sections` 正文 | 约 13.4 KB(含上述两段) | 7,043 B | 只留 baseline + 领域两段 |
|
||||
| 三领域 career/marriage/wealth 正文 | 约 25.3 KB | 18,914 B | 同上 |
|
||||
|
||||
9 段运行时节选与 `providesSkillDiscovery: "on-demand"` 未回退。
|
||||
|
||||
## 既有断言改动
|
||||
|
||||
| 文件 | 原值 | 新值 | 原因 |
|
||||
| --- | --- | --- | --- |
|
||||
| `consultation-session-history` | 最近 12 条 × 头部 4000 字 | 按模型预算从最旧丢整条;单条 12,000 + 省略标记 | BUG-555 静默砍结论 |
|
||||
| `consultation-methodology` | sections 含 Full-spectrum / Event judgment skeleton | 这两段在系统块;领域清单仍随工具结果 | 工具结果不在缓存前缀里 |
|
||||
| `chat-session-authority` / consult select | `...,chart_profile_role` | 另加 `context_summary` | 检查点读写 |
|
||||
| `application-billing-contract` | 三处 `usages.push(result.totalUsage)` | 一处,在 `streamWithOverflowRetry` | 溢出重试共用首次 stream |
|
||||
| `admin-usage-aggregate-contract` | 只有 30 天功能用量 | 另加 cache 7/30 天窗口 | BUG-556 命中率不可见 |
|
||||
|
||||
## 测试
|
||||
|
||||
| 命令 | 结果 |
|
||||
| --- | --- |
|
||||
| 本单相关 `npx tsx --test`(history / summary / cache contract / methodology / skill-binding / generation-settings / admin usage / onboarding-route) | 58 pass / 0 fail / cancelled 0;摘要超时用例 cancelled 0 |
|
||||
| `./node_modules/.bin/tsc --noEmit`(先删 `.next`) | 0 错 |
|
||||
| 改动文件 eslint `--quiet` | 0 error |
|
||||
| `page.tsx` | 未改 |
|
||||
| `npm run build -- --webpack` | 编译通过;类型检查停在既有 `dossierResponse` 路由导出(与 BUG-551/553 相同),未打印 `○ /` |
|
||||
| 全量 `npx tsx --test tests/*.test.ts`(排除 `tests/database-*.test.ts`) | 2726 tests / 2712 pass / 14 fail:Docker 地址池耗尽 + 1 条 onboarding 超时竞态(单跑通过) |
|
||||
| `npm run test:db` | **blocked**:见 `BLOCKED.md`。迁移在一次性 Postgres 上经 stub 表语法检查通过 |
|
||||
|
||||
## 收尾限制
|
||||
|
||||
- 溢出重试只包住 `agent.stream()` 抛错;流开始之后供应商才报超窗时不会第二次缩窗(咨询公共事件不能发 `attempt.reset`)。
|
||||
- 摘要失败只 `console.warn("session context summary failed", error)`,不打印摘要正文。
|
||||
- `costMicrousd` 仍按全价输入单价。
|
||||
@@ -0,0 +1,40 @@
|
||||
# Staging 人肉复核 · 普通咨询上下文与缓存(2026-09-06)
|
||||
|
||||
给产品负责人。不要把真实出生资料、真实用户问题或对话正文写进任何记录。试用问题用虚构句,例如「半年内换工作时机」。
|
||||
|
||||
对应 BUG-555、BUG-556。测之前先做第 0 条。部署前需先跑 Gitea `Migrate Staging Database`(本迁移与已积压的 `5010000_personal_report_longform_appendices.sql` 一起)。
|
||||
|
||||
## 0. 确认测的是新版本
|
||||
|
||||
浏览器打开 `https://staging.jyotisha.chat/api/health`,看 `deployment.gitCommit` 前 8 位是否等于本单合入 staging 后的提交。不一致 = 先别测。
|
||||
|
||||
## 1. 多轮之后还能对上前面的应期(P0)
|
||||
|
||||
1. 新建一次普通咨询(不要用生时校正)。
|
||||
2. 用虚构问题连续问 6 轮以上,其中至少一轮要出带应期的 Level 2 报告。
|
||||
3. 再问:「你前面说的应期是哪年」。
|
||||
|
||||
- ✅ 预期:答案与前面报告里的应期一致,或明确说那一轮被收进会话摘要。界面没有新的「加载中」态。
|
||||
- ❌ 失败:模型像没看过上一轮;或出现截断却完全不提应期。
|
||||
|
||||
## 2. 后台用量看得到缓存(P0)
|
||||
|
||||
1. 在 staging 用同一会话至少跑 3 轮普通咨询。
|
||||
2. 打开后台用量 / 定价测算页。
|
||||
|
||||
- ✅ 预期:有「缓存命中」表(模型 / 运行数 / 命中率 / 缓存占比 / 读 / 写 / 未命中)。用量列表有「缓存读 tokens」列。没有缓存数据的模型不进命中率分母。
|
||||
- ❌ 失败:表不存在,或命中率把没有 `metadata.cache` 的运行算进去。
|
||||
|
||||
## 3. 三家供应商对照(P1)
|
||||
|
||||
同一套虚构追问,分别用 DeepSeek、OpenAI、Anthropic 各跑一条(后台有配置的才测)。
|
||||
|
||||
- ✅ 预期:三家都能正常回答。Anthropic 多轮后缓存读 tokens 可以大于 0。OpenAI / DeepSeek 不因多了标记而失败。
|
||||
- ❌ 失败:某家因历史缓存标记报错;或 Anthropic 多轮后缓存读永远是 0 且系统块断点也不在。
|
||||
|
||||
## 4. 超长对话仍是一次等待(P1)
|
||||
|
||||
用已经很长的会话再发一条虚构追问。
|
||||
|
||||
- ✅ 预期:用户仍只看到一次等待、一次揭幕。失败时仍是原来的失败文案,不会出现第二次转圈。
|
||||
- ❌ 失败:失败后自动又转一圈,或页面上出现新的重试提示。
|
||||
Reference in New Issue
Block a user