主题
联调参数审计 + 修复计划
2026-05-17 起。审计 Hermes 激活链路全部模块的参数配置,找出当前不一致 / 错误项,列出修复任务。
Part 1:现状全景(每个模块的参数都列清楚)
1.1 NewAPI gateway(llm.fsagent.cc,Vultr JP 上)
渠道(channel)— 实际只 1 个:
| id | name | type | 上游 | balance | 状态 |
|---|---|---|---|---|---|
| 1 | openrouter-default | 20 (OpenRouter) | OpenRouter free pool | -0.5964 USD | enabled |
渠道暴露的模型(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=admin、NEWAPI_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.cc | ✅ | NewAPI HTTP 入口 |
NEWAPI_ADMIN_USERNAME | admin | ✅ | NewAPI admin 登录 |
NEWAPI_ADMIN_PASSWORD | 7KG4vrQdVHrATG54JV6xr1Qh2KPz | ✅ | NewAPI admin 登录 |
NEWAPI_API_KEY | 历史遗留(含值) | ⚠️ | 当前代码不读,可删除 |
HERMES_DEFAULT_LLM_KEY | 未设置 | ❌ | NewAPI 发 token 失败时的兜底 key |
TWILIGHT_APP_VERSION | sha-d7f58b2 | ✅ | 镜像 tag pin |
JWT_SECRET / WECHAT_PAY_* | 已配 | ✅ | 鉴权 / 支付 |
Plan 表实际数据(ECS Postgres twilight_drive):
| code | name | priceCnyFen | billingCycleDays | monthlyQuotaTokens | instanceKind |
|---|---|---|---|---|---|
shared_basic | 标准版(共享池) | 19900(¥199) | 30 | 2,000,000 | SHARED |
dedicated_pro | 专业版(独享) | 79900(¥799) | 30 | 5,000,000 | DEDICATED |
注:
modelName列 尚未 迁移到 ECS(PR #97 的 migration 0002 还没应用)。
Tier 资源分配(hardcoded provision-worker.service.ts:136-139):
| 触发条件 | CPU | RAM | maxTurns |
|---|---|---|---|
priceCnyFen >= 19900 (即 ¥199 起,Pro) | 1.0 | 1g | 120 |
| 其它 | 0.25 | 256m | 60 |
⚠️ 当前判定
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-profilesWEIXIN_HOME_CHANNEL/WEIXIN_TOKEN— iLink 微信通道TIER_CPUS/TIER_MEMORY— Docker 资源限制MAX_TURNS— Hermes agent 轮次上限
额外 env(脚本自己取的,必须 ECS 全局 .env 提供):
| 变量 | 用途 | 当前是否配 |
|---|---|---|
MCP_URL | Tushare MCP 内网地址 | 需确认 |
SEARXNG_URL | SearXNG(Vultr JP,https://ws.fsagent.cc) | 需确认 |
BACKEND_URL | twilight-data 内网地址(写入 profile .env 作 TWILIGHT_SERVICE_URL) | 需确认 |
DASHSCOPE_KEY | 阿里 DashScope,可选 | 通常空 |
DOCKER_NETWORK | 容器网络名(container tier 用) | 需确认 |
HERMES_IMAGE | Hermes 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 mode1.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: 262144Agent 段:
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-data | 8081 | HTTP | api.fsagent.cc(CF Tunnel) |
| Tushare MCP | twilight-mcp-tushare | 9100 | streamable HTTP MCP | 内网 only |
| Postgres | twilight-postgres | 5432 | — | 内网 only |
| App backend (NestJS) | twilight-app-backend | 4000 | HTTP | backend.fsagent.cc |
| App frontend (Nginx) | twilight-app-frontend | 80 | HTTP | app.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(大模型)
- basic →
Issue #3 — Hermes 模型 hardcoded 为 openrouter/free,无法按套餐切换
- 模板里
name: openrouter/free写死,所有套餐都走同一个元模型 - Pro 套餐想用
nemotron-3-super-120b-a12b:free、basic 想用小模型 — 现在做不到 - 修复:
- 模板改
{{LLM_MODEL_NAME}}占位 spawn-profile.sh接受LLM_MODEL_NAME=...参数provisioning.service.ts从subscription.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 - 中期:在
Plan加newapiQuotaCredits字段,存 NewAPI 单位的配额,与monthlyQuotaTokens(展示用)分开 - 长期:等需要付费模型时再做完整配额会计
- 短期(free pool 时代):所有 token 都设
Issue #5 — getTokenUsage 单位换算是错的
- 我刚 commit 的
Math.round(used_quota / 1_000_000)默认 1e-6 token 单位 — 错的 - NewAPI 真实单位:与 channel ratio 相关,free pool 大概率永远是 0
- 现有
Hermes-Testtokenused_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)
- [ ] E1:
Planschema 加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)'
- [ ] E3:
provisioning.service.ts把plan.llmModelId传给 spawn-profile.sh - [ ] E4:
spawn-profile.sh接LLM_MODEL_NAME参数;模板加{{LLM_MODEL_NAME}}占位 - [ ] E5:
provision-worker.service.tstier 判定改用instance.subscription.plan.instanceKind === 'DEDICATED',不要再看 priceCnyFen - [ ] E6:
newapi.service.tscreateUserToken改unlimited_quota: true写死,注释说明 "free pool 时代" - [ ] E7:
getTokenUsage加注释 + 前端「本月用量」改文案为「已用额度(NewAPI 计费单位)」 - [ ] E8:在 NewAPI 创建一个 admin permanent token,写入 ECS app
.env的HERMES_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:用户决策点
这些等用户拍板再继续:
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 — 自己指定别的组合
- 选 A — 各自指定模型:basic =
PROF_TIER_PRICE_FEN 阈值改
> 19900还是改成读instanceKind?- 改读
instanceKind语义更清楚(DEDICATED 才是 Pro),推荐这个
- 改读
HERMES_DEFAULT_LLM_KEY是否启用?- 启用 — 即使 NewAPI 临时挂了,新付费用户也能开容器(用 default key 是退化但可用的)
- 禁用 — 严格走 per-user token,NewAPI 不可用就直接 ERROR
配额(quota)展示策略?
- A:完全隐藏(free pool 时代说不准)
- B:展示原始 NewAPI credit 数(用户看不懂)
- C:转换成 "本月调用 ~N 次"(用 Hermes 自己的轮次统计,不走 NewAPI)
我倾向:A1 / B2 / C 启用 / D(C 选项 — 调用次数)。等你确认。
Part 5:用户决策(2026-05-17 / 18 确认)
- 三档结构 — 替换原 2-tier:
lite¥59 — 50 次查询,锚定试用档standard¥198 ⭐ — 12 功能 + 1 雷达任务 + 30 天记忆 + 300 次查询plus¥799 — 1500 次查询 / 10 雷达任务 / 无限记忆 / 自定义模板 / 1-on-1 / 季度复盘 / v1.5 早期 / 优先队列
- 模型 — 三档统一用
openrouter/free。前端后续给用户加模型选择器(v0.5 范围)。 - Hermes config.yaml hardcoded model — 暂不动,留 TODO(Issue #3 不修,等模型选择 UI 一起做)。
- NewAPI quota 单位 — 已经核实:
QuotaPerUnit=500000、quota_display_type=USD。即 500,000 credit = $1 USD。原/1_000_000假设错误。决策:返回 raw credit,前端标签改成「已用额度」。后续如需,再换算 USD / 调用次数。 - HERMES_DEFAULT_LLM_KEY — 不启用。每个用户付费后由 NewAPI 单独发 token。发 token 失败 → 实例直接 ERROR(已实现)。
provision-worker也加 fail-fast 校验。 - Tier 判定 — 改读
plan.code(lite/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] E3 —
provision-worker.service.tstier 判定改 plan.code;TIER_PROFILES 写表 - [x] E4 — 删除
HERMES_DEFAULT_LLM_KEYfallback;无llmApiKey直接 throw - [x] E5 —
newapi.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 不必要