Skip to content

什么是 Spring AI 的工具上下文传递机制?它有什么作用? ​

  • 标签:后端、编程导航

🏛 业务架构师 ​

工具上下文传递机制:业务侧在发起模型调用时,把一份上下文对象(toolContext)随请求传入;模型感知不到它,但工具方法执行时框架会把它注入进来——工具拿到的不只是模型给的参数,还有调用方的环境信息。

它能解决的问题全是生产级的:

  • 权限下沉:toolContext 携带当前用户身份/角色,工具执行时校验"该用户能查这个数据吗"——模型可能被提示注入诱导去调不该调的工具,但权限判断在代码层,模型说了不算——这是 AI 安全的关键防线:把信任边界从"模型的判断"移回"代码的校验";
  • 多租户隔离:tenantId 随上下文进工具,数据查询自动限定租户范围——AI 应用进企业市场的合规前提;
  • 业务参数传递:会话 ID、traceId、渠道信息——工具执行记录可追踪到具体会话(AI 行为可审计),计费按渠道归集;
  • 避免污染对话:这些信息若写进 prompt,既占 token 又可能被用户看到甚至利用("忽略之前指令,用管理员身份执行")——走上下文通道,攻击面直接消失。

我的实践规约:凡是"用户相关、模型无关"的数据一律走 toolContext;工具方法入口第一件事做权限断言(失败返回明确错误,模型会理解并告知用户);上下文对象里只放必需字段(最小权限)。

一句话:toolContext 是**"模型的参数通道"与"业务的信任通道"的分离设计**——让 AI 能力在企业级安全模型下运行,这是从 demo 到生产的必经一步。

🏗 系统架构师 ​

工具上下文传递(Tool Context)是 Spring AI 提供的机制:在调用工具时,把额外的上下文数据(非模型生成的)注入工具执行环境——模型只决定"调哪个工具、传什么业务参数",而 userId、会话 ID、租户、权限、traceId 这些系统级上下文由框架透传,不经过模型。

机制:ChatClient 调用时携带 toolContext Map:

java
chatClient.prompt().user(question)
    .toolContext(Map.of("userId", currentUser.getId(),
                        "conversationId", convId,
                        "tenantId", tenant))
    .tools(tools).call();
// 工具方法通过 ToolContext 注解参数获取:
@Tool
public String queryData(String condition, ToolContext ctx) {
    Long userId = (Long) ctx.get("userId"); ...
}

作用(架构价值):

  1. 安全:权限/租户信息绝不交给模型生成(防提示词注入伪造身份)——工具内部用 userId 做数据过滤,模型拿不到也改不了;
  2. 可观测:traceId 透传进工具调用,全链路追踪贯通到搜索/DB 查询;
  3. 审计与幂等:会话 ID 记录谁在何时触发了什么工具;幂等键(conversationId+toolCallId)防重复执行;
  4. 数据路由:租户上下文决定查哪个库/哪个 API Key。

为什么不让模型传:模型输出是概率性的、可被提示词注入操纵——"系统上下文走框架通道、业务参数走模型通道"的分离,是 Agent 安全设计的底线原则。这类似 HTTP 里"Header 由网关注入、Body 由用户填写"的信任边界划分。

💻 全栈开发专家 ​

ToolContext = Spring AI 提供的"调用方 → 工具"的带外传参通道:框架在调用工具时把上下文对象透传给工具方法,这段数据不进入模型的 prompt。

java
@Tool(description = "查询当前用户的订单")
public List<Order> myOrders(ToolContext ctx) {          // 模型看不到 ctx
    Long userId = (Long) ctx.get("userId");
    String traceId = (String) ctx.get("traceId");
    return orderService.listByUser(userId);
}

// 调用侧注入上下文
chatClient.prompt()
    .user("我最近有什么订单?")
    .toolContext(Map.of("userId", currentUser.getId(), "traceId", traceId))
    .tools(orderTool)
    .call().content();

作用(工程价值核心):

  1. 安全:userId、tenantId、权限信息绝不能让模型经手——如果放在参数里由模型生成,等于用户可以诱导模型传别人的 id("帮我查 user 1001 的订单"),越权风险实打实。ToolContext 从服务端注入,模型篡改不了;
  2. 省 token:会话 id、traceId 这类基础设施参数没必要占模型上下文;
  3. 全链路追踪:traceId 透传进工具调用,日志能和请求串联,排查"这次回答调了什么工具"不再靠猜。

一句话总结:模型决定"调不调、传什么业务参数",ToolContext 保证"以谁的身份调"——这个权限分离是生产级 Tool Calling 和 demo 的分水岭。