AI Agent Harness Engineering 财务领域应用:记账、报税与财务分析自动化

AI Agent Harness Engineering 财务领域应用:记账、报税与财务分析自动化

引言

在当今数字化快速发展的时代,人工智能(AI)正在深刻地改变着各个行业的运作模式,财务领域也不例外。传统的财务工作流程,如记账、报税和财务分析,往往需要大量的人工操作,不仅耗时耗力,还容易出现人为错误。随着AI技术的不断进步,特别是AI Agent(智能代理)技术的成熟,我们现在有能力实现财务工作的全面自动化,显著提高效率和准确性。

痛点引入

对于大多数企业和财务专业人士来说,日常的财务工作面临着以下几个主要挑战:

  1. 数据录入繁琐:手动录入大量的交易数据是一项极其枯燥且容易出错的工作。
  2. 合规要求复杂:税务法规和财务准则不断变化,保持合规性需要持续的学习和更新。
  3. 分析时效性差:传统的财务分析往往基于历史数据,难以及时提供决策支持。
  4. 资源消耗大:企业需要投入大量的人力物力来维持财务部门的正常运作。
  5. 数据孤岛问题:不同系统之间的数据难以整合,导致信息不完整。

这些问题不仅增加了企业的运营成本,也限制了财务部门从传统的"记账先生"向战略合作伙伴角色的转变。

解决方案概述

本文将介绍如何利用AI Agent Harness Engineering技术构建一个全面的财务自动化系统,实现记账、报税和财务分析的智能化和自动化。AI Agent是一种能够感知环境、做出决策并执行行动的智能系统,而Harness Engineering则是一种系统化的方法,用于设计、构建和管理这些AI Agent,使其能够可靠地执行复杂任务。

我们将探讨:

  • 如何设计和训练财务领域的AI Agent
  • 如何实现自动化的交易分类和记账
  • 如何利用AI进行合规性检查和自动报税
  • 如何构建智能财务分析系统,提供实时洞察
  • 如何确保系统的安全性、可靠性和可扩展性

最终效果展示

在文章的最后,我们将展示一个完整的AI财务助手系统,该系统能够:

  • 自动从银行对账单、发票和收据中提取数据
  • 智能分类交易并自动记账
  • 根据最新法规生成税务报告
  • 提供实时财务分析和预测
  • 通过自然语言界面回答用户的财务问题

这个系统将显著减少人工干预,提高财务工作的准确性和效率,同时为企业决策提供更有价值的洞察。


准备工作

在开始构建我们的AI财务自动化系统之前,我们需要准备必要的开发环境、工具和基础知识。本节将指导您完成这些准备工作。

环境/工具

为了实现财务领域的AI Agent应用,我们需要以下环境和工具:

  1. 编程语言

    • Python 3.8+:作为主要开发语言,拥有丰富的AI/ML库和财务工具
    • JavaScript/TypeScript:用于构建前端界面和交互组件
  2. AI/ML框架

    • LangChain:用于构建AI Agent应用的框架
    • OpenAI API/Anthropic API:提供大语言模型能力
    • Hugging Face Transformers:用于自然语言处理任务
    • TensorFlow/PyTorch:用于自定义模型训练
  3. 财务工具和库

    • Pandas:用于数据处理和分析
    • NumPy:用于数值计算
    • Plotly/Matplotlib:用于数据可视化
    • Beancount/ledger-cli:用于复式记账
    • Tax-Calculator:用于税务计算(根据具体国家/地区选择)
  4. 开发和部署工具

    • Docker:用于容器化部署
    • FastAPI/Flask:用于构建API服务
    • PostgreSQL/MongoDB:用于数据存储
    • Redis:用于缓存和会话管理
    • Git:用于版本控制
  5. 开发环境设置

    # 创建虚拟环境
    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
    

基础知识

