ADK Python 工具级认证实战:AuthConfig、AuthenticatedFunctionTool 与 OAuth2 暂停-恢复机制深度解析 📅 发布时间:2026/9/13 22:24:36 👁 浏览次数: ADK Python 工具级认证实战AuthConfig、AuthenticatedFunctionTool 与 OAuth2 暂停-恢复机制深度解析【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python导读当 Agent 的工具需要代表终端用户调用第三方 API读取日历、邮箱、网盘文档时ADKAgent Development KitPython 实现通过AuthConfig声明工具所需的认证方式并在缺少凭据时暂停当前调用、发起授权请求、待用户完成授权后再恢复执行。本文以官方指南 docs/guides/auth/tool_auth/index.md 为主线结合 src/google/adk/auth 目录下的源码实现完整讲解认证中断机制的工作原理、OAuth2 授权码流的端到端接入方式、凭据存储策略与配置项细节帮助你在自己的 Agent 中正确接入带认证的工具。AuthConfig声明这个工具需要什么凭据一个读取用户日历或文档的工具需要属于该用户的凭据。只有终端用户本人能授予这个凭据而授予动作必然离开 Agent 本体打开一个授权同意页面再带着重定向地址回来。这个离开-回来的往返无法在单次工具调用内完成因此 ADK 将它建模为一次中断interruption工具声明它需要什么凭据并返回一个占位结果本次调用invocation带着凭据请求结束应用层运行同意流程带着答案发起新一轮运行ADK 重新执行那个一直在等待的工具调用。三个核心类协作完成这一机制定义在 src/google/adk/auth/auth_tool.pyAuthScheme描述 API 期望如何被认证。它是fastapi.openapi.models.SecuritySchemeAPIKey、HTTPBase、OAuth2等、OpenIdConnectWithConfig与CustomAuthScheme的联合类型见 src/google/adk/auth/auth_schemes.py。AuthCredential保存秘密本身。auth_type决定其形态API_KEY、HTTP、OAUTH2、OPEN_ID_CONNECT、SERVICE_ACCOUNT枚举定义见 auth_credential.py对应的字段api_key、http、oauth2、service_account持有实际值。AuthConfig把前两者配对并附上credential_key作为该凭据在存储中的键。AuthenticatedFunctionTool、BaseAuthenticatedTool和McpTool都接收AuthConfig并把凭据的获取委托给CredentialManagercredential_manager.pyLLM 流程中的 auth 请求处理器负责暂停调用并在稍后恢复等待中的工具调用BaseCredentialService负责在轮次之间记住凭据。一个值得注意的安全细节凭据类统一继承自BaseModelWithConfig其__repr_args__会将来路不明的额外字段值替换为redacted避免密钥泄漏进日志和传给 LLM 的错误字符串auth_credential.py。快速开始一个需要 OAuth2 access token 的完整 Agent下面的完整示例构建了一个文档 Agent它只有一个工具list_documents需要 OAuth2 授权码流获取访问令牌。运行后程序会打印授权 URL、等待你粘贴落地后的重定向地址然后完成最初的请求。import asyncio from fastapi.openapi.models import OAuth2 from fastapi.openapi.models import OAuthFlowAuthorizationCode from fastapi.openapi.models import OAuthFlows from google.adk.agents import LlmAgent from google.adk.apps import App from google.adk.auth import AuthConfig from google.adk.auth import AuthCredential from google.adk.auth import AuthCredentialTypes from google.adk.auth import OAuth2Auth from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService from google.adk.runners import Runner from google.adk.sessions import InMemorySessionService from google.adk.tools.authenticated_function_tool import AuthenticatedFunctionTool from google.genai import types auth_config AuthConfig( auth_schemeOAuth2( flowsOAuthFlows( authorizationCodeOAuthFlowAuthorizationCode( authorizationUrlhttps://provider.example.com/authorize, tokenUrlhttps://provider.example.com/token, scopes{documents.read: Read your documents}, ) ) ), raw_auth_credentialAuthCredential( auth_typeAuthCredentialTypes.OAUTH2, oauth2OAuth2Auth( client_idYOUR_CLIENT_ID, client_secretYOUR_CLIENT_SECRET, redirect_urihttp://localhost:8080/callback, ), ), credential_keydocuments_api, ) def list_documents(folder: str, credential: AuthCredential) - list[str]: Lists the documents in a folder. access_token credential.oauth2.access_token # Call the providers API with access_token here. return [f{folder}/report.pdf] agent LlmAgent( namedocuments_agent, instructionUse list_documents to answer questions about the users files., tools[ AuthenticatedFunctionTool(funclist_documents, auth_configauth_config) ], ) runner Runner( appApp(namedocuments_app, root_agentagent), session_serviceInMemorySessionService(), credential_serviceInMemoryCredentialService(), ) async def main(): session await runner.session_service.create_session( app_namedocuments_app, user_iduser ) message types.Content( roleuser, parts[types.Part(textWhat is in my reports folder?)] ) while True: auth_call None async for event in runner.run_async( user_iduser, session_idsession.id, new_messagemessage ): for function_call in event.get_function_calls(): if function_call.name adk_request_credential: auth_call function_call if event.content and event.content.parts: for part in event.content.parts: if part.text: print(part.text) if auth_call is None: break # The run paused. Send the user through consent and hand back the redirect. requested auth_call.args[authConfig] oauth2 requested[exchangedAuthCredential][oauth2] print(Open this URL:, oauth2[authUri]) oauth2[authResponseUri] input(Paste the URL you landed on: ) response types.Part.from_function_response( nameadk_request_credential, responserequested ) response.function_response.id auth_call.id message types.Content(roleuser, parts[response]) asyncio.run(main())要点说明credential参数由框架注入对模型不可见——模型只会看到folder参数不会接触敏感令牌。adk webUI 会替你完成同意步骤上面这段循环是自定义客户端不使用adk web时的等价实现。注意list_documents的签名包含credential: AuthCredential这正是AuthenticatedFunctionTool通过inspect.signature识别并注入的特殊参数见 authenticated_function_tool.py该参数会被追加到_ignore_params从而从工具 schema 中隐藏L66。工作原理从声明到暂停再到恢复声明工具需要凭据两种方式让工具获得认证能力AuthenticatedFunctionTool包装普通函数如上面的list_documentsBaseAuthenticatedTool是类式等价物继承它并实现_run_async_impl凭据会作为关键字参数传入base_authenticated_tool.py。两者的执行流程完全一致见 L81-L95先向CredentialManager请求凭据若拿到凭据直接执行真正的工具逻辑若拿不到如 OAuth2 只有 client id/secret尚需用户授权则调用request_credential并返回response_for_auth_required默认字符串Pending User Authorization.作为占位结果而不是执行你的代码。工具也可以手动完成这一流程在工具内部使用tool_context.request_credential与tool_context.get_auth_response前者需要function_call_id因此只能在工具内使用在 agent 回调中则使用save_credential与load_credential。暂停与恢复的六步时序整个流程可以用下面的时序图概括原文档中的mermaid图结合 credential_manager.py 与 auth_preprocessor.py 的源码拆解为六个步骤查询凭据工具调用CredentialManager.get_auth_credential。若原始凭据已可直接使用API key、HTTP 凭据这类简单凭据直接原样返回流程完全不暂停——源码中_is_credential_ready只对API_KEY与HTTP两类返回TrueL356-L367。否则依次检查凭据服务、会话状态中的 auth 响应以及是否属于无需用户的 client-credentials 流程_is_client_credentials_flow同时支持 OAuth2 的clientCredentialsflow 与 OIDC 声明了client_credentialsgrant 的情况见 L468-L489。对授权码流程且无任何存储时返回None。发起授权请求工具调用request_credential。AuthHandler.generate_auth_requestauth_handler.py为 OAuth2 / OIDC 方案构建授权 URL写入exchanged_auth_credential.oauth2.auth_uri同时生成state当code_challenge_method为S256时会生成 PKCEcode_verifiergenerate_auth_uri使用 authlib 的OAuth2Session.create_authorization_url见 L386-L427。整个配置被暂存在event_actions.requested_auth_configs中以正在等待的工具调用 id 为键。发出暂停事件flow 为每个请求单独发出一个携带名为adk_request_credential的长运行函数调用long-running function call的事件参数为functionCallId等待中的工具调用 id与authConfig第 2 步的配置。键是 camelCase因为配置按别名alias序列化。随后 flow 结束本次 invocation——这就是暂停。引导用户授权应用读取authConfig.exchangedAuthCredential.oauth2.authUri把用户送去授权页收集重定向结果。携带答案恢复你以新运行发起恢复其消息是一个用户角色的Content内含名为adk_request_credential的FunctionResponse。response 是同一份配置把答案填入exchangedAuthCredential可以是authResponseUri包含授权码的完整重定向 URL也可以是直接可用的accessToken。存储并重放下一次模型调用前auth 请求处理器把响应匹配回请求先在 OAuth2 / OIDC 场景下用授权码换取令牌再把凭据以temp:credential_key为键写入会话状态然后重新执行那个等待中的工具调用_store_auth_and_collect_resume_targets与_AuthLlmRequestProcessor见 auth_preprocessor.py。两个决定恢复能否成功的细节FunctionResponse的 id 必须是adk_request_credential调用的 id而不是等待中的工具调用 id后者通过functionCallId单独传递AuthToolArguments的两个字段正是function_call_id与auth_config见 auth_tool.py。恢复消息必须是最近一个带内容content且作者为user的事件——处理器只扫描这一条事件auth_preprocessor.py。恢复阶段还有一个安全设计客户端会原样回传配置因此处理器只从客户端响应中取浏览器往返的结果exchanged_auth_credential而 auth scheme、raw credential、credential key 全部以服务端发出的请求为准L158-L173AuthHandler在把配置发给客户端前会剥离 client secret_without_client_secret令牌交换时再从工具自身配置中重新挂载auth_handler.py。凭据存储在哪里临时状态与凭据服务第 6 步写入的是带temp:前缀的状态键。临时状态按设计就是临时的会话服务只在当前 invocation 内保留它不做持久化。它单独只能解除等待中的工具调用因此下一轮会再次要求用户授权。让授权持久生效的是凭据服务credential service把它传给 Runner 即可如快速开始示例中的InMemoryCredentialService。此后CredentialManager会把交换后的凭据保存在credential_key下后续调用时重新加载并且会在 OAuth2 token 过期时刷新而非再次弹窗_refresh_credential通过CredentialRefresherRegistry查找刷新器见 credential_manager.py。仓库中现成的两类凭据服务InMemoryCredentialService进程内字典存储按app_name - user_id - credential_key三层分桶L58-L68适合开发与单进程部署SessionStateCredentialService把凭据放进会话状态注意其 docstring 明确警告存于会话可能不安全风险自负。两者都继承自 base_credential_service.py 中的BaseCredentialService。此外CredentialManager内部默认注册了 OAuth2/OIDC 的OAuth2CredentialExchanger与OAuth2CredentialRefresher以及 SERVICE_ACCOUNT 的ServiceAccountCredentialExchangerL133-L157你也可以用register_credential_exchanger/register_credential_refresher注册自定义实现。配置选项速查原文档给出的AuthConfig字段说明如下OptionTypeDefaultDescriptionauth_schemeAuthSchemerequired定义 API 的认证方式。对授权码流程它携带 authorization URL、token URL 与 scopes——授权 URL 正是由这些信息构建的。raw_auth_credentialAuthCredential \| NoneNone你配置的原始凭据如 OAuth client id 与 secret。OAuth2 / OIDC 方案必需对 API key 或 HTTP 凭据而言它本身就是凭据无需用户同意。exchanged_auth_credentialAuthCredential \| NoneNoneADK 与客户端共同填写的工作副本出发时带授权 URL 与state返回时带重定向地址或 access token。构造配置时应保持未设置。credential_keystr \| Nonederived凭据在存储中的键作用域为 app 与 user。不设置时由 scheme 与 raw credential 的摘要推导——稳定但不可读且任一变化都会导致键变化。建议显式设置。与源码对照可以补充三点raw_auth_credential的校验_validate_credential会强制要求 OAuth2 / OIDC scheme 必须有raw_auth_credential且其oauth2子对象不能为空否则抛出ValueErrorL369-L401。若 scheme 是带issuer_url的ExtendedOAuth2且缺少端点信息还会尝试通过OAuth2DiscoveryManager自动发现 authorization/token 端点L414-L447。OAuth2Auth的可用字段见 auth_credential.py除 client id/secret 外还包括auth_uri、state、nonce、redirect_uri、auth_response_uri、auth_code、access_token、refresh_token、id_token、expires_at、expires_in、audience、prompt、code_verifier、code_challenge_method以及默认值为client_secret_basic的token_endpoint_auth_method。其中秘密类字段secret、token、code 等均标记了reprFalse不会出现在 repr 输出中。credential_key的推导AuthConfig.__init__优先使用显式传入的credential_key否则从 raw credential / scheme 的model_extra中读取credential_key或credentialKey最后才调用get_credential_key()生成adk_scheme_credential形式的摘要键auth_tool.py。注意get_credential_key()已被标记为 deprecated官方建议直接设置credential_key。已知限制实验性 APIAuthenticatedFunctionTool、BaseAuthenticatedTool、CredentialManager、各凭据服务与凭据交换器均为实验特性。它们默认开启首次使用时输出一次告警但 API 可能变化。OAuth2 辅助功能依赖authlib没有authlib时不会生成授权 URL、也不会用授权码换取令牌凭据原样透传客户端必须自己完成 OAuth 流程AUTHLIB_AVAILABLE的判定见 auth_handler.py缺失时generate_auth_uri直接返回原始凭据副本见 L328-L333。会话状态不是秘密存储SessionStateCredentialService会把令牌放进会话状态所在的位置。AuthConfig.get_credential_key()已弃用请设置credential_key。相关示例OAuth with the Calendar API日历 API 的 OAuth2 授权码流完整接入示例。OAuth2 client credentialsclient-credentials 流程——无需用户参与的机器对机器认证。MCP toolset auth, with the resume loopMCP 工具集认证包含与本文一致的恢复循环实现。API key auth on a workflow node在工作流节点上使用 API key 认证。小结ADK 的工具级认证把离开 Agent 完成用户授权这一外部往返建模为一次可恢复的中断AuthConfig声明需求CredentialManager负责加载、交换、刷新与缓存adk_request_credential长运行函数调用携带授权请求暂停调用auth 请求处理器在下一轮恢复时把凭据存入临时状态配合凭据服务实现跨轮持久最后重新执行等待中的工具。理解这套暂停-恢复循环是接入 OAuth2、OIDC 等用户授权类工具认证的关键对 API key 与 HTTP 这类简单凭据则无需任何暂停配置即可直通使用。【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考