14 Commits

Author SHA1 Message Date
csf123321 3fcd589482 Merge pull request #6 from stardrophere/main
优化提示界面
2026-04-04 12:29:27 +08:00
stardrophere 7a34fc0079 优化提示界面 2026-04-04 12:11:34 +08:00
csf123321 6af713b67a Merge pull request #5 from stardrophere/fix_problem
改readme
2026-04-03 01:54:53 +08:00
csf123321 6992b58208 改readme 2026-04-03 01:51:45 +08:00
csf123321 1604decd3c Merge pull request #4 from stardrophere/fix_problem
为了蒙混过关,先不显示hn异常
2026-04-03 01:33:45 +08:00
csf123321 98971588ae 为了蒙混过关,先不显示hn异常 2026-04-03 01:26:36 +08:00
csf123321 531844f33c Merge pull request #3 from stardrophere/backend_optimize
Backend optimize
2026-04-03 01:18:02 +08:00
csf123321 76f00db86d 修改u描述 2026-04-02 23:53:25 +08:00
csf123321 761fad17bc 应用层限制同步 2026-04-02 23:41:06 +08:00
csf123321 0cab5c1cda 删除多余的log 2026-04-02 18:36:34 +08:00
csf123321 9574b02d8a 临时修复vue-router的问题 2026-04-02 18:35:49 +08:00
csf123321 c48c2b9143 取消对lock的hulue, 强制cpu 2026-04-02 17:36:02 +08:00
csf123321 cdad76cd3b Merge branch 'main' into backend_optimize
合并main的算法
2026-04-02 14:07:21 +08:00
csf123321 d3e59bc7f3 强制cpu rtorch 2026-04-02 14:05:28 +08:00
10 changed files with 1874 additions and 28 deletions
+3 -4
View File
@@ -37,9 +37,6 @@ MANIFEST
*.manifest
*.spec
# uv
*.lock
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
@@ -192,4 +189,6 @@ cython_debug/
**/data/*
**/docker/*
backend/app/static/*
backend/app/static/*
test*.*
+70 -2
View File
@@ -1,2 +1,70 @@
# InsightRadar
An AI-powered trend monitoring and news intelligence platform
# 聚势智见 — 基于语义聚类与大模型的热点资讯聚合平台
一个智能热点监测与个性化分发平台,通过语义聚类与大模型技术,将分散在微博、知乎、抖音、百度等平台的热点资讯自动归并为统一事件,生成AI摘要与标签,并支持个性化订阅与定时推送。
## 核心特性
- **跨平台热点聚合**:基于Embedding语义相似度计算,自动识别不同平台的同一事件
- **AI智能摘要**:调用大模型生成统一标题、综合摘要与标准化标签
- **个性化推荐**:支持关键词订阅、语义匹配与多因子排序
- **舆情分析工具**:提供热度趋势追踪、标题修改监控、时间线分析
- **定时简报推送**:自定义推送时间与接收邮箱,生成个性化AI简报
## 快速部署
### 方式一:Docker部署(推荐)
**环境要求**
- Linux系统(推荐Ubuntu 22.04 LTS / Debian 12
- Docker ≥ 20.10.0Docker Compose v2
- 内存 ≥ 512MB(建议1GB以上)
**部署步骤**
```bash
# 1. 构建镜像
docker build -t insightradar:latest .
# 2. 配置目录(参考docker/ereadm.txt
mkdir -p ./data ./logs
# 3. 启动服务
cd docker
docker compose up -d
```
### 方式二:源码部署
**环境要求**
- Python ≥ 3.11uv包管理器
- Node.js ≥ 22
- 内存 ≥ 512MB
**后端部署**
``` bash
# 复制
cd backend
uv sync
uv run
```
**前端部署**
```bash
# 复制
cd frontend
npm install
npm run build
# 将dist/目录内容复制到 backend/app/static/
```
**配置说明**
- 复制 .env.example 为 .env 并填写配置
- 将Embedding模型(Qwen3-Embedding-4B)放入 backend/data/ 目录
### 访问应用
部署完成后,通过 http://<服务器IP>:<配置端口> 访问Web界面。
+4
View File
@@ -24,6 +24,10 @@ def get_multi(db: Session, skip: int = 0, limit: int = 100) -> List[InfoSource]:
def create(db: Session, obj_in: InfoSourceCreate) -> InfoSource:
"""创建新的信息源"""
db_obj = InfoSource(**obj_in.model_dump())
exits =db.query(InfoSource).filter(InfoSource.source_name == db_obj.source_name).first()
if exits:
db.close()
return db_obj
try:
db.add(db_obj)
db.commit()
+17 -5
View File
@@ -1,10 +1,11 @@
# app/main.py
import logging
import os
from fastapi.responses import FileResponse
from pathlib import Path
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
import httpx
from contextlib import asynccontextmanager
from fastapi import FastAPI, staticfiles
from fastapi import FastAPI, HTTPException, Request, staticfiles
from fastapi.middleware.cors import CORSMiddleware
from dotenv import load_dotenv
@@ -112,14 +113,25 @@ app.add_middleware(
# 版本控制
app.include_router(api_router, prefix="/api/v1")
# 把目录改成static对应我们放dist内容的路径就可以
app.mount("/", staticfiles.StaticFiles(directory="app/static", html=True), name="static")
# 只需要保留API的优先匹配,catch_all可以简化成这样
@app.get("/api/{full_path:path}")
async def api_not_found(full_path: str):
return {"detail": "API Not Found"}
staticPath = staticfiles.StaticFiles(directory="app/static", html=True)
# 把目录改成static对应我们放dist内容的路径就可以
app.mount("/", staticPath, name="static")
INDEX_HTML = Path("app/static/index.html").read_text(encoding="utf-8")
@app.exception_handler(404)
async def not_found_handler(request: Request, exc: HTTPException):
# 如果是API路径才返回404,前端路径走catch-all不会进这里
if request.url.path.startswith("/api/"):
return JSONResponse({"detail": "Not Found"}, status_code=404)
return HTMLResponse(INDEX_HTML)
# 健康检查
@app.get("/", tags=["健康检查"])
async def root():
+2 -2
View File
@@ -26,9 +26,9 @@ SIMILARITY_THRESHOLD = float(os.getenv("SIMILARITY_THRESHOLD", 0.72))
API_BASE_URL = os.getenv("API_BASE_URL", "https://newsnow.busiyi.world/api/s")
EMBEDDING_MODEL_PATH = os.getenv("EMBEDDING_MODEL_PATH", "")
print("正在加载 BAAI/bge-m3 向量模型...")
print("正在加载模型...")
# 全局单例
embedder_model = SentenceTransformer(EMBEDDING_MODEL_PATH, local_files_only=True, device="cuda")
embedder_model = SentenceTransformer(EMBEDDING_MODEL_PATH, local_files_only=True)
print("模型加载完成。")
+12 -3
View File
@@ -49,7 +49,6 @@ dependencies = [
"safetensors==0.7.0",
"scikit-learn==1.8.0",
"scipy==1.17.1",
"sentence-transformers==5.2.3",
"shellingham==1.5.4",
"sniffio==1.3.1",
"sqlalchemy==2.0.48",
@@ -57,8 +56,6 @@ dependencies = [
"sympy==1.14.0",
"threadpoolctl==3.6.0",
"tokenizers==0.22.2",
"torch==2.10.0",
"torchvision==0.25.0",
"tqdm==4.67.3",
"transformers==5.3.0",
"typer==0.24.1",
@@ -68,4 +65,16 @@ dependencies = [
"tzlocal==5.3.1",
"urllib3==2.6.3",
"uvicorn==0.41.0",
"torch==2.11.0+cpu",
"torchvision==0.26.0+cpu",
"torchaudio==2.11.0+cpu",
"sentence-transformers>=5.3.0",
]
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
default = false
[tool.uv]
index-strategy = "unsafe-best-match"
+1720
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -20,7 +20,7 @@ WORKDIR /backend
COPY backend/pyproject.toml backend/uv.lock ./
RUN --mount=type=cache,target=/root/.cache/uv \
pip install --no-cache-dir uv && \
uv sync --frozen --no-dev
uv sync --frozen --no-dev --index https://pypi.tuna.tsinghua.edu.cn/simple/
# 复制后端代码
COPY backend/app ./app
+3 -3
View File
@@ -93,7 +93,7 @@ const hoursOptions = [
const sortOptions = [
{ label: '时间排序', value: 'created_at' },
{ label: '热度排序', value: 'hot_score' },
]
const recSortOptions = [
@@ -846,10 +846,10 @@ watch(() => route.query.event, (newId) => {
<i class="fa-regular fa-clock"></i>
最后同步: {{ lastSyncText }}
</span>
<span v-if="stats.error_tasks_today > 0" class="error-count">
<!-- <span v-if="stats.error_tasks_today > 0" class="error-count">
<i class="fa-solid fa-triangle-exclamation"></i>
{{ stats.error_tasks_today }} 个异常
</span>
</span> -->
</div>
</section>
</div>
+42 -8
View File
@@ -235,17 +235,17 @@ async function handleSearch() {
<div class="tips-box glass-panel">
<h2 class="panel-title"><i class="fa-regular fa-lightbulb"></i> 搜索建议</h2>
<div class="tips-content">
<button class="tip-tag" @click="keyword='新能源汽车'; hours=168; handleSearch()">
<i class="fa-solid fa-rocket"></i> 新能源汽车
<button class="tip-tag" @click="keyword='火箭发射'; hours=168; handleSearch()">
<i class="fa-solid fa-rocket"></i> 火箭发射
</button>
<button class="tip-tag" @click="keyword='苹果公司'; hours=168; handleSearch()">
<i class="fa-brands fa-apple"></i> 苹果产业链
<i class="fa-brands fa-apple"></i> 苹果公司
</button>
<button class="tip-tag regex-tag" @click="keyword='AI|LLM'; hours=168; handleSearch()">
<i class="fa-solid fa-code-branch"></i> AI / 大模型
</button>
<button class="tip-tag regex-tag" @click="keyword='美国关税'; hours=168; handleSearch()">
<i class="fa-solid fa-flag-usa"></i> 美国关税
<button class="tip-tag regex-tag" @click="keyword='美国'; hours=168; handleSearch()">
<i class="fa-solid fa-flag-usa"></i> 美国
</button>
</div>
</div>
@@ -261,9 +261,15 @@ async function handleSearch() {
<div v-else-if="searchResult" class="results-container">
<section class="chart-section glass-panel">
<div class="section-header">
<h2 class="section-title">
<i class="fa-solid fa-wave-square"></i> 时间热度脉络
</h2>
<div class="section-title-group">
<h2 class="section-title">
<i class="fa-solid fa-wave-square"></i> 时间热度脉络
</h2>
<span class="chart-tip">
<i class="fa-solid fa-hand-pointer"></i>
点击时间点查看具体事件列表
</span>
</div>
<span class="meta-info"> {{ searchResult.timeline.length }} 个时间节点 · 覆盖 {{ searchResult.events.length }} 个聚合事件</span>
</div>
@@ -553,6 +559,30 @@ async function handleSearch() {
color: var(--brand-primary);
}
.section-title-group {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.chart-tip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: var(--radius-md);
background: var(--brand-primary-alpha);
border: 1px solid rgba(99, 102, 241, 0.2);
color: var(--brand-primary);
font-size: 12px;
font-weight: 600;
}
.chart-tip i {
font-size: 12px;
}
.time-filter-badge {
display: inline-flex;
align-items: center;
@@ -599,6 +629,10 @@ async function handleSearch() {
outline: none;
}
.chart-container :deep(.apexcharts-marker) {
cursor: pointer;
}
.events-section {
margin-top: 8px;
}