Grok 金融功能上线后,AI 应用开发者最应该关注的不是多了个对话入口,而是它把对话模型和真实银行账户连接了起来。用户授权之后,可以在对话中询问账户余额、交易明细,甚至触发金融操作。这个能力放在产品侧叫“金融功能”,放在工程侧却是一整套 AI Agent 与银行开放平台之间的集成链路。要真正理解或者复用这个能力,需要从工程视角拆解整条链路:Grok 如何获得授权、银行账户数据怎么进入上下文、模型怎样安全地调用外部工具、上线前又要检查哪些点。这篇文章会把“连接银行账户”拆成可实现的模块,并给出可落地的工程建议。
1. “Grok 金融功能上线”背后的技术真相
1.1 Grok 是对话模型,不是收银台
很多第一次接触这个能力的人会以为,Grok 连接银行账户之后,模型自己就能“看到”银行流水。这个理解不太准确。大语言模型本身不持有银行凭证,也不直接访问数据库,它做的事情是根据用户输入生成下一个文本片段。要查询真实账户余额,必须由后端服务去调用银行接口,把接口返回结果再拼装成模型可以理解的上下文。
因此,“Grok 连接银行账户”更准确的说法是:Grok 作为对话入口,通过工具调用触发一个已经获得授权、且受控的银行数据服务。模型负责判断用户意图、决定调用哪个工具、根据工具结果组织语言;真正发起网络请求、携带令牌、读取余额的是后端程序。这样拆分之后,职责才清晰:模型管语义,平台管数据,银行管账户。
这种拆分不是多余的。如果模型直接接触凭证和数据库,会带来三个问题:一是密钥无法做审计,无法确认哪一次提问读取了哪条账户数据;二是模型会产生幻觉,可能在用户没有授权时编造出余额;三是每次用户对话都携带完整账户信息,会显著扩大泄露面。所以工程上一定要在模型与银行数据之间隔一层可执行、可审计的代理服务。
1.2 银行账户连接的三条主流技术路线
金融功能要连接银行账户,底层通常有三条路线。第一条是银行官方开放银行 API,银行直接把账户查询、交易明细、转账能力封装成 REST API,第三方通过 OAuth 获得授权。第二条是聚合账户服务商,这类服务商已经接入了多家银行,开发者接入一个 SDK 就能使用多银行账户查询,省去逐家对接的重复工作。第三条是屏幕抓取或非官方协议,通过模拟登录网银页面来读取账户信息,这种方式实现快,但银行侧几乎都不允许,稳定性也差。
从工程角度看,三条路线的取舍并不是“能用就行”,而是安全、成本和维护成本的综合平衡。下表列出常见的对比维度:
| 技术路线 | 接入成本 | 数据覆盖 | 安全合规风险 | 维护成本 |
|---|---|---|---|---|
| 银行官方开放 API | 较高,需要逐家申请 | 覆盖取决于银行开放范围 | 低,符合银行规范 | 较低,接口稳定 |
| 聚合服务商 | 中等,一个 SDK 接入多家 | 覆盖取决于服务商合作银行 | 中,需要评估服务商资质 | 中,依赖服务商能力 |
| 屏幕抓取 | 低,开发快 | 覆盖广,能模拟用户 | 高,易违反银行条款 | 高,网页一变就失效 |
产品侧选择哪种路线,通常要结合目标市场、银行开放程度和合规预算。这里不推断 Grok 使用了哪一家,但从通用实现思路看,一个可靠的金融功能至少不会把屏幕抓取当长期方案。对于个人开发者或技术团队,最稳妥的切入点是申请银行开放平台沙箱,或者使用成熟的聚合服务商测试环境。
1.3 为什么不能直接让用户输入网银账号密码
一个最容易踩的坑是:为了让模型查到余额,直接在对话框里让用户填写网银用户名和密码,然后后端拿去模拟登录。这样做从产品演示上看最快,但只要进入真实环境,问题立刻暴露。
首先是合规问题。几乎所有银行都不允许第三方保存或使用用户网银密码,一旦发生资金损失,责任很难界定。其次是安全边界问题,网银密码是全账户权限,用它换取单次余额查询,会让整个账户暴露在风险中。第三是维护问题,银行页面结构、验证码、二次校验变化后,屏幕抓取流程随时会断,排查成本非常高。
正规做法是使用 OAuth 授权码模式。用户点击“连接银行账户”后,平台跳转到银行的授权页面,用户在银行侧输入密码并选择授权范围,银行回调一个一次性授权码,后端再用授权码换取访问令牌。整个过程中,平台始终接触不到用户的网银密码,只能拿到受限的、可撤销的令牌。这也是“连接银行账户”功能在架构上必须成立的前提。
不要把 AI 助手的工具能力变成绕过银行授权的通道。模型可以聪明地调用工具,但授权链路必须走标准协议。
2. 设计一个可复用的 AI 金融助手架构
2.1 模块划分与数据流
把“Grok 连接银行账户”抽象成系统设计后,至少要拆出六个模块:对话网关、模型服务、工具执行器、银行连接服务、令牌存储、审计服务。对话网关负责接收用户消息和调用模型;模型服务负责意图理解和工具选择;工具执行器负责解析模型的工具调用请求,并触发真正的业务逻辑;银行连接服务统一封装 OAuth、账户列表、余额查询等能力;令牌存储负责保存和刷新访问令牌;审计服务记录每一次数据访问。
数据流的顺序决定了安全边界。一个典型的查询链路是这样的:用户先在平台端发起银行授权,跳转到银行页面,银行回调授权码;后端用授权码换取令牌并保存到令牌存储;之后用户向 Grok 提问“查一下我的账户余额”;Grok 返回一个工具调用,例如get_account_balance;工具执行器从令牌存储取令牌,调用银行连接服务;银行返回余额后,工具执行器把结果作为工具响应交回模型;最终模型用自然语言组织回答。这里的关键是,工具调用请求虽然由模型生成,但真正执行前,后端的用户绑定和权限校验不能缺席。
2.2 OAuth 授权码模式如何接入银行账户
在金融场景中,OAuth 授权码模式是最适合的授权协议。它要求后端先构造一个授权链接,把用户引导到银行侧。链接中常见参数包括response_type=code、client_id、redirect_uri、scope和state。state参数是防 CSRF 的关键,必须是一个服务端生成的随机值,并在回调时校验。scope决定申请哪些权限,应该坚持最小化原则,例如只申请accounts:read,不要顺手申请transactions:write。
后端回调接口的示例代码如下,这里用 Node.js 和 Express 演示思路:
app.get('/callback', async (req, res) => { const { code, state } = req.query; if (state !== req.session.oauthState) { return res.status(400).json({ error: 'state mismatch' }); } const token = await exchangeCodeForToken(code); await tokenStore.save(userId, token); res.redirect('/dashboard'); });这一段代码需要特别说明:exchangeCodeForToken不是本地方法,必须由后端调用银行的 token 接口完成,授权码只能从后端发出去,不能在浏览器端通过 AJAX 调用。原因是client_secret不能暴露给前端,而且授权码交换请求需要携带密钥。保存token时,也要同时保存用户 ID、账户 ID 关联关系和过期时间,方便后续刷新。
2.3 Function Calling:让 Grok 在对话中调用余额查询工具
要让模型在对话中触发账户查询,常见做法是给模型传入工具定义。工具调用机制可以理解成:模型不直接执行函数,而是输出一个结构化的“我要调用这个函数,参数是什么”。由后端应用真正执行函数,并把结果作为消息返回给模型,模型再基于结果生成最终回答。
以一个查询余额的工具为例,工具 Schema 大致长这样:
{ "type": "function", "function": { "name": "get_account_balance", "description": "查询指定账户的当前余额。仅当用户明确授权且账户属于当前用户时调用。", "parameters": { "type": "object", "properties": { "account_id": { "type": "string", "description": "账户 ID,来自账户列表接口" } }, "required": ["account_id"] } } }这里的description要写清楚“只查当前用户已授权的账户”,原因是路径非常明确:模型可能因为上下文歧义把account_id猜错,后端必须校验这个账户确实属于当前登录用户,否则不能执行。尤其要注意,模型可能生成不存在的账户 ID,或者尝试查询另一个用户的账户,这类异常必须在工具执行器中被拦截,而不是依赖模型的伦理判断。
2.4 会话与令牌的生命周期
OAuth 令牌和普通登录令牌的生命周期管理是两个问题。银行返回的access_token通常有效期较短,用于实际调用账户接口;refresh_token有效期较长,用于在access_token过期后换取新令牌。开发时很容易只保存access_token,忽略刷新流程,结果用户几分钟后再次提问就报 401。
比较好的做法是在令牌存储中保存四类信息:用户 ID、账户 ID、access_token、refresh_token,同时记录 token 过期时间。每次工具执行前先检查access_token是否快过期,如果快过期则先刷新再请求。用户执行“解绑银行账户”操作时,要删除令牌,并尽量调用银行侧的撤销接口让令牌失效。不要只在前端删除绑定状态,因为后端可能还留着可以继续访问账户的令牌。
3. 用最小链路跑通“AI 查询账户余额”
3.1 环境与依赖准备
如果要在本地复现“AI 查询账户余额”的最小链路,至少需要四样东西:一个支持工具调用的大模型 API 密钥、一个银行开放平台沙箱账号、一个可被银行回调的公网地址、一个用于保存用户状态的后端服务。
技术栈可以按团队习惯选,Node.js 或 Python 都可以。下面以 Node.js 为例准备依赖:
npm install express express-session axios dotenvexpress-session用来保存 OAuthstate和临时用户状态,axios用来调用银行接口和大模型 API,dotenv用来加载环境变量。沙箱环境里可以把回调地址配成测试域名,不需要真实生产域名,但必须是银行沙箱能够访问到的 HTTPS 地址。
环境变量示例:
BANK_CLIENT_ID=your_client_id BANK_CLIENT_SECRET=your_client_secret BANK_REDIRECT_URI=https://your.domain/callback BANK_TOKEN_URL=https://sandbox.bank.example/oauth/token BANK_API_BASE=https://sandbox.bank.example/api/v13.2 银行侧回调接口
回调接口是授权链路的入口。用户从银行侧跳回来后,后端要验证state,用授权码换取令牌,然后把令牌和用户绑定。下面的示例展示exchangeCodeForToken的常见实现:
async function exchangeCodeForToken(code) { const params = new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: process.env.BANK_REDIRECT_URI, client_id: process.env.BANK_CLIENT_ID, client_secret: process.env.BANK_CLIENT_SECRET }); const { data } = await axios.post(process.env.BANK_TOKEN_URL, params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); return data; }这里的redirect_uri必须与发起授权时保持一致,否则银行会拒绝交换。data中通常包含access_token、refresh_token、expires_in、scope。建议把expires_in转成