静态 API key 交代不了是谁|AI Engineer × Kim Maida & Mason Egger

中文深度整理,非逐字翻译
原始标题
Stop Giving Your AI Agents API Keys - Kim Maida, Keycard & Mason Egger, Temporal
节目 / 来源
AI Engineer
嘉宾
Kim Maida;Mason Egger
原始发布日期
2026-11-06
时长
约 53 分钟
内容类型
会议工坊 / 智能体身份与访问控制
原始内容
前往原始来源 ↗

Kim Maida 在 Keycard 做智能体运行时的身份和访问。Mason Egger 是 Temporal 的解决方案架构师。他讲 durable execution,持久执行:进程死了,状态还能从断点接上。两个人用同一个费用台把这两件事接在一起。费用台叫 Expense Desk,智能体用 LangChain,写在这个自定义应用里。

没有身份的那一版,她让智能体提交一笔 75 美元的报销,用途是工坊零食。状态是待审批。活动记录写的是:这笔费用由一把 API key 授权创建。没有用户是谁,智能体自己也还没有身份。她再去批准别人的一笔。创建者仍是那把钥匙,批准人同样没有留下记录。这场演示的报销不会真打款。

她把这种做法拆成三处。凭证对不上用户身份,也对不上智能体身份。钥匙里有的权限,智能体全部拿得到。人也不能处处站在回路里点确认:人自己会出错,很多智能体还要自己跑。

字幕把名字和控制台标识听岔了。开场自报是 Kim Maida,下文用这个名字。以智能体身份继续时,账户被听成 expense desk agent K Mida。后文控制台里的应用和用户被听成 Kitematic。活动记录上她指出的结论照写,不猜 GitHub 用户名。授权服务器和审计界面,字幕多次听成 Keycloak,又说令牌由 Keycard 签发、SDK 和保险库也在 Keycard。下文把这场的身份、策略和审计记在 Keycard 一侧。

原始来源:Stop Giving Your AI Agents API Keys - Kim Maida, Keycard & Mason Egger, Temporal。中文深度整理,非逐字翻译。本文依据 YouTube 自动生成的英文字幕,专有名词和数字可能有识别误差。注册码、展位和沙箱里逐步跟做的操作不纳入。

先登录,权限先收成一截

她用的开放标准叫 token exchange,口播说大约从 2020 年就有,扩展的是 OAuth 2。授权服务器核验身份、签发令牌。她点名的身份提供方是 Google、Okta、Auth0 这一类,它们提供授权服务器。

身份链要进执行路径,用户得先登录。登录之后,授权服务器请用户同意:把一部分资源访问委托出去,权限是用户权限里的一截,叫做 scopes。她把这一步称作第一次收窄。发出的那枚令牌标明用户是谁、访问到哪一层。字幕把这枚令牌听成 token I。后文它变成 subject token,也就是智能体代表谁在办事的那个主体。

能跑代码的 OAuth 客户端才做得到交换。她举了三种位置:MCP 客户端和第三方 MCP 服务器之间的网关,自定义的智能体应用,或者包住现成编程智能体的命令行外壳。今天走的是第二种。Expense Desk 自己实现了通向 expense MCP 服务器的 token exchange。这台 MCP 服务器坐在智能体和 ledger API 之间,给智能体一组定好的工具,并连到下游资源。账本 API 再连到真正存报销的数据库。MCP 服务器对下一跳也做 token exchange。

运行时是跟智能体循环待在一起、负责把调用执行掉的那个进程。今天的运行时就是 Expense Desk 这个应用。一条提示的路径她走了一遍:用户把提示交给运行时,运行时调用模型;模型提出工具调用,MCP 客户端派到 MCP 服务器;服务器执行工具,再调资源 API;响应沿原路回来,循环直到模型满意,再把回答交回用户。

一次工具调用,换一枚短令牌

提示和 subject token 一起进到 Expense Desk。循环跑起来,模型提出一次工具调用。智能体向安全令牌服务证明自己:可以用客户端凭证,也可以用工作负载身份,并带上用户的 subject token。subject token 声明用户是谁、授权到哪一层。

这一次交换只问一件事:允许这次工具调用访问 MCP 服务器,而且只覆盖这一次调用。她说手上有三块信息。请求访问的智能体是谁。智能体代表的用户是谁。用户授予智能体的访问到哪一层。策略对着这三块评估:向哪个资源要什么访问,智能体是谁,它在为谁办事。委托链和所要的访问都落在策略里,Keycard 才签发一枚只能在这台 MCP 服务器上用的访问令牌。这枚令牌通常数分钟内过期。她说永远不要把它存下来。

令牌作为 bearer 交给 MCP 客户端,再拿去调用 expense MCP 服务器。服务器核验令牌,用它授权这次工具调用。工具还要碰 ledger API,于是 MCP 服务器发起第二跳:把自己收到的那枚令牌交给 Keycard,换成访问账本 API 的令牌。Keycard 再按策略铸一枚凭证,范围只够这一次工具调用需要的访问。MCP 服务器拿它去调账本 API。API 核验之后放行。这一枚令牌 MCP 服务器也不保存,调用一结束就丢掉。响应再沿循环回到用户。

