news-sentiment-finbert
GitHub提供FinBERT中文金融情绪识别管线的安装、运维、词库增强及故障排查技能,支持环境适配与数据重算。
Trigger Scenarios
Install
npx skills add qusong0627/QuantMind --skill news-sentiment-finbert -g -y
SKILL.md
Frontmatter
{
"name": "news-sentiment-finbert",
"description": "RSS 新闻情绪识别(FinBERT 中文金融情感)安装与运维 — 情绪管线架构、transformers 安装、FinBERT 权重下载、字典法扩充、全量重算、情绪筛选\/条形图\/个股资讯标签的使用。在 QuantBot \/ Claude Code 中排查新闻情绪不生效、重新安装 FinBERT、扩充情绪词、触发新闻情绪重算时使用。触发词:新闻情绪、情绪识别、FinBERT、情绪不生效、情绪都是中性、字典法、新闻重算、news sentiment"
}
⚙️ 运行环境契约(最高优先级,先于本文其余内容执行)
本技能可能运行在 QuantBot(QwenPaw 容器) 或宿主机/本地 Claude Code。执行前先探测环境(
which docker、API 连通性),并遵守以下映射规则:
- 后端 API 地址:QwenPaw / 容器网络内一律用
http://quantmind:8000(quantmind是 docker 网络别名);仅宿主机调试用http://127.0.0.1:8000。正文中出现的127.0.0.1:8000、localhost:800x,在 QwenPaw 环境下自动替换为http://quantmind:8000。- 取数脚本执行:凡 import 了
pandas / duckdb / psycopg2 / numpy / sqlalchemy等重依赖或backend包的脚本,必须在 quantmind 容器内执行(QwenPaw 本地 venv 无这些依赖):脚本源三选一:宿主机 repodocker cp <脚本路径> quantmind:/tmp/<脚本名> && docker exec -w /app quantmind python3 /tmp/<脚本名> <参数>skills/<name>/scripts/、QwenPaw 工作区/app/working/workspaces/default/skills/<name>/scripts/、挂载目录/quantmind/skills/<name>/scripts/。纯标准库脚本(无重依赖)可在 QwenPaw 本地直接跑。- 报告落盘:股票报告页可见的 MD/PDF 报告,直接写
/data/reports/trading_agents/{市场或类别}/{股票名}/(QwenPaw 对/app/db有写权限,直接写文件,不要 docker cp);过程数据 facts 写/data/reports/<类别>/(/data可写)。- MD → PDF 转换(按优先级降级): ①
docker exec -w /app quantmind python3 backend/scripts/md_to_pdf_report.py <输入.md> <输出.pdf>(研报级排版,首选); ② docker 不可用时,改用 QwenPaw 内置- 本文中的
~/.claude、cp -r ... ~/.claude/skills等说明仅适用于本地 Claude Code 维护者,QuantBot 不要执行。
新闻情绪识别(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 复现导入
# 词表已固化: backend/scripts/data/finance_sentiment_lexicon.tsv (51,213 词)
# 导入脚本: backend/scripts/import_sentiment_lexicon.py
# 本地
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__)"
# celery worker 也要有(enrich/rebuild 在 celery 里跑)
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)
"
# 权重缓存位置: /root/.cache/huggingface/hub/models--bardsai--finance-sentiment-zh-base
# celery worker 复用(主容器导出 → tar 管道 → celery 导入)
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',''))")
# 全量重算(force=true 覆盖已 enrich 的)
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"$BASE/api/v1/news/enrichment/rebuild-all?force=true"
# 或增量(跳过 model_version 已是最新的)
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]] — 数据字段口径
Version History
- 2b47c67 Current 2026-09-02 21:03


