Skip to content

联调参数审计 + 修复计划

2026-05-17 起。审计 Hermes 激活链路全部模块的参数配置,找出当前不一致 / 错误项,列出修复任务。


Part 1:现状全景(每个模块的参数都列清楚)

1.1 NewAPI gateway(llm.fsagent.cc,Vultr JP 上)

渠道(channel)— 实际只 1 个:

idnametype上游balance状态
1openrouter-default20 (OpenRouter)OpenRouter free pool-0.5964 USDenabled

渠道暴露的模型(13 个,全部 OpenRouter free pool):

inclusionai/ring-2.6-1t:free
nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free
nvidia/nemotron-3-super-120b-a12b:free
nvidia/nemotron-3-nano-30b-a3b:free
nvidia/nemotron-nano-12b-v2-vl:free
nvidia/nemotron-nano-9b-v2:free
poolside/laguna-m.1:free
poolside/laguna-xs.2:free
liquid/lfm-2.5-1.2b-thinking:free
liquid/lfm-2.5-1.2b-instruct:free
baidu/cobuddy:free
arcee-ai/trinity-large-thinking:free
openrouter/free          ← OpenRouter 自身的 "free 路由" 元模型

Token issuance 默认参数backend/newapi.service.ts:75-96):

ts
{
  name: `twilight-${userId}-${instanceId}`,
  remain_quota: quotaTokens ?? 500_000,   // 单位:NewAPI quota credit(≠ 实际 tokens)
  unlimited_quota: !quotaTokens,           // Plan 有配额则 false
  models: [],                              // ← 空数组 = 渠道所有模型都放行
  subnet: "",
  expired_time: -1,                        // 永不过期
}

现有 token:

  • Hermes-Test:used_quota=3938(不是 token 数,是 NewAPI 内部账单单位)、unlimited=true

Admin 凭据:

  • NEWAPI_ADMIN_USERNAME=adminNEWAPI_ADMIN_PASSWORD=7KG4vrQdVHrATG54JV6xr1Qh2KPz
  • 已写入 /home/twilight/twilight-app/.env,backend health 显示 newapi: ok

1.2 Backend (twilight-app-backend,NestJS) 配置

关键 env vars(/home/twilight/twilight-app/.env):

变量当前值状态用途
NEWAPI_BASE_URL默认 https://llm.fsagent.ccNewAPI HTTP 入口
NEWAPI_ADMIN_USERNAMEadminNewAPI admin 登录
NEWAPI_ADMIN_PASSWORD7KG4vrQdVHrATG54JV6xr1Qh2KPzNewAPI admin 登录
NEWAPI_API_KEY历史遗留(含值)⚠️当前代码不读,可删除
HERMES_DEFAULT_LLM_KEY未设置NewAPI 发 token 失败时的兜底 key
TWILIGHT_APP_VERSIONsha-d7f58b2镜像 tag pin
JWT_SECRET / WECHAT_PAY_*已配鉴权 / 支付

Plan 表实际数据(ECS Postgres twilight_drive):

codenamepriceCnyFenbillingCycleDaysmonthlyQuotaTokensinstanceKind
shared_basic标准版(共享池)19900(¥199)302,000,000SHARED
dedicated_pro专业版(独享)79900(¥799)305,000,000DEDICATED

