# AI 扫描小票记账：Claude 与 Codex 工作流

*2026-09-01*

咖啡馆小票上的总额是 `$27.82`，银行卡通知还停在“待处理”，纸边已经在笔记本电脑旁卷了起来。再放一周，商户名、小费和分类只会更难想起来。

趁小票还在手边，Claude 或 Codex 就能把照片变成一份待审核的记账草稿。智能体读取照片中能看清的内容，对照账本里的账户、分类和近期记录，再逐字展示准备执行的写入内容。只有你明确批准这一次写入后，账本才会发生变化。

这里要先讲清楚产品边界。**Expense Budget Tracker 没有内置摄像头或小票扫描器，也不存储小票图片。**你所使用的 Claude、Codex 或模型提供商负责识别你提供的图片，Expense Budget Tracker 只保存经过批准的结构化账本数据。

能不能识别图片，取决于你使用的 AI 客户端和模型配置。[Anthropic 文档说明 Claude 可以处理和分析视觉输入](https://platform.claude.com/docs/en/intro)，而 [OpenAI Responses API 接受文本、图片或文件输入](https://developers.openai.com/api/reference/cli/resources/responses/methods/create)。这并不表示每个 Claude 或 Codex 客户端都能自动打开本地照片：客户端还必须支持附件或能够访问相应的文件路径，并获得你的许可。图片识别很有用，但金额仍需要人工审核。

![Claude 或 Codex 把小票照片整理成账本记录预览，获批后再写入](/blog/ai-receipt-scanner-expense-tracker.png)

## 这套工作流里的“扫描器”具体做什么

这是一套由 AI 辅助的小票记账工作流，不是应用内置的摄像头功能。

可以把它理解为一次分工：模型处理你提供的小票图片，Expense Budget Tracker 接收经过批准的账本数据。

| 阶段 | 发生什么 |
|---|---|
| 拍摄 | 你给小票拍照，或把扫描文件保存在 Claude 或 Codex 可以访问的位置。 |
| 解读 | 模型读取商户、日期、小计、折扣、税费、小费、总额、币种和付款线索等可见字段，并标出任何不确定之处。 |
| 检查账本 | 智能体读取选定的 Expense Budget Tracker 工作区、实时 schema、账户、分类和疑似重复的记录。 |
| 预览 | 智能体展示提取出的事实、不确定项、分类选择和准备执行的具体写入内容。 |
| 批准 | 你明确批准这一次写入，或纠正内容并要求生成新的预览。 |
| 写入并核对 | 智能体写入一笔结构化交易，再从账本中读回核对。 |
| 对账 | 银行卡或银行交易正式入账后，你把它与已经记录的小票条目匹配，而不是再导入一份副本。 |

最后的对账步骤，才会让这套 **AI 扫描小票记账**流程留下可审计的账本。正确读出 `$27.82` 只是开始。这笔记录还必须使用正确的工作区、账户、正负号、币种和分类，也不能误判重复记录。

如果你需要的是一款内置拍照入口、还能保存小票附件的应用，这款产品并不适合。Expense Budget Tracker 负责接收和保存结构化财务数据；需要长期保留的小票图片，仍应放在你平时使用的文件系统、文档归档工具或其他服务中。

## 直连 Agent API 与远程 MCP 连接器，二选一

Expense Budget Tracker 支持两种连接路径。它们访问的是同一类财务数据，但认证凭据和客户端的工作方式不同。

### 供 Claude Code、Codex 和支持 HTTP 的智能体使用的直连 Agent API

直连 API 的入口是：

```text
https://api.expense-budget-tracker.com/v1/
```

智能体先调用 `GET /v1/`，按照发现响应中的说明继续，通过电子邮件验证码完成接入，并将返回的长期有效 `ApiKey` 存放在聊天记忆之外。随后，它调用 `/v1/me`，列出并选择工作区，检查 `/v1/schema`，通过 `/v1/sql/query` 读取数据，再通过 `/v1/sql/execute` 提交一次经过批准的变更。

如果 Claude Code 或 Codex 已经能够打开你电脑上的小票文件并发起 HTTP 请求，这条路径很合适。[AI 智能体设置指南](/zh/docs/agent-setup/)介绍了接入顺序，[API 参考文档](/zh/docs/api/)则说明当前的读写约定。更完整的 Claude Code 终端工作流可参阅[如何用 Claude Code 追踪支出并管理预算](/zh/blog/how-to-track-expenses-and-manage-your-budget-with-claude-code/)。

### 使用浏览器 OAuth 的远程 MCP

支持 MCP 的客户端也可以连接到：

```text
https://mcp.expense-budget-tracker.com/mcp
```

这条路径通过浏览器 OAuth 授权，使用 MCP 访问令牌和刷新令牌，而不是 Agent API 的 `ApiKey`。读取时使用 `list_workspaces`、`get_schema` 和 `sql_query` 等工具；写入则需要单独的 `expenses:write` 权限，并通过 `sql_execute` 执行。

远程连接器不会因此获得你电脑上照片的访问权限。客户端或模型仍需通过自身支持的附件或文件机制获取小票。[MCP 连接器指南](/zh/docs/mcp-connector/)介绍了 OAuth 流程和权限范围。

无论选择哪条路径，顺序都一样：先识别小票，再检查账本，然后展示预览，最后对这一次写入单独授权。

## 一张小票，从照片到入账的完整流程

假设你拍了一张清晰的餐厅小票，内容如下：

| 小票字段 | 可见值 |
|---|---|
| 商户 | North Street Cafe, New York |
| 日期和时间 | 2026 年 8 月 31 日 12:17 |
| 餐食小计 | `$24.00` |
| 折扣 | `-$2.00` |
| 税费 | `$1.82` |
| 小费 | `$4.00` |
| 最终总额 | `$27.82` |
| 付款线索 | 尾号为 `4242` 的 Visa 卡 |

金额能对上：`$24.00 - $2.00 + $1.82 + $4.00 = $27.82`。这项检查很有用，但只能证明数字彼此吻合，不能证明目标账户或分类正确。智能体仍然需要查看账本。

### 1. 先确认目标，再解读照片

要求智能体识别并展示：

- 当前登录身份对应的账户信息
- 准确的工作区名称和 ID
- 目标账本账户及其币种
- 小票日期和时区
- 小票币种

在这个例子中，假设用户确认工作区是 `Personal`，ID 为 `workspace-personal-example`；账户是 `a-visa_4242-usd`，币种为 `USD`；小票时间按纽约当地时间解释。这些都是假设值，并非产品内置的工作区、账户或分类名称。银行卡末四位只能提供线索，不能成为智能体自行选择账户的依据。如果两张已保存的银行卡都以 `4242` 结尾，智能体应该停下来询问究竟使用了哪一张。

### 2. 读取实时 schema

智能体应该调用：

```text
GET https://api.expense-budget-tracker.com/v1/schema
```

`/v1/schema` 返回的内容，才是当前可用数据库关系、字段、约束和写入规则的准确信息。文章中的示例可能过时；生成 SQL 之前，智能体应以实时 schema 为准。按照当前账本 schema，执行 `INSERT` 时必须明确包含已经确认的 `workspace_id`。为 API 密钥选择并保存工作区，只会设置请求上下文，写入行时仍不能省略这一列。

### 3. 分别提取事实、线索和不确定项

一份可靠的提取报告应该像这样：

| 字段 | 提取值 | 可信度或待确认问题 |
|---|---|---|
| 商户 | North Street Cafe | 清楚 |
| 交易时间 | `2026-08-31 12:17` 当地时间 | 清楚；用户已确认纽约时区 |
| 币种 | USD | 根据 `$` 和已确认地点判断；用户已确认账户币种 |
| 小计 | `24.00` | 清楚 |
| 折扣 | `-2.00` | 清楚 |
| 税费 | `1.82` | 清楚 |
| 小费 | `4.00` | 清楚 |
| 实付总额 | `27.82` | 清楚，且金额计算一致 |
| 付款方式 | 尾号为 `4242` 的 Visa 卡 | 线索与用户确认的账户匹配 |

不要把看不清的文字硬塞进一个确定字段。如果总额的最后一位可能是 `2`，也可能是 `7`，正确的输出应该是“总额不清楚；需要另一张图片或人工确认”，而不是猜一个看起来合理的数字。

### 4. 决定记录一笔交易，还是拆分分类

这张小票上的消费全是餐饮，因此，如果 `Dining Out` 已经是工作区中的现有分类，记录一行账本数据就很合理。税费、折扣和小费共同解释了最终金额；在普通个人预算中，不需要为它们分别创建记录。

如果一张超市小票同时包含食品、药品和厨房电器，就可能需要拆分分类。在当前账本模型中，这意味着创建多行记录：它们通常属于同一个账户，并共享同一个 `event_id`。每一行都有自己的分类和带正负号的金额，这些金额之和必须准确等于原始付款金额：对于一笔 `$27.82` 的支出，应合计为 `-27.82`。

预览应该说明如何分摊整张小票共同承担的税费、折扣、服务费和舍入差额。有些小票适合按比例分摊，有些则适合把明确对应某件商品的折扣分给该商品。没有一条规则适合所有小票。如果只能靠猜，就保留一行并使用范围更广的现有分类，或者询问用户。

智能体应该先查询账本中的现有分类，不要为了看起来整齐而临时发明一套新分类：

```sql
SELECT category, COUNT(*) AS use_count
FROM ledger_entries
WHERE kind = 'spend'
  AND category IS NOT NULL
GROUP BY category
ORDER BY use_count DESC
LIMIT 100
```

### 5. 查询可能重叠的记录，只返回候选项

拟定插入语句前，先查询已确认账户在小票日期附近的记录。对于示例小票，可以使用这条范围较窄的查重查询：

```sql
SELECT entry_id, event_id, ts, account_id, amount, currency, category, counterparty, note, external_id
FROM ledger_entries
WHERE event_id = 'receipt-2026-08-31-north-street-cafe-2782'
   OR (
     account_id = 'a-visa_4242-usd'
     AND currency = 'USD'
     AND ts >= '2026-08-29 00:00:00-04'
     AND ts < '2026-09-03 00:00:00-04'
     AND amount = -27.82
   )
ORDER BY ts
LIMIT 100
```

智能体将这条语句发送到 `POST /v1/sql/query`。这里的 `event_id` 是智能体为该事件创建的字符串值；如果拆分分类，所有相关记录都会共享这个值。它可以用来组织多行记录，也方便精确查询，但数据库不会把它当作唯一的去重键。完全相同的 `event_id` 是值得调查的强信号，不代表可以删除或覆盖任何内容。

日期相近、金额相同、商户类似的记录也只能算重复候选项。两笔真实发生的咖啡馆消费完全可能金额相同；银行卡扣款从待处理变为正式入账时，也可能出现不同的商户名称或时间戳。

如果账本里很可能已经有匹配记录，预览应该把候选项和小票提取结果并列展示。用户解决冲突之前，不应再提出插入操作。

### 6. 展示完整账本记录和将要执行的 SQL

假设重复查询没有返回候选项，而且 `Dining Out` 是已有分类。此时，只读预览就可以给出具体内容：

| 账本字段 | 准备写入的值 |
|---|---|
| 工作区 | `Personal` (`workspace-personal-example`) |
| 账户 | `a-visa_4242-usd` |
| 时间戳 | `2026-08-31 12:17:00-04` |
| 金额 | `-27.82` |
| 币种 | `USD` |
| 类型 | `spend` |
| 分类 | `Dining Out` |
| 交易对方 | `North Street Cafe` |
| 备注 | `Receipt subtotal 24.00; discount -2.00; tax 1.82; tip 4.00` |
| 重复候选项 | 所查时间范围内没有候选项 |

预览还应该逐字展示准备发送的 SQL：

```sql
INSERT INTO ledger_entries (
  event_id,
  ts,
  account_id,
  amount,
  currency,
  kind,
  category,
  counterparty,
  note,
  workspace_id
)
VALUES (
  'receipt-2026-08-31-north-street-cafe-2782',
  '2026-08-31 12:17:00-04',
  'a-visa_4242-usd',
  -27.82,
  'USD',
  'spend',
  'Dining Out',
  'North Street Cafe',
  'Receipt subtotal 24.00; discount -2.00; tax 1.82; tip 4.00',
  'workspace-personal-example'
)
```

这仍然只是预览。智能体应该说明预期效果——在已确认的工作区中新增一行账本记录——然后等待“批准这一次写入”之类的明确授权。只要任何值发生变化，包括工作区、账户、金额、分类或备注，旧预览就会失效，必须重新生成。

### 7. 只执行批准过的语句，再读回核对

获得批准后，直连 Agent API 会把准确语句发送到：

```text
POST https://api.expense-budget-tracker.com/v1/sql/execute
```

随后，智能体通过 `/v1/sql/query` 进行核对：

```sql
SELECT entry_id, event_id, ts, account_id, amount, currency, kind, category, counterparty, note, workspace_id
FROM ledger_entries
WHERE event_id = 'receipt-2026-08-31-north-street-cafe-2782'
LIMIT 100
```

读回结果必须恰好包含一行，并且与批准过的预览一致。只收到 HTTP 成功响应，却没有逐项比较，核对工作就还没完成。

### 8. 银行卡扣款正式入账后再对账

小票记录的是柜台当场发生的交易，正式入账的银行卡交易则确认最终计入账户的金额。日后这笔交易出现在银行卡导出文件中时，查询同一账户、金额、日期范围、商户线索和任何稳定的来源 ID。如果证据表明它对应先前根据小票写入的那一行，导入时就应排除对账单中的这一行，而不是再创建一笔账本记录。

如果正式入账的金额不同，不要创建余额调整记录。先调查是否读错了小票总额、小费后来发生变化、预授权金额变成了最终结算金额，或银行卡对外币交易进行了换算。把修正内容明确展示出来，再单独请求批准。

处理较大批次时，[银行对账单导入指南](/zh/blog/how-to-import-bank-statements-into-an-expense-tracker/)介绍了重叠期间和转账的处理方式。[预算与银行余额对账指南](/zh/blog/how-to-reconcile-your-budget-with-your-bank-balance/)则说明如何把已经入账的账户活动与确认无误的余额进行比较。

## 小票识别最容易出问题的地方

小票照片本来就不规整。谨慎的 Claude 或 Codex 小票识别流程应该主动暴露下面这些问题，而不是悄悄补齐信息，只求结果看起来顺滑。

| 棘手情况 | 安全处理方式 |
|---|---|
| 照片模糊、过暗、折叠或裁切不全 | 标出无法读取的字段，要求提供新图片或人工填写数值；只要总额、日期或币种仍不确定，就不要写入。 |
| 小计与总额 | 重新计算照片中看得见的各项金额，并使用实际支付的最终金额。检查税费是已经包含在内还是另行加入，也要确认总额中是否已经包含服务费。 |
| 小费 | 区分印刷的建议小费、手写小费、银行卡预授权金额和最终总额。之后再与正式入账的银行卡金额比较。 |
| 折扣和优惠券 | 保留最终实付总额。如果拆分分类，应展示折扣如何分摊，而不是默默分配。 |
| 退货和退款 | 把退货小票视为一笔待处理或已完成退款的凭证，不要当作普通收入。只有当退款状态和金额明确时，才在收款账户中将它记录为单独的结构化条目，并在正式入账后进行对账。 |
| 分类拆分小票 | 为相关记录使用同一个 `event_id`；展示每个分类的带正负号金额、分摊规则、舍入决定和合计值。各行之和必须等于最终实付总额。 |
| 现金还是银行卡 | 使用实际付款账户。照片中的商户标志，甚至画面里出现的钱包，都不能证明付款方式；小票没有付款方式一栏时应询问用户。 |
| 外币 | 明确保留小票币种。如果银行卡账户使用另一种币种，不要虚构汇率或结算金额；等待扣款正式入账，或取得准确且经过确认的金额。 |
| 重复候选项 | 比较日期、带正负号金额、账户、币种、商户和任何稳定的来源标识符。把候选项交给用户决定，不要擅自跳过或插入。 |

税务分类同样不能想当然。小票可以作为记账凭证，但模型不应只凭商户名称就认定一笔消费属于可抵扣的业务支出，也不能据此给出法律或税务结论。税务或报销所需的文件应保存在你自己的文档系统中；涉及个人具体情况的问题，请咨询具备资质的专业人士。

## 可直接复用的 Claude 或 Codex 提示词

智能体连接好以后，粘贴下面这段提示词，再替换方括号中的值。刚上手时，最好每次只处理一张小票。

```text
处理 [准确文件路径或附件] 中的小票图片，并使用 Expense Budget Tracker。
如果使用直连 API，请从 https://api.expense-budget-tracker.com/v1/ 开始，
按照发现响应中的说明继续。使用保存在聊天记忆之外的 ApiKey。如果当前连接使用 MCP，
则调用 MCP 的工作区/schema/查询工具，并遵守下方相同的批准边界。

先不要写入任何数据。使用直连 API 时，展示我的 /me 账户信息。列出可用工作区，
让我确认准确的工作区。检查 /v1/schema（或调用 get_schema）。查询可用账户，
让我确认准确的账户及其币种。不要仅凭银行卡末四位推断账户。

读取图片，提取商户、小票日期和时间、币种、小计、税费、小费、服务费、折扣、
退货、最终总额和付款线索。标出任何看不清、裁切不全、有歧义或金额计算不一致的内容。
绝不要编造缺失值。告诉我适合使用一行账本记录还是拆分分类；提出分类名称前，
先查询我已有的分类。如果拆分，请让相关记录共用一个 event_id，并证明各行带正负号的
金额之和等于最终总额。

使用只读查询，检查已确认账户和日期范围内重叠的账本记录。展示重复候选项及其证据；
不要因为日期和金额相同，就认定它一定是同一笔消费，也不要认定它一定是一笔新消费。

然后展示只读预览，其中包括已确认的工作区、准确账户、带正负号的金额、准确币种、
时间戳、类型、现有分类、交易对方、备注、适用时的每个拆分金额，以及重复候选项。
逐字展示准备执行的 SQL，在每条 INSERT 中包含已确认的 workspace_id，并说明预计受影响
的行数。不要创建余额调整记录、猜测出的换算金额或额外记录。

停下来，等待我对这一次具体写入给予单独且明确的批准。获得批准后，只通过
/v1/sql/execute（或 sql_execute）发送获批语句。通过 /v1/sql/query（或 sql_query）
读回该记录，并把每个已存储字段与获批预览逐项比较。如果存在差异，只报告问题，
不要再修改数据。之后，帮我把这条小票记录与正式入账的银行或银行卡交易匹配，
不要创建重复记录。
```

这段提示词故意把边界设得很严格。“用 AI 扫描小票”听起来只是提取信息，但真正代价高的错误通常发生在提取之后：选错账户、选错币种、擅自新建分类、重复插入，或未经审核就执行写入。

## 用最直白的话讲清数据边界

不连接银行账户，并不代表数据不会离开你的电脑。这套工作流中的每个环节都有自己的边界。

| 边界 | 涉及的数据 |
|---|---|
| 你的设备或客户端 | 小票最初是一张照片或一个文件。客户端只能获得你授予的文件访问权限。 |
| Claude、Codex 和所选模型提供商 | 小票内容和你的指令会按照该提供商与客户端的条款和设置处理。请根据自己的配置核对这些条款；Expense Budget Tracker 无法代替 Anthropic 或 OpenAI 提供额外的隐私保证。 |
| 直连 Agent API 或 MCP 连接器 | 智能体会发送任务所需的具体工作区/schema 读取、账本查询和经过批准的结构化写入。Expense Budget Tracker 的这两种连接方式都不需要接收小票图片。 |
| Expense Budget Tracker 存储 | 在这套工作流中，Expense Budget Tracker 只保存金额、币种、账户、分类、交易对方和备注等经过批准的结构化财务数据，不保存小票图片。 |
| 你的小票归档 | 如果退货、保修、报销或留档需要原始图片，应由你另选位置保存。 |

如果你想使用一款[无需关联银行账户的预算应用](/zh/blog/budget-app-without-bank-linking/)，这套方案可能很合适：小票工作流不需要长期连接银行数据，每一项准备写入账本的变更也都会先展示给你。你仍然需要选择 AI 提供商、授予文件访问权限，并通过 Expense Budget Tracker 的连接方式发送必要的财务读取和写入。

## 这种小票扫描方式适合你吗？

如果你所说的“支持小票扫描的记账软件”是指内置手机拍照入口并保存附件，请选择专门为这项工作设计的产品。如果你希望用 AI 识别小票，同时在每次写入账本前都能看到完整内容，并把结构化记录与原始图片分开保存，这套工作流就更合适。

如果你需要下面这些能力，可以选择这套工作流：

- 不连接银行账户也能根据小票记账
- 由 Claude 或 Codex 负责图片解读和账本查询
- 以已有账户和分类为准进行记账
- 每次写入前都查看完整预览
- 把可能重复的记录列为候选项，不擅自替你判断
- 得到经过核对、日后还能对账的结构化账本记录

Expense Budget Tracker 也不打算取代支持搜索的小票归档工具。

先拿一张清晰的小票、一个已确认的账户和一个熟悉的分类试一遍。核对金额计算，批准一笔具体记录，写入后读回，再等扣款正式入账时完成匹配。这个简短闭环会留下一条日后依然看得懂的审计轨迹。

---
*[查看此页面的 HTML 样式版本](https://expense-budget-tracker.com/zh/blog/ai-receipt-scanner-expense-tracker/)*

*支持 OAuth 的远程 MCP 客户端可连接 `https://mcp.expense-budget-tracker.com/mcp` 并使用 OAuth Bearer 访问。必需权限为 `expenses:read`。客户端还可请求可选的 `expenses:write` 权限；该权限会显示在 OAuth 同意屏幕上，修改数据时必须具备。*

*命令行和直接使用 HTTP 的智能体可从以下地址开始使用独立的 Agent API `GET https://api.expense-budget-tracker.com/v1/` 并按照发现响应获取 ApiKey。*

*提示：在本站任意 URL 后追加 `.md`，即可获得该页面的纯 Markdown 版本。*