news 2026/6/16 17:37:22

当 AI Agent 调用工具时,用户 ID 从哪来?——Spring AI ToolContext 传值实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
当 AI Agent 调用工具时,用户 ID 从哪来?——Spring AI ToolContext 传值实战

一个绕不开的问题

你在用 Spring AI 开发 Agent 应用,一切都很美好——模型能理解自然语言,能自主决定调用哪个工具、传什么参数,直到你遇到了这样一个场景:

@Tool(description="查询用户账户余额")StringgetBalance(StringaccountType){returnbankService.getBalance(userId,accountType);// ^^^^^^// 这个 userId 从哪来?}

用户 ID 不能让 LLM 填。它属于运行时上下文,是请求进来时应用层已经确定的信息,不应该、也不能让模型来"猜"。

类似的场景很多:

  • 用户 ID、租户 ID——多租户系统中的安全边界
  • 会话 ID——需要关联当前对话上下文
  • 权限 Token——工具需要携带鉴权信息访问外部 API
  • 请求来源、渠道标识——工具行为需要根据来源差异化

这类参数有一个共同特征:它们属于"确定的上下文",不是 LLM 应该生成的"推理参数"。如果把它们混入工具的输入参数让模型来填,轻则幻觉(模型编造一个 userId),重则越权(模型填了别人的 userId)。

Spring AI 提供了ToolContext机制来优雅地解决这个问题。


ToolContext 是什么

一句话概括:ToolContext 是一个由应用层注入、对 LLM 不可见的键值对容器,在工具执行时自动传递给工具方法。

它的核心设计思想:

┌──────────────┐ toolContext(Map) ┌──────────────┐ │ Application │ ──────────────────────────▶│ Tool │ │ (应用层) │ userId, tenantId, ... │ (工具方法) │ └──────────────┘ └──────────────┘ ┌──────────────┐ toolInput(参数) ┌──────────────┐ │ LLM │ ─────────────────────▶│ Tool │ │ (大语言模型) │ accountType, ... │ (工具方法) │ └──────────────┘ └──────────────┘
  • LLM 负责填充的:工具的业务参数(如accountType)——模型根据用户意图推理得出
  • 应用层负责注入的:上下文参数(如userId)——应用层在调用时确定,透传给工具

两条路径,互不干扰。LLM 完全感知不到 ToolContext 的存在,既不会在工具的 JSON Schema 中看到它,也不会试图为它生成值。


具体用法

Spring AI 提供了两种等价的工具定义方式,都支持 ToolContext:

方式一:@Tool注解式——在方法签名中声明 ToolContext

ToolContext作为方法的最后一个参数,Spring AI 会自动注入:

classCustomerTools{@Tool(description="查询客户信息")CustomergetCustomerInfo(Longid,ToolContexttoolContext){// 从 ToolContext 中取出应用层注入的租户 IDStringtenantId=(String)toolContext.getContext().get("tenantId");returncustomerRepository.findById(id,tenantId);}@Tool(description="查询账户余额")StringgetBalance(StringaccountType,ToolContexttoolContext){StringuserId=(String)toolContext.getContext().get("userId");returnbankService.getBalance(userId,accountType);}}

注意:ToolContext参数对模型是隐藏的。模型只看到idaccountType,不会看到ToolContext

方式二:BiFunction<T, ToolContext, R>编程式——函数式定义

等价地,用BiFunction定义工具逻辑,第二个参数就是ToolContext

publicclassAccountInfoToolimplementsBiFunction<String,ToolContext,String>{@OverridepublicStringapply(Stringquery,ToolContexttoolContext){StringuserId=(String)toolContext.getContext().get("userId");StringtenantId=(String)toolContext.getContext().get("tenantId");if(userId==null){return"用户未登录,无法查询";}returnaccountService.query(userId,tenantId,query);}}// 构建 ToolCallbackToolCallbackaccountTool=FunctionToolCallback.builder("get_account_info",newAccountInfoTool()).description("查询当前用户的账户信息").inputType(String.class).build();

两种方式完全等价,选择哪种取决于你的编码风格:

  • 注解式更简洁,适合工具逻辑简单的场景
  • 编程式更灵活,适合需要复杂构造或复用的场景

在调用端注入 ToolContext

定义好工具后,在调用时注入上下文数据。根据入口不同,方式略有差异。

ChatClient 入口

Stringresponse=ChatClient
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/16 17:35:35

2026打游戏用什么电脑笔记本好,学生党不花冤枉钱

对于大多数大学生玩家而言&#xff0c;预算往往是决定购买决策的首要因素。6000-8000元是多数学生能够承受的价格区间&#xff0c;但在这个区间里&#xff0c;既要保证流畅运行主流3A游戏&#xff0c;又要兼顾学习、编程、设计等用途&#xff0c;还要屏幕不伤眼、续航不拉胯&am…

作者头像 李华
网站建设 2026/6/16 17:30:50

3个实用场景:如何用安卓手机轻松启动Switch游戏机

3个实用场景&#xff1a;如何用安卓手机轻松启动Switch游戏机 【免费下载链接】NXLoader My first Android app: Launch Fuse Gele payloads from stock Android (CVE-2018-6242) 项目地址: https://gitcode.com/gh_mirrors/nx/NXLoader NXLoader是一款创新的安卓应用&a…

作者头像 李华
网站建设 2026/6/16 17:08:00

Kronos金融时序预测模型:从两阶段架构到量化实战的完整指南

Kronos金融时序预测模型&#xff1a;从两阶段架构到量化实战的完整指南 【免费下载链接】Kronos Kronos: A Foundation Model for the Language of Financial Markets 项目地址: https://gitcode.com/GitHub_Trending/kronos14/Kronos Kronos作为首个面向金融K线序列的开…

作者头像 李华
网站建设 2026/6/16 17:01:13

企业AI落地困局与破局

在过去的两年里&#xff0c;人工智能技术以惊人的速度渗透进商业世界&#xff0c;大模型的能力边界不断拓展&#xff0c;各类AI应用如雨后春笋般涌现。然而&#xff0c;当我们将视线从炫酷的技术发布转向真实的企业经营场景&#xff0c;会发现一个巨大的鸿沟&#xff1a;中小企…

作者头像 李华
网站建设 2026/6/16 16:54:55

杭州索川科技:专业电摩控制器与电机测试台解决方案

杭州索川科技&#xff1a;专业电摩控制器与电机测试台解决方案 引言&#xff1a;测试台在电摩行业的重要性 随着电动摩托车&#xff08;电摩&#xff09;市场的快速发展&#xff0c;控制器和电机作为电摩的核心部件&#xff0c;其性能、可靠性和安全性直接决定了整车的品质。杭…

作者头像 李华