Courses
手动录入数据是生产力的大敌。如果您的团队依赖从运营类邮件(如发票、预订确认或客户反馈表)中提取数据,您很可能对传统解析方法的种种噩梦不陌生。
过去,开发者通常依赖正则表达式(Regex)来提取诸如发票号或账单金额等信息。但正则非常脆弱:只要供应商更改邮件模板,或随意加了一个换行,整个解析器就会悄无声息地失效。
在本教程中,我将向您展示如何使用生成式 AI 构建一条全自动、无界面的流水线来解决这一问题。我们会编写一个轻量脚本,读取未读的 Gmail 邮件,将非结构化文本传给 Gemini API 进行智能抽取,并将完美结构化的 JSON 数据直接记录到 Google 表格中。
先决条件:
- 一个 Google 账号
- 具备 JavaScript(Google Apps Script)基础
- 一个 Gemini API 密钥
什么是 Google Apps Script?
Google Apps Script 是一个基于 JavaScript 的云端快速开发平台,可用于自动化、定制和扩展 Google Workspace 应用(如 Google 文档、表格、幻灯片等)。
由于平台直接运行在 Google 服务器上,您无需安装软件、配置本地环境或管理基础设施。
Apps Script 使开发者/低代码开发者能够以编程方式与 Docs、Sheets、Slides、Drive 等 Google Workspace 工具交互。您可以通过自定义菜单、按钮点击或基于时间的计划来构建和运行自动化,或使用该服务构建并发布 Google Workspace Marketplace 附加组件。
Google Apps Script 的工作原理
Google Apps Script 屏蔽了后端配置和 API 鉴权的繁重工作,让您可以把精力直接放在业务逻辑和数据上。它通过以下特性实现:
- 云原生编辑器: 您可以在基于 Web 的 IDE 中编写、调试和部署 JavaScript 代码,全部在浏览器内完成。
- 内置服务: 无需与 OAuth 和复杂 API 调用纠缠,您可以使用开箱即用的全局对象(如SpreadsheetApp、MailApp 或 DriveApp)以原生方式与 Google 服务交互。
- 事件驱动触发器: 脚本可通过自定义菜单按钮手动执行,或使用基于时间的触发器(例如,每天午夜)和事件驱动触发器(例如,用户提交 Google 表单时)自动执行。
Google Apps Script 架构:Gmail 到 Sheets 的流水线
在编写脚本之前,让我们先梳理数据如何在这条流水线中流动。该架构依赖四个相互配合的组件:
- Gmail(来源): 流程首先使用标准搜索运算符(如 is:unread label:invoices)查询收件箱。脚本会筛选出正确的邮件会话,并提取纯文本正文及任何附加的 PDF 文件。
- Apps Script(计算): 充当编排层。它遍历未读会话,将附件格式化为 base64 数据,并处理错误报告与批处理逻辑。
- Gemini API(转换引擎): Apps Script 会将多模态负载(邮件文本加 PDF)发送给 Gemini 2.5 Flash 模型。通过在 API 调用中传入严格的 JSON 响应模式,我们迫使模型提取出精确的值(如供应商、总金额和货币),而不是生成对话式文本。
- Google 表格(目的地): 一旦 Gemini 返回结构化 JSON,Apps Script 会解析结果,将其打包为单个数组,并使用 setValues() 将数据批量插入到活动表格的下一行空行中。

为何在本次构建中使用 Google Apps Script 作为计算层?
首要好处是彻底消除基础设施负担。不需要任何服务器配置,也不必为了读取邮件或写入表格而设置 Docker 容器或复杂的 OAuth 流程。
由于 Apps Script 与 GmailApp 和 SpreadsheetApp 等服务原生集成,Google 会在内部处理身份验证。您只需编写代码并连接端点即可。
步骤 1:准备 Google Apps Script 环境
在编写任何代码之前,我们需要准备外部依赖。这包括获取 Gemini API 的访问权限、创建目标表格,以及配置 Gmail 收件箱以正确标记进入的发票邮件。
生成 Gemini API 密钥
为实现智能数据抽取,我们需要使用 Gemini API。
- 前往 Google AI Studio,并使用您的 Google 账号登录。
- 在左侧导航菜单中,点击 Get API key。
- 点击 Create API key 按钮。您可以选择在现有 Google Cloud 项目中创建此密钥,或让 AI Studio 为您新建一个项目。
- 复制生成的字符串。
安全最佳实践: 不要丢失此密钥,更重要的是,不要计划在代码中以明文硬编码它。先将其保存在剪贴板或安全记事本中;我们将在下一步把它安全地存入 Apps Script 环境。

