# ai-ledge **Repository Path**: icprozl/ai-ledge ## Basic Information - **Project Name**: ai-ledge - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-01 - **Last Updated**: 2026-07-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI 智能记账本 基于 PyQt6 的本地化桌面记账应用,集成 **Whisper.cpp 语音识别** + **Ollama Qwen2.5 自然语言理解**,说话或打字即可自动记账。所有数据与 AI 模型均在本地运行,隐私零泄露。 --- ## 技术栈 | 层级 | 技术 | |------|------| | 桌面 UI | PyQt6 + QSS 自定义样式 | | 语音识别 | Whisper.cpp (`ggml-tiny.bin`) | | NLP 语义解析 | Ollama + Qwen2.5 1.5B(HTTP API) | | 离线兜底 | 正则 + 关键词匹配(无 AI 时可用) | | 数据库 | SQLite(WAL 模式) | | 导出 | openpyxl → Excel | | 环境 | Python 3.9 + conda | --- ## 架构设计 ``` ┌──────────────────────────────────────────────────────┐ │ main.py 入口 │ │ 启动预热 → 初始化数据库 → 检查模型 │ └────────────┬────────────────────────────┬────────────┘ │ │ ┌────────▼────────┐ ┌───────▼────────┐ │ frontend/ │ │ backend/ │ │ (PyQt6 UI) │◄────────►│ (业务逻辑) │ │ │ 信号槽 │ │ │ main_window.py │ │ services/ │ │ ├─ home_page │ │ ├─ bill_service│ │ ├─ stats_page │ │ ├─ budget_svc │ │ └─ budget_page │ │ ├─ stats_svc │ │ │ │ └─ export_svc │ │ widgets/ │ │ │ │ └─ voice_button│ │ ai/ │ │ │ │ ├─ nlp_parser │ │ resources/ │ │ ├─ model_mgr │ │ └─ style.qss │ │ └─ speech_rec │ └─────────────────┘ │ │ │ database/ │ │ └─ connection │ └─────────────────┘ ┌──────────────────────────────────────────────────────┐ │ 外部依赖(本地运行) │ │ Ollama Server (localhost:11434) + Qwen2.5:1.5B │ │ Whisper.cpp (whisper-cli.exe + ggml-tiny.bin) │ └──────────────────────────────────────────────────────┘ ``` ### 页面设计 | 页面 | 功能 | |------|------| | **记账页** (HomePage) | AI 输入栏 + 语音按钮 + 手动表单 + 账单列表 + 预算进度条 | | **统计页** (StatsPage) | 饼图 + 折线趋势图 + 分类排行榜 | | **预算页** (BudgetPage) | 总预算 + 分类预算设置 | ### 数据流 ``` 用户语音 → VoiceButton(按住录音) → Whisper转录 → AI解析 用户文字 → 输入框 → 点击"AI 识别" → Ollama NLP解析 │ ┌─────────────────────────────┘ ▼ {category, amount, type, note} │ ▼ 自动填入表单 → 用户核对 → 手动点保存 → 刷新列表/预算条 ``` --- ## 项目结构 ``` ai-ledger/ ├── main.py # 程序入口,初始化DB、模型预热、启动UI ├── requirements.txt # 依赖列表 ├── README.md # 本文件 ├── frontend/ # PyQt6 前端层 │ ├── main_window.py # 主窗口:工具栏 + QStackedWidget 页面路由 │ ├── pages/ │ │ ├── home_page.py # 记账主页(AI输入+语音+表单+列表+预算条) │ │ ├── stats_page.py # 统计分析页(饼图+折线图+排行) │ │ └── budget_page.py # 预算设置页 │ ├── widgets/ │ │ └── voice_button.py # 语音按钮(按住录音,松开识别) │ └── resources/ │ └── style.qss # 全局 QSS 样式表 ├── backend/ # 业务逻辑 + 数据层 │ ├── database/ │ │ └── connection.py # SQLite 单例连接(WAL模式,自动建表) │ ├── services/ │ │ ├── bill_service.py # 账单 CRUD │ │ ├── budget_service.py # 预算管理 + 进度计算 │ │ ├── stats_service.py # 统计分析(饼图/趋势/排行/SQL聚合) │ │ └── export_service.py # Excel 导出(openpyxl) │ └── ai/ │ ├── nlp_parser.py # Ollama HTTP API NLP 解析 + 离线正则兜底 │ ├── model_manager.py # AI 模型生命周期管理 + 预热 │ └── speech_recognizer.py # Whisper.cpp 语音转文字封装 └── data/ └── ledger.db # SQLite 数据库文件(自动创建) ``` --- ## 开发过程记录 ### 阶段一:项目搭建 → 启动崩溃修复 **问题 1:`import pyaudio` 导致启动崩溃** - **现象**:`main.py` 启动时报 `ModuleNotFoundError: No module named 'pyaudio'`,整个应用无法启动 - **原因**:`voice_button.py` 顶部 `import pyaudio`,该库未安装且仅在录音时使用 - **解决**:移除顶层 `import pyaudio`,改为在 `_start_recording()` 方法内延迟导入并包裹 try-except - **影响文件**:`frontend/widgets/voice_button.py` **问题 2:`AA_UseHighDpiPixmaps` 属性不存在** - **现象**:`AttributeError: AA_UseHighDpiPixmaps` - **原因**:Qt 6.0 移除了 `Qt.ApplicationAttribute.AA_UseHighDpiPixmaps`,高 DPI 渲染默认启用 - **解决**:删除 `app.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps)` 行,同时清理不再使用的 `from PyQt6.QtCore import Qt` - **影响文件**:`main.py` ### 阶段二:NLP 自然语言识别失败 **问题 3:输入"我吃饭吃了100",AI 听不懂** - **现象**:用户输入自然语言后,AI 解析结果金额为 0 或分类错误 - **原因分析**: 1. Ollama 未安装,系统走离线正则兜底 2. 金额正则 `(\d+)\s*(?:元|块|¥)` 要求必须有单位,"100" 裸数字匹配不到 3. 关键词覆盖少,很多常见词汇未命中 - **解决**: 1. 金额正则改为 `(\d+)\s*(?:元|块|¥|块钱)?`,单位可选 2. 关键词从 20+ 扩展到 60+,覆盖更多场景 3. 增加收入关键词优先判断,避免误判 4. 提取备注时自动去除金额部分 - **影响文件**:`backend/ai/nlp_parser.py` ### 阶段三:安装 Ollama **问题 4:Ollama 未安装** - **解决**: 1. 下载 OllamaSetup.exe 并静默安装 2. 设置 `OLLAMA_MODELS=E:\OllamaModels` 环境变量,模型存到 E 盘 3. 拉取 `qwen2.5:1.5b` (986MB) 模型 4. GPU 加速自动启用(NVIDIA RTX 5060 8GB) **问题 5:Python 找不到 Ollama** - **现象**:conda 环境的 Python 在 PATH 中找不到 `ollama.exe` - **原因**:`subprocess.run(["ollama", ...])` 依赖系统 PATH - **解决**:自动搜索常见安装路径(`%LOCALAPPDATA%\Programs\Ollama`、`Program Files`、`E:\Ollama`、系统 PATH),再回退到 HTTP API 方式 - **影响文件**:`backend/ai/nlp_parser.py`、`backend/ai/model_manager.py` **问题 6:Python 3.9 不支持 `str | None` 类型注解** - **现象**:`TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'` - **原因**:`str | None` 联合类型语法是 Python 3.10+ 特性,conda 环境为 Python 3.9 - **解决**:改为注释式类型注解 `# type: () -> str | None` - **影响文件**:`backend/ai/nlp_parser.py`、`backend/ai/model_manager.py` ### 阶段四:AI 解析性能优化 **问题 7:AI 反馈太慢(2.5 秒)** - **现象**:每次 AI 解析需要 2.5 秒以上 - **根因分析**: | 方案 | 冷启动 | 热缓存 | |------|--------|--------| | `ollama run` CLI 子进程(旧) | ~2.5s | ~0.5s | | HTTP API 直连(新) | ~2.4s | ~0.4s | 真正的问题是**冷启动**:模型从磁盘加载到 GPU 需要 ~2.4 秒。用 CLI 子进程每次启动新进程,模型用完可能被卸载,导致频繁冷启动。 - **解决方案(三项优化)**: 1. **CLI → HTTP API**:`subprocess.run(["ollama", "run", ...])` 改为 `POST http://localhost:11434/api/generate` - 消除子进程启动开销 - 模型在 Ollama 服务中常驻,不会随进程结束而卸载 2. **启动预加载**:应用启动时后台发送预热请求,模型提前加载到 GPU - 用户打开应用后第一次输入就是热缓存速度 3. **延长驻留时间**:每次请求设置 `keep_alive: "30m"`,模型查询后继续驻留 30 分钟 - 防止用户短暂离开后回来又要冷启动 - **最终效果**:每次查询 **2.5s → 0.4s**,快 6 倍 - **影响文件**:`backend/ai/nlp_parser.py`、`backend/ai/model_manager.py`、`main.py` **问题 8:GBK 编码错误** - **现象**:subprocess 后台线程报 `UnicodeDecodeError: 'gbk' codec can't decode byte 0x99` - **原因**:Windows 下 Python subprocess 默认使用 GBK 解码输出 - **解决**:subprocess.run 统一添加 `encoding="utf-8", errors="replace"`;后续改为 HTTP API 后彻底消除此问题 - **影响文件**:`backend/ai/nlp_parser.py`、`backend/ai/model_manager.py` ### 问题汇总 | # | 问题 | 类型 | 解决方式 | |---|------|------|----------| | 1 | `pyaudio` 未安装导致启动崩溃 | 依赖 | 延迟导入 + try-except | | 2 | `AA_UseHighDpiPixmaps` 已废弃 | API 变更 | 删除该行 | | 3 | 正则金额匹配不认裸数字 | 逻辑缺陷 | 金额正则单位改为可选 | | 4 | 关键词覆盖不足 | 功能不完整 | 关键词 20→60+ | | 5 | Ollama 不在 PATH | 路径 | 自动搜索 + HTTP API | | 6 | `str \| None` 不兼容 Python 3.9 | 语法 | 注释式类型注解 | | 7 | AI 解析 2.5s 太慢 | 性能 | HTTP API + 预加载 + keep_alive | | 8 | GBK 编码报错 | 编码 | utf-8 + HTTP API 消除 | --- ## 已知缺陷 以下问题已识别但暂未修复,按优先级排列: | # | 问题 | 影响范围 | 说明 | |---|------|----------|------| | 1 | **数据库连接可能泄漏** | `backend/database/connection.py` | 所有 Service 手动 `get_connection()` + `close()`,中间抛异常时 `close()` 不执行。应改为 `with` 上下文管理器 | | 2 | **Whisper 语音模型未下载** | `backend/ai/speech_recognizer.py` | `ggml-tiny.bin` 和 `whisper-cli.exe` 未随项目分发,需首次启动时下载(~75MB),但下载逻辑 `ensure_whisper()` 目前仅在手动调用时触发 | | 3 | **Ollama 服务未自动启动** | `backend/ai/nlp_parser.py` | 应用假定 Ollama 已在后台运行,若服务未启动则静默回退到离线正则,无提示告知用户 | | 4 | **离线正则金额误匹配** | `backend/ai/nlp_parser.py` | `_fallback_parse` 中金额正则 `(\d+)` 可能误匹配电话号码、日期、数量等非金额数字 | | 5 | **无批量操作支持** | `backend/services/bill_service.py` | `delete_all()` 只能全清,不支持按日期范围/分类批量删除 | | 6 | **主窗口关闭时未释放资源** | `main.py` | 未在 `closeEvent` 中停止 Ollama 模型驻留或关闭数据库连接池 | --- ## 待优化模块 以下为架构和代码质量层面的改进方向,不影响功能但值得重构: | 模块 | 现状 | 改进方向 | |------|------|----------| | **home_page.py** | 415 行,UI + 业务逻辑混在一起 | 抽取业务逻辑到 Presenter/ViewModel,拆分控件构建方法 | | **stats_page.py** | QPainter 手绘图表 | 引入 pyqtgraph 或 matplotlib,减少 ~150 行绘图代码,支持交互 | | **voice_button.py** | 音频参数硬编码(16000Hz / 1024 buffer) | 提取为常量或配置项,支持设备选择 | | **nlp_parser.py** | system prompt 写死在代码里 | 移到配置文件或单独 `.txt` 文件,方便调优提示词 | | **style.qss** | 单一主题 | 支持浅色/深色主题切换 | | **bill_service.py** | 无分页,`get_recent(50)` 硬编码 | 加上分页参数 `offset/limit`,应对大数据量 | | **export_service.py** | 仅支持 Excel | 增加 CSV 导出(零依赖),支持 PDF 账单报告 | | **全局** | 缺少单元测试 | 为 Service 层和 NLP 解析器添加 pytest 用例 | | **全局** | 日志仅靠 print / QMessageBox | 引入 logging 模块,写入文件方便排查问题 | | **全局** | 无键盘快捷键 | 记账页 Ctrl+Enter 触发 AI 解析,Ctrl+S 保存等 | | **全局** | Ollama 模型固定为 qwen2.5:1.5b | 支持在设置中选择模型(qwen2.5:3b / llama3.2 等),利用更大模型提升精度 | --- ## 快速开始 ### 环境要求 - Windows 10/11 - Python 3.9+ - CUDA GPU(可选,加速 Ollama 推理) ### 安装步骤 ```bash # 1. 克隆项目 cd ai-ledger # 2. 安装 Python 依赖 pip install -r requirements.txt # 3. 安装 Ollama(如未安装) # 下载 https://ollama.com/download/OllamaSetup.exe 安装 # 4. 拉取 NLP 模型(约 1GB) ollama pull qwen2.5:1.5b # 5. 启动应用 python main.py ``` 首次启动时将自动: - 创建 `data/ledger.db` SQLite 数据库 - 后台预热 Ollama 模型到 GPU - 检测 Whisper 模型状态 ### 使用方式 | 方式 | 操作 | |------|------| | **文字记账** | 输入框输入 "午饭外卖25块" → 点击 "✨ AI记账" | | **语音记账** | 按住 🎤 按钮说话 → 松开自动识别并记账 | | **手动记账** | 填写金额/分类/日期 → 点击保存 | | **统计查看** | 点击 "📊 统计" 标签 | | **预算设置** | 点击 "💰 预算" 标签 | | **数据导出** | 点击 "📤 导出" → 生成 Excel 文件 |