| name | news-sentiment-finbert |
| description | RSS 新闻情绪识别(FinBERT 中文金融情感)安装与运维 — 情绪管线架构、transformers 安装、FinBERT 权重下载、字典法扩充、全量重算、情绪筛选/条形图/个股资讯标签的使用。在 QuantBot / Claude Code 中排查新闻情绪不生效、重新安装 FinBERT、扩充情绪词、触发新闻情绪重算时使用。触发词:新闻情绪、情绪识别、FinBERT、情绪不生效、情绪都是中性、字典法、新闻重算、news sentiment |
新闻情绪识别(FinBERT)安装与运维技能
QuantMind 的 RSS 新闻情绪识别管线:Huntly 抓取 → celery 每分钟 enrich → 股票/行业/事件标签 + 情绪分 → news_article_enrichment 表 → 前端 RSS 面板/个股资讯 Tab 展示利好/利空。
本技能覆盖情绪层的安装、验证、重算、排查、词库增强。
1. 架构与数据流
Huntly (RSS 抓取, 381 源, 41.5 万文章)
→ celery-beat 每 60s: news-enrich-recent (run_enrichment_batch)
→ NewsMatcher (Aho-Corasick + finance_lexicon 5 万词: 股票/行业/事件/情感)
→ sentiment.score() (两级融合)
字典法 dict_score (finance_lexicon sentiment_pos/neg 词权重差)
+ FinBERT (bardsai/finance-sentiment-zh-base, CPU, conf≥0.55 才融合)
→ 写入 news_article_enrichment 表 (model_version 带 +finbert 后缀表示 FinBERT 生效)
→ 前端:
/rss-news NewsPanel: 情绪筛选/利好利空强度排序/统计栏情绪分布条形图
个股终端「个股资讯」Tab: 每篇利好/利空标签 (stock_news 关联 enrichment)
情绪词库:finance_lexicon 已从最初的 230 词扩充到 51,887 词(sentiment_pos 27,227 / sentiment_neg 24,089 / event 571),来源见第 6 节。
6. 情绪词库增强(从 230 词 → 5 万词)
情绪词分两级来源,统一存 finance_lexicon:
6.1 现成情感词典(通用,可复现导入)
| 来源 | 词数 | 说明 |
|---|
| DLUT 情感本体库 (github.com/yizhanmiao/DLUT-Emotionontology) | 22,001 | 中文情感词汇本体,强度 1-9,极性 1正/2负 |
| pysenti (github.com/shibing624/pysenti) | 9,870 | 内置情感词典,连续分数 -7~+7 |
| NTUSD 台大词典 (github.com/ntunlplab/NTUSD) | 20,485 | 繁体,Big5 编码,需转简体 |
6.2 RSS 标题提炼(金融语境,最精准)
用 41.5 万条 Huntly 标题 + enrichment 已有情绪标签,做共现统计(对数似然比 LLR 筛选):
- bearish 标题高频词 → 负向金融情绪词(下跌/暴跌/跌破/违规/立案/警示)
- bullish 标题高频词 → 正向金融情绪词(涨超/涨停/新高/买入/上调)
- 产出 5,705 词,贴合 A股/监管/业绩语境,通用词典覆盖不到的(如"暂停开户""被重锤")都在这
6.3 复现导入
python3 backend/scripts/import_sentiment_lexicon.py
docker exec quantmind python3 /app/backend/scripts/import_sentiment_lexicon.py
导入后重载 matcher:
curl -s -X POST -H "$AUTH" "$BASE/api/v1/news/enrichment/run"
6.4 新增情绪词的完整流程
- 从新数据源提取词(如再跑 RSS 统计)
- 写入
backend/scripts/data/finance_sentiment_lexicon.tsv(term\tpos|neg\tweight)
- 跑
import_sentiment_lexicon.py 导入
- 触发
/enrichment/run 或 rebuild 让新词生效
2. 情绪不生效 / 全是中性 的排查(最常见)
症状:RSS 面板里利好/利空占比极低(<5%),几乎全 neutral,置信度恒 0.3。
根因:FinBERT 模型没装上(容器缺 transformers),回退纯字典法,而字典法词太少打不出分。
判断方法:查 model_version 有没有 +finbert 后缀。
docker exec quantmind python3 -c "
import asyncio
from backend.shared.database_manager_v2 import get_session
from sqlalchemy import text
async def main():
async with get_session() as s:
r = await s.execute(text(\"SELECT sentiment_label, COUNT(*) FROM news_article_enrichment WHERE model_version LIKE '%finbert%' GROUP BY sentiment_label ORDER BY 2 DESC\"))
print('+finbert 分布:', r.fetchall())
r = await s.execute(text(\"SELECT COUNT(*) FROM news_article_enrichment WHERE model_version NOT LIKE '%finbert%'\"))
print('无 finbert 条数:', r.fetchone()[0])
asyncio.run(main())
"
- 有
+finbert 且有 bullish/bearish 分布 → FinBERT 正常
- 全 neutral / 无
+finbert → FinBERT 没生效,按第 3 节处理
3. 安装 / 修复 FinBERT
3.1 检查 transformers 是否在容器里
docker exec quantmind python3 -c "import transformers; print(transformers.__version__)"
docker exec quantmind-celery python3 -c "import transformers; print(transformers.__version__)"
3.2 缺的话手动装(临时修复,镜像重建后固化)
docker exec quantmind pip install --no-cache-dir transformers
docker exec quantmind-celery pip install --no-cache-dir transformers
3.3 下载/同步 FinBERT 权重(~100MB,离线可用)
docker exec quantmind python3 -c "
from backend.services.api.news import sentiment as s
s._model_ready=False; s._model_failed=False
s._try_load(); print('ready=', s._model_ready)
"
docker exec quantmind tar -C /root/.cache/huggingface/hub -cf - models--bardsai--finance-sentiment-zh-base \
| docker exec -i quantmind-celery sh -c 'mkdir -p /root/.cache/huggingface/hub && tar -C /root/.cache/huggingface/hub -xf -'
3.4 验证 FinBERT 可用
docker exec quantmind-celery python3 -c "
import os
os.environ['HF_HUB_OFFLINE']='1'
from transformers import pipeline
p = pipeline('sentiment-analysis', model='bardsai/finance-sentiment-zh-base', device=-1)
print(p('公司业绩暴雷,股价暴跌')[0]) # 期望 label=negative, score>0.9
"
3.5 重启 celery worker 让新代码/新依赖生效
docker restart quantmind-celery
sleep 8
docker exec quantmind-celery python3 -c "
from backend.services.api.news import sentiment as s
s._ensure_loading()
import time
for _ in range(40):
if s.is_available(): break
time.sleep(1)
print('FinBERT ready:', s.is_available())
"
4. 触发全量重算(历史情绪重打)
model_version 变了(比如补上 +finbert 后)需要重算历史才能让旧文章带上情绪。force=true 全量重算 41 万条 Huntly 文章,约 3-4 小时后台跑。
TOKEN=$(curl -s -X POST $BASE/api/v1/auth/login -H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123","tenant_id":"default"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))")
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"$BASE/api/v1/news/enrichment/rebuild-all?force=true"
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"$BASE/api/v1/news/enrichment/rebuild-all"
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/news/enrichment/rebuild-progress"
注意:rebuild 在 celery worker 的后台线程跑,重启 celery 会中断 rebuild。重算期间别重启 celery。
5. 字典法增强(FinBERT 不可用时的兜底)
情绪是两级融合,FinBERT 置信度 <0.55 或不可用时用字典法。字典词在 finance_lexicon 表(kind=sentiment_pos/neg,weight 为强度)。
扩充方式:
- 改仓库
backend/scripts/seed_a_share_stocks.py 的 _BUILTIN_SENTIMENT_POS/NEG 常量(已含 2026-08 扩充的 ~80 个词)
- 重跑 seed:
docker exec quantmind python3 /app/backend/scripts/seed_a_share_stocks.py
- 或直接 SQL 插入(幂等,先查后插):
docker exec quantmind python3 -c "
import asyncio
from backend.shared.database_manager_v2 import get_session
from sqlalchemy import text
async def main():
async with get_session() as s:
await s.execute(text(\"INSERT INTO finance_lexicon (term, kind, weight, enabled) VALUES ('涨停','sentiment_pos',1.0,true)\"))
await s.commit()
asyncio.run(main())
"
- 词表改动立即生效(NewsMatcher 每 600s 重载 / 可触发
/enrichment/run 重算新文章)
6. 情绪数据怎么用(前端已就绪)
| 位置 | 说明 |
|---|
/rss-news NewsPanel | 情绪筛选(利好/利空/中性)、利好/利空强度排序、统计栏情绪分布条形图(红利好/绿利空/灰中性) |
| 个股终端 → 个股资讯 Tab | 每篇标题前利好/利空标签(stock_news 按 huntly_page_id join enrichment) |
/news/articles API | sentiment=bullish/bearish/neutral 过滤 + sort=sentiment_bullish/sentiment_bearish |
/news/enrichment/stats | 当前筛选下的情绪分布计数(前端统计栏数据源) |
7. 相关代码文件
backend/services/api/news/sentiment.py — FinBERT 懒加载/后台加载/失败重试(_RETRY_AFTER 冷却 300s)
backend/services/api/news/enricher.py — enrich 管线,0.6字典+0.4FinBERT 融合
backend/services/api/news/matcher.py — Aho-Corasick 匹配 + 字典分
backend/scripts/seed_a_share_stocks.py — finance_lexicon 内置词(含情绪词)
backend/scripts/import_sentiment_lexicon.py — 5 万情绪词批量导入脚本(可复现)
backend/scripts/data/finance_sentiment_lexicon.tsv — 合并情绪词表 51,213 词(RSS提炼+DLUT+pysenti+NTUSD)
backend/services/api/routers/news.py — /news/* 路由(articles/enrichment/stats/sources)
backend/services/api/routers/stock_terminal.py — /stock-terminal/news 个股资讯(带情绪标签)
electron/src/features/news/components/NewsPanel.tsx — RSS 面板(情绪分布条形图)
electron/src/features/stock-terminal/components/tabs/NewsTab.tsx — 个股资讯情绪标签
docker/Dockerfile.oss + requirements/ai.txt — transformers 依赖 + FinBERT 权重预下载
8. 相关技能
- [[quantmind-operations]] — RSS 新闻对接与分析(文章拉取/过滤/富化统计)
- [[quantdb-fields]] — 数据字段口径