Skip to content

[Feature] 后台 LLM 调用 Token 消耗过高,附优化建议 #77

@baianjo

Description

@baianjo

Problem Statement

注:下述内容不全是vibe出来的,我查账查日志 看了一整天了hh。。优化建议我也去社区学习查看过,算得上是最佳实践之一,还算靠谱,实际细节优化可以继续向Claude opus求证。但不排除对项目理解有误的情况,那就是我的锅

另外”始终提取0个模式“这个问题确实很大,我看有人反馈过了

问题描述

在两个活跃群(高峰期 10-20 条/分钟)的真实部署场景下,用户对话 Token 占比仅约 2%,后台自学习任务消耗了约 98% 的 API 费用,即使qwen3.5这样便宜的API能在四个小时内消耗约 ¥70。通过日志分析和代码审阅,费用主要来自三个后台功能:

功能 占费用比重 核心瓶颈
黑话挖掘 jargon_miner ~70% 单批 20 候选词 × 每词最多 3 次 LLM 调用,且输出未限长
话题检测 enhanced_interaction ~25% 每条消息触发 1 次 LLM 调用,无节流
社交关系上下文注入 ~3% 每次主对话注入多维上下文,持续增加 input token
(不完全准确)

详细费用归因

1. 黑话挖掘(最大消耗源)

调用放大链条: 每累积 10 条消息触发 mine_jargon()extract_candidates() 单批可产出 20 个候选词 → 每个候选词在达到出现阈值 [3,6,10,20,40,60,100] 时触发三步推断法(最多 3 次 LLM 调用)。

实测数据(11 分钟窗口):

  • jargon_miner 日志条目:172 次(110 INFO + 62 WARN/解析失败)
  • 批量提取:16 批 × 20 候选词 = 320 次单词判断
  • 62 次"推断解析失败"——LLM 生成了完整响应但格式不合规,纯浪费

Output Token 失控的根因: 三步推断法未对 max_tokens 做限制。"识别黑话"的解释(infer_meaning Step 1)每次输出 300-600 个汉字(≈600-1200 tokens),而实际只需要一句话的含义摘要。这导致账单中 output/input 比达到约 15:1

2. 话题检测(第二大消耗源)

_detect_topic_change()message_count >= 3对每条消息发起 1 次 filter_chat_completion 调用(enhanced_interaction.py:196-214)。11 分钟内触发 162 次。输出虽短(2-4 字话题名),但累积调用量巨大。

3. 解析失败的隐性浪费

三步推断法使用 JSON 输出格式,但未配置 response_format/json_mode。LLM 返回非法 JSON → 解析失败 → 调用白白付费。11 分钟内 62 次解析失败,按每次平均 800 output tokens 估算 ≈ 49,600 tokens 纯浪费。


优化建议

🔴 P0:立即见效(预估降低 60-70% 费用)

1. 为黑话推断添加 max_tokens 限制

# jargon_miner.py - infer_meaning() 的 3 次调用
# 建议 Step1/Step2: max_tokens=150, Step3: max_tokens=100
# 含义摘要不需要超过 50 个汉字

当前"识别黑话"单次输出 600-1200 tokens,限制后降至 150 tokens,单项节省 75-85%

2. 话题检测添加采样/节流机制

# enhanced_interaction.py - _detect_topic_change()
# 方案 A:每 N 条消息检测一次(建议 N=5)
# 方案 B:添加冷却时间(建议 30 秒)
# 方案 C:仅在消息内容变化度(如简单关键词重叠率)超阈值时才调 LLM

从每条消息 1 次 → 每 5 条 1 次,单项节省 80%

3. 强制 JSON 输出格式

# 对所有期望 JSON 返回的调用,添加:
# response_format={"type": "json_object"}(如模型支持)
# 或在 prompt 末尾追加:"Only output valid JSON, no other text."
# 并添加 1 次重试(而非静默丢弃)

消除解析失败的纯浪费(当前约占总费用 5-8%)。

🟡 P1:架构级优化(预估再降 15-20%)

4. 黑话候选词去重与合并
当前同一批内 20 个候选词各自独立调用 LLM。建议:

  • 合并为 1 次批量判断调用(prompt 中列出所有候选词 + 上下文,要求批量返回 JSON 数组)
  • 单批 20 词从 20×3=60 次调用 → 3 次调用,降低 95%

5. 话题检测改用本地方案
2-4 字的话题名完全可以用 TF-IDF / 关键词提取(类似 jieba.analyse.extract_tags)实现,精度略低但 0 Token 消耗。仅在检测到显著话题切换时才用 LLM 精炼。

6. 社交上下文注入预算控制
SocialContextInjector.format_complete_context() 注入的多维上下文(心理状态、情绪、好感度、社交网络、表达风格、行为指导)每次主对话都完整附加。建议:

  • 设置注入内容总 token 上限(如 500 tokens)
  • 按重要性优先级截断
  • 对无变化的维度跳过注入(与上次 diff)

🟢 P2:可选配置项

7. 暴露费用相关参数为用户可配置项

jargon_mining:
  enabled: true
  batch_size: 10          # 当前硬编码
  max_candidates_per_batch: 20  # 建议可调,默认改为 5
  inference_max_tokens: 150     # 新增
  
topic_detection:
  enabled: true
  sample_rate: 5          # 每 N 条消息检测一次
  cooldown_seconds: 30    # 冷却时间

环境信息

  • 部署:2 个 QQ 群(日常活跃,高峰 10-20 msg/min)
  • 模型:qwen3.5plus,输入0.8输出4.8,仅比deepseek输出贵一块八
  • 插件版本:最新 main 分支
  • 日均 API 费用:约 ¥70(其中用户实际对话仅占 ~¥1.4)

复现方式

将插件默认配置部署到任意活跃群(>5 msg/min),观察 24 小时 API 用量。后台任务的 Token 消耗将远超前台对话。

Proposed Solution

省钱

Alternatives Considered

No response

Feature Scope

Learning System (学习系统)

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions