用AI打造智能语音记账系统:从需求到实现的全流程
本文记录了我与AI助手(严主任)一起,从零开始搭建智能语音记账系统的完整过程。系统实现了通过自然语言对话自动记账,并同步到飞书多维表格的功能。
一、背景与需求
作为一名普通用户,我一直在寻找一种更便捷的记账方式。传统的记账APP需要手动选择分类、填写金额,操作繁琐。我的需求很简单:
“说出来,就能记上账”
具体来说:
- 通过自然语言描述消费(如”中午吃海底捞花了355元”)
- AI自动解析金额、分类、日期
- 自动写入我的飞书记账本
- 无需手动选择分类
二、技术方案选型
2.1 数据存储:飞书多维表格
我选择飞书多维表格作为记账本的存储载体,原因:
- 云端存储,多端同步
- 支持丰富的字段类型(单选、日期、公式等)
- 支持自动化流程
- 便于后续数据分析
2.2 技术架构
用户语音/文字 → AI解析 → 飞书Bitable API → 记账本
核心组件:
- 自然语言解析:提取金额、分类、日期、成员
- 智能分类:基于语义分析匹配消费类型
- 飞书API:Bitable写入接口
三、实现过程
3.1 第一步:API权限检查
首先检查对飞书记账本的访问权限:
import requests
APP_TOKEN = “your_app_token” TABLE_ID = “your_table_id”
获取字段信息
url = f”https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/fields” resp = requests.get(url, headers={“Authorization”: f”Bearer {token}”})发现的问题:
- ✅ 可以读取表结构
- ✅ 可以写入文本、数字、日期字段
- ❌ 级联单选字段无法通过API写入
我的账本原本使用级联字段(一级分类关联二级分类),但飞书API不支持写入这种带条件的级联字段。
解决方案:将级联字段改为普通单选字段,牺牲部分联动约束,换取API可写入性。
3.2 第二步:自然语言解析
实现核心解析逻辑:
def parse_natural_language(text):
result = {
'amount': None,
'category1': None,
'category2': None,
'member': '默认成员',
'date': int(datetime.now().timestamp() * 1000),
'transaction_type': '支出'
}
# 提取金额
# 支持:35、35.5、35块、35块5、35元
# 智能分类分析
# ...
return result
金额提取支持多种格式:
- “35” → 35.0
- “35.5” → 35.5
- “35块” → 35.0
- “35块5” → 35.5
- “35元” → 35.0
3.3 第三步:智能分类的实现
这是整个系统最核心的部分。我实现了两层分类策略:
第一层:精确关键词匹配
CATEGORY_KEYWORDS = {
"外卖": ("食", "外卖"),
"食材": ("食", "食材"),
"加油": ("行", "加油"),
"打车": ("行", "打车"),
# ... 61个分类
}
第二层:语义推理(Fallback)
当没有直接匹配的关键词时,基于语义规则推理:
# 食的推理
if any(word in text for word in ['吃', '饭', '餐', '火锅', '烧烤', '餐厅']):
if any(word in text for word in ['外卖', '美团', '饿了么']):
return '食', '外卖'
elif any(word in text for word in ['零食', '水果', '奶茶']):
return '食', '零食水果'
else:
return '食', '馆子'
效果对比:
- 输入:”今天去吃了海底捞355元”
- 旧方案:无法识别(”海底捞”不在关键词列表)
- 新方案:食-馆子(基于”吃”+”餐厅”语义)
3.4 第四步:飞书Bitable写入
def add_record_to_bitable(record_data, token):
url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/records"
fields = {
"交易类型": record_data['transaction_type'],
"日期": record_data['date'],
"金额": record_data['amount'],
"备注": record_data['remark'],
"年份": datetime.fromtimestamp(record_data['date']/1000).strftime("%Y")
}
# 分类使用文本名称(不是ID)
if record_data['category1']:
fields['一级分类'] = record_data['category1']
if record_data['category2']:
fields['二级分类'] = record_data['category2']
resp = requests.post(url, headers={...}, json={"fields": fields})
return resp.json()
关键发现:
- 写入单选字段时,使用选项名称(如”食”)而非选项ID
- 使用ID会导致飞书显示为ID文本(如”opt5rwyvJY”)
四、遇到的问题与解决
问题1:级联字段无法写入
现象:尝试写入级联字段时返回错误:
cascade single select with conditions is not supported
解决:将级联字段改为普通单选字段,在代码层面维护分类映射关系。
问题2:选项名称显示为ID
现象:飞书表格中分类显示为”opt5rwyvJY”而非”食”
原因:使用了选项ID而非选项名称写入
解决:改用选项名称(如”食”、”外卖”)写入
问题3:二级分类名称有空格
现象:二级分类如”外卖 “(带2个空格),导致匹配失败
解决:
1. 手动清理飞书表格中的选项名称,删除多余空格
2. 代码中兼容处理(去除空格匹配)
问题4:分类映射不完整
现象:”海底捞”无法匹配到”食-馆子”
解决:增加语义推理层,基于关键词(如”吃”、”餐厅”)智能推断分类,而非仅依赖精确匹配。
五、最终效果
使用方式
支持两种输入格式:
带前缀:
记账:中午吃海底捞355元
记账:昨天加油300块
无前缀:
今天外卖35
给宝宝买奶粉289
识别能力
| 输入 | 识别结果 | 说明 |
|---|---|---|
| “今天去吃了海底捞355元” | 食-馆子,355元 | 基于”吃”+”餐厅”语义 |
| “昨天加油300块” | 行-加油,300元 | 关键词匹配 |
| “给宝宝买奶粉289” | 娃-宝宝食品,289元 | 成员识别为”核桃Zr” |
| “打车去公司28元” | 行-打车,28元 | 关键词+语义 |
| “看电影花了50” | 休闲娱乐-影视文化,50元 | 语义推理 |
六、技术亮点
1. 分层分类策略
- 第一层:精确关键词匹配(高准确率)
- 第二层:语义推理(高覆盖率)
2. 多维度信息提取
- 金额:多种格式兼容
- 日期:相对/绝对日期
- 成员:自动识别
- 收入/支出:自动判断
3. 容错设计
- 分类无法识别时留空,不阻止记账
- 日期默认今天
- 成员使用默认值
七、代码分享
完整代码已开源在GitHub(可根据实际情况补充链接),核心文件:
accounting.py:主程序accounting_cli.py:命令行入口ACCOUNTING_GUIDE.md:使用文档
核心依赖:
pip install requests
八、总结与展望
通过这个项目,我实现了一个真正零操作门槛的记账方式:
- ✅ 不用打开APP
- ✅ 不用选择分类
- ✅ 不用填写表单
- ✅ 说出来就记好
未来优化方向:
1. 接入语音识别,直接语音记账
2. 增加消费趋势分析
3. 自动识别重复消费(如”又是麦当劳”)
4. 与预算系统联动,超支预警
—
*本文记录了我与AI助手(严主任)共同开发记账系统的完整过程。从需求提出到最终落地,历时约6小时,全程通过自然语言对话完成,无需编写复杂代码。*
*作者:老达*
*日期:2026-02-27*
