Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

tracking: LLM 适配器线协议与 usage(3 项)—— 坏请求/坏数值被固化

Đang mở
#193 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
15/100
Loại issue
Lỗi
Độ rõ ràng
Cần làm rõ
Mức độ hoạt động
Sôi nổi
Công nghệ
rust
Lĩnh vực
ai, backend

Hướng nghiên cứu

This is a tracking issue that produces no code itself; the work sits in sub-issues #163, #157 and #168, which must land in order. Start with the usage-normalization sub-issue #163: read the usage mapping in src/llm/anthropic.rs, the threshold check in src/compact/mod.rs, and the cost logic in src/llm/pricing.rs. Done means the acceptance items listed in the issue pass, such as prompt_tokens matching total input in the adapter test near lines 1530-1540.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

优先级 P0 · 依赖:无 · 类型:tracking(簇设计入口,不产出代码;子单全部落地后关闭)
记号:正文中的「不变量 #N」指 .dev/AGENTS.md 编号,不是 issue 编号;#150 这类才是 issue。本簇子单头部写 depends-on: #N 表示落地串行(同文件/同契约),不会与语义阻塞混淆。

基线 HEAD = 14bac15a(子单同基线)。本单来自一次全仓审计:先按根因归簇,再拆成可独立验收的子单。

共同根因