为了充分理解和实现本文中的内容,读者应该具备以下基础知识:

  1. 财务会计基础

    • 复式记账原理
    • 财务报表(资产负债表、利润表、现金流量表)
    • 基本税务概念和流程
  2. 编程基础

    • Python编程(数据结构、函数、类等)
    • 基本的Web开发概念
    • RESTful API设计原则
  3. AI/ML基础

    • 大语言模型(LLM)的基本概念和应用
    • Prompt工程的基本原则
    • 向量数据库和相似度搜索的基本概念
  4. 推荐学习资源

    • 《财务会计教程》(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则能够利用大语言模型的理解和推理能力,处理更加复杂和灵活的财务工作。

核心要素组成

  1. 感知模块:用于收集和理解环境信息(如银行交易、发票、市场数据等)
  2. 推理/决策模块:基于感知到的信息和目标进行推理和决策
  3. 行动模块:执行具体的财务操作(如记账、生成报告、发送提醒等)
  4. 记忆模块:存储历史信息和学习到的知识

概念结构

感知

影响

环境

感知模块

推理/决策模块

记忆模块

行动模块

Harness Engineering

核心概念:Harness Engineering( harness在这里可以理解为"驾驭"或"工程化管理")是一种系统化的方法论,用于设计、构建、测试和部署可靠的AI Agent系统。它关注的是如何将AI能力与软件工程最佳实践相结合,构建可信赖、可扩展的AI应用。

问题背景:虽然大语言模型展现出了强大的能力,但直接将其应用于生产环境面临着可靠性、一致性、安全性等多方面的挑战。Harness Engineering正是为了解决这些问题而诞生的。

核心要素组成

  1. Agent设计模式:结构化的Agent架构设计方法
  2. 工具使用框架:让Agent能够安全有效地使用外部工具和API
  3. 评估与监控:持续监控Agent的性能和行为
  4. 安全与合规:确保Agent的操作符合安全规范和监管要求
  5. 反馈循环:收集用户反馈,持续改进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=kx)=j=1KexTwjexTwk

其中,wkw_kwk是类别kkk的权重向量,KKK是类别的总数。

我们通过最小化交叉熵损失来训练模型:

L(w)=−∑i=1N∑k=1Kyiklog⁡P(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=1Nk=1KyiklogP(yi=kxi;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 (1i=1pϕiLi)(1L)dYt=c+(1+i=1qθiLi)εt

其中:

  • YtY_tYt是时间序列值
  • LLL是滞后算子,LkYt=Yt−kL^k Y_t = Y_{t-k}LkYt=Ytk
  • 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 (1i=1pϕiLi)(1i=1PΦiLiS)(1L)d(1LS)DYt=c+(1+i=1qθiLi)(1+i=1QΘ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(LVaR1α)=1α

其中,LLL是损失随机变量。

我们使用蒙特卡洛模拟来计算VaR:

  1. 定义投资组合的价值函数V(t)V(t)V(t)
  2. 模拟NNN个可能的未来价值V1(T),V2(T),...,VN(T)V_1(T), V_2(T), ..., V_N(T)V1(T),V2(T),...,VN(T)
  3. 计算每个模拟路径的损失Li=V(0)−Vi(T)L_i = V(0) - V_i(T)Li=V(0)Vi(T)
  4. 对损失进行排序,找到第αN\alpha NαN个最小的损失,即为VaR

对于更复杂的财务风险评估,我们可以使用CVaR(条件风险价值),它衡量的是超过VaR的平均损失:

CVaR1−α=E[L∣L≥VaR1−α] CVaR_{1-\alpha} = E[L | L \geq VaR_{1-\alpha}] CVaR1α=E[LLVaR1α]


系统架构设计

在本节中,我们将详细介绍AI财务自动化系统的架构设计,包括系统的整体结构、各组件的功能和它们之间的交互关系。

系统整体架构

我们的AI财务自动化系统采用微服务架构,以确保系统的可扩展性、可维护性和容错性。整体架构分为以下几层:

外部系统集成

数据层

AI能力层

应用服务层

API网关层

用户界面层

Web管理界面

移动应用

聊天机器人接口

API网关/认证

记账服务

报税服务

分析服务

AI Agent协调服务

文档理解Agent

交易分类Agent

合规检查Agent

预测分析Agent

对话Agent

交易数据库

知识库向量存储

用户配置存储

审计日志存储

银行API

税务系统API

ERP系统集成

all

核心组件设计

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 = [
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值