注:modelName尚未 迁移到 ECS(PR #97 的 migration 0002 还没应用)。

Tier 资源分配(hardcoded provision-worker.service.ts:136-139):

触发条件CPURAMmaxTurns
priceCnyFen >= 19900 (即 ¥199 起,Pro)1.01g120
其它0.25256m60

⚠️ 当前判定 PROF_TIER_PRICE_FEN = 19900,所以 shared_basic(也是 19900)已经被判成 "Pro"。两个 Plan 行为完全一致 — 价格分层没生效。


1.3 spawn-profile.sh(ECS 上,/twilight/source/scripts/admin/spawn-profile.sh

入参:

bash
spawn-profile.sh <PROFILE_NAME> <TIER> [KEY=VAL ...]
# TIER ∈ {prefab, container}
# KEY=VAL 来自 backend.provisioning.service.ts:185-198

从 backend 传入的 KV:

  • LLM_API_KEY — NewAPI 发的 token key(或 HERMES_DEFAULT_LLM_KEY 兜底)
  • PROFILE_BASE/home/twilight/data/hermes-profiles
  • WEIXIN_HOME_CHANNEL / WEIXIN_TOKEN — iLink 微信通道
  • TIER_CPUS / TIER_MEMORY — Docker 资源限制
  • MAX_TURNS — Hermes agent 轮次上限

额外 env(脚本自己取的,必须 ECS 全局 .env 提供):

变量用途当前是否配
MCP_URLTushare MCP 内网地址需确认
SEARXNG_URLSearXNG(Vultr JP,https://ws.fsagent.cc需确认
BACKEND_URLtwilight-data 内网地址(写入 profile .envTWILIGHT_SERVICE_URL需确认
DASHSCOPE_KEY阿里 DashScope,可选通常空
DOCKER_NETWORK容器网络名(container tier 用)需确认
HERMES_IMAGEHermes Docker 镜像默认 nousresearch/hermes-agent:0.13.0

生成的 profile .env(每个用户独立):

dotenv
TWILIGHT_SERVICE_URL=<BACKEND_URL>
TWILIGHT_API_TOKEN=<随机生成>
LLM_API_KEY=<NewAPI 发的 token>
DASHSCOPE_API_KEY=<可选>
SEARXNG_URL=<...>
WEIXIN_HOME_CHANNEL=<...>
WEIXIN_TOKEN=<...>
GATEWAY_ALLOW_ALL_USERS=true
TUSHARE_TOKEN=               # 空,走 MCP service mode

1.4 Hermes config.yaml.template(profile/template-stock-research-pro/

模型段(关键):

yaml
model:
  provider: custom
  default: openrouter/free        # ← hardcoded
  name: openrouter/free           # ← hardcoded,是发给 NewAPI 的 model 字段
  base_url: https://llm.fsagent.cc/v1
  api_key: ${LLM_API_KEY}
  context_length: 262144

Agent 段:

yaml
agent:
  max_turns: {{MAX_TURNS}}        # 由 spawn-profile.sh sed 注入(60 or 120)
  api_max_retries: 3
  reasoning_effort: medium
  gateway_timeout: 1800
  ...

MCP 段:

yaml
mcp_servers:
  tushare:
    url: {{MCP_URL}}              # spawn-profile.sh 注入
    tools: include: [32 个 tool]
  searxng:
    url: ${SEARXNG_URL}           # 从 .env 加载

Placeholder 体系:

  • {{XXX}} — spawn-profile.sh 提供时 sed 注入
  • ${XXX} — Hermes 运行时从 profile .env 读取

当前 {{...}} 占位符仅 2 个:{{MCP_URL}}{{MAX_TURNS}}{{PROFILE_NAME}} 在 sed 命令里但模板未引用)


1.5 数据链路(数据后端 + MCP)

服务容器端口协议暴露域名
twilight-data (FastAPI)twilight-data8081HTTPapi.fsagent.cc(CF Tunnel)
Tushare MCPtwilight-mcp-tushare9100streamable HTTP MCP内网 only
Postgrestwilight-postgres5432内网 only
App backend (NestJS)twilight-app-backend4000HTTPbackend.fsagent.cc
App frontend (Nginx)twilight-app-frontend80HTTPapp.fsagent.cc

Part 2:发现的问题(按严重度)

P0(生产已坏 / 立即修)

Issue #1 — PROF_TIER_PRICE_FEN = 19900 让两个 Plan 共享资源限制

  • priceCnyFen >= 19900 是 Pro 判定,但 shared_basic 价格就是 19900 → 也被算成 Pro
  • 后果:两个套餐配额、CPU、RAM、maxTurns 全部一致,价格分层无意义
  • 修复:阈值改成 > 19900 或单独读 Plan.instanceKind 来分(DEDICATED → Pro)

Issue #2 — Plan.modelName 计划 seed "Qwen2.5 72B" 与实际渠道不符

  • 渠道只有 OpenRouter free pool 13 个模型,没有 Qwen
  • 计划文档原稿是猜的,必须按真实情况重新 seed
  • 修复:seed 改成 'OpenRouter 免费池(Nemotron / Liquid 等 13 模型)',或:
    • basic → liquid/lfm-2.5-1.2b-instruct:free(小模型)
    • pro → nvidia/nemotron-3-super-120b-a12b:free(大模型)

Issue #3 — Hermes 模型 hardcoded 为 openrouter/free,无法按套餐切换

  • 模板里 name: openrouter/free 写死,所有套餐都走同一个元模型
  • Pro 套餐想用 nemotron-3-super-120b-a12b:free、basic 想用小模型 — 现在做不到
  • 修复
    1. 模板改 {{LLM_MODEL_NAME}} 占位
    2. spawn-profile.sh 接受 LLM_MODEL_NAME=... 参数
    3. provisioning.service.tssubscription.plan.modelName 或新字段读取并传入

Issue #4 — NewAPI quota 单位不是 token,Plan.monthlyQuotaTokens 传错

  • 当前 createUserToken(name, plan.monthlyQuotaTokens) 把 token 数当 NewAPI quota credit 用
  • NewAPI 默认 $1 = 500,000 quota credits,2,000,000 credits ≈ $4 USD(按 OpenRouter ratio 计费)
  • 对 free pool(ratio=0):消耗永远是 0,配额永远跑不完
  • 对未来加付费渠道:2M credits 用户实际只能用约 $4,与 "2M tokens" 承诺差几个数量级
  • 修复
    • 短期(free pool 时代):所有 token 都设 unlimited_quota: true
    • 中期:在 PlannewapiQuotaCredits 字段,存 NewAPI 单位的配额,与 monthlyQuotaTokens(展示用)分开
    • 长期:等需要付费模型时再做完整配额会计

Issue #5 — getTokenUsage 单位换算是错的

  • 我刚 commit 的 Math.round(used_quota / 1_000_000) 默认 1e-6 token 单位 — 错的
  • NewAPI 真实单位:与 channel ratio 相关,free pool 大概率永远是 0
  • 现有 Hermes-Test token used_quota=3938,按 1e-6 换算 = 0 tokens;按 NewAPI 文档(500k/$1)= $0.008
  • 修复:先保留方法但加注释 "TODO: unit ≠ tokens, currently shows raw NewAPI credit count"。前端展示文案改成 "已用额度" 不是 "已用 tokens"

P1(功能缺失 / 部署阻塞)

Issue #6 — HERMES_DEFAULT_LLM_KEY 未设置

  • NewAPI 发 token 失败时 fallback 用这个;当前为空 → 用户容器直接拿不到 LLM key
  • 修复:在 NewAPI 上手工创建一个永久 admin token,写入 ECS app .env

Issue #7 — MCP_URL / SEARXNG_URL / BACKEND_URL / DOCKER_NETWORK 在 ECS 上是否设了?

  • spawn-profile.sh 全靠这些 env,缺一个就生成残缺 profile
  • 修复:登 ECS 查 /home/twilight/twilight/.env(data stack)or /home/twilight/twilight-app/.env(app stack)— 看 spawn-profile.sh 调用时 cwd 在哪、env 怎么来的,按需补齐

Issue #8 — NewAPI token models: [] 没有白名单

  • 现在 free pool 单一渠道无所谓
  • 未来加付费渠道(如 GPT-5),所有用户 token 自动获得访问权 — 失控
  • 修复:根据 Plan.allowedModelIds(新字段)显式 set;过渡期可硬编码 free pool 13 个

Issue #9 — 微信渠道 token 长效性

  • iLink bot_token 写入 profile .env,永久挂载
  • 若 iLink token 失效,profile 必须 rebind 才能恢复(rebind 流程已加,✅)
  • 但没有自动 health probe — 用户挂在异常状态没人发现
  • 修复:worker 加 cron,每 10 分钟 ping iLink get_bot_status,失败超 3 次自动标 ERROR

P2(清理 / 文档)

Issue #10 — NEWAPI_API_KEY 这个变量没人读

  • 是上一个设计的遗产,删除避免误导
  • 修复:从 ECS app .env 删,从 PR #97 描述里说明

Issue #11 — config.yaml.template 注释提到 "P1.1 Initiative B 才有 provisioner"

  • 现在已经有 provisioner(spawn-profile.sh),注释过时
  • 修复:注释扫一遍

Part 3:修复任务清单

立即做(Phase E — 联调修复,1 个 PR)

  • [ ] E1Plan schema 加 llmModelId String?(实际发给 NewAPI 的 model id);modelName 改成纯展示字段
  • [ ] E2:Migration 0003:补 llmModelId;seed 现有两个 Plan:
    • shared_basic: llmModelId='openrouter/free', modelName='OpenRouter 免费池'
    • dedicated_pro: llmModelId='nvidia/nemotron-3-super-120b-a12b:free', modelName='Nemotron 120B (Pro)'
  • [ ] E3provisioning.service.tsplan.llmModelId 传给 spawn-profile.sh
  • [ ] E4spawn-profile.shLLM_MODEL_NAME 参数;模板加 {{LLM_MODEL_NAME}} 占位
  • [ ] E5provision-worker.service.ts tier 判定改用 instance.subscription.plan.instanceKind === 'DEDICATED',不要再看 priceCnyFen
  • [ ] E6newapi.service.ts createUserTokenunlimited_quota: true 写死,注释说明 "free pool 时代"
  • [ ] E7getTokenUsage 加注释 + 前端「本月用量」改文案为「已用额度(NewAPI 计费单位)」
  • [ ] E8:在 NewAPI 创建一个 admin permanent token,写入 ECS app .envHERMES_DEFAULT_LLM_KEY
  • [ ] E9:登 ECS 检查 MCP_URL / SEARXNG_URL / BACKEND_URL / DOCKER_NETWORK,缺啥补啥
  • [ ] E10:app .env 删除 NEWAPI_API_KEY

跟进做(Phase F — 健壮性)

  • [ ] F1:worker cron + iLink health probe,自动标 ERROR
  • [ ] F2:NewAPI token model 白名单(按 Plan)
  • [ ] F3:当真有付费模型时,重新设计配额会计(Plan.newapiQuotaCredits + 真实 token-to-credit 换算)

部署(Phase D — 已经存在,等代码就绪)

  • [ ] ECS 上跑 migration 0002 + 0003
  • [ ] CI 构建新 sha,部署到 ECS
  • [ ] 前端 StatusPage 验证「LLM 模型」「已用额度」展示正常

Part 4:用户决策点

这些等用户拍板再继续:

  1. basic 和 pro 是否真的要用不同 LLM? 还是都用 openrouter/free 元模型(让 OpenRouter 自己选路由)?

    • 选 A — 各自指定模型:basic = liquid/lfm-2.5-1.2b-instruct:free、pro = nvidia/nemotron-3-super-120b-a12b:free
    • 选 B — 都用 openrouter/free,只在资源(CPU/RAM/maxTurns)上分层
    • 选 C — 自己指定别的组合
  2. PROF_TIER_PRICE_FEN 阈值改 > 19900 还是改成读 instanceKind

    • 改读 instanceKind 语义更清楚(DEDICATED 才是 Pro),推荐这个
  3. HERMES_DEFAULT_LLM_KEY 是否启用?

    • 启用 — 即使 NewAPI 临时挂了,新付费用户也能开容器(用 default key 是退化但可用的)
    • 禁用 — 严格走 per-user token,NewAPI 不可用就直接 ERROR
  4. 配额(quota)展示策略?

    • A:完全隐藏(free pool 时代说不准)
    • B:展示原始 NewAPI credit 数(用户看不懂)
    • C:转换成 "本月调用 ~N 次"(用 Hermes 自己的轮次统计,不走 NewAPI)

我倾向:A1 / B2 / C 启用 / D(C 选项 — 调用次数)。等你确认。


Part 5:用户决策(2026-05-17 / 18 确认)

  1. 三档结构 — 替换原 2-tier:
    • lite ¥59 — 50 次查询,锚定试用档
    • standard ¥198 ⭐ — 12 功能 + 1 雷达任务 + 30 天记忆 + 300 次查询
    • plus ¥799 — 1500 次查询 / 10 雷达任务 / 无限记忆 / 自定义模板 / 1-on-1 / 季度复盘 / v1.5 早期 / 优先队列
  2. 模型 — 三档统一用 openrouter/free。前端后续给用户加模型选择器(v0.5 范围)。
  3. Hermes config.yaml hardcoded model — 暂不动,留 TODO(Issue #3 不修,等模型选择 UI 一起做)。
  4. NewAPI quota 单位 — 已经核实:QuotaPerUnit=500000quota_display_type=USD。即 500,000 credit = $1 USD。原 /1_000_000 假设错误。决策:返回 raw credit,前端标签改成「已用额度」。后续如需,再换算 USD / 调用次数。
  5. HERMES_DEFAULT_LLM_KEY不启用。每个用户付费后由 NewAPI 单独发 token。发 token 失败 → 实例直接 ERROR(已实现)。provision-worker 也加 fail-fast 校验。
  6. Tier 判定 — 改读 plan.codelite / standard / plus),不再用 priceCnyFen。旧码 shared_basic / dedicated_pro 留作兼容映射。

Part 6:Phase E 任务执行表(这一轮做完)

  • [x] E1 — schema.prisma 加 llmModelId / maxMonthlyQueries / radarTasks / memoryDays / isActive
  • [x] E2 — migration 0003:补字段、deactivate 旧 plan、插入 lite/standard/plus
  • [x] E3provision-worker.service.ts tier 判定改 plan.code;TIER_PROFILES 写表
  • [x] E4 — 删除 HERMES_DEFAULT_LLM_KEY fallback;无 llmApiKey 直接 throw
  • [x] E5newapi.service.ts.getTokenUsage 返回 raw credit + 注释引用 QuotaPerUnit
  • [x] E6 — 前端 StatusPage label 改「本月已用额度 / 调用单位」
  • [ ] E7(部署) — 应用 migration 0003 到 ECS、CI 重建镜像、回归 StatusPage / 付费流
  • [ ] E8(清理) — ECS app .env 删除 NEWAPI_API_KEY 死字段(保留 NEWAPI_ADMIN_*
  • [ ] E9(清理)config.yaml.template 注释扫一遍("P1.1 Initiative B" 过时)

Part 7:Phase F 后续 backlog(不在这一轮)

  • F1 — 前端价格表 PricingPage 改成 3-tier 卡片
  • F2 — Plan.llmModelId 真正接入 spawn-profile.sh / config.yaml.template(替换硬编码 openrouter/free
  • F3 — 用户可在 StatusPage 自选模型,dropdown 来源 NewAPI GET /api/channel/<id>.models
  • F4 — iLink bot health probe + auto-ERROR
  • F5 — NewAPI token models 白名单(按 Plan 范围)
  • F6 — 真实 token-accounting:若加付费渠道,需要按 ModelRatio 把 credit 反推 token;现阶段 free pool 不必要

团队内部文档