基于LLM与OCR的银行流水智能分析系统:从PDF解析到财务预测
1. 项目概述当大语言模型遇上个人财务分析最近在折腾一个挺有意思的项目起因是我自己处理银行流水时被那些密密麻麻的交易记录搞得头大。每个月手动分类、统计、预测费时费力不说还容易出错。市面上虽然有一些记账软件但要么功能太死板要么对中文PDF流水的解析能力堪忧更别提基于历史数据给你一些个性化的财务洞察了。于是我就琢磨着能不能用现在火热的LLM大语言模型和自动化技术自己搭一套系统把从解析银行流水PDF到生成财务分析报告再到未来支出预测的整个流程给串起来。这个项目的核心目标很明确实现银行流水文档的端到端自动化处理与智能分析。它不是一个简单的PDF解析工具而是一个集成了文档理解、信息抽取、数据分析和预测建模的完整解决方案。想象一下你只需要把下载的银行流水PDF扔进去系统就能自动识别出每一笔交易的日期、金额、对方账户、摘要并智能地将其归类为“餐饮”、“交通”、“购物”等类别然后基于清洗后的结构化数据生成可视化的月度收支图表、消费结构饼图最后还能利用时间序列模型对你的未来支出进行趋势预测提醒你潜在的财务风险。这套系统非常适合两类人一是像我这样有技术背景希望用自动化解放双手并对个人财务有深度管理需求的开发者二是中小微企业主或自由职业者他们需要定期分析对公或对私流水但缺乏专业的财务团队。通过这个项目你不仅能得到一个实用的工具更能深入理解LLM在文档处理领域的落地、时间序列预测的实战以及如何构建一个数据驱动的自动化管道。2. 核心架构与工具选型思路一套系统的骨架决定了其稳定性和扩展性。在设计之初我重点考虑了以下几个维度的需求文档解析的准确性、信息处理的智能化、数据分析的深度以及整个流程的自动化程度。基于这些我选择了以下技术栈并阐述了背后的理由。2.1 文档解析层为什么选择OCRLLM双引擎银行流水PDF通常有两种格式一种是文本型PDF里面的文字可以直接复制另一种是扫描件或图像型PDF本质上是图片。对于前者用PyPDF2或pdfplumber直接提取文本效率很高。但现实很骨感很多银行提供的流水是后者或者是混合型包含印章、表格线等干扰信息。因此一个鲁棒的解析层必须能处理这两种情况。我采用了“OCR为主文本提取为辅”的双引擎策略。核心工具是PaddleOCR。选择它一是因为其对中文场景的识别精度经过大量实践验证特别是对印刷体中文和数字的识别效果很好二是因为它开源免费且提供了丰富的预训练模型能准确识别表格结构这对于提取交易列表至关重要。对于纯文本PDF则用pdfplumber作为快速通道备用。但OCR提取出来的是一行行零散的文本如何把它们组装成结构化的交易记录日期、摘要、收入、支出、余额传统规则方法如正则表达式对格式多变的不同银行流水适配成本极高。这里就是LLM大显身手的地方。我将OCR识别出的、按页整理好的文本块连同一些格式提示如“以下是某页银行流水文本”一起发送给大语言模型如GPT-4、Claude 3或开源的Qwen2.5-72B-Instruct指令其按照指定的JSON格式输出结构化的交易记录。LLM强大的上下文理解和指令跟随能力让它能很好地处理“摘要描述不规整”、“金额位置飘忽”等传统规则的噩梦场景。注意直接使用OCR结果调用LLM可能会因识别错误导致“垃圾进垃圾出”。一个有效的技巧是在发送给LLM前先对OCR结果进行简单的启发式清洗比如合并被错误断行的同一字段过滤掉明显无意义的字符这能显著提升后续步骤的准确性。2.2 数据处理与智能分类层LLM输出的结构化数据JSON格式是分析的基石。这一层主要负责数据清洗、标准化和智能分类。数据清洗包括处理金额中的千分位逗号、统一日期格式如将“2024年1月1日”转为“2024-01-01”、处理收入/支出标识有些流水用正负号有些用“收”“支”文字。这里用pandas配合自定义函数就能高效完成。交易分类是体现“智能”的关键。同样我们可以借助LLM。为每一笔交易我们提供“交易对方”和“摘要”信息让LLM从我们预定义的分类列表如餐饮美食、交通出行、生活缴费、文化娱乐、医疗健康、投资理财、收入等中选择最合适的一个。为了提高效率并降低成本这里可以采用“零样本”或“少样本”提示Few-Shot Prompting即给出几个正确分类的例子让模型学会分类标准。对于大量流水也可以考虑先用LLM标注几百条数据然后训练一个轻量级的文本分类模型如scikit-learn的TF-IDF 分类器或微调一个小的BERT模型来执行批量分类这样更经济。2.3 分析、预测与可视化层当数据被清洗和分类后就进入了核心的价值挖掘阶段。基础分析利用pandas和numpy可以轻松实现月度总收入/总支出、净储蓄、各分类消费占比、日均消费、最高单笔消费等。这些是财务健康度的基础指标。可视化使用matplotlib或更美观的seaborn、plotly来生成图表。例如用折线图展示月度净现金流趋势用饼图展示消费结构用柱状图对比各分类月度花费。自动化的报告可以将这些图表和关键指标整合到一个HTML或PDF文件中。时序预测是项目的进阶部分。我们可以将历史月度总支出或各分类支出构建为一个时间序列。使用statsmodels库中的SARIMAX模型或prophet库由Facebook开源来预测未来3-6个月的支出趋势。prophet对趋势变化点、季节性和节假日效应的处理非常友好且不需要像传统ARIMA模型那样进行复杂平稳性检验和参数调优对于非专业数据科学家更易上手。预测结果可以直观地告诉你照当前消费习惯下个月可能超支的风险有多大。2.4 技术栈清单与选型理由总结组件推荐工具/库选型理由与备注PDF解析pdfplumber,PyPDF2轻量处理文本型PDF效率高。pdfplumber在提取表格和文本位置信息上更优。OCR引擎PaddleOCR开源中文识别精度高支持表格识别社区活跃。替代方案可考虑Tesseract但中文需额外训练数据。大语言模型OpenAI GPT-4/3.5-Turbo, Anthropic Claude, 或开源模型Qwen, DeepSeek闭源API易用性最佳智能程度高。开源模型可本地部署数据隐私性极强但需要一定的GPU资源。数据处理pandas,numpy数据分析领域事实标准功能强大生态完善。可视化matplotlib,seaborn,plotlymatplotlib基础seaborn统计图表美观plotly交互性强适合网页报告。时序预测prophet,statsmodelsprophet简单直观自动化程度高适合有季节性的消费数据。statsmodels更传统、灵活可进行更复杂的计量经济学分析。任务编排Apache Airflow,Prefect, 或简单脚本 cron对于定期如每月运行的自动化流程需要调度工具。轻量需求用cron即可复杂依赖推荐Prefect它比Airflow更现代、易用。部署与交付本地脚本Web应用Streamlit/Dash或API服务FastAPI快速验证用脚本。交互式分析用Streamlit极简。需要集成到其他系统则用FastAPI构建REST API。这个选型平衡了能力、成本、开发效率和生态支持。它构建了一个从非结构化文档到结构化洞察的完整管道。3. 从零搭建分步实现核心流程理论讲完了我们动手把它搭起来。我会以一个典型的月度中文银行流水PDF扫描件为例拆解每一步的代码和操作。3.1 环境准备与依赖安装首先创建一个干净的Python虚拟环境这是管理项目依赖的好习惯。# 创建并激活虚拟环境以conda为例 conda create -n bank-statement-analyzer python3.10 conda activate bank-statement-analyzer # 安装核心依赖 pip install paddlepaddle paddleocr pdfplumber pandas numpy matplotlib seaborn plotly prophet scikit-learn # 如果使用OpenAI API pip install openai # 如果使用FastAPI构建Web服务 pip install fastapi uvicorn # 如果使用Streamlit构建界面 pip install streamlit对于PaddleOCR如果安装后运行时提示缺少某些库可能需要根据官方文档安装额外的系统依赖如libgl1-mesa-glx。在Linux和WSL2环境下通常比较顺利。3.2 第一步PDF解析与文本提取我们假设有一个名为statement_202405.pdf的扫描件PDF。首先尝试用pdfplumber直接提取文本如果提取出的文本很少或为空则判定为扫描件启动OCR流程。import pdfplumber from paddleocr import PaddleOCR import os def extract_text_from_pdf(pdf_path): 尝试提取PDF文本失败则返回None触发OCR all_text try: with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: page_text page.extract_text() if page_text: all_text page_text \n--- Page Break ---\n # 简单判断如果提取的文本长度小于100字符很可能是个扫描件 if len(all_text.strip()) 100: print(提取文本过少疑似扫描件启动OCR...) return None return all_text except Exception as e: print(f使用pdfplumber提取文本时出错: {e}) return None def ocr_pdf_to_text(pdf_path): 使用PaddleOCR对PDF进行OCR识别 # 初始化PaddleOCR使用中英文模型开启表格识别use_angle_clsTrue可启用方向分类 ocr_engine PaddleOCR(use_angle_clsTrue, langch, show_logFalse) result ocr_engine.ocr(pdf_path, clsTrue) # 整理OCR结果按页和行组织文本 ocr_text_by_page [] for page_idx, page in enumerate(result): page_text_lines [] for line in page: text line[1][0] # 识别出的文本 # 可以在这里加入置信度过滤例如 if line[1][1] 0.7: page_text_lines.append(text) # 将一页内的文本用换行符连接模拟原始布局 ocr_text_by_page.append(\n.join(page_text_lines)) # 用分页符连接各页文本方便后续处理 final_text \n--- Page Break ---\n.join(ocr_text_by_page) return final_text # 主流程 pdf_file statement_202405.pdf raw_text extract_text_from_pdf(pdf_file) if raw_text is None: raw_text ocr_pdf_to_text(pdf_file) print(OCR处理完成。) else: print(直接文本提取成功。) # 此时raw_text变量中包含了整个PDF的文本内容包含“--- Page Break ---”作为分页标记。实操心得PaddleOCR在处理清晰扫描件时速度很快但针对模糊、有复杂背景或盖章的流水识别率会下降。一个提升效果的小技巧是在OCR前可以先用opencv对PDF转换成的图像进行简单的预处理比如灰度化、二值化、去噪这能显著提升文字区域的对比度改善识别效果。不过对于大多数银行电子流水直接OCR的效果已经足够好。3.3 第二步调用LLM进行结构化信息抽取这是最核心也最体现“智能”的一步。我们将OCR得到的原始文本通过精心设计的提示词Prompt交给LLM来提取结构化信息。import openai # 示例使用OpenAI API若用其他模型需调整 import json import re # 假设你已经设置了OPENAI_API_KEY环境变量 client openai.OpenAI() def extract_transactions_with_llm(raw_text): 使用LLM从原始文本中提取结构化交易记录 # 1. 构造系统提示词定义角色和任务 system_prompt 你是一个专业的银行流水分析助手。你的任务是从用户提供的银行流水文本中准确提取出每一笔交易记录并以严格的JSON格式输出。 每笔交易记录必须包含以下字段 - date: 交易日期格式必须统一为YYYY-MM-DD。 - description: 交易描述或对方账户名保留原始信息。 - income: 收入金额如果没有收入则为0或空。 - expense: 支出金额如果没有支出则为0或空。 - balance: 交易后的余额。 注意金额字段请统一为数字浮点数去除货币符号和千分位逗号。日期请尽力识别并转换。 流水文本可能包含表头、页脚等无关信息请只提取有效的交易行。 # 2. 构造用户消息包含实际的流水文本 # 为了控制成本和处理长文本可以按页或分段处理。这里假设raw_text已经分页。 pages raw_text.split(--- Page Break ---) all_transactions [] for i, page_text in enumerate(pages[:3]): # 示例只处理前3页避免token超限 if not page_text.strip(): continue user_message f请从以下银行流水文本第{i1}页中提取所有交易记录 {page_text[:3000]} # 限制每页文本长度防止超出模型上下文 try: response client.chat.completions.create( modelgpt-4-turbo-preview, # 或 gpt-3.5-turbo 以节省成本 messages[ {role: system, content: system_prompt}, {role: user, content: user_message} ], temperature0.1, # 低温度保证输出稳定性 response_format{type: json_object} # 强制JSON输出仅部分模型支持 ) result_text response.choices[0].message.content # 解析JSON page_result json.loads(result_text) # 假设LLM返回的JSON中有一个transactions的列表 if transactions in page_result: all_transactions.extend(page_result[transactions]) else: # 如果返回的就是列表直接扩展 if isinstance(page_result, list): all_transactions.extend(page_result) except Exception as e: print(f处理第{i1}页时出错: {e}) continue return all_transactions # 执行提取 transactions extract_transactions_with_llm(raw_text) print(f共提取到 {len(transactions)} 笔交易记录。) print(json.dumps(transactions[:2], indent2, ensure_asciiFalse)) # 打印前两笔看看重要提示直接使用GPT-4等API会产生费用且长文本需要切分处理。对于生产环境或隐私要求高的场景强烈建议使用本地部署的开源大模型如Qwen2.5-72B-Instruct。可以使用vLLM或llama.cpp等推理框架进行高效部署和调用提示词设计逻辑是通用的。本地部署虽然初期设置复杂但无使用成本数据完全私有。3.4 第三步数据清洗与智能分类LLM返回的数据可能仍有一些不一致我们需要进行清洗并为其添加分类标签。import pandas as pd import numpy as np from datetime import datetime def clean_and_classify_transactions(transactions_list): 清洗交易数据并添加分类 df pd.DataFrame(transactions_list) # 1. 数据清洗 # 确保金额是数值型 for col in [income, expense, balance]: if col in df.columns: # 去除逗号转换为浮点数 df[col] pd.to_numeric(df[col].astype(str).str.replace(,, ), errorscoerce) # 清洗日期字段 if date in df.columns: # 尝试多种日期格式解析 df[date] pd.to_datetime(df[date], errorscoerce, formatmixed) # 删除无法解析日期的行 df df.dropna(subset[date]) # 2. 智能分类示例使用规则关键词生产环境建议用LLM或训练模型 # 先定义一个简单的关键词映射作为后备 category_keywords { 餐饮美食: [餐厅, 饭店, 快餐, 咖啡, 奶茶, 美团, 饿了么, 超市食品], 交通出行: [地铁, 公交, 滴滴, 出租车, 加油, 停车, 高速, ETC], 生活缴费: [水费, 电费, 燃气费, 话费, 网费, 物业费], 文化娱乐: [电影, 演出, 视频会员, 游戏, 书店, 旅游], 购物消费: [淘宝, 京东, 拼多多, 商场, 服饰, 数码], 投资理财: [基金, 股票, 理财, 保险, 定投], 收入: [工资, 薪资, 转账收入, 奖金, 报销], 其他: [] # 默认类别 } def categorize_transaction(description): if not isinstance(description, str): return 其他 desc_lower description.lower() for category, keywords in category_keywords.items(): for kw in keywords: if kw.lower() in desc_lower: return category return 其他 df[category] df[description].apply(categorize_transaction) # 3. 高级分类调用LLM对“其他”类或不确定的交易进行精准分类可选 # 这里可以设计一个函数将描述发送给LLM让其从预定义列表中选择。 # 为节省成本可以只对规则分类结果为“其他”的记录进行LLM分类。 return df # 执行清洗与分类 df_cleaned clean_and_classify_transactions(transactions) print(df_cleaned.head()) print(f数据概览:\n{df_cleaned.info()}) print(f分类统计:\n{df_cleaned[category].value_counts()})清洗后的DataFrame是一个干净的结构化数据集包含了日期、描述、收入、支出、余额和类别为后续分析做好了准备。3.5 第四步基础财务分析与可视化现在我们可以轻松地计算各种财务指标并生成图表。import matplotlib.pyplot as plt import seaborn as sns plt.style.use(seaborn-v0_8-darkgrid) # 使用一个美观的样式 sns.set_palette(husl) def perform_basic_analysis(df): 执行基础财务分析并生成图表 # 确保有日期索引 df.set_index(date, inplaceTrue) df.sort_index(inplaceTrue) # 1. 月度聚合分析 # 将支出转为正数方便计算如果之前支出记录为负数这里需要调整 df[expense_positive] df[expense].abs() monthly_data df.resample(M).agg({ income: sum, expense_positive: sum, balance: last # 取月末余额 }).rename(columns{expense_positive: expense}) monthly_data[savings] monthly_data[income] - monthly_data[expense] print( 月度财务摘要 ) print(monthly_data) # 2. 消费分类统计最近一个月 latest_month df[df.index df.index.max().replace(day1)] # 获取本月数据 if not latest_month.empty: category_expense latest_month.groupby(category)[expense_positive].sum().sort_values(ascendingFalse) print(f\n 本月({latest_month.index[0].strftime(%Y-%m)})消费分类 ) print(category_expense) # 3. 可视化 fig, axes plt.subplots(2, 2, figsize(16, 12)) # 3.1 月度收支趋势 axes[0, 0].plot(monthly_data.index, monthly_data[income], markero, label收入, linewidth2) axes[0, 0].plot(monthly_data.index, monthly_data[expense], markers, label支出, linewidth2) axes[0, 0].plot(monthly_data.index, monthly_data[savings], marker^, label结余, linestyle--, linewidth2) axes[0, 0].set_title(月度收入、支出与结余趋势, fontsize14, fontweightbold) axes[0, 0].set_xlabel(月份) axes[0, 0].set_ylabel(金额 (元)) axes[0, 0].legend() axes[0, 0].grid(True, alpha0.3) # 3.2 月度结余柱状图 axes[0, 1].bar(monthly_data.index.strftime(%Y-%m), monthly_data[savings], colornp.where(monthly_data[savings] 0, lightgreen, lightcoral)) axes[0, 1].axhline(y0, colorblack, linestyle-, linewidth0.8) axes[0, 1].set_title(月度净结余正为储蓄负为超支, fontsize14, fontweightbold) axes[0, 1].set_xlabel(月份) axes[0, 1].set_ylabel(结余 (元)) axes[0, 1].tick_params(axisx, rotation45) # 3.3 本月消费分类饼图 if not latest_month.empty and not category_expense.empty: axes[1, 0].pie(category_expense.values, labelscategory_expense.index, autopct%1.1f%%, startangle90) axes[1, 0].set_title(f本月({latest_month.index[0].strftime(%Y-%m)})消费结构, fontsize14, fontweightbold) else: axes[1, 0].text(0.5, 0.5, 暂无本月分类数据, hacenter, vacenter, fontsize12) axes[1, 0].set_title(本月消费结构, fontsize14, fontweightbold) # 3.4 余额变化曲线 axes[1, 1].plot(df.index, df[balance], marker., linestyle-, linewidth1, alpha0.7) axes[1, 1].set_title(账户余额变化曲线, fontsize14, fontweightbold) axes[1, 1].set_xlabel(日期) axes[1, 1].set_ylabel(余额 (元)) axes[1, 1].grid(True, alpha0.3) plt.tight_layout() plt.savefig(financial_analysis_report.png, dpi300, bbox_inchestight) plt.show() return monthly_data, category_expense # 执行分析 monthly_summary, category_spending perform_basic_analysis(df_cleaned.copy()) # 传入副本避免修改原数据运行这段代码你将得到一份包含四张关键图表的可视化报告直观展示了财务状况。3.6 第五步使用Prophet进行未来支出预测预测未来可以帮助我们更好地进行财务规划。这里使用Facebook的Prophet库它非常适合具有季节性的消费数据。from prophet import Prophet import pandas as pd def forecast_expenses(monthly_data, periods6): 使用Prophet预测未来支出 # Prophet要求输入列名为 [ds, y]这里我们用历史月度总支出 df_prophet monthly_data[[expense]].reset_index() df_prophet.columns [ds, y] # ds为日期y为数值支出 df_prophet[ds] df_prophet[ds].dt.to_timestamp() # 确保是时间戳格式 # 检查数据量Prophet至少需要2个周期数据 if len(df_prophet) 2: print(历史数据不足无法进行预测。) return None, None # 初始化并拟合模型 # 可以在这里添加节假日参数holidays或调整季节性和趋势参数 model Prophet( yearly_seasonalityTrue, # 默认开启年季节性 weekly_seasonalityFalse, # 月度数据不需要周季节性 daily_seasonalityFalse, seasonality_modemultiplicative # 季节性模式additive或multiplicative ) model.fit(df_prophet) # 创建未来时间框架 future model.make_future_dataframe(periodsperiods, freqM) # 预测未来periods个月 forecast model.predict(future) # 可视化预测结果 fig1 model.plot(forecast) plt.title(f月度支出预测未来{periods}个月, fontsize14, fontweightbold) plt.xlabel(日期) plt.ylabel(支出 (元)) plt.tight_layout() plt.savefig(expense_forecast.png, dpi300, bbox_inchestight) plt.show() # 可视化预测组件趋势、季节性 fig2 model.plot_components(forecast) plt.tight_layout() plt.savefig(forecast_components.png, dpi300, bbox_inchestight) plt.show() # 返回预测结果和模型 return forecast, model # 执行预测假设我们有足够的历史月度数据 if len(monthly_summary) 2: forecast_result, prophet_model forecast_expenses(monthly_summary, periods3) if forecast_result is not None: # 查看未来预测值 future_forecast forecast_result[[ds, yhat, yhat_lower, yhat_upper]].tail(3) print( 未来三个月支出预测 ) print(future_forecast.set_index(ds)) else: print(历史月度数据不足请累积至少两个月的数据后再进行预测。)Prophet会生成一张预测图展示历史数据、预测值以及不确定性区间。组件图则会分解出趋势和年度季节性让你清晰看到消费是否在增长以及是否有特定的月份如年底、假期消费会激增。4. 系统集成与自动化部署至此核心功能模块都已实现。但作为一个自动化系统我们需要将它们串联起来并考虑如何定期、自动地运行。4.1 构建自动化任务管道我们可以编写一个主脚本main_pipeline.py将上述所有步骤封装成函数并按顺序调用。为了处理可能出现的错误如API调用失败、文件不存在需要加入适当的异常处理和日志记录。# main_pipeline.py 示例框架 import logging from pathlib import Path logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) def main(): pdf_path Path(./inbox/statement.pdf) # 假设流水PDF放在inbox文件夹 output_dir Path(./reports) output_dir.mkdir(exist_okTrue) try: logger.info(开始处理银行流水: %s, pdf_path.name) # 1. 解析PDF raw_text extract_text_from_pdf(pdf_path) if raw_text is None: raw_text ocr_pdf_to_text(pdf_path) logger.info(PDF解析完成。) # 2. LLM信息抽取 transactions extract_transactions_with_llm(raw_text) logger.info(交易信息抽取完成共 %d 条记录。, len(transactions)) # 3. 数据清洗与分类 df clean_and_classify_transactions(transactions) # 保存原始数据 df.to_csv(output_dir / cleaned_transactions.csv, indexFalse, encodingutf-8-sig) logger.info(数据清洗完成已保存。) # 4. 分析与可视化 monthly_summary, category_spending perform_basic_analysis(df.copy()) monthly_summary.to_csv(output_dir / monthly_summary.csv) logger.info(基础分析完成图表已生成。) # 5. 预测如果数据足够 if len(monthly_summary) 2: forecast, _ forecast_expenses(monthly_summary, periods3) if forecast is not None: forecast.to_csv(output_dir / expense_forecast.csv, indexFalse) logger.info(支出预测完成。) logger.info(流水处理与分析全部完成报告保存在 %s 目录。, output_dir) except FileNotFoundError: logger.error(输入文件不存在: %s, pdf_path) except Exception as e: logger.exception(处理过程中发生未预期错误: %s, e) if __name__ __main__: main()4.2 部署方式选择根据使用场景可以选择不同的部署方式本地命令行工具这是最简单的形式。安装好依赖后定期将银行流水PDF放入指定文件夹如./inbox然后运行python main_pipeline.py。可以用操作系统的定时任务如Linux的cron或Windows的“任务计划程序”实现每月自动执行。Web应用Streamlit如果你想要一个漂亮的交互界面。Streamlit能让你快速构建一个Web应用上传PDF后点击按钮即可查看分析结果和图表。# app_streamlit.py import streamlit as st import pandas as pd from main_pipeline import * # 导入之前的函数需要适当调整 st.title( 智能银行流水分析系统) uploaded_file st.file_uploader(上传你的银行流水PDF文件, type[pdf]) if uploaded_file is not None: with st.spinner(正在解析和分析流水...): # 保存上传的文件 with open(./temp.pdf, wb) as f: f.write(uploaded_file.getbuffer()) # 调用处理管道 # ... (调用上述各步骤函数) # 显示结果 st.dataframe(df_cleaned) st.image(financial_analysis_report.png) st.image(expense_forecast.png)API服务FastAPI如果你希望其他系统如移动端App、其他后台服务也能调用这个分析能力。用FastAPI可以快速构建RESTful API接收PDF文件返回JSON格式的分析结果。# main_fastapi.py from fastapi import FastAPI, File, UploadFile from fastapi.responses import JSONResponse import tempfile import os from main_pipeline import * # 需要重构函数以支持内存处理 app FastAPI() app.post(/analyze-statement/) async def analyze_statement(file: UploadFile File(...)): # 保存上传的临时文件 with tempfile.NamedTemporaryFile(deleteFalse, suffix.pdf) as tmp: tmp.write(await file.read()) tmp_path tmp.name try: # 调用处理逻辑返回JSON结果 result your_processing_function(tmp_path) # 你的处理函数返回字典 return JSONResponse(contentresult) finally: os.unlink(tmp_path) # 删除临时文件4.3 成本控制与优化建议LLM API成本信息抽取和分类是调用LLM的主要开销。为了控制成本预处理过滤在调用LLM前用简单的正则表达式或规则先过滤掉明显非交易行的文本如页眉、页脚、广告。使用更便宜的模型对于分类任务可以尝试使用gpt-3.5-turbo其效果通常足够且成本远低于GPT-4。批量处理将一页内的所有交易一次性发送给LLM而不是逐条发送能减少请求次数和冗余的提示词token。本地模型长期来看在本地部署一个7B-14B参数量的优秀开源模型如Qwen2.5-7B-Instruct在消费级GPU上即可运行能彻底消除API成本和数据隐私顾虑。计算资源时序预测Prophet和OCRPaddleOCR在CPU上运行即可。如果处理大量文件可以考虑将OCR部分放到性能更强的机器上或者使用GPU加速PaddleOCR安装GPU版本的PaddlePaddle。5. 常见问题、避坑指南与进阶思路在实际搭建和运行过程中你肯定会遇到各种各样的问题。这里我总结了一些常见的坑和解决办法。5.1 信息抽取不准怎么办这是最常见的问题根源通常在于OCR识别错误或LLM提示词不精准。OCR精度提升图像预处理对于模糊的PDF先用pdf2image库将其转换为图片然后使用OpenCV进行预处理如灰度化、高斯模糊、二值化、形态学操作可以大幅提升PaddleOCR的识别率。调整OCR参数PaddleOCR初始化时可以调整det_db_thresh检测阈值、rec_db_thresh识别阈值等参数针对你的流水字体进行微调。尝试其他OCR引擎如果PaddleOCR对某种特定字体效果不佳可以尝试Tesseract并为其指定中文训练数据chi_sim。LLM提示词工程提供示例Few-Shot在系统提示词中直接给出一两条格式完美的交易记录示例让模型模仿输出格式。明确格式要求在提示词中严格规定JSON的字段名和类型甚至可以使用JSON Schema来描述。分而治之如果流水格式复杂可以设计多轮对话或分步骤提示。例如先让LLM识别出表格区域再对表格区域进行结构化提取。后处理校验对LLM输出的JSON进行格式和逻辑校验。例如检查日期是否合理、收入和支出是否同时有值、余额是否连续等对异常记录进行标记或重新处理。5.2 交易分类效果不佳简单的关键词匹配覆盖有限且无法处理新出现的商户名。构建更全的关键词库持续收集交易描述人工标注一批数据不断扩充关键词库。可以利用LLM帮你从描述中提取核心实体如“XX餐饮店”-“餐饮”。采用文本分类模型用LLM标注500-1000条高质量的分类数据。使用scikit-learn的TfidfVectorizer提取文本特征然后用LogisticRegression或SVM训练一个分类器。这种方法速度快效果也不错。如果想追求更高精度可以微调一个轻量级的预训练语言模型如BERT的中文变体bert-base-chinese。使用transformers库在小批量标注数据上微调几轮分类准确率通常能超过95%。这需要一些机器学习基础但一旦完成后续分类几乎零成本且速度快。5.3 预测模型不靠谱Prophet预测出的曲线可能很平或者波动奇怪。数据质量与长度Prophet需要足够的历史数据才能捕捉趋势和季节性。建议至少提供12个月一个完整周期以上的月度数据。数据越少预测不确定性越大。调整模型参数Prophet的changepoint_prior_scale参数控制趋势变化的灵活性seasonality_prior_scale控制季节性强度。可以通过交叉验证来调整这些超参数。考虑外部因素如果你的消费受特殊事件影响大如双十一、春节可以通过add_regressor方法将这些事件作为额外回归量加入模型。模型评估不要盲目相信预测结果。将历史数据分成训练集和测试集在测试集上评估预测误差如MAE, MAPE了解模型的预测能力究竟如何。5.4 如何扩展更多功能基础框架搭建好后可以很容易地加入新功能多账户/多币种支持在数据模型中增加account和currency字段。处理时根据流水来源识别账户汇率转换可以集成外部API。自定义规则与预警设置预算规则如“餐饮月度预算2000元”当实际消费接近或超过预算时系统自动发送邮件或消息通知集成钉钉、企业微信、Telegram Bot等。现金流预测不仅预测支出还可以结合已知的固定收入工资和定期账单预测未来几个月的现金流情况提前预警资金缺口。生成自然语言报告最后一步将分析结果关键指标、图表解读、预测结论再用LLM润色生成一段易于理解的、带有人性化建议的文本报告让分析结果更具可读性。这个项目从想法到实现是一个典型的“用技术解决生活痛点”的过程。它涉及了OCR、LLM、数据分析、可视化、预测建模等多个技术点的串联。最大的挑战往往不在某个单一技术的深度而在于如何让这些模块稳定、高效地协同工作并处理好各种边界情况和异常。当你看到系统自动吞下一份杂乱的PDF吐出一份清晰的分析报告和未来预测时那种成就感是非常实在的。希望这个详细的拆解能给你提供一个坚实的起点你可以根据自己的具体需求对这个系统进行裁剪、优化和扩展。