有个问题咨询一下各位大神
该怎么记录预付机票/酒店呢?
对于其它的预付我都有一个assets,例如assets:prepaid:gas
但我不知道要不要把已经定好的里程票记录在assets里面,这样会让balance sheet看起来有点怪怪,毕竟这部分点已经“不可用”了
有个问题咨询一下各位大神
该怎么记录预付机票/酒店呢?
对于其它的预付我都有一个assets,例如assets:prepaid:gas
但我不知道要不要把已经定好的里程票记录在assets里面,这样会让balance sheet看起来有点怪怪,毕竟这部分点已经“不可用”了
遇事不决Equity
近日又尝试开始折腾。
第一步,用 marker 把 statement PDF 转为 Markdown。这个marker连US Bank那种transaction table乱七八糟的都能整理得干干净净,非常amazing!
from marker.converters.pdf import PdfConverter
from marker.models import create_model_dict
from marker.output import text_from_rendered
converter = PdfConverter(
artifact_dict=create_model_dict(),
)
rendered = converter(file_path)
text, _, images = text_from_rendered(rendered)
print(text)
第二步,用 Instructor 把非结构化的 Markdown 转为结构化的 JSON。
Statement class 定义如下。银行相关的 account name 会从文件里面读出,动态生成一个Enum作为validator。Closing date,account和balance可以直接生成balance directive。Transactions需要再做处理生成transaction directives。这一步现在调用OpenAI的API,尝试过用local llm做,不给OpenAI喂个人数据,但是速度和结果还是差太多了。
def get_accounts() -> list[str]:
beancount_file_path = "/path/to/my/accounts.beancount"
accounts_file = open(beancount_file_path, "r", encoding="utf-8")
accounts_lines = accounts_file.readlines()
accounts = []
for line in accounts_lines:
parts = line.strip().split(" ")
if (
len(parts) >= 4
and (
parts[2].startswith("Assets:Bank:")
or parts[2].startswith("Liabilities:CreditCard:")
or parts[2].startswith("Liabilities:Loan:")
)
):
accounts.append(parts[2])
return accounts
accounts = get_accounts()
Account = Enum("Account", ((a, a) for a in accounts), type=str)
class Transaction(BaseModel):
date: datetime = Field(description="Post date")
description: str = Field(description="Human readable transaction description")
amount: float = Field(description="Transaction amount")
is_credit: bool = Field(description="Is this a credit, e.g., payment or refund")
class BankStatement(BaseModel):
closing_date: datetime = Field(description="Statement closing date")
account: Account = Field(
description="Account name determined by bank, card and owner"
)
balance: float = Field(description="New balance after this statement")
is_credit: bool = Field(description="Is balance a credit, e.g., negative balance")
transactions: List[Transaction] = Field(
description="List of transactions in this statement"
)
openai_api_key = "sk-xxxxx"
client = instructor.from_openai(OpenAI(api_key=openai_api_key))
def extract(content: str) -> BankStatement:
statement = client.chat.completions.create(
model="gpt-4o-mini",
response_model=BankStatement,
messages=[
{
"role": "user",
"content": content,
}
],
)
return statement
现在就做到这一步为止,整体结果可以说非常的好!但是,这里的transaction想要生成Beancount的transaction,还缺乏payee和posting account。后面的想法是对以前的transactions做embedding,然后新的transaction去模糊查询以前的transaction,匹配出payee和posting account。一个transaction拥有多个posting的情况估计还是需要手动处理。
最后一步是要把生成的directives放在合适的文件。每个人都有自己的beancount文件划分方式,这里估计会重复利用上一步的embedding数据库,猜出应该存放的文件路径。最后需要工具放在文件的合适位置(例如文件内部使用时间排序)。
大伙有什么想法?
太牛了
我最近也在思考这个事情,我认为bean importer其实是一个高度定制化的东西
每个人对导入器的需求都不一样
就像我可能希望导入器可以自动生成对应的返点
然后我可能比较喜欢ofx导入,去重很方便
我还没有试过OFX哦。提供这个文件的银行多吗?它本身就是结构化数据吧?
这个目测很复杂。Groceries在不同银行还有不同定义,像UAR这种Apple Pay好像在 statement 在看不到具体信息吧。
只有大银行提供
是的,主要是会有一个ofx 交易 uuid,唯一的,查重很方便
我目前的想法是根据不同的银行/Account写一张不同的lookup table
但我还真不知道Groceries在不同银行还有不同定义?不都是一样的吗?
这些可能就要手动adjust一下了,或者默认UAR的消费都是Apple Pay,自动apply apple pay的points,然后少数非apple pay手动调整一下
其实我个人的想法是大部分的日常消费商户其实是比较固定的,如果能总结出lookup table,对于这部分经常重复交易的商户就很省事了。然后每次一旦有新的商户交易,再添加一条到DB就行了,不太需要用到LLM的
关键词->payee->Category
For each Credit Card account:
Category->CashBackRate->自动生成完整的一条记录
最典型的是Walmart吧。而Walmart又分Walmart Neighborhood Market和Walmart Supercemter。Neighborhood 那个有的时候有的地区会被算 groceries store的。
Wow,我在看chroma,他在添加记录的时候需要一个id。我才想起来beancount directive全程没有id这个概念,只能用index弱化表示。
你在导入之后,OFX的uuid会作为tag或者note挂在transaction下面吗?
Lookup table或者正则的最大的问题是,corner case处理不完,大量重复的人工工作,整体又不值得写code处理每一个小的corner case。我感觉积分这个也可以尝试用embedding+query history data的方式去先生成一遍,然后再修正。毕竟同一个店,同一种MS都是重复发生的,人工处理了第一遍,让LLM处理第二第三遍。
最后,积分记录会和 statement 上的数字 cross validate 一下吗?我的 transaction 上的信用卡的支出会最后匹配上 statement balance,以确保没有漏了transaction。积分记录多了少了好像也很难查出来?
bos没有Walmart,烦恼少一半
是的,会作为meta data
嗯嗯,也是个方案,可以试试
差不多每个季度或者半年check一次吧,然后稍微抹平一下diff。 有些statement上会有点数summary
而且amex是有api可以导出点数记录的
我最近用llm导入 手动纠错一张卡的话15-30min 12个月的
这个速度很快了,基于ofx还是pdf?
直接pdf给llm 输出后自己人眼校对 最终跟我银行自己的差3刀多
其实人眼校对看数字和加tag都挺必要的… 我手动加了些重要交易或者旅游之类的tag
问下 beancount 的前端 fava 有办法自定义 column 的顺序吗?
之前是按照 currency 的定义顺序来的,后来某个版本更新了以后变成了按首字母……
USD 就排到很后面,不是很好找,谢谢。
有点东西,用的哪个llm?反正我试过让chatgpt OCR 不好用
就chatgpt的4.5啊
可能我那个时候还是3的时代吧
大佬们,你们是支出的时候手动记账,然后账单出来把记录的支出和账单对账吗?
依赖账单真的不会拖一年没整理吗…比如我
会 ,我现在也是
下个月应该总算忙完一段时间,再搞搞上面的方案
具体怎么搞啊
我发现 https://global.americanexpress.com/rewards/summary 可以导出CSV,但是每次只让选30天,感觉太少了
edit:上面有
自己发个request可以下载任意时间区间的(最早是两年前而不是Amex网页显示的一年前),赞!
(不过联名卡好像不行?)