# quotation-extraction-system
**Repository Path**: liuchangng/quotation-extraction-system
## Basic Information
- **Project Name**: quotation-extraction-system
- **Description**: 基于大模型的跨模态内容理解系统,自动从文本/音频/视频中抽取大众喜欢的哲理句,输出评分(含分项标尺)、标签(层级+置信度)、原文定位(两步定位法)、解释与受众画像,并可视化展示。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-07-20
- **Last Updated**: 2026-07-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Quotation Extraction System
[](https://www.python.org/)
[](https://fastapi.tiangolo.com/)
[](https://nextjs.org/)
[](./LICENSE)
**基于大模型的跨模态哲理语句抽取系统**
自动从文本/音频/视频中提取高质量哲理句,输出多维评分、标签体系、原文定位与受众画像。
## 功能特性
- **内容上传**:支持 PDF / DOCX / TXT 文件上传与直接文本输入(拖拽上传)
- **文本解析**:分页/分段解析,生成带 `[页x|段y]` 定位标记的结构化文本
- **哲理句抽取**:LLM 抽取大众风格哲理句(情绪共鸣 / 启发性 / 励志 / 易懂 / 简洁)
- **评分体系**:4 维度分项评分(情绪/启发/易懂/文学)+ 固定权重综合分,嵌入评分标尺防漂移
- **标签体系**:主题 / 情绪 / 价值观 三层标签 + 置信度
- **两步定位**:粗定位(LLM 引用标记)+ 精定位(原文回查),失败返回 null 防幻觉
- **反鸡汤过滤**:黑名单词过滤绝对化表述("注定/绝对/一定/永远/必然")
- **去重策略**:同一文件(大文件分块合并后)内重复的哲理句自动去重,仅保留评分最高的一条;**跨文件**重复仍保留,用于频率统计
- **频率统计**:自动统计每句在多少份不同文档中出现,频率越高说明该句越流行、越有用;首页展示"高频哲理句"榜与"高频句数"指标
- **自适应分块**:上传文本过大时自动按目标大小(默认 4000 字符)分块,块间 10% 重叠(段落边界对齐,防哲理句被切断),块数由文件大小自动决定;低于阈值(2000字符)则整体一次抽取
- **异步抽取**:上传后立即返回,抽取在后台线程执行;前端实时轮询进度("正在抽取第 X/Y 块…"+ 进度条),大文件无需等待
- **防重复上传**:上传文件时计算内容 MD5 并持久化(`file_md5`),若已存在"已完成"的同 MD5 任务则直接复用,跳过解析与抽取,节省资源
- **受众画像**:预测哲理句打动的人群标签
- **首页 Dashboard**:统计概览(文档数 / 完成数 / 哲理句数 / 来源分布 / 高频句数)+ Top10 评分最高 + 🔥 热门哲理句(按频率)+ 文档库列表(分页,点击进入查看,展示文件 MD5)
- **可视化展示**:哲理句卡片、评分徽章、标签云、定位跳转、分页轮询
## 技术栈
| 层 | 技术 |
|----|------|
| 后端 | Python 3.13 + FastAPI + SQLAlchemy + SQLite(uv 管理依赖) |
| 前端 | Next.js 14 + React 18 + TypeScript + TailwindCSS + React Query |
| 包管理 | 后端 uv(pyproject.toml + uv.lock),前端 npm |
| LLM | OpenAI 兼容接口(GPT-4o / Qwen / DeepSeek 等) |
| 文本解析 | pdfplumber / python-docx |
## 目录结构
```
quotation-extraction-system/
├── server/ # 后端 FastAPI
│ ├── app/
│ │ ├── main.py # 应用入口(中间件/路由/生命周期)
│ │ ├── config.py # 集中配置(env 校验 fail-fast)
│ │ ├── errors.py # 类型化错误 + 全局异常处理
│ │ ├── database.py # SQLAlchemy 引擎/会话
│ │ ├── logger.py # 结构化日志
│ │ └── features/ # feature-first 组织
│ │ ├── upload/ # 上传与文本解析
│ │ ├── extract/ # 抽取核心(LLM/Prompt/过滤/定位)
│ │ └── task/ # 任务管理与数据模型
│ ├── pyproject.toml # uv 项目配置与依赖声明
│ ├── uv.lock # uv 锁定文件(可复现构建)
│ ├── requirements.txt # 兼容旧 pip 方式(保留)
│ ├── .gitignore
│ └── .env.example
└── client/ # 前端 Next.js
├── src/
│ ├── app/ # 页面:首页 Dashboard / 新建抽取 / 结果展示
│ ├── components/ # 组件:StatsOverview/TopSentences/DocumentList/Pagination/StatusChip/SentenceCard/Uploader…
│ ├── api/ # typed fetch API client
│ ├── utils/ # 前端日志(控制台 + 上报后端)
│ └── types/ # 类型定义(与后端契约对齐)
└── package.json
```
## 快速开始
### 1. 启动后端(uv 管理,推荐)
需先安装 [uv](https://docs.astral.sh/uv/):`pip install uv` 或 `winget install astral-sh.uv`。
```bash
cd server
cp .env.example .env # 按需配置 LLM_API_KEY(留空走 mock 演示)
uv sync # 创建/同步 .venv 并安装全部依赖(依据 uv.lock)
uv run python run.py # http://localhost:8002(端口见 .env 的 APP_PORT)
```
> uv 会自动创建 `.venv`、解析 `pyproject.toml` 并按 `uv.lock` 精确安装,无需手动 venv/pip。
> 常用命令:`uv add <包名>` 添加依赖、`uv remove <包名>` 移除、`uv sync --reinstall` 重建环境。
>
> 兼容旧方式(不推荐):`python -m venv .venv && .venv/Scripts/activate && pip install -r requirements.txt && python run.py`
### 2. 启动前端
```bash
cd client
npm install
npm run dev # http://localhost:3000
```
打开 http://localhost:3000 即可使用:首页查看统计/Top10/文档库 → 新建抽取(上传或粘贴文本)→ 查看哲理句卡片。
> 前端通过 `client/.env.local` 的 `NEXT_PUBLIC_API_URL` 指向后端地址(默认 `http://localhost:8002`),需与后端端口一致。
## 配置说明
后端 `server/.env` 关键配置:
| 变量 | 说明 | 默认值 |
|------|------|--------|
| `LLM_API_BASE` | LLM 接口地址(OpenAI 兼容) | `https://api.openai.com/v1` |
| `LLM_API_KEY` | API Key(**留空走 mock 演示模式**) | 空 |
| `LLM_MODEL` | 模型名 | `gpt-4o` |
| `DATABASE_URL` | 数据库连接串 | `sqlite:///./data/app.db` |
| `CORS_ORIGINS` | 允许的前端地址(逗号分隔;`*` 放行全部;localhost/127.0.0.1 自动双向兼容) | `http://localhost:3000` |
| `EXTRACT_CHUNK_MIN_SIZE` | 分块阈值:文本字符数低于此值不分块 | `2000` |
| `EXTRACT_CHUNK_SIZE` | 每块目标字符数(3000-4000 适合 LLM 抽取) | `4000` |
| `EXTRACT_CHUNK_OVERLAP` | 块间重叠百分比(10-15% 防边界断裂) | `0.10` |
| `POPULAR_MIN_FREQ` | 频率统计阈值:出现于 ≥ 该份文档数即记为"高频句" | `2` |
> 未配置 `LLM_API_KEY` 时,系统走 mock 模式返回示例数据,便于前端联调与演示。接入真实大模型只需填入 Key 与 Base URL。
## API 文档
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/health` | 健康检查 |
| POST | `/api/upload` | 上传文件(TXT/PDF/DOCX) |
| POST | `/api/upload/text` | 直接提交文本 |
| POST | `/api/extract` | 抽取哲理句(**异步**:立即返回,后台线程执行,前端轮询进度) |
| GET | `/api/tasks` | 任务列表(分页信封:`{total,limit,offset,items}`,每项含 `sentence_count`) |
| GET | `/api/tasks/stats` | 统计概览(文档数 / 完成数 / 哲理句数 / 来源分布 / 高频句数) |
| GET | `/api/sentences/top` | 跨任务 Top 评分哲理句(?limit=10,含 `task_id`/`source_type` 便于跳转) |
| GET | `/api/sentences/frequent` | 热门哲理句(按跨文档出现频率排序,?limit=10,含 `frequency` 字段) |
| GET | `/api/tasks/{id}` | 任务状态(含 `sentence_count`、`file_md5`、`progress`、`parts_total`) |
| GET | `/api/tasks/{id}/result` | 抽取结果(分页) |
> **清空数据**:系统不提供清空接口,直接清空数据库即可——`uv run python scripts/clear_data.py`(清空任务+哲理句+上传文件),加 `--drop` 可删库重建表(用于模型字段变更后的结构重置)。
启动后端后访问 http://localhost:8002/docs 查看交互式 API 文档(Swagger)。
## 核心设计
### 评分公式
```
Score = 0.4×Emotion + 0.3×Insight + 0.2×Clarity + 0.1×Literary
```
四舍五入为整数。System Prompt 内嵌评分标尺,要求 LLM 给出分项分数 + 理由,防止打分漂移。
### 两步定位法(防幻觉)
1. **粗定位**:LLM 引用原文中的 `[页x|段y]` 定位标记
2. **精定位**:后端回查标记是否真实存在 + 句子是否逐字出现,计算 `match_confidence`
3. **兜底**:精定位失败 → `match_confidence = null`,绝不"猜一个"
### 反鸡汤规则
- 黑名单词:注定 / 绝对 / 一定 / 永远 / 必然
- 偏好:承认生活复杂性 + 给出微小希望,而非廉价乐观
### 频率统计(热门句)
同一文件(含大文件分份合并后)内重复的哲理句会自动去重,只保留评分最高的一条。**跨文件**的重复句仍各自保留。
每句按归一化文本(去空格/标点/大小写)聚合,统计其出现于**多少份不同文档**(`frequency`)。
频率越高,说明该句越被多方印证、越流行有用。首页据此展示:
- **高频句数**指标:出现于 ≥ `POPULAR_MIN_FREQ` 份文档的哲理句数量
- **🔥 热门哲理句**榜单:按 `frequency` 降序,标注"出现于 N 份文档"
> 如需彻底重置数据库(如模型字段变更),可用脚本:`uv run python scripts/clear_data.py --drop`
## 扩展方向
当前为文本核心闭环,已预留扩展接口:
| 能力 | 当前实现 | 扩展方向 |
|------|---------|---------|
| 元数据库 | SQLite | 切换连接串迁移 PostgreSQL |
| 频率/相似度检索 | 归一化文本聚合(内存计算) | 抽象接口接入 Milvus / 向量库做语义聚类 |
| 音频/视频 | 预留 ASR 接口 | 接入 Whisper API |
| 意境图 | 预留生成接口 | Unsplash 兜底 + SDXL/DALL·E |
## License
MIT