准备 Google 表格并保护密钥
我们需要一个存储抽取数据的地方。该表格也将作为自动化脚本的启动台。
1. 设置表头 新建一个空白的 Google 表格。在第一行创建以下列标题,以匹配我们将在脚本中定义的 JSON 模式:
- Date(日期)
- Vendor(供应商)
- Invoice Number(发票号)
- Total Amount(总金额)
- Currency(货币)
- Items Summary(项目摘要)
- Email Link(邮件链接)
冻结顶行,以便在数据增多时表头始终可见。

2. 访问 Apps Script 并存储 API 密钥 现在表格已创建,我们可以从表格中直接打开 Apps Script 编辑器。这会自动将代码与此特定电子表格绑定。
- 在 Google 表格中,从顶部菜单点击 扩展程序 > Apps Script。

- 编辑器打开后,在左侧边栏点击齿轮图标,打开 项目设置。

- 下拉至底部,找到 脚本属性(Script Properties)部分。
- 点击 添加脚本属性(Add script property)。
- 在 Property 字段中,精确输入
GEMINI_API_KEY。 - 在 Value 字段中,粘贴您之前从 AI Studio 复制的 API 密钥。
- 点击 保存脚本属性(Save script properties)。

我们稍后编写的代码将会在运行时安全地获取该值,而不会暴露您的凭据。
步骤 2:编写 Google Apps Script 代码
在 Apps Script 编辑器中,先删除任何默认代码。我们将逐步构建自动化引擎。
1. 配置与界面设置
首先定义一个全局配置对象。这是整洁代码的最佳实践,将所有硬编码变量集中于一处,今后只需修改顶部这块即可。
我们还会添加一个 onOpen() 函数。这个内置的 Apps Script 触发器会在您的 Google 表格中自动创建自定义菜单,使非技术用户无需打开代码编辑器即可手动运行脚本。
const CONFIG = {
searchQuery: "is:unread label:invoices",
model: "gemini-2.5-flash",
adminEmail: Session.getActiveUser().getEmail() // Automatically emails the script owner if it crashes
};
/**
* Creates a custom menu in Google Sheets so non-technical users can run the script manually.
*/
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('🤖 AI Automations')
.addItem('Fetch New Invoices', 'processInvoices')
.addToUi();
}
2. 编排设置
processInvoices() 函数充当主编排器。它首先从我们先前设置的脚本属性中安全获取 API 密钥。
随后,使用内置的 GmailApp 服务查询收件箱。
通过调用 GmailApp.search(CONFIG.searchQuery),脚本会检索符合我们 is:unread label:invoices 规则的邮件会话数组。
如果找到匹配项,它会遍历每个会话,获取最新邮件,提取纯文本正文,并分离所有 PDF 附件。
function processInvoices() {
const apiKey = PropertiesService.getScriptProperties().getProperty("GEMINI_API_KEY");
if (!apiKey) {
throw new Error("GEMINI_API_KEY is missing! Please set it in Script Properties.");
}
const threads = GmailApp.search(CONFIG.searchQuery);
if (threads.length === 0) return;
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const batchData = [];
const processedThreads = [];
3. 处理邮件与多模态 PDF
在遍历邮件会话时,我们会获取最新一封邮件。然而,发票很少以纯文本形式出现;它们几乎总是以 PDF 附件的形式发送。
借助原生多模态的 Gemini 2.5 Flash,我们的脚本会遍历 latestMessage.getAttachments(),抓取所有 PDF,将其转换为 Base64 字符串,并作为 inlineData 直接传入 API 负载。
Gemini 会以视觉方式“读取”PDF,并像读取文本一样轻松地抽取数据。
for (let i = 0; i < threads.length; i++) {
const thread = threads[i];
const latestMessage = thread.getMessages().pop();
const emailBody = latestMessage.getPlainBody();
const emailDate = latestMessage.getDate();
const emailLink = https://mail.google.com/mail/u/0/#inbox/${thread.getId()};
const attachments = latestMessage.getAttachments();
try {
const geminiParts = [];
geminiParts.push({ text: "Extract invoice details from this email and any attached documents:\n\n" + emailBody });
// Add PDF attachments as base64 to the prompt
for (const attachment of attachments) {
if (attachment.getContentType() === "application/pdf") {
geminiParts.push({
inlineData: {
mimeType: "application/pdf",
data: Utilities.base64Encode(attachment.getBytes())
}
});
}
}
// Invoke Gemini API
const extractedData = callGeminiAPI(apiKey, geminiParts);
4. 批量写入与错误处理
为优化性能,我们不会一行一行地往表格写入数据。
相反,我们将抽取的 JSON 数据推入数组(batchData),最后使用 setValues() 一次性写入。
注意 try...catch 块。
如果 Gemini 出现幻想或在某封邮件上失败,脚本不会整体崩溃。它会优雅地捕获错误,并使用 MailApp.sendEmail() 立即通知管理员,从而确保高可观测性。
// Push to batch array instead of writing one-by-one
batchData.push([
emailDate,
extractedData.vendor || "Unknown Vendor",
extractedData.invoiceNumber || "N/A",
extractedData.totalAmount || 0.00,
extractedData.currency || "USD",
extractedData.itemsSummary || "N/A",
emailLink
]);
processedThreads.push(thread);
} catch (error) {
MailApp.sendEmail(CONFIG.adminEmail, "🚨 Invoice Pipeline Error", error.toString());
}
}
// Batch Write to Sheets
if (batchData.length > 0) {
const lastRow = Math.max(sheet.getLastRow(), 1);
sheet.getRange(lastRow + 1, 1, batchData.length, batchData[0].length).setValues(batchData);
}
// Mark all processed threads as read so they aren't parsed again
processedThreads.forEach(t => t.markRead());
}
最后,脚本会对每封已处理的邮件调用 thread.markRead()。这样可确保下次运行时,脚本会忽略这些邮件,只处理新邮件。
5. Gemini API 助手函数(JSON 模式)
调用大语言模型时,开发者常用类似“返回一个 JSON 块”的提示工程。然而,模型常会附带对话式内容(例如“好的,这是您的数据:[JSON]”),从而导致下游代码解析器崩溃。
通过设置 responseMimeType: "application/json" 并提供严格的 responseSchema,我们可强制 Gemini REST API 仅以保证的 JSON 对象回复,且包含与我们表格列标题完全一致的字段。
function callGeminiAPI(apiKey, partsArray) {
const url = https://generativelanguage.googleapis.com/v1beta/models/${CONFIG.model}:generateContent?key=${apiKey};
const responseSchema = {
type: "OBJECT",
properties: {
vendor: { type: "STRING" },
invoiceNumber: { type: "STRING" },
totalAmount: { type: "NUMBER" },
currency: { type: "STRING" },
itemsSummary: { type: "STRING" }
},
required: ["vendor", "totalAmount"]
};
const payload = {
contents: [{ parts: partsArray }],
generationConfig: {
responseMimeType: "application/json",
responseSchema: responseSchema
}
};
const options = {
method: "POST",
contentType: "application/json",
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const jsonResponse = JSON.parse(response.getContentText());
return JSON.parse(jsonResponse.candidates[0].content.parts[0].text);
}
步骤 3:将 Google Apps Script 流水线自动化
代码只有在让您的工作更轻松时才有价值。
Google Apps Script 提供强大的执行环境,既可交互式、面向用户,也可完全无界面,以后台定时任务运行。
方法一:通过表格界面触发
如果您是为非技术型的会计团队构建此工具,他们大概不希望为了处理当天发票而打开 Apps Script 代码编辑器。
由于我们在代码中包含了 onOpen() 函数,Apps Script 会在每次打开电子表格时,将自定义菜单自动嵌入 Google 表格界面。
- 返回到您的 Google 表格并刷新页面。
- 等待几秒,查看顶部菜单栏。您会看到一个新的下拉菜单,名为 🤖 AI Automations。
- 点击它并选择 Fetch New Invoices。

- 首次运行时,Google 会要求您授权该脚本(以代表您读取 Gmail 并发送邮件)。授权后,脚本将实时执行。您可以亲眼看到抽取的 JSON 数据填充到各行中!
方法二:通过基于时间的触发器进行无界面自动化
如果您想要真正“设好即忘”的自动化,您可以使用原生的基于时间的触发器,让流水线在后台自主运行。
- 在 Apps Script 编辑器左侧边栏点击触发器(时钟图标)。

- 在右下角点击 Add Trigger。

- 将函数设置为 processInvoices。
- 将事件来源更改为 Time-driven,并设置为按小时触发(例如,每 6 小时)。
- 点击保存。

现在,您的无界面 AI 流水线已完全自主。
即使您的电脑完全关闭,Google 的服务器也会每六小时唤醒一次,扫描收件箱,通过 Gemini 解析发票, 将数据记录到表格,然后再进入休眠。
您可以在 Apps Script 编辑器中点击“执行”选项卡查看日志,以监控这些后台执行的成败情况。
步骤 4:测试 AI 数据抽取
代码就位后,我们需要测试以确保抽取流水线如预期工作。
我们来看两个实际场景,了解 Gemini API 如何处理不同数据格式,以及实际响应长什么样。
测试 1:纯文本邮件抽取
首先,我们测试直接嵌入在邮件正文中的发票。
- 给自己发送一封主题为“Invoice INV-2023-88493”的邮件。
- 在正文中写入非结构化文本,例如:“Hi Team, please pay Acme Corp $450.00 USD for the Q3 software license.”
- 给该邮件应用 invoices 标签,并确保其保持未读状态。
- 通过自定义菜单运行脚本。
- 生成或下载一份示例 PDF 发票(例如来自 ProTech Cloud Services 的 $20,700.00)。
- 将该 PDF 文档通过邮件发送给自己。邮件正文可以完全留空!
- 应用 invoices 标签并将其标记为未读。
- 运行脚本。流水线会检测到附件,将其转换为 Base64,Gemini 会以视觉方式读取该文档以抽取供应商与总金额。


在幕后,Gemini 接收到这段非结构化文本,并严格遵循我们的 JSON 模式。
它不会返回对话式填充,而是直接给出原始负载。
由于响应是完美结构化的 JSON,我们的 batchData.push() 数组可以无缝读取 extractedData.totalAmount,并将 6550 直接写入 Google 表格,无需复杂的正则逻辑。

测试 2:从 PDF 附件中抽取
接下来,我们通过传入一份 PDF 文档来测试 Gemini 2.5 Flash 的真正实力。


这正是该架构价值的体现。
您无需编写自定义 OCR(光学字符识别)逻辑、管理独立的 PDF 解析库,或用正则去匹配总金额。
LLM 原生处理了非结构化的视觉数据。
结论
自动化业务流程并不需要管理笨重的基础设施或与复杂的授权流程搏斗。
通过将 Google Apps Script 与 Gemini API 配合使用,我们构建了一条智能、无服务器的数据流水线,完全运行在您日常使用的云生态中。
该架构的优势在于其简洁性。无需配置服务器、设置 Webhook,或在外部服务器上管理安全凭据文件,Apps Script 原生处理上下文、认证与执行。
与此同时,Gemini 免除了对脆弱正则或昂贵 OCR 软件的依赖,只需一次 API 调用,就能将凌乱的邮件正文与非结构化 PDF 转化为可预测的数据库行。
若想更进一步,探索如何通过编程访问 LLM 构建更多应用,我建议查看 DataCamp 的《Python 中的 API 入门》课程,或深入学习如何设计智能工作流的AI Agent Fundamentals 课程。