spring-projects / spring-projects/spring-ai

Feature Request: Expose advisor context to tool functions for read and write access during tool execution

Open
#6,898 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

Title

Feature Request: Expose advisor context to tool functions for read and write access during tool execution

Body

Expected Behavior

Tool functions should be able to read and write the advisor ChatClientRequest.context() while a tool is executing.

Preferred developer experience example (Option A: expose advisor context via ToolContext API):

@Tool
public String queryDatabase(ToolContext toolContext) {
    // Read advisor context value propagated from advisor chain
    String tenantId = (String) toolContext.getAdvisorContext().get("tenantId");

    // Write execution state back into advisor context for downstream advisors
    toolContext.getAdvisorContext().put("tool.db.execution.ms", 120L);
    return "query result";
}

After tool execution completes, values written by the tool inside getAdvisorContext() should be visible to subsequent advisor after() callbacks in the advisor chain.

Alternative acceptable options:

  1. Built‑in bidirectional mirroring advisor to sync keys between request.context() and request.toolContext().
  2. Tool lifecycle hooks (onToolStart / onToolComplete) within advisor layer providing access to both ChatClientRequest and tool execution result.

Current Behavior

Spring AI 2.0 maintains two fully isolated context containers:

  1. ChatClientRequest.context() — advisor‑chain context, accessible only in advisor before() / after() methods. Tool functions cannot access this context at all.
  2. ChatClientRequest.toolContext() — passed to tool functions as a snapshot. Modifications inside tool functions do not flow back to the original ChatClientRequest.

There is no native framework way for tool logic to:

  • Read state populated by advisors.
  • Publish runtime state from tool execution back into advisor chain for logging, metrics, agent state management in downstream advisors.

Context

Use cases driving this feature

I am building agent and observability logic that spans both advisors and tool invocations:

  • Propagate trace/correlation id, tenant information from advisor layer into every tool call.
  • Capture tool runtime metadata (latency, custom business metrics) inside tool implementation and let later advisors aggregate, persist or export those metrics.
  • Build multi‑step agent workflows where tool execution needs to signal state changes to advisor chain.
Known workaround

Today the only workaround is manually sharing a thread‑safe mutable container (e.g. ConcurrentHashMap), injecting the same object reference into both request.context() and request.toolContext().

// Setup shared mutable state
ConcurrentHashMap<String, Object> sharedState = new ConcurrentHashMap<>();

chatClient.prompt()
        .user("user query")
        .context(Map.of("sharedState", sharedState))
        .toolContext(Map.of("sharedState", sharedState))
        .advisors(new MyObservabilityAdvisor())
        .tools(myTool)
        .call();
// Tool implementation
@Tool
public String myTool(ToolContext toolContext) {
    ConcurrentHashMap<String,Object> state =
            (ConcurrentHashMap<String, Object>) toolContext.getContext().get("sharedState");
    state.put("tool.latency.ms", 78);
    return "finished";
}

This workaround works but has significant downsides:

  • Requires repetitive boilerplate for every prompt invocation.
  • No type‑safety, relies on raw Object casts and string keys.
  • Easy to make mistakes: if developer forgets to inject identical reference to both contexts, state sharing silently breaks with no warning.
  • No discoverable framework API; developers have to invent this pattern themselves.
Related references
  • #4079: Advisor context feature (external context passing to advisors, does not cover tool‑advisor sharing)
  • #4997: Tool execution moved into advisor layer, makes this feature technically feasible, but advisor context is still not exposed to tool functions.

Note: Parallel tool‑calling thread‑safety must be considered for any mutable context exposed to tool functions.

中文翻译 Chinese Translation

期望行为(Expected Behavior)

工具函数在执行期间应当可以读取、写入 advisor 的 ChatClientRequest.context()

理想编码示例(方案A:通过 ToolContext API 暴露 advisor 上下文):

@Tool
public String queryDatabase(ToolContext toolContext) {
    // 读取 advisor 链传递过来的上下文数据
    String tenantId = (String) toolContext.getAdvisorContext().get("tenantId");

    // 将工具运行状态写回 advisor 上下文,供后续 advisor after() 使用
    toolContext.getAdvisorContext().put("tool.db.execution.ms", 120L);
    return "query result";
}

工具执行完成后,工具写入 advisor context 的数据,能够在后续 advisor 的 after() 回调中可见。

其他可接受方案:

  1. 提供内置双向同步 Advisor,自动双向同步 request.context()request.toolContext() 指定 key。
  2. 在 Advisor 层提供工具生命周期钩子 onToolStart / onToolComplete,同时暴露 ChatClientRequest 和工具执行结果。

当前行为(Current Behavior)

Spring AI 2.0 存在两套完全隔离的上下文容器:

  1. ChatClientRequest.context():Advisor 链上下文,仅能在 Advisor 的 before() / after() 访问;工具函数完全无法读取。
  2. ChatClientRequest.toolContext():传递给工具函数的是一份快照;工具内部修改不会回流原始 ChatClientRequest

框架原生没有能力实现:

  • 工具读取 Advisor 设置的状态;
  • 工具运行时产生状态回传给 Advisor 链,用于日志、指标、Agent 状态管理。

背景说明(Context)

使用场景

我正在开发横跨 Advisor 和工具调用的 Agent 与可观测能力:

  • 将 trace、租户信息从 Advisor 层透传到每一次工具调用;
  • 在工具内部采集运行元数据(耗时、业务指标),交由下游 Advisor 做聚合、落库;
  • 构建多步 Agent,工具执行需要向 Advisor 链回传状态变更。
当前变通方案

现有的唯一方案:手动创建线程安全可变容器 ConcurrentHashMap,把同一个对象引用同时放入 request.context()request.toolContext()

该方案存在明显缺陷:

  • 每次调用 Prompt 都需要重复样板代码;
  • 无类型安全,强制类型转换 + 字符串 key;
  • 极易出错:漏放任意一处,上下文共享静默失效,无报错提示;
  • 不属于框架公开API,开发者需要自己摸索这套模式。
相关 Issue
  • #4079:Advisor context,解决外部传入上下文到 Advisor,未覆盖工具‑Advisor互通;
  • #4997:工具执行逻辑迁移到 Advisor 层,为本特性提供实现基础,但尚未把 advisor context 暴露给工具。

注意:开放可变上下文给工具,需要考虑并行工具调用场景下的线程安全问题。

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the ToolContext and ChatClientRequest context/toolContext entry points through advisor before() and after() callbacks, including the related designs in issues #4079 and #4997. Define and validate one context-sharing approach, including write-back visibility after tool execution and behavior for parallel tool calls; done means the selected API and its semantics are covered by tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
ai, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.