工坊把「谁可以要令牌」和「谁可以收令牌」拆成两种登记,同一件东西可以兼有两种身份。应用负责发起 token exchange,再往下游换。智能体应用要能收到用户登录后的 subject token,所以再登记成资源。她把凭证提供方设成口播里的 zone provider,并让这个资源由该智能体应用提供。这个关联告诉授权服务器:该应用可以交换发给这个资源的登录令牌。没有这层关联,交换请求会被拒绝。

依赖她接了四样。智能体自己的资源登记。已经配好、全场共用的 expense MCP。放在 Keycard 保险库里的大模型 API key,这样密钥不必躺在磁盘上。还有一个 user info 端点,用来回答登录用户是谁。沙箱里她把 client ID 和 client secret 贴进环境文件。她说生产环境多半会用工作负载身份;Keycard 的命令行也可以动态填入密钥,不必把它们放在磁盘上。这场演示走的是沙箱里的客户端凭证。

代码侧,每次智能体要对 MCP 发工具调用,就现要一枚只覆盖这一次调用的令牌。SDK 里取的是 keycard zone token,真正去铸令牌的是口播里的 mint。没有 subject token,也就是没有登录用户时,智能体用 client credentials grant,拿 client ID 和 client secret 向令牌服务表明自己,并以自己的身份办事。

全场用的是预先配好的共享 Keycard 组织。跟着做的人进的是同一个组织,能看到别人在做什么。智能体各跑各的,MCP 服务器是同一台。API key 可能相同,也可能不同。在加上身份之前,没有办法分辨。

备忘录写代 Kim,审计只看见应用

给智能体登记身份之后,她以智能体身份继续,问现在用的是哪个账户、自己的审批上限是多少。审批上限是:作为用户,你可以替别人的报销批准多少钱。回答是签入为 expense desk agent,也就是他们填了 client ID 和 secret 的那个应用。智能体的审批上限大约 50 美元。

她接着说:替我报一笔 75 美元,用途是工坊周边。智能体提交了。备忘录写着代表 Kim。活动详情写的是这笔由这个具体的智能体提交。她说「代表 Kim」只是她告诉它的话。用户还没有登录,它没有什么可核验的,这笔费用落在智能体自己的身份下面。

审计日志里,她把范围收到最近一小时、资源收到 expense MCP。看到的凭证是用 client credentials grant 签发的。行为者是应用自己,访问的是 MCP 资源。

expense MCP 服务器有多份登记:它接收来自智能体应用的令牌,再把这枚令牌换成下游、用来访问 ledger API 的令牌。她滤到它对账本 API 的调用。委托链上,发起请求的应用是 expense MCP 服务器,它代表的是智能体那个应用,访问的资源是 ledger API。这是智能体以自己身份办事时留下的那条链。

她要的下一步是:智能体做了这件事,代表 Kim,用的是 Kim 的权限。她不要它只说成「Kim 做的」。

人登录之后,50 美元和 100 美元是两本账

加入共享组织时,登录方式是 GitHub。组织名被听成 workshop AI NYC。费用台再签入时,因为已经登录,直接进去了,界面上能看见自己的邮箱。再问当前账户,智能体知道这是一个登录过的人。和身份元数据绑在一起的审批上限是 100 美元。

先前那些报销不会被改写。筛成全部费用,早先的记录仍写着由 API key 提交。她也看到 Expense Desk 智能体代表别人办事的例子。人可以以自己的身份行动,智能体以自己身份行动的能力还留着。这场工坊里,这体现为智能体可以自行批准低于某个金额的报销。

她提交 30 美元,用途是团队办公用品。智能体先核验她是谁,再提交。因为它可以自行批准 49.99 美元以下的报销,它提交之后又以自己的身份批准了。活动记录写:由她的邮箱创建,由 expense desk agent 批准,并给出这个智能体的标识。理由是:符合条件的办公用品,低于 50 美元,业务目的清楚。大约 50 美元、49.99 美元和理由里的 under $50 都出现在这一段,字幕没有说明它们是不是同一条规则。

再提交一笔超过智能体能自行处理的上限的 75 美元,状态停在待审批,要人来看。她让智能体批准某一笔,口播是 approve expense 123。智能体回答:不能批准或拒绝自己的报销,得请另一位审阅人。她说这是因为现在有身份了:知道谁登录着,也知道智能体自己是谁。她补了一句,前面没有演示,但在那之前,她本可以批准自己的,也可以批准任何人的,因为系统不知道任何人是谁。

她再拒掉别人的一笔待审批。活动记录写:创建者是 expense desk agent,因为那笔来自前面的轮次;拒绝的人是她的身份。谁提交、谁批准或拒绝,记录上分得开。

