ShashwatNarayan/Personal-Financial-Intelligence-System
GitHub: ShashwatNarayan/Personal-Financial-Intelligence-System
一个全栈个人金融平台,通过智能流水解析、五阶段分类引擎、异常检测和 Gemini 自然语言查询,实现银行流水的自动化分析与财务洞察。
Stars: 1 | Forks: 0
# 个人金融智能系统 — *ArthaLens*






## 在线演示
**[https://arthalens.onrender.com/](https://arthalens.onrender.com/)**
## 项目概述
**ArthaLens** 是一个全栈个人金融平台,能够导入原始的 HDFC 和 SBI 银行流水,通过多阶段智能流水线自动对每笔交易进行分类,并将结果以交互式分析仪表板的形式展示。除了静态图表之外,它还能检测消费异常、审计周期性订阅、匹配退款,并允许用户提出纯英文问题(例如“上个月我在食物上花了多少钱?”),这些问题将由带有严格防护措施的 AI 层来解答。最关键的是,它具有*学习*能力——用户所做的每一次更正都会被记住,并应用于该实体的所有未来交易中。
## 核心功能
- **银行流水解析 (HDFC + SBI)** — 根据流水特征自动检测发卡行,然后分发给特定银行的解析器。通过多引擎回退机制 (`openpyxl` → `xlrd` → CSV),处理旧版 `.xls`、现代 `.xlsx`、标题行偏移、分隔行以及不同的列命名约定。
- **智能分类流水线(5步优先级系统)** — 一笔交易通过用户专属更正记忆、共享的(所有者初始化的)跨用户记忆、实体/平台检测以及关键词匹配来解决,并带有平滑回退——每个阶段都带有置信度级别(`high` / `medium` / `low`)。平台名称基于全词边界进行匹配,以避免误报(例如,“VI”不再匹配“VIA”)。
- **异常检测(滑动窗口 Z-score)** — 按月汇总每个类别的支出,并针对 3 个月的滑动基准测试*每一个*月份(这样历史异常也会浮现,而不仅仅是最近的月份),标记出具有统计学意义的激增或下降(可配置 Z-score 阈值,需要至少 3 个月的历史数据)。最低绝对金额底线会抑制那些在统计学上显著但在财务上微不足道的波动,并且每个异常都会根据严重程度 (`moderate` / `high` / `critical`) 进行标记。
- **订阅审计** — 检测按月/季度/年度周期发生的经常性扣款——包括带有跳过月份的*间歇性*订阅——跟踪成本趋势,并标记“僵尸”长期和高成本订阅。
- **退款检测** — 使用严格的标准将退款入账匹配回原始购买行为:金额完全一致 (±₹1)、同一商户/实体、扣款被分类为*购物*、在 60 天的前向时间窗口内、一对一匹配。这样,被报销的费用就不会扭曲您的真实支出 (`net_amount`)。为了防止工资、利息或 P2P 转账产生的误报,该机制被刻意设计得非常保守。
- **反馈记忆循环** — 用户的更正会被持久化到按用户划分的 `entity_memory` 表中,并应用于所有匹配的交易,因此分类器会随着每次编辑变得更加智能。来自指定**所有者账户**的更正还会额外传播到共享的 `global_entity_memory` 存储中,从而改善所有用户的分类。
- **AI 驱动的自然语言查询 (Gemini + 意图路由)** — 一个关键词预分类器将每个问题路由到成本最低且正确的处理程序(订阅 / 建议 / SQL),从而最大限度地减少 LLM 调用。SQL 问题会被转换为沙盒化的、经过验证的 `SELECT` 语句,并由只读数据库角色执行。
- **通过电子邮件重置密码** — 一个安全的忘记密码流程 (Flask-Mail),使用无状态的、已签名的 `itsdangerous` token(30分钟有效期;数据库中不存储重置 token 列)。
- **交互式 Plotly 仪表板 + 专属交易视图** — 类别明细呈现为可点击的圆环图,并附带月度趋势图;点击类别切片或特定月份,可深入到专属的**交易**页面 (`/transactions`),该页面提供完整的搜索、类别过滤、排序、分页加载和内联重新分类功能。可折叠的趋势、异常和订阅面板以及响应式的深色/浅色主题构成了完整的 UI。
- **去重 (SHA-256 + MD5)** — 每个上传文件的 SHA-256 哈希值会拦截重复上传;每笔交易的 MD5 指纹强制执行行级唯一性,因此重新上传重叠的流水绝不会导致重复计算。
## 系统架构
```
┌──────────┐ ┌─────────┐ ┌──────────────┐ ┌────────┐ ┌──────────┐ ┌────────────┐ ┌────────┐
│ UPLOAD │──▶│ PARSE │──▶│ CATEGORIZE │──▶│ STORE │──▶│ ANALYZE │──▶│ VISUALIZE │──▶│ QUERY │
└──────────┘ └─────────┘ └──────────────┘ └────────┘ └──────────┘ └────────────┘ └────────┘
.xls/.xlsx bank detect 5-step priority Postgres anomalies/ Plotly NL → AI
+ SHA-256 HDFC / SBI + confidence (Neon) subscriptions dashboard (Gemini)
dedup parser + MD5 fingerprint reimbursements drill-down intent-routed
```
**应用设计。** 后端遵循 **Flask 应用工厂模式**(`app/__init__.py` 中的 `create_app()`),功能被拆分到各个 **Blueprint** 中 — `api`(数据 + 处理)、`auth`(登录/注册/重置)、`main`(页面路由)和 `ai`(自然语言查询)。横切关注点(SQLAlchemy、Migrate、Login、Limiter、CSRF、Mail)作为扩展注册到工厂中,使应用程序保持可测试性且无导入循环。
**分离部署:Render + Neon。** 应用服务器和数据库被刻意分离开来:
- **Render** 托管 Flask Web 服务(通过 `wsgi.py` 在 **Gunicorn** 下运行),负责处理 HTTP、解析和分析计算。
- **Neon** 提供无服务器架构的 **PostgreSQL** 作为托管的、自动扩缩容的数据层。
这种计算与存储的分离意味着无状态的 Web 层可以在不触及数据的情况下重启或扩缩容,并且 Neon 的连接池和冷启动行为在引擎配置中得到了明确处理(`pool_pre_ping`、`pool_recycle=300`、连接 + 语句超时以及 TCP keepalive)。AI 查询路径在断开连接时还会自动重试一次,因此 Neon 冷启动只会表现为短暂的延迟,而不是错误。AI 引擎通过**独立的只读角色** (`AI_DB_URL`) 连接到*同一个* Neon 数据库,在数据库层面强制执行最小权限原则。
## 技术栈
| 层级 | 技术 |
|---|---|
| **Backend** | Python 3.11+, Flask 3.1, Flask-SQLAlchemy 3.1, Flask-Migrate 4.1, Flask-Login 0.6, Flask-WTF (CSRF), Flask-Limiter 4.1, Flask-CORS, Flask-Mail |
| **数据库** | PostgreSQL (Neon, 无服务器) via SQLAlchemy 2.0 + `psycopg2-binary` |
| **AI / 数据** | Google Gemini 2.5 Flash (`google-generativeai` 0.8), pandas 2.3, NumPy 2.4 |
| **解析** | `openpyxl` 3.1, `xlrd` 2.0 (多引擎 Excel 回退) |
| **Frontend** | 服务端渲染 Jinja2 模板, 原生 JavaScript, Plotly.js, HTML5 / CSS3 (Bootstrap + Tailwind via CDN) |
| **安全** | Werkzeug 密码哈希 (PBKDF2-SHA256), `itsdangerous` token, CSRF 防护, 只读数据库角色, 速率限制 |
| **部署** | Render (Gunicorn 26) + Neon PostgreSQL; Sentry SDK 错误监控 |
## 项目结构
```
Personal-Financial-Intelligence-System/
├── app/
│ ├── __init__.py # App factory: extensions + blueprint registration
│ ├── models.py # SQLAlchemy models (6 tables)
│ ├── cli.py # Custom CLI commands (deduplicate, clear-data, backfill-global-memory)
│ ├── api/ # Blueprint: upload, transactions, insights, corrections
│ ├── auth/ # Blueprint: registration, login, password reset
│ ├── main/ # Blueprint: landing, dashboard + transactions page routes
│ ├── ai/
│ │ └── query_engine.py # NL → intent routing → SQL/opinion/subscription
│ ├── analytics/
│ │ ├── bank_detector.py # Auto-detects bank, dispatches to the right parser
│ │ ├── bank_statement_parser.py # HDFC statement parser (multi-format)
│ │ ├── sbi_parser.py # SBI statement parser
│ │ ├── categorization.py # SmartCategorizer — the 5-step priority pipeline
│ │ ├── entity_resolver.py # Entity extraction + platform/person/merchant typing
│ │ ├── entity_memory.py # Legacy JSON heuristic cache (dormant — DB memory is canonical)
│ │ ├── anomaly_detector.py # Z-score anomaly detection per category/month
│ │ ├── subscription_auditor.py# Recurring-payment detection + cost-trend flags
│ │ ├── reimbursement_detector.py # Strict refund-to-purchase matching
│ │ └── temporal_insights.py # MoM trends, growth, spending acceleration
│ ├── templates/ # Jinja2 templates (auth, dashboard, legal, base)
│ └── static/ # CSS, JS, images, legal documents
├── migrations/ # Alembic migration history (flask db upgrade)
├── data/
│ └── entity_memory.json # Legacy heuristic cache (no longer written to)
├── config.py # Config object (env-driven)
├── flask_app.py # Local dev entry point (app.run)
├── wsgi.py # Production entry point (gunicorn wsgi:app)
├── Procfile # Process definition: web: gunicorn wsgi:app
├── render.yaml # Render service + env-var config
├── .python-version # Pins Python 3.11 on deploy
└── requirements.txt
```
## 设置与安装
### 前置条件
- Python **3.11+**
- 一个 PostgreSQL 数据库(免费的 [Neon](https://neon.tech) 项目即可完美运行)
- 一个免费的 [Google AI Studio](https://aistudio.google.com) API key 用于 Gemini
### 1. 克隆并创建虚拟环境
```
git clone https://github.com/ShashwatNarayan/Personal-Financial-Intelligence-System.git
cd Personal-Financial-Intelligence-System
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
```
### 2. 安装依赖项
```
pip install -r requirements.txt
```
### 3. 配置环境变量
在项目根目录下创建一个 `.env` 文件(显示的值为占位符——切勿提交真实的机密信息):
```
# Required to boot
DATABASE_URL=postgresql://:@/ # full-access app role
SECRET_KEY= # app refuses to start if unset
# Required for the AI query feature
AI_DB_URL=postgresql://:@/ # read-only role for the AI engine
GEMINI_API_KEY=
# Required for the password-reset feature
MAIL_USERNAME=
MAIL_PASSWORD=
# Optional
OWNER_EMAIL=
# RECAPTCHA_SITE_KEY / RECAPTCHA_SECRET_KEY (reCAPTCHA v3 — wired but currently disabled)
```
### 4. 应用数据库迁移
```
flask db upgrade
```
### 5. 运行应用
```
python flask_app.py
```
在浏览器中打开 **http://localhost:5000**。
位于 **[arthalens.onrender.com](https://arthalens.onrender.com/)** 的生产部署在 Render 上运行,使用 `gunicorn wsgi:app`,并且数据库迁移 (`flask db upgrade`) 作为构建步骤的一部分自动运行。
## 工作原理 — 流水线深度解析
### 1. 5步分类优先级
当处理一笔交易时,`SmartCategorizer` (`app/analytics/categorization.py`) 首先解析实体(商户 / 个人 / 平台),然后遍历一个有序的优先级链,并在第一次自信匹配时停止——最强的信号优先:
1. **用户专属数据库实体记忆(您的更正)** → 如果该用户之前更正过此实体且存储的置信度 ≥ 0.9,则该类别以 **high** 置信度胜出。这就是反馈循环带来的回报。
2. **全局实体记忆(跨用户,所有者初始化)** → 一个由指定所有者账户进行更正的共享存储;如果命中,则以 **high** 置信度应用受信任的类别,并将这种管理策略传播给每个用户。*(此处还存在一个遗留的共享 JSON 缓存,但出于隐私原因已禁用。)*
3. **基于实体的分类** → 解析器将实体分类为*平台*(例如 Swiggy、Netflix → 高置信度)、*个人*(UPI 对端 → `Transfer / P2P`,中置信度)或*商户*,并直接映射已知的平台。
4. **关键词匹配** → 一个精选的类别-关键词词典(食品与餐饮、交通、购物、 utilities、娱乐、医疗保健、租金、教育、ATM/现金)提供了 **medium** 置信度的回退。
5. **默认** → 任何未匹配的内容都会在 **low** 置信度下被标记为 **Other**,并排入队列等待用户审查。
用户和所有者的更正会被写回记忆中,因此系统会不断改进。
### 2. AI 查询意图路由
与其将每个问题都发送给 LLM,不如让 `query_engine.py` 运行一个低成本的关键词预分类器 (`_detect_intent`),将其路由到成本最低且正确的处理程序:
- **`subscription`** → 直接由 `SubscriptionAuditor` 回答 — **不调用 LLM**。
- **`opinion`** → 在本地计算汇总统计数据(总计、月平均值、热门类别),然后通过**单次** Gemini 调用将它们转化为具有预算感知能力的叙述性答案。
- **`sql`** → Gemini 将问题翻译为 PostgreSQL 的 `SELECT` 语句,随后对其进行验证、限定在用户范围内,以只读方式执行,并通过第二次 Gemini 调用将数据行渲染为友好的答案(数据库值在到达 prompt 之前会被脱敏)。
这使得系统保持快速、低成本,并对 API 速率限制具有弹性——三个意图中有两个完全不触及 LLM 配额。
### 3. 反馈循环
当用户在仪表板中编辑类别时:
1. 新的类别会根据允许的类别集进行验证,然后记录到 `corrections` 表中(`old → new` 的审计跟踪)。
2. 该映射将以提升后的置信度 upsert 到用户专属的 `entity_memory` 表中。
3. 来自同一实体的**所有**现有交易都将被重新标记。
4. 未来的上传会命中分类链的第 1 步,并自动继承该更正。
5. 如果更正来自所有者账户,它也会被写入 `global_entity_memory`,从而改善每个用户的分类。
模型并没有被重新训练——它只是在*记忆*,这比重新训练更快、完全可解释,并且(对于单个用户而言)对该账户是私密的。
## 数据库 Schema
六张表,具有用户作用域的级联删除和用于幂等导入的唯一约束:
| 表 | 描述 |
|---|---|
| **`users`** | 账户记录 — 电子邮件、哈希密码、时间戳;所有按用户划分的数据的根。 |
| **`transactions` | 每笔解析后的交易 — 日期、实体、金额、类别、类型、置信度、报销标志,以及强制执行按用户划分的行唯一性的 MD5 `fingerprint`。 |
| **`entity_memory`** | 按用户划分的学习到的实体 → 类别映射,包含置信度和更正计数(支持分类的第 1 步)。 |
| **`global_entity_memory`** | 跨用户的、所有者初始化的实体 → 类别映射,在所有账户间共享(支持分类的第 2 步)。数据库级别的 CHECK 约束确保每个 `entity_name` 都以小写形式存储并去除首尾空格。 |
| **`corrections`** | 不可变的审计日志,记录每次用户重新分类 (`old_category → new_category`)。 |
| **`uploads_log`** | 每个上传流水对应一行 — 文件名、检测到的银行、行数,以及一个阻止重复上传的 SHA-256 `file_hash`。 |
## 安全
- **密码哈希** — 凭据以带盐的 Werkzeug 哈希值 (PBKDF2-SHA256) 的形式存储;从不持久化明文密码。
- **快速失败机密** — 如果未设置 `SECRET_KEY`,应用将拒绝启动。
- **只读 AI 数据库角色** — AI 查询引擎通过专用的最小权限 `AI_DB_URL` 角色连接,因此生成的查询在物理上*无法*修改数据。
- **自动注入 `user_id`** — 每个 AI 生成的 SQL 查询都会在服务器端被重写,强制 `user_id = `,无论 LLM 生成什么,都保证账户之间严格的数据隔离。
- **经过验证的、沙盒化的 SQL** — 生成的 SQL 必须是单个 `SELECT`;危险关键词 (`DROP / DELETE / INSERT / UPDATE / ALTER / TRUNCATE / GRANT / REVOKE`)、集合操作 (`UNION / EXCEPT / INTERSECT`)、堆叠语句 (`;`) 以及对敏感表(如 `users`)的访问在执行前都会被拒绝,并且自动 `LIMIT` 会限制结果大小。
- **Prompt 注入缓解** — 在将数据库值插入到 LLM prompt 之前对其进行脱敏,降低了精心构造的商户名称劫持响应的风险。
- **输入验证** — 分类更正在存储前会根据允许的类别集进行验证。
- **无状态密码重置 token** — 重置链接使用签名的、具有过期时间的 `itsdangerous` token;数据库中不存储任何重置机密。
- **CSRF 防护** — 所有改变状态的表单提交都受 Flask-WTF CSRF token 保护。
- **速率限制** — Flask-Limiter 对身份验证和 API 请求进行限流,以保护应用程序和 LLM 配额。
## AI 归属
## 法律
在应用内的 **`/privacy`** 和 **`/terms`** 路径下提供了**隐私政策**和**服务条款**。
## 作者
**Shashwat Narayan**
GitHub: [ShashwatNarayan/Personal-Financial-Intelligence-System](https://github.com/ShashwatNarayan/Personal-Financial-Intelligence-System)
## 许可证
基于 **MIT License** 发布 — 详情请参阅 [LICENSE](LICENSE)。
标签:后端开发, 测试用例, 逆向工具