三条都在适配器边界上,且都会把一次坏请求/坏响应固化成长期故障:

  1. usage 未归一化(#163):Anthropic 的 input_tokens 只算未命中缓存的部分,而适配器只把它赋给 prompt_tokens(src/llm/anthropic.rs:663-667、:1314-1324;适配器自己的测试 :1530-1540 就显示「总输入 150、prompt_tokens=100」)。后果两条:src/compact/mod.rs:139-143 只要 actual > 0 就用 token 阈值并完全跳过字节回退 → 缓存开启后主动压缩永不触发,直到撞上下文 400;src/llm/pricing.rs:198-210 在 cache_hit_tokens == 0 时按 prompt_tokens 计费 → 首次请求(缓存写 = 整段前缀)完全不计成本,命中时缓存写也只按输入价(实际 1.25×)。
  2. 请求里出现非首位 system 角色(#157):extract_system_message 只看 messages.first()(anthropic.rs:948-956),其余 System 由 serialize_message 输出 "role":"system"(:1146-1151)。Anthropic 只接受 user/assistant。三种触发:deferred tools 块被 prepend 在系统提示之前(src/run_core.rs:1180-1182,api.anthropic.com 默认满足 supports_deferred_tools,而 TodoWrite 恒注册)→ 每次请求都 400;压缩后重注入的 System 附件(src/runtime.rs:895-969);技能 glob 注入(run_core.rs:795-800)。现有 tests/deferred_tool_loading.rs 用不校验 role 的 mock,测不出。
  3. 中断落盘空 assistant 消息(#168):have_partial 把「只有 tool_call 增量、无文本」也算数(src/llm/openai.rs:851-866、anthropic.rs:555-569),随后把 tool_calls 清空(openai.rs:886-902)并 push 一条空 content 的 assistant 消息(src/run_core.rs:1716-1717);Anthropic 序列化会发出空文本块(anthropic.rs:1183-1187),而 #16 的空 content 守卫只在 tool_calls 非空时生效(openai.rs:1240)→ 此后每一轮都 400。

子单与依赖

子单 依赖 范围
#163 无 usage 归一化(prompt = input + cache_read + cache_creation)+ pricing 按 hit/miss 计费(含 cache-write 倍率)
#157 depends-on: #163 所有 System 角色进顶层 system(或转 user reminder);deferred 块放到系统提示之后
#168 depends-on: #157 只在有文本时保留半成品;空 content 且无 tool_calls 的 assistant 消息在落盘与序列化两侧都跳过

落地思路

  1. 顺序有讲究:#163 不动请求形状,先落它可以让 A 簇的压缩触发判据(#162 → #158 → #156)建立在正确数值上;#157 改请求形状,需同步改 tests/deferred_tool_loading.rs 的断言与新增「无 messages[*].role == system」的形状测试;#168 跨 openai.rs/anthropic.rs/run_core.rs,放最后避免与前两者抢同一处序列化代码。
  2. #163 一处改动两处受益(压缩触发 + 计费),若评审要求拆单,可拆 usage 归一化与 pricing 费率两单,但必须同批落地——只改一处会造成重复计费或漏计。
  3. pricing.rs 的改动要与归一化后的 prompt_tokens 语义对齐(保持「hit + miss == 总输入」),并给 ModelPricing 预留 cache-write 倍率字段。
  4. Anthropic 的 400 结论基于官方 role 约束 + 序列化代码路径,未打真 API 验证(无凭据);#157 建议在验收里要求:新增请求形状测试 + 有条件时打一次真实端点。

簇级验收

  1. anthropic.rs:1530-1540 的期望改为 prompt_tokens == 总输入;
  2. cache_read=0, cache_creation=40_000, input=50 时成本不为 0;缓存场景下 should_compact 能按真实上下文触发;
  3. 序列化后 messages[*].role 只出现 user/assistant,System 文本全在顶层 system;tests/deferred_tool_loading.rs 增加 role 断言;
  4. tool_call 阶段断流后 transcript 无空 assistant 消息,后续构造的请求体不含空文本块。

边界

不含 #90 已落地的 prompt cache 开启策略本身;不含其它 provider(OpenAI 兼容侧)的 usage 语义——若发现同型问题,另开单。

实施顺序、PR 拆分与验收矩阵(补充 · 2026-10-10)

顺序(必须串行,前两条都改 src/llm/anthropic.rs)

波 子单 理由
W1 #163 只改 usage 数值与计费,不动请求形状;先落它,A 簇 #162 → #158 → #156 的压缩触发才建立在正确数值上
W2 #157 改请求形状(role 与 system 位置),需要同步改 tests/deferred_tool_loading.rs 的断言
W3 #168 跨 openai.rs/anthropic.rs/run_core.rs,且要处理"空 content 消息"这一新判据,放最后避免与 #157 抢同一处序列化代码

与 A 簇的交互(重要):#163 与 A 簇 #162 是"输入正确性"的两半——#162 保证用单次读数,#163 保证读数本身包含缓存 token。两单落地前不要调 default_compact_threshold_tokens。

跨簇文件重叠:src/llm/anthropic.rs 被 #163/#157/#168 共用(同簇内串行即可);src/runtime.rs 不在本簇范围(#163 只改适配器与 pricing)。

PR 拆分:#163 若被要求拆成"usage 归一化"与"pricing 费率",必须同一批合并(只改一处会造成重复计费或漏计),建议单 PR 两 commit。

簇级验收 → 测试映射

验收 落点
prompt_tokens == 总输入 改 src/llm/anthropic.rs:1530-1540 的期望
冷缓存步成本 ≠ 0 src/llm/pricing.rs 新增单测
缓存场景能触发压缩 src/compact/mod.rs 或 runtime 级用例(结合 A 簇 #162 的 runtime 测试)
无 messages[*].role == "system" 新增请求形状测试(anthropic.rs 单测 + tests/deferred_tool_loading.rs 加 role 断言)
中断后无空 assistant 消息 src/run_core.rs/tests/ 新增(mock 流先发 tool_call 增量再断流)
Ngôn ngữ chính
Rust
Star
4
Fork
0
Merge trung bình
5 giờ 32 phút
Pull request đã merge (30 ngày)
7

Chuẩn bị môi trường

  • Có Dockerfile hoặc tệp Docker Compose
  • Không có mẫu pull request
  • Không có hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của jeffkit/recursive

Tất cả issue của jeffkit/recursive

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.