diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java index 45f70fc..46f9ab6 100644 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java +++ b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/CodeactAgent.java @@ -195,6 +195,12 @@ public static class CodeactAgentBuilder extends Builder { // Keep reference to ChatModel for code generation private ChatModel chatModel; + // SubAgent system prompt (for Codeact phase code generation) + private String subAgentSystemPrompt; + + // state keys to propagate from parent agent (react phase) to child agent (codeact phase) + private List stateKeysToPropagate = new ArrayList<>(); + // GraphLifecycleListeners (用于可观测性) private List lifecycleListeners = new ArrayList<>(); @@ -245,6 +251,22 @@ public CodeactAgentBuilder subAgentHooks(List hooks) { return this; } + /** + * Set state keys to propagate from parent agent (react phase) to child agent (codeact phase) + */ + public CodeactAgentBuilder stateKeysToPropagate(List keys) { + this.stateKeysToPropagate = keys != null ? new ArrayList<>(keys) : new ArrayList<>(); + return this; + } + + /** + * Set state keys to propagate from parent agent (react phase) to child agent (codeact phase), in variable argument form + */ + public CodeactAgentBuilder stateKeysToPropagate(String... keys) { + this.stateKeysToPropagate = new ArrayList<>(Arrays.asList(keys)); + return this; + } + public CodeactAgentBuilder experienceProvider(ExperienceProvider experienceProvider) { this.experienceProvider = experienceProvider; return this; @@ -260,6 +282,14 @@ public CodeactAgentBuilder fastIntentService(FastIntentService fastIntentService return this; } + /** + * Set custom system prompt for the Codeact sub-agent (code generation phase). + */ + public CodeactAgentBuilder subAgentSystemPrompt(String systemPrompt) { + this.subAgentSystemPrompt = systemPrompt; + return this; + } + /** * Enable/disable initial code generation hook */ @@ -337,8 +367,8 @@ public CodeactAgentBuilder returnSchemaRegistry(ReturnSchemaRegistry registry) { /** * Set the ToolRegistryBridgeFactory for customizing ToolRegistryBridge creation. * - *

可用于添加可观测性、任务记录等功能。 - * 如果不设置,将使用默认的 ToolRegistryBridge。 + *

If not set, the default factory will be used which creates standard + * ToolRegistryBridge instances. * * @param factory the ToolRegistryBridgeFactory to use * @return CodeactAgentBuilder instance for chaining @@ -613,7 +643,7 @@ public CodeactAgent build() { null, // Will be set by ReactAgent new OverAllState(), // Placeholder this.codeactToolRegistry, // Pass CodeactTool registry - this.toolRegistryBridgeFactory, // Pass ToolRegistryBridgeFactory for observability + this.toolRegistryBridgeFactory, // Pass custom factory (null will use default) this.allowIO, this.allowNativeAccess, this.executionTimeoutMs @@ -1005,8 +1035,9 @@ private ModelInterceptor createCodeactSubAgentInterceptor() { .fastIntentService(this.fastIntentService) .includeDefaultCodeGenerator(true) // 使用默认代码生成器 .hooks(this.subAgentHooks) // Pass sub-agent hooks - .returnSchemaRegistry(this.codeactToolRegistry != null ? - this.codeactToolRegistry.getReturnSchemaRegistry() : null) + .returnSchemaRegistry(this.codeactToolRegistry != null ? this.codeactToolRegistry.getReturnSchemaRegistry() : null) + .stateKeysToPropagate(this.stateKeysToPropagate) // 传递需要跨 agent 传递的 state keys + .subAgentSystemPrompt(this.subAgentSystemPrompt) .build(); } } diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java index a854c9c..a489436 100644 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java +++ b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/BaseAgentTaskTool.java @@ -18,13 +18,17 @@ import com.alibaba.cloud.ai.graph.CompiledGraph; import com.alibaba.cloud.ai.graph.OverAllState; import com.alibaba.cloud.ai.graph.agent.BaseAgent; +import com.alibaba.cloud.ai.graph.agent.tools.ToolContextConstants; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ToolContext; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; @@ -37,6 +41,7 @@ *

  • 支持 Map<String, BaseAgent> 而不是 Map<String, ReactAgent>
  • *
  • 通过 CompiledGraph.invoke() 调用而不是 agent.call()
  • *
  • 从 OverAllState 中提取结果
  • + *
  • 支持配置 stateKeysToPropagate,将父 agent 的指定 state 传递给子 agent
  • * * * @author Assistant Agent Team @@ -48,8 +53,22 @@ public class BaseAgentTaskTool implements BiFunction subAgents; + /** + * 需要从父 agent 的 OverAllState 传递给子 agent 的 state keys 列表。 + *

    + * 这是一个通用扩展点,允许业务方配置需要跨 agent 传递的状态。 + */ + private final List stateKeysToPropagate; + public BaseAgentTaskTool(Map subAgents) { + this(subAgents, Collections.emptyList()); + } + + public BaseAgentTaskTool(Map subAgents, List stateKeysToPropagate) { this.subAgents = subAgents; + this.stateKeysToPropagate = stateKeysToPropagate != null + ? new ArrayList<>(stateKeysToPropagate) + : Collections.emptyList(); } @Override @@ -71,22 +90,25 @@ public String apply(TaskRequest request, ToolContext toolContext) { // 3. Build inputs from description or structured inputs Map inputs; if (request.structuredInputs != null && !request.structuredInputs.isEmpty()) { - inputs = request.structuredInputs; + inputs = new HashMap<>(request.structuredInputs); logger.info("BaseAgentTaskTool#apply 使用结构化输入: {}", inputs.keySet()); } else { inputs = buildInputsFromDescription(request.description); logger.info("BaseAgentTaskTool#apply 从描述解析输入: {}", inputs.keySet()); } - // 4. Invoke the subagent through CompiledGraph + // 4. Propagate configured state keys from parent agent + propagateParentStateKeys(toolContext, inputs); + + // 5. Invoke the subagent through CompiledGraph CompiledGraph compiledGraph = subAgent.getAndCompileGraph(); Optional resultOpt = compiledGraph.invoke(inputs); - // 5. Extract result from state + // 6. Extract result from state OverAllState resultState = resultOpt.orElseThrow(() -> new IllegalStateException("SubAgent returned empty result")); - // 6. Try to get generated_code or any string result + // 7. Try to get generated_code or any string result String result = resultState.value("generated_code", String.class) .orElseGet(() -> extractAnyStringResult(resultState)); @@ -100,6 +122,52 @@ public String apply(TaskRequest request, ToolContext toolContext) { } } + /** + * 从父 agent 的 OverAllState 中提取配置的 keys,传递给子 agent。 + *

    + * 这是一个通用扩展点,允许业务方将父 agent 的状态(如评估结果)传递给子 agent。 + * + * @param toolContext 工具上下文,包含父 agent 的 OverAllState + * @param inputs 子 agent 的输入 Map,将被追加父 agent 的状态 + */ + private void propagateParentStateKeys(ToolContext toolContext, Map inputs) { + if (stateKeysToPropagate.isEmpty()) { + return; + } + + if (toolContext == null || toolContext.getContext() == null) { + logger.debug("BaseAgentTaskTool#propagateParentStateKeys - toolContext 为空,跳过状态传递"); + return; + } + + Object stateObj = toolContext.getContext().get(ToolContextConstants.AGENT_STATE_CONTEXT_KEY); + if (!(stateObj instanceof OverAllState)) { + logger.debug("BaseAgentTaskTool#propagateParentStateKeys - 父 agent state 不存在或类型不匹配,跳过状态传递"); + return; + } + + OverAllState parentState = (OverAllState) stateObj; + int propagatedCount = 0; + + for (String key : stateKeysToPropagate) { + Optional valueOpt = parentState.value(key); + if (valueOpt.isPresent()) { + Object value = valueOpt.get(); + inputs.put(key, value); + propagatedCount++; + logger.debug("BaseAgentTaskTool#propagateParentStateKeys - 传递状态: key={}, valueType={}", + key, value.getClass().getSimpleName()); + } else { + logger.debug("BaseAgentTaskTool#propagateParentStateKeys - 状态 key={} 在父 agent 中不存在,跳过", key); + } + } + + if (propagatedCount > 0) { + logger.info("BaseAgentTaskTool#propagateParentStateKeys - 完成状态传递: propagatedCount={}, keys={}", + propagatedCount, stateKeysToPropagate); + } + } + /** * 从任务描述构建输入参数 * 尝试解析描述中的结构化信息 diff --git a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java index 9a49b08..b09e8e6 100644 --- a/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java +++ b/assistant-agent-autoconfigure/src/main/java/com/alibaba/assistant/agent/autoconfigure/subagent/CodeactSubAgentInterceptor.java @@ -74,6 +74,18 @@ public class CodeactSubAgentInterceptor extends ModelInterceptor { private final ExperienceExtensionProperties experienceExtensionProperties; private final FastIntentService fastIntentService; + /** + * 需要从父 agent 传递给子 agent 的 state keys。 + * 通过此配置,业务方可以指定需要跨 agent 传递的状态。 + */ + private final List stateKeysToPropagate; + + /** + * 子 Agent 的系统提示词(用于代码生成阶段)。 + * 如果设置了此字段,将传递给 CodeGeneratorSubAgent 用于定制代码生成行为。 + */ + private final String subAgentSystemPrompt; + private CodeactSubAgentInterceptor(Builder builder) { this.systemPrompt = builder.systemPrompt != null ? builder.systemPrompt : DEFAULT_SYSTEM_PROMPT; this.subAgents = new HashMap<>(builder.subAgents); @@ -84,6 +96,10 @@ private CodeactSubAgentInterceptor(Builder builder) { this.experienceProvider = builder.experienceProvider; this.experienceExtensionProperties = builder.experienceExtensionProperties; this.fastIntentService = builder.fastIntentService; + this.stateKeysToPropagate = builder.stateKeysToPropagate != null + ? new ArrayList<>(builder.stateKeysToPropagate) + : Collections.emptyList(); + this.subAgentSystemPrompt = builder.subAgentSystemPrompt; // 添加默认code-generator和condition-code-generator(对标general-purpose) if (includeDefaultCodeGenerator && builder.defaultModel != null) { @@ -95,7 +111,8 @@ private CodeactSubAgentInterceptor(Builder builder) { builder.defaultLanguage, false, // 不是条件判断函数 builder.hooks, - builder.returnSchemaRegistry + builder.returnSchemaRegistry, + builder.subAgentSystemPrompt // 传递自定义系统提示词 ); this.subAgents.put("code-generator", codeGenAgent); @@ -107,7 +124,8 @@ private CodeactSubAgentInterceptor(Builder builder) { builder.defaultLanguage, true, // 是条件判断函数 builder.hooks, - builder.returnSchemaRegistry + builder.returnSchemaRegistry, + builder.subAgentSystemPrompt ); this.subAgents.put("condition-code-generator", conditionCodeGenAgent); @@ -115,7 +133,12 @@ private CodeactSubAgentInterceptor(Builder builder) { } // 创建内部 BaseAgentTaskTool(对标 SubAgentInterceptor 创建 TaskTool) - BaseAgentTaskTool taskTool = new BaseAgentTaskTool(this.subAgents); + // 传入 stateKeysToPropagate,支持将父 agent 的状态传递给子 agent + BaseAgentTaskTool taskTool = new BaseAgentTaskTool(this.subAgents, this.stateKeysToPropagate); + + if (!this.stateKeysToPropagate.isEmpty()) { + logger.info("CodeactSubAgentInterceptor# 配置状态传递: keys={}", this.stateKeysToPropagate); + } // 创建WriteCodeTool和WriteConditionCodeTool(委托给 BaseAgentTaskTool) CodeFastIntentSupport codeFastIntentSupport = @@ -133,6 +156,15 @@ private CodeactSubAgentInterceptor(Builder builder) { /** * 创建默认代码生成子Agent(对标createGeneralPurposeAgent) + * + * @param model ChatModel 实例 + * @param tools CodeactTool 列表 + * @param interceptors 拦截器列表 + * @param language 编程语言 + * @param isCondition 是否为条件判断函数 + * @param hooks Hook 列表 + * @param returnSchemaRegistry 返回值 Schema 注册表 + * @param customSystemPrompt 自定义系统提示词(可选,为 null 时使用默认提示词) */ private BaseAgent createDefaultCodeGeneratorAgent( ChatModel model, @@ -141,7 +173,8 @@ private BaseAgent createDefaultCodeGeneratorAgent( Language language, boolean isCondition, List hooks, - ReturnSchemaRegistry returnSchemaRegistry) { + ReturnSchemaRegistry returnSchemaRegistry, + String customSystemPrompt) { List modelInterceptors = new ArrayList<>(); if (interceptors != null) { @@ -153,7 +186,7 @@ private BaseAgent createDefaultCodeGeneratorAgent( } if (isCondition) { - return CodeGeneratorSubAgent.builder() + CodeGeneratorSubAgent.Builder builder = CodeGeneratorSubAgent.builder() .name("condition-code-generator") .description("Generate condition function code that returns boolean") .chatModel(model) @@ -162,10 +195,14 @@ private BaseAgent createDefaultCodeGeneratorAgent( .modelInterceptors(modelInterceptors) .hooks(hooks) .isCondition(true) - .returnSchemaRegistry(returnSchemaRegistry) - .build(); + .returnSchemaRegistry(returnSchemaRegistry); + + if (customSystemPrompt != null && !customSystemPrompt.isEmpty()) { + builder.customSystemPrompt(customSystemPrompt); + } + return builder.build(); } else { - return CodeGeneratorSubAgent.builder() + CodeGeneratorSubAgent.Builder builder = CodeGeneratorSubAgent.builder() .name("code-generator") .description("Generate function code based on requirements") .chatModel(model) @@ -174,8 +211,12 @@ private BaseAgent createDefaultCodeGeneratorAgent( .modelInterceptors(modelInterceptors) .hooks(hooks) .isCondition(false) - .returnSchemaRegistry(returnSchemaRegistry) - .build(); + .returnSchemaRegistry(returnSchemaRegistry); + + if (customSystemPrompt != null && !customSystemPrompt.isEmpty()) { + builder.customSystemPrompt(customSystemPrompt); + } + return builder.build(); } } @@ -236,11 +277,28 @@ public static class Builder { private ExperienceExtensionProperties experienceExtensionProperties; private FastIntentService fastIntentService; + /** + * 需要从父 agent 传递给子 agent 的 state keys。 + * 通过此配置,业务方可以指定需要跨 agent 传递的状态。 + */ + private List stateKeysToPropagate; + + /** + * 子 Agent 的自定义系统提示词(用于代码生成阶段)。 + * 如果设置了此字段,将传递给 CodeGeneratorSubAgent 用于定制代码生成行为。 + */ + private String subAgentSystemPrompt; + public Builder systemPrompt(String systemPrompt) { this.systemPrompt = systemPrompt; return this; } + public Builder subAgentSystemPrompt(String systemPrompt) { + this.subAgentSystemPrompt = systemPrompt; + return this; + } + public Builder defaultModel(ChatModel model) { this.defaultModel = model; return this; @@ -296,6 +354,30 @@ public Builder hooks(List hooks) { return this; } + /** + * 配置需要从父 agent 传递给子 agent 的 state keys。 + *

    + * 这是一个通用扩展点,允许业务方将父 agent 的状态传递给 Codeact 子 agent。 + * + * @param keys 需要传递的 state key 列表 + * @return this builder + */ + public Builder stateKeysToPropagate(List keys) { + this.stateKeysToPropagate = keys; + return this; + } + + /** + * 配置需要从父 agent 传递给子 agent 的 state keys(可变参数形式)。 + * + * @param keys 需要传递的 state keys + * @return this builder + */ + public Builder stateKeysToPropagate(String... keys) { + this.stateKeysToPropagate = Arrays.asList(keys); + return this; + } + /** * 添加自定义子Agent(对标addSubAgent) */ diff --git a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java index a244395..fac1cf9 100644 --- a/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java +++ b/assistant-agent-common/src/main/java/com/alibaba/assistant/agent/common/constant/CodeactStateKeys.java @@ -28,6 +28,8 @@ private CodeactStateKeys() { // Utility class } + // ==================== 代码生成上下文 ==================== + /** * Key for storing the list of generated codes in the current session * Type: List<GeneratedCode> @@ -63,5 +65,72 @@ private CodeactStateKeys() { * Type: Boolean */ public static final String INITIAL_CODE_GEN_DONE = "initial_code_gen_done"; + + // ==================== 工具白名单配置 ==================== + + /** + * 可用工具名称白名单 + * + *

    类型:List<String> + *

    示例:["search_app", "reply_user", "get_project_info"] + *

    用途:精确指定允许使用的工具,工具名称对应 CodeactTool.getName() + *

    为空或不存在时:不按名称筛选 + * + *

    注意:这里存储的是工具的 name(CodeactTool.getName()),不是独立的 ID。 + * 如果评估时 LLM 输出的是缩写/简短 ID,上层应用需要在写入 state 前将 ID 转换为对应的工具 name。 + */ + public static final String AVAILABLE_TOOL_NAMES = "available_tool_names"; + + /** + * 可用工具组白名单 + * + *

    类型:List<String> + *

    示例:["search", "reply", "app_helper"] + *

    用途:按工具组筛选,组名对应 CodeactToolMetadata.targetClassName() + *

    为空或不存在时:不按组筛选 + */ + public static final String AVAILABLE_TOOL_GROUPS = "available_tool_groups"; + + /** + * 白名单模式 + * + *

    类型:String + *

    可选值: + * - "INTERSECTION"(默认):名称白名单和组白名单取交集 + * - "UNION":名称白名单和组白名单取并集 + * - "NAME_ONLY":仅使用名称白名单 + * - "GROUP_ONLY":仅使用组白名单 + *

    为空或不存在时:默认为 INTERSECTION + */ + public static final String WHITELIST_MODE = "tool_whitelist_mode"; + + // ==================== 工具上下文(只读) ==================== + + /** + * 注入的全部 codeact 工具列表 + * + *

    类型:List<CodeactTool> + *

    由 CodeGeneratorSubAgent.init_context 节点注入 + *

    上层应用可读取此列表进行评估 + */ + public static final String CODEACT_TOOLS = "codeact_tools"; + + /** + * 筛选后的 codeact 工具列表 + * + *

    类型:List<CodeactTool> + *

    由 CodeGeneratorNode 筛选后写入(可选) + *

    用于调试和审计 + */ + public static final String FILTERED_CODEACT_TOOLS = "filtered_codeact_tools"; + + /** + * 编程语言 + * + *

    类型:String + *

    示例:"python", "java" + *

    由 CodeGeneratorSubAgent.init_context 节点注入 + */ + public static final String LANGUAGE = "language"; } diff --git a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java index aadac5a..e8f2f1c 100644 --- a/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java +++ b/assistant-agent-extensions/src/main/java/com/alibaba/assistant/agent/extension/dynamic/mcp/McpDynamicToolFactory.java @@ -147,8 +147,8 @@ private CodeactTool createToolFromCallback(ToolCallback callback, ObjectMapper o String inputSchema = definition.inputSchema(); // 类名:使用配置的 server 名称或默认前缀 - // 归一化后的类名(如 my-mcp-server -> my_mcp_server) - String targetClassName = nameNormalizer.normalizeClassName(defaultTargetClassNamePrefix); + // 归一化后的类名(如 my-mcp-server -> my_mcp_server_tools) + String targetClassName = nameNormalizer.normalizeClassName(defaultTargetClassNamePrefix) + "_tools"; String targetClassDescription = defaultTargetClassDescription; // 优先检查是否实现了 McpServerAwareToolCallback 接口 @@ -157,7 +157,7 @@ private CodeactTool createToolFromCallback(ToolCallback callback, ObjectMapper o String displayName = serverAwareCallback.getServerDisplayName(); if (serverName != null && !serverName.isEmpty()) { - targetClassName = nameNormalizer.normalizeClassName(serverName); + targetClassName = nameNormalizer.normalizeClassName(serverName) + "_tools"; targetClassDescription = displayName != null ? displayName : serverName; // 使用原始工具名而不是 ToolDefinition 中的名称 toolName = serverAwareCallback.getToolName(); @@ -171,7 +171,7 @@ else if (!serverSpecs.isEmpty()) { for (Map.Entry entry : serverSpecs.entrySet()) { McpServerSpec spec = entry.getValue(); // 使用 serverSpec 中的名称 - targetClassName = nameNormalizer.normalizeClassName(spec.getServerName()); + targetClassName = nameNormalizer.normalizeClassName(spec.getServerName()) + "_tools"; targetClassDescription = spec.getDescription(); break; // 目前只支持单个 server,使用第一个 }