回到审计。第一跳:发起 token exchange 的是她的智能体应用,代表的是作为人类用户的她,访问的是 expense MCP。路径是:智能体拿她登录时得到的 sign-in token,向 Keycard 换成下一跳、也就是 MCP 服务器的令牌;再拿这枚 MCP 令牌,按该工具对应的 scopes 去调用。第二跳:expense MCP 服务器代表她发起交换,访问的是 ledger API。它把自己从智能体收到的令牌交出去,换成账本 API 的令牌。这些跳可以顺着追,因为同一段会话的会话号在第一跳和第二跳上是一致的。她没有在现场把会话号逐个对读。

凭证留在执行的那一刻

Mason 把 Temporal 定义成 durable execution 框架。代码被执行,应用状态被保持。应用失败或出错,它把状态重建到失败点,再继续往下走。函数调用、分布式系统中断,带着重试和失败处理。

他接触的客户和用户要的是:智能体要能耐久。智能体本身就是一个循环,不停对外部服务做工具调用。他说智能体到底是分布式系统,上面多了一点思考。分布式系统里已经处理过的问题,用在这个循环上。目的是别丢数据,也别把大模型再调一遍。

结构上,工作流像父函数,活动像子函数。循环做成父调用,就是一个工作流。他的说法是:每一次让 Keycard 为用户做认证,可以是一个工作流;每一次工具调用,是一个活动。活动会被预期为可能失败。失败了就重试,或者重建状态再看。系统是代码优先的,活动看起来像普通 Python。字幕把其中一段函数签名听成 async def SS,装饰器听成 grant resource。下文不补源码。

演示的结算工作流里有两段活动:debit ledger,以及 mark settled。中间有一段计时,用来模拟等人审批要花的时间。活动上的那个装饰器是 Keycard 团队做的 interceptor 钩子。活动执行时,它去 Keycard 取权限和身份,再回来,于是这条 Temporal 工作流和它的活动可以沿用前面说的身份隔离。单个工人可以有自己的身份,一整组工人也可以有自己的身份,并据此识别和限制范围。

Temporal 是工人加服务的模型。服务保住执行和应用状态。工人是一队进程,他比作 Kubernetes 里的 pod,或 pod 里的进程。现场先启动的工人,进程号是 572,跑的是前面那个会自动接收报销的智能体。客户端再发起一笔结算,出现结算工作流的 ID。工人日志里,这一次调用做了借记,并现铸了凭证。

他在 Temporal 的网页界面里看到应用还在跑,然后把应用冷杀掉。传统做法要把状态存进数据库,回来再灌进去。这里 debit ledger 的输出已经留在 Temporal 里:活动任务完成,借记结果是 4,200。字幕没有给 4,200 的单位。当时没有工人在跑,工作流任务超时,应用是死的。他再把工人拉起来。应用从失败点接上:用先前的响应把智能体状态重建出来,大模型调用没有重做,已经完成的活动没有重跑。它回放这些动作,再把执行往后推完。事后看事件历史,mark settled 也有结果。

他解释为什么智能体在乎这件事。大规模调用大模型不便宜,还有令牌成本和一堆失败状态。他举的例子是:循环走了 12 步,整段状态丢了。那你要么自己在数据库里维持状态,再写重建和重试,要么用这样一套持久执行。智能体可以下线、可以失败,大模型供应商可以中断,AWS 的整个区域也可以丢掉。算力回来之后,从最后那一点继续。失败如果足够短,用户可能不知道服务断过。他拿刚才那段等待说明:对方知道审批大约要等一分钟,而工人在失败后大约 5 秒内重启,客户端那一侧的人多半注意不到。收束时他又说,API 中断,或者一个 AWS 区域消失一天,这种事会发生。区域消失一天是他的口播,字幕没有给出哪一次中断。

密钥没有进这次的 Temporal 事件历史。一段 Python 脚本检查的结果是:历史里没有 JWT 材料。各步函数的结果在,借记和 mark settled 的结果也在。他进到事件历史里,说因为没有存,所以找不到那些密钥。

把敏感数据留在外面,他讲了两条。一条是 Keycard 提供的 interceptor:凭证根本不进工作流数据,到执行时再注入。他比作 just-in-time 凭证。另一条是 Temporal 的 payload encoder。演示的网页界面里,数据当时以明文放着。你可以自己加密:按 Temporal 的模式拉起一个 sidecar,定加密算法和加密策略。无论自建还是云上,送进 Temporal 的数据会按这个策略加密。他说他们读不了,也不掌握你的密钥。他们关心的是把这些步骤排好序。明文界面是加密打开之前的样子。脚本证明的是这次历史里没有 JWT,不是账本金额也被加密过。

他点名可以试的集成有 OpenAI、Pydantic AI,以及 LangChain 和其他几家。

费用台这一侧把智能体身份和工人接上。Temporal 是执行层:进程死掉、API 中断,或者整个区域的算力暂时不在,工人回来之后从最后一步继续。演示里每一次工具调用要能回答三件事:哪个智能体、代表哪个用户、这一次拿到的是哪一截权限。这是共享组织里的沙箱。它把这条链怎么记、令牌怎么用完即弃走了一遍。