AI Agent Harness Engineering 财务领域应用:记账、报税与财务分析自动化
引言
在当今数字化快速发展的时代,人工智能(AI)正在深刻地改变着各个行业的运作模式,财务领域也不例外。传统的财务工作流程,如记账、报税和财务分析,往往需要大量的人工操作,不仅耗时耗力,还容易出现人为错误。随着AI技术的不断进步,特别是AI Agent(智能代理)技术的成熟,我们现在有能力实现财务工作的全面自动化,显著提高效率和准确性。
痛点引入
对于大多数企业和财务专业人士来说,日常的财务工作面临着以下几个主要挑战:
- 数据录入繁琐:手动录入大量的交易数据是一项极其枯燥且容易出错的工作。
- 合规要求复杂:税务法规和财务准则不断变化,保持合规性需要持续的学习和更新。
- 分析时效性差:传统的财务分析往往基于历史数据,难以及时提供决策支持。
- 资源消耗大:企业需要投入大量的人力物力来维持财务部门的正常运作。
- 数据孤岛问题:不同系统之间的数据难以整合,导致信息不完整。
这些问题不仅增加了企业的运营成本,也限制了财务部门从传统的"记账先生"向战略合作伙伴角色的转变。
解决方案概述
本文将介绍如何利用AI Agent Harness Engineering技术构建一个全面的财务自动化系统,实现记账、报税和财务分析的智能化和自动化。AI Agent是一种能够感知环境、做出决策并执行行动的智能系统,而Harness Engineering则是一种系统化的方法,用于设计、构建和管理这些AI Agent,使其能够可靠地执行复杂任务。
我们将探讨:
- 如何设计和训练财务领域的AI Agent
- 如何实现自动化的交易分类和记账
- 如何利用AI进行合规性检查和自动报税
- 如何构建智能财务分析系统,提供实时洞察
- 如何确保系统的安全性、可靠性和可扩展性
最终效果展示
在文章的最后,我们将展示一个完整的AI财务助手系统,该系统能够:
- 自动从银行对账单、发票和收据中提取数据
- 智能分类交易并自动记账
- 根据最新法规生成税务报告
- 提供实时财务分析和预测
- 通过自然语言界面回答用户的财务问题
这个系统将显著减少人工干预,提高财务工作的准确性和效率,同时为企业决策提供更有价值的洞察。
准备工作
在开始构建我们的AI财务自动化系统之前,我们需要准备必要的开发环境、工具和基础知识。本节将指导您完成这些准备工作。
环境/工具
为了实现财务领域的AI Agent应用,我们需要以下环境和工具:
-
编程语言:
- Python 3.8+:作为主要开发语言,拥有丰富的AI/ML库和财务工具
- JavaScript/TypeScript:用于构建前端界面和交互组件
-
AI/ML框架:
- LangChain:用于构建AI Agent应用的框架
- OpenAI API/Anthropic API:提供大语言模型能力
- Hugging Face Transformers:用于自然语言处理任务
- TensorFlow/PyTorch:用于自定义模型训练
-
财务工具和库:
- Pandas:用于数据处理和分析
- NumPy:用于数值计算
- Plotly/Matplotlib:用于数据可视化
- Beancount/ledger-cli:用于复式记账
- Tax-Calculator:用于税务计算(根据具体国家/地区选择)
-
开发和部署工具:
- Docker:用于容器化部署
- FastAPI/Flask:用于构建API服务
- PostgreSQL/MongoDB:用于数据存储
- Redis:用于缓存和会话管理
- Git:用于版本控制
-
开发环境设置:
# 创建虚拟环境 python -m venv ai-finance-env source ai-finance-env/bin/activate # Linux/Mac # 或 .\ai-finance-env\Scripts\activate # Windows # 安装核心依赖 pip install langchain openai pandas numpy plotly fastapi uvicorn sqlalchemy
基础知识
为了充分理解和实现本文中的内容,读者应该具备以下基础知识:
-
财务会计基础:
- 复式记账原理
- 财务报表(资产负债表、利润表、现金流量表)
- 基本税务概念和流程
-
编程基础:
- Python编程(数据结构、函数、类等)
- 基本的Web开发概念
- RESTful API设计原则
-
AI/ML基础:
- 大语言模型(LLM)的基本概念和应用
- Prompt工程的基本原则
- 向量数据库和相似度搜索的基本概念
-
推荐学习资源:
- 《财务会计教程》(Charles T. Horngren等著)
- LangChain官方文档:https://python.langchain.com/
- OpenAI API文档:https://platform.openai.com/docs
- Python for Data Analysis (Wes McKinney著)
核心概念
在深入探讨如何构建AI财务自动化系统之前,我们需要先了解一些核心概念,这些概念将贯穿全文,是我们后续讨论的基础。
AI Agent
核心概念:AI Agent(智能代理)是一种能够感知环境、做出决策并执行行动以实现特定目标的自主系统。在财务领域,AI Agent可以理解为一个"智能财务助手",它能够自动处理各种财务任务。
问题背景:传统的软件系统通常需要明确的指令和固定的流程,难以处理复杂、不确定或动态变化的任务。而AI Agent则能够利用大语言模型的理解和推理能力,处理更加复杂和灵活的财务工作。
核心要素组成:
- 感知模块:用于收集和理解环境信息(如银行交易、发票、市场数据等)
- 推理/决策模块:基于感知到的信息和目标进行推理和决策
- 行动模块:执行具体的财务操作(如记账、生成报告、发送提醒等)
- 记忆模块:存储历史信息和学习到的知识
概念结构:
Harness Engineering
核心概念:Harness Engineering( harness在这里可以理解为"驾驭"或"工程化管理")是一种系统化的方法论,用于设计、构建、测试和部署可靠的AI Agent系统。它关注的是如何将AI能力与软件工程最佳实践相结合,构建可信赖、可扩展的AI应用。
问题背景:虽然大语言模型展现出了强大的能力,但直接将其应用于生产环境面临着可靠性、一致性、安全性等多方面的挑战。Harness Engineering正是为了解决这些问题而诞生的。
核心要素组成:
- Agent设计模式:结构化的Agent架构设计方法
- 工具使用框架:让Agent能够安全有效地使用外部工具和API
- 评估与监控:持续监控Agent的性能和行为
- 安全与合规:确保Agent的操作符合安全规范和监管要求
- 反馈循环:收集用户反馈,持续改进Agent性能
财务自动化核心流程
核心概念:财务自动化核心流程是指将AI技术应用于记账、报税和财务分析等传统财务工作的标准化流程。
概念联系:
核心概念对比
| 概念 | 主要目标 | 关键技术 | 应用场景 | 优势 | 挑战 |
|---|---|---|---|---|---|
| 传统财务软件 | 流程标准化 | 关系数据库、工作流引擎 | 固定流程的财务处理 | 稳定、可预测 | 缺乏灵活性、难以处理非结构化数据 |
| 独立AI工具 | 特定任务优化 | 机器学习模型 | 发票识别、欺诈检测 | 精度高、自动化程度高 | 功能单一、集成困难 |
| AI Agent系统 | 端到端财务自动化 | LLM、Agent框架、工具集成 | 全流程财务处理 | 灵活性高、适应性强 | 复杂度高、可靠性挑战 |
| Harness Engineering | 可靠的AI系统构建 | 软件工程方法、AI测试框架 | AI应用开发和运维 | 提高AI系统可靠性 | 需要额外的工程投入 |
AI Agent财务系统的数学模型
在构建AI Agent财务系统时,我们需要用到一些数学模型来支持决策、预测和优化。本节将介绍几个关键的数学模型。
交易分类模型
交易分类是智能记账的核心,我们可以使用多类别分类模型来自动识别交易类型。
数学模型:
我们使用softmax回归来进行多类别交易分类。给定一个交易特征向量xxx,模型计算该交易属于类别kkk的概率:
P(y=k∣x)=exTwk∑j=1KexTwj P(y=k|x) = \frac{e^{x^T w_k}}{\sum_{j=1}^{K} e^{x^T w_j}} P(y=k∣x)=∑j=1KexTwjexTwk
其中,wkw_kwk是类别kkk的权重向量,KKK是类别的总数。
我们通过最小化交叉熵损失来训练模型:
L(w)=−∑i=1N∑k=1KyiklogP(yi=k∣xi;w) L(w) = -\sum_{i=1}^{N} \sum_{k=1}^{K} y_{ik} \log P(y_i=k|x_i; w) L(w)=−i=1∑Nk=1∑KyiklogP(yi=k∣xi;w)
其中,yiky_{ik}yik是指示变量,如果第iii个样本属于类别kkk则为1,否则为0。
财务预测模型
财务预测是财务分析的重要组成部分,我们使用时间序列模型来预测未来的财务指标。
数学模型:
我们使用ARIMA(自回归综合移动平均)模型来进行财务时间序列预测:
(1−∑i=1pϕiLi)(1−L)dYt=c+(1+∑i=1qθiLi)εt (1 - \sum_{i=1}^{p} \phi_i L^i) (1 - L)^d Y_t = c + (1 + \sum_{i=1}^{q} \theta_i L^i) \varepsilon_t (1−i=1∑pϕiLi)(1−L)dYt=c+(1+i=1∑qθiLi)εt
其中:
- YtY_tYt是时间序列值
- LLL是滞后算子,LkYt=Yt−kL^k Y_t = Y_{t-k}LkYt=Yt−k
- ppp是自回归阶数
- ddd是差分阶数
- qqq是移动平均阶数
- ϕi\phi_iϕi是自回归系数
- θi\theta_iθi是移动平均系数
- εt\varepsilon_tεt是白噪声误差项
对于具有季节性特征的财务数据,我们可以使用SARIMA(季节性ARIMA)模型:
(1−∑i=1pϕiLi)(1−∑i=1PΦiLiS)(1−L)d(1−LS)DYt=c+(1+∑i=1qθiLi)(1+∑i=1QΘiLiS)εt (1 - \sum_{i=1}^{p} \phi_i L^i)(1 - \sum_{i=1}^{P} \Phi_i L^{iS}) (1 - L)^d (1 - L^S)^D Y_t = c + (1 + \sum_{i=1}^{q} \theta_i L^i)(1 + \sum_{i=1}^{Q} \Theta_i L^{iS}) \varepsilon_t (1−i=1∑pϕiLi)(1−i=1∑PΦiLiS)(1−L)d(1−LS)DYt=c+(1+i=1∑qθiLi)(1+i=1∑QΘiLiS)εt
其中:
- SSS是季节性周期长度
- PPP是季节性自回归阶数
- DDD是季节性差分阶数
- QQQ是季节性移动平均阶数
- Φi\Phi_iΦi是季节性自回归系数
- Θi\Theta_iΘi是季节性移动平均系数
风险评估模型
在财务决策中,风险评估是非常重要的。我们使用VaR(风险价值)模型来评估财务风险。
数学模型:
VaR是指在一定置信水平1−α1-\alpha1−α下,投资组合在未来特定时期内可能遭受的最大损失:
P(L≤VaR1−α)=1−α P(L \leq VaR_{1-\alpha}) = 1-\alpha P(L≤VaR1−α)=1−α
其中,LLL是损失随机变量。
我们使用蒙特卡洛模拟来计算VaR:
- 定义投资组合的价值函数V(t)V(t)V(t)
- 模拟NNN个可能的未来价值V1(T),V2(T),...,VN(T)V_1(T), V_2(T), ..., V_N(T)V1(T),V2(T),...,VN(T)
- 计算每个模拟路径的损失Li=V(0)−Vi(T)L_i = V(0) - V_i(T)Li=V(0)−Vi(T)
- 对损失进行排序,找到第αN\alpha NαN个最小的损失,即为VaR
对于更复杂的财务风险评估,我们可以使用CVaR(条件风险价值),它衡量的是超过VaR的平均损失:
CVaR1−α=E[L∣L≥VaR1−α] CVaR_{1-\alpha} = E[L | L \geq VaR_{1-\alpha}] CVaR1−α=E[L∣L≥VaR1−α]
系统架构设计
在本节中,我们将详细介绍AI财务自动化系统的架构设计,包括系统的整体结构、各组件的功能和它们之间的交互关系。
系统整体架构
我们的AI财务自动化系统采用微服务架构,以确保系统的可扩展性、可维护性和容错性。整体架构分为以下几层:
核心组件设计
1. AI Agent协调服务
AI Agent协调服务是整个系统的大脑,负责任务分配、Agent调度和工作流管理。它使用状态机来管理复杂的财务流程:
2. 文档理解Agent
文档理解Agent负责处理各种非结构化财务文档,如发票、收据、银行对账单等。它结合了OCR技术和大语言模型,从文档中提取结构化信息。
核心功能:
- 文档分类(发票、收据、对账单等)
- 关键信息提取(日期、金额、交易方等)
- 表格数据提取和解析
- 异常检测和标记
3. 交易分类Agent
交易分类Agent负责对银行交易和财务记录进行自动分类,是智能记账的核心组件。
核心功能:
- 基于规则的初步分类
- 机器学习模型分类
- 分类置信度评估
- 人工反馈学习循环
4. 合规检查Agent
合规检查Agent确保财务操作符合相关法规和会计准则,是自动报税的关键保障。
核心功能:
- 法规知识库维护
- 交易合规性检查
- 税务计算和优化
- 风险评估和预警
系统接口设计
为了确保系统各组件之间的高效通信,我们设计了以下关键接口:
1. 任务管理API
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
from enum import Enum
class TaskType(str, Enum):
BOOKKEEPING = "bookkeeping"
TAX_FILING = "tax_filing"
FINANCIAL_ANALYSIS = "financial_analysis"
class TaskStatus(str, Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
FAILED = "failed"
REQUIRES_HUMAN_INTERVENTION = "requires_human_intervention"
class FinancialTask(BaseModel):
task_id: str = Field(..., description="任务唯一标识符")
task_type: TaskType = Field(..., description="任务类型")
status: TaskStatus = Field(default=TaskStatus.PENDING, description="任务状态")
priority: int = Field(default=5, description="任务优先级,1-10,数字越大优先级越高")
parameters: Dict[str, Any] = Field(default_factory=dict, description="任务参数")
created_at: str = Field(..., description="任务创建时间")
updated_at: str = Field(..., description="任务更新时间")
result: Optional[Dict[str, Any]] = Field(default=None, description="任务结果")
errors: Optional[List[str]] = Field(default=None, description="错误信息")
# 任务管理API
class TaskManagerAPI:
async def create_task(self, task_type: TaskType, parameters: Dict[str, Any], priority: int = 5) -> str:
"""创建新任务并返回任务ID"""
pass
async def get_task_status(self, task_id: str) -> TaskStatus:
"""获取任务状态"""
pass
async def cancel_task(self, task_id: str) -> bool:
"""取消任务"""
pass
async def get_task_result(self, task_id: str) -> Optional[Dict[str, Any]]:
"""获取任务结果"""
pass
2. 文档处理API
class DocumentType(str, Enum):
INVOICE = "invoice"
RECEIPT = "receipt"
BANK_STATEMENT = "bank_statement"
TAX_DOCUMENT = "tax_document"
OTHER = "other"
class DocumentProcessingResult(BaseModel):
document_id: str
document_type: DocumentType
confidence: float
extracted_data: Dict[str, Any]
tables: List[Dict[str, Any]]
issues: Optional[List[str]] = None
class DocumentProcessingAPI:
async def upload_document(self, file_path: str, file_type: Optional[str] = None) -> str:
"""上传文档并返回文档ID"""
pass
async def process_document(self, document_id: str) -> DocumentProcessingResult:
"""处理文档并返回提取的结构化数据"""
pass
async def classify_document(self, document_id: str) -> DocumentType:
"""仅分类文档类型"""
pass
3. 财务数据API
class TransactionType(str, Enum):
INCOME = "income"
EXPENSE = "expense"
TRANSFER = "transfer"
INVESTMENT = "investment"
class Transaction(BaseModel):
transaction_id: str
date: str
amount: float
currency: str
description: str
transaction_type: TransactionType
category: str
account: str
merchant: Optional[str] = None
tags: Optional[List[str]] = None
metadata: Optional[Dict[str, Any]] = None
class FinancialDataAPI:
async def add_transaction(self, transaction: Transaction) -> str:
"""添加交易记录"""
pass
async def get_transactions(self, start_date: str, end_date: str,
account: Optional[str] = None,
category: Optional[str] = None) -> List[Transaction]:
"""获取交易记录"""
pass
async def update_transaction_category(self, transaction_id: str, category: str) -> bool:
"""更新交易分类"""
pass
async def get_balance(self, account: str, date: Optional[str] = None) -> float:
"""获取账户余额"""
pass
智能记账系统实现
智能记账是AI财务自动化系统的基础功能,它能够自动识别、分类和记录财务交易,大大减少人工录入的工作量。在本节中,我们将详细介绍智能记账系统的实现。
交易数据获取
首先,我们需要从各种来源获取交易数据。这些来源包括银行API、CSV文件导入、手动输入和文档识别等。
import os
import csv
from datetime import datetime
from typing import List, Dict, Any, Optional
import pandas as pd
from abc import ABC, abstractmethod
# 交易数据源抽象基类
class TransactionDataSource(ABC):
@abstractmethod
def fetch_transactions(self, start_date: datetime, end_date: datetime) -> List[Dict[str, Any]]:
"""获取指定日期范围内的交易"""
pass
# 银行API数据源
class BankAPIDataSource(TransactionDataSource):
def __init__(self, bank_name: str, api_key: str, account_id: str):
self.bank_name = bank_name
self.api_key = api_key
self.account_id = account_id
self.base_url = self._get_bank_api_url(bank_name)
def _get_bank_api_url(self, bank_name: str) -> str:
"""根据银行名称获取API URL"""
bank_urls = {
"example_bank": "https://api.examplebank.com/v1",
# 其他银行的API URL
}
return bank_urls.get(bank_name, "")
def fetch_transactions(self, start_date: datetime, end_date: datetime) -> List[Dict[str, Any]]:
"""从银行API获取交易"""
import requests
# 实际情况下,这里会调用真实的银行API
# 这里仅作示例
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
params = {
"account_id": self.account_id,
"start_date": start_date.isoformat(),
"end_date": end_date.isoformat()
}
try:
response = requests.get(f"{self.base_url}/transactions", headers=headers, params=params)
response.raise_for_status()
return response.json().get("transactions", [])
except requests.RequestException as e:
print(f"从银行API获取交易失败: {e}")
return []
# CSV文件数据源
class CSVDataSource(TransactionDataSource):
def __init__(self, file_path: str):
self.file_path = file_path
def fetch_transactions(self, start_date: datetime = None, end_date: datetime = None) -> List[Dict[str, Any]]:
"""从CSV文件获取交易"""
if not os.path.exists(self.file_path):
print(f"CSV文件不存在: {self.file_path}")
return []
try:
# 使用pandas读取CSV文件
df = pd.read_csv(self.file_path)
# 假设CSV文件有以下列:date, description, amount, currency
# 实际情况下,需要根据不同的CSV格式进行调整
transactions = []
for _, row in df.iterrows():
try:
# 解析日期
date_str = str(row.get("date", row.get("Date", "")))
transaction_date = self._parse_date(date_str)
# 检查日期范围
if start_date and transaction_date < start_date:
continue
if end_date and transaction_date > end_date:
continue
transaction = {
"date": transaction_date.isoformat(),
"description": str(row.get("description", row.get("Description", ""))),
"amount": float(row.get("amount", row.get("Amount", 0))),
"currency": str(row.get("currency", row.get("Currency", "USD"))),
"source": "csv",
"source_file": os.path.basename(self.file_path)
}
transactions.append(transaction)
except Exception as e:
print(f"处理CSV行失败: {e}")
continue
return transactions
except Exception as e:
print(f"读取CSV文件失败: {e}")
return []
def _parse_date(self, date_str: str) -> datetime:
"""尝试多种格式解析日期"""
date_formats = [
"%Y-%m-%d", "%d/%m/%Y", "%m/%d/%Y",
"%Y/%m/%d", "%d-%m-%Y", "%m-%d-%Y",
"%b %d, %Y", "%B %d, %Y"
]
for fmt in date_formats:
try:
return datetime.strptime(date_str.strip(), fmt)
except ValueError:
continue
raise ValueError(f"无法解析日期: {date_str}")
交易分类系统
交易分类是智能记账的核心环节,我们将实现一个结合规则引擎和机器学习的混合分类系统,以提高分类的准确性和适应性。
import re
import json
from typing import List, Dict, Any, Tuple, Optional
from collections import defaultdict
import pickle
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
class TransactionCategory:
"""交易分类定义"""
# 收入类别
INCOME_SALARY = "收入:工资"
INCOME_BONUS = "收入:奖金"
INCOME_INVESTMENT = "收入:投资收益"
INCOME_OTHER = "收入:其他"
# 支出类别
EXPENSE_FOOD = "支出:餐饮"
EXPENSE_TRANSPORTATION = "支出:交通"
EXPENSE_HOUSING = "支出:住房"
EXPENSE_UTILITIES = "支出: utilities"
EXPENSE_ENTERTAINMENT = "支出:娱乐"
EXPENSE_HEALTH = "支出:医疗"
EXPENSE_EDUCATION = "支出:教育"
EXPENSE_SHOPPING = "支出:购物"
EXPENSE_OTHER = "支出:其他"
# 转账类别
TRANSFER_INTERNAL = "转账:内部"
TRANSFER_EXTERNAL = "转账:外部"
@classmethod
def get_all_categories(cls) -> List[str]:
"""获取所有分类"""
return [value for key, value in cls.__dict__.items()
if not key.startswith("__") and isinstance(value, str)]
class RuleBasedClassifier:
"""基于规则的交易分类器"""
def __init__(self, rules_file: Optional[str] = None):
self.rules = defaultdict(list)
if rules_file and os.path.exists(rules_file):
self.load_rules(rules_file)
else:
self._init_default_rules()
def _init_default_rules(self):
"""初始化默认分类规则"""
# 工资收入
self.add_rule(TransactionCategory.INCOME_SALARY, ["工资", "薪水", "salary", "payroll"], "description")
# 餐饮支出
self.add_rule(TransactionCategory.EXPENSE_FOOD, ["餐厅", "饭店", "外卖", "咖啡", "restaurant", "cafe", "uber eats"], "description")
# 交通支出
self.add_rule(TransactionCategory.EXPENSE_TRANSPORTATION, ["地铁", "公交", "出租车", "加油", "gas", "uber", "lyft", "subway"], "description")
# 住房支出
self.add_rule(TransactionCategory.EXPENSE_HOUSING, ["房租", "租金", "rent", "mortgage"], "description")
# 娱乐支出
self.add_rule(TransactionCategory.EXPENSE_ENTERTAINMENT, ["电影", "游戏", "netflix", "spotify", "演唱会"], "description")
def add_rule(self, category: str, keywords: List[str], field: str = "description"):
"""添加分类规则"""
for keyword in keywords:
self.rules[category].append({
"keyword": keyword.lower(),
"field": field
})
def save_rules(self, file_path: str):
"""保存规则到文件"""
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(dict(self.rules), f, ensure_ascii=False, indent=2)
def load_rules(self, file_path: str):
"""从文件加载规则"""
with open(file_path, 'r', encoding='utf-8') as f:
self.rules = defaultdict(list, json.load(f))
def classify(self, transaction: Dict[str, Any]) -> Tuple[Optional[str], float]:
"""
基于规则分类交易
返回:
(category, confidence): 分类结果和置信度
"""
description = transaction.get("description", "").lower()
# 检查每个分类的规则
category_matches = defaultdict(int)
total_keywords = 0
for category, rules in self.rules.items():
for rule in rules:
total_keywords += 1
keyword = rule["keyword"]
if keyword in description:
category_matches[category] += 1
if not category_matches:
return None, 0.0
# 找出匹配最多的分类
best_category = max(category_matches.items(), key=lambda x: x[1])
confidence = best_category[1] / max(1, total_keywords / len(self.rules))
return best_category[0], min(1.0, confidence)
class MLBasedClassifier:
"""基于机器学习的交易分类器"""
def __init__(self, model_file: Optional[str] = None):
self.model = None
self._init_model()
if model_file and os.path.exists(model_file):
self.load_model(model_file)
def _init_model(self):
"""初始化机器学习模型"""
# 创建一个TF-IDF向量化器和随机森林分类器的管道
self.model = Pipeline([
('tfidf', TfidfVectorizer(max_features=1000, ngram_range=(1, 2))),
('classifier', RandomForestClassifier(n_estimators=100, random_state=42))
])
def train(self, transactions: List[Dict[str, Any]], categories: List[str]):
"""训练分类模型"""
# 提取交易描述作为特征
descriptions = [t.get("description", "") for t in transactions]
# 训练模型
self.model.fit(descriptions, categories)
def classify(self, transaction: Dict[str, Any]) -> Tuple[Optional[str], float]:
"""
基于机器学习模型分类交易
返回:
(category, confidence): 分类结果和置信度
"""
if self.model is None:
return None, 0.0
description = transaction.get("description", "")
# 预测分类和概率
predicted_category = self.model.predict([description])[0]
probabilities = self.model.predict_proba([description])[0]
# 获取预测类别的概率作为置信度
category_index = list(self.model.classes_).index(predicted_category)
confidence = probabilities[category_index]
return predicted_category, confidence
def evaluate(self, transactions: List[Dict[str, Any]], categories: List[str]):
"""评估模型性能"""
descriptions = [t.get("description", "") for t in transactions]
predictions = self.model.predict(descriptions)
return classification_report(categories, predictions)
def save_model(self, file_path: str):
"""保存模型到文件"""
with open(file_path, 'wb') as f:
pickle.dump(self.model, f)
def load_model(self, file_path: str):
"""从文件加载模型"""
with open(file_path, 'rb') as f:
self.model = pickle.load(f)
class HybridTransactionClassifier:
"""混合交易分类器,结合规则和机器学习"""
def __init__(self, rules_file: Optional[str] = None, model_file: Optional[str] = None):
self.rule_classifier = RuleBasedClassifier(rules_file)
self.ml_classifier = MLBasedClassifier(model_file)
self.confidence_threshold = 0.7 # 高置信度阈值
self.low_confidence_count = 0 # 低置信度分类计数
self.feedback_data = [] # 用于收集用户反馈的数据
def classify(self, transaction: Dict[str, Any]) -> Tuple[str, float, bool]:
"""
分类交易
返回:
(category, confidence, requires_review): 分类结果、置信度和是否需要人工审核
"""
# 首先尝试基于规则的分类
rule_category, rule_confidence = self.rule_classifier.classify(transaction)
if rule_category and rule_confidence >= self.confidence_threshold:
return rule_category, rule_confidence, False
# 尝试机器学习分类
ml_category, ml_confidence = self.ml_classifier.classify(transaction)
if ml_category:
# 结合两种方法的结果
if rule_category and rule_category == ml_category:
# 两种方法一致,提高置信度
combined_confidence = min(1.0, (rule_confidence + ml_confidence) / 2 + 0.1)
return ml_category, combined_confidence, combined_confidence < self.confidence_threshold
else:
# 两种方法不一致,使用置信度较高的
if rule_confidence > ml_confidence:
return rule_category, rule_confidence, True
else:
return ml_category, ml_confidence, ml_confidence < self.confidence_threshold
# 如果没有可用的分类结果,使用默认分类
default_category = TransactionCategory.EXPENSE_OTHER if transaction.get("amount", 0) < 0 else TransactionCategory.INCOME_OTHER
return default_category, 0.1, True
def add_feedback(self, transaction: Dict[str, Any], correct_category: str):
"""添加用户反馈,用于改进分类器"""
self.feedback_data.append({
"transaction": transaction,
"correct_category": correct_category
})
# 定期重新训练模型
if len(self.feedback_data) >= 10:
self._retrain_model()
def _retrain_model(self):
"""使用反馈数据重新训练模型"""
if not self.feedback_data:
return
transactions = [item["transaction"] for item in self.feedback_data]
categories = [item["correct_category"] for item in self.feedback_data]
# 重新训练模型
self.ml_classifier.train(transactions, categories)
# 清空反馈数据
self.feedback_data = []
记账核心实现
现在我们将实现记账系统的核心功能,包括交易处理、账户管理和复式记账。
import uuid
from datetime import datetime
from typing import List, Dict, Any, Optional, Tuple
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
# 账户类型
class AccountType(Enum):
ASSET = "资产" # 资产账户,如现金、银行存款
LIABILITY = "负债" # 负债账户,如信用卡、贷款
EQUITY = "权益" # 权益账户,如所有者权益
INCOME = "收入" # 收入账户
EXPENSE = "费用" # 费用账户
# 交易状态
class TransactionStatus(Enum):
PENDING = "待处理"
CATEGORIZED = "已分类"
RECONCILED = "已对账"
REVIEWED = "已审核"
@dataclass
class Account:
"""账户类"""
account_id: str = field(default_factory=lambda: str(uuid.uuid4()))
name: str = ""
account_type: AccountType = AccountType.ASSET
currency: str = "USD"
balance: float = 0.0
is_active: bool = True
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
metadata: Dict[str, Any] = field(default_factory=dict)
@dataclass
class LedgerEntry:
"""分类账条目"""
entry_id: str = field(default_factory=lambda: str(uuid.uuid4()))
transaction_id: str = ""
account_id: str = ""
debit_amount: float = 0.0 # 借方金额
credit_amount: float = 0.0 # 贷方金额
description: str = ""
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
@dataclass
class FinancialTransaction:
"""财务交易"""
transaction_id: str = field(default_factory=lambda: str(uuid.uuid4()))
date: str = ""
description: str = ""
amount: float = 0.0
currency: str = "USD"
source_transaction_id: Optional[str] = None # 原始交易ID(如银行交易ID)
category: Optional[str] = None
status: TransactionStatus = TransactionStatus.PENDING
ledger_entries: List[LedgerEntry] = field(default_factory=list)
tags: List[str] = field(default_factory=list)
metadata: Dict[str, Any] = field(default_factory=dict)
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
class LedgerSystem(ABC):
"""分类账系统抽象基类"""
@abstractmethod
def add_account(self, account: Account) -> str:
"""添加账户"""
pass
@abstractmethod
def get_account(self, account_id: str) -> Optional[Account]:
"""获取账户"""
pass
@abstractmethod
def get_all_accounts(self) -> List[Account]:
"""获取所有账户"""
pass
@abstractmethod
def update_account_balance(self, account_id: str, amount: float) -> bool:
"""更新账户余额"""
pass
@abstractmethod
def add_transaction(self, transaction: FinancialTransaction) -> str:
"""添加交易"""
pass
@abstractmethod
def get_transaction(self, transaction_id: str) -> Optional[FinancialTransaction]:
"""获取交易"""
pass
@abstractmethod
def get_transactions(self, start_date: Optional[str] = None,
end_date: Optional[str] = None,
account_id: Optional[str] = None,
category: Optional[str] = None,
status: Optional[TransactionStatus] = None) -> List[FinancialTransaction]:
"""获取交易列表"""
pass
@abstractmethod
def post_transaction(self, transaction: FinancialTransaction,
debit_account_id: str,
credit_account_id: str) -> bool:
"""过账交易(创建分类账条目)"""
pass
class InMemoryLedgerSystem(LedgerSystem):
"""内存分类账系统实现(用于演示)"""
def __init__(self):
self.accounts: Dict[str, Account] = {}
self.transactions: Dict[str, FinancialTransaction] = {}
self.ledger_entries: Dict[str, LedgerEntry] = {}
# 初始化默认账户
self._init_default_accounts()
def _init_default_accounts(self):
"""初始化默认账户"""
# 资产账户
cash_account = Account(
name="现金",
account_type=AccountType.ASSET,
currency="USD"
)
self.add_account(cash_account)
bank_account = Account(
name="银行存款",
account_type=AccountType.ASSET,
currency="USD"
)
self.add_account(bank_account)
# 负债账户
credit_card_account = Account(
name="信用卡",
account_type=AccountType.LIABILITY,
currency="USD"
)
self.add_account(credit_card_account)
# 收入账户
salary_account = Account(
name="工资收入",
account_type=AccountType.INCOME,
currency="USD"
)
self.add_account(salary_account)
# 费用账户
food_account = Account(
name="餐饮费用",
account_type=AccountType.EXPENSE,
currency="USD"
)
self.add_account(food_account)
transport_account = Account(
name="交通费用",
account_type=AccountType.EXPENSE,
currency="USD"
)
self.add_account(transport_account)
def add_account(self, account: Account) -> str:
"""添加账户"""
self.accounts[account.account_id] = account
return account.account_id
def get_account(self, account_id: str) -> Optional[Account]:
"""获取账户"""
return self.accounts.get(account_id)
def get_all_accounts(self) -> List[Account]:
"""获取所有账户"""
return list(self.accounts.values())
def update_account_balance(self, account_id: str, amount: float) -> bool:
"""更新账户余额"""
account = self.get_account(account_id)
if not account:
return False
# 根据账户类型更新余额
if account.account_type in [AccountType.ASSET, AccountType.EXPENSE]:
# 资产和费用账户,借方增加余额
account.balance += amount
else:
# 负债、权益和收入账户,贷方增加余额
account.balance -= amount
account.updated_at = datetime.now().isoformat()
return True
def add_transaction(self, transaction: FinancialTransaction) -> str:
"""添加交易"""
self.transactions[transaction.transaction_id] = transaction
return transaction.transaction_id
def get_transaction(self, transaction_id: str) -> Optional[FinancialTransaction]:
"""获取交易"""
return self.transactions.get(transaction_id)
def get_transactions(self, start_date: Optional[str] = None,
end_date: Optional[str] = None,
account_id: Optional[str] = None,
category: Optional[str] = None,
status: Optional[TransactionStatus] = None) -> List[FinancialTransaction]:
"""获取交易列表"""
filtered_transactions = list(self.transactions.values())
# 按日期过滤
if start_date:
filtered_transactions = [t for t in filtered_transactions if t.date >= start_date]
if end_date:
filtered_transactions = [t for t in filtered_transactions if t.date <= end_date]
# 按账户过滤(检查是否有相关分类账条目)
if account_id:
filtered_transactions = [
t for t in filtered_transactions
if any(entry.account_id == account_id for entry in t.ledger_entries)
]
# 按类别过滤
if category:
filtered_transactions = [t for t in filtered_transactions if t.category == category]
# 按状态过滤
if status:
filtered_transactions = [


被折叠的 条评论
为什么被折叠?



