动态创建Agent02

动态创建Agent02-解决参数加V的问题

java 复制代码
public class ParamMeta {
    private final String paramName;    // 变量名(反射调用时传入的实际参数名)
    private final Class<?> paramType;  // 参数的 Class 类型
    private final String vAnnotationValue; // @V("xxx") 注解内部的值。如果为 null,代表该参数不加 @V 注解

    public ParamMeta(String paramName, Class<?> paramType, String vAnnotationValue) {
        this.paramName = paramName;
        this.paramType = paramType;
        this.vAnnotationValue = vAnnotationValue;
    }

    // 快捷工厂方法 1:生成一个带 @V 注解的常规大模型参数
    public static ParamMeta withV(String paramName, Class<?> paramType, String vValue) {
        return new ParamMeta(paramName, paramType, vValue);
    }

    // 快捷工厂方法 2:生成一个纯净的、无任何注解的透传参数
    public static ParamMeta pure(String paramName, Class<?> paramType) {
        return new ParamMeta(paramName, paramType, null);
    }

    // Getters ...
    public String getParamName() { return paramName; }
    public Class<?> getParamType() { return paramType; }
    public String getVAnnotationValue() { return vAnnotationValue; }
    public boolean hasVAnnotation() { return vAnnotationValue != null; }
}
java 复制代码
package com.zishi.ai.shdemo.code.arch;

import com.zishi.ai.shdemo.util.InterfaceUtil;
import dev.langchain4j.agentic.Agent;
import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.jar.asm.Opcodes;

import java.util.List;

public class UltraAgentFactoryV2 {

    public static Object createDynamicAgent(
            String agentName,
            String agentDescription,
            String systemPrompt,
            String userPromptMethod,
            List<ParamMeta> parameters,
            ChatModel model) {
        try {
            // 步骤 1:创建接口
            DynamicType.Builder<?> builder = createInterfaceBuilder(agentName);

            // 步骤 2:生成包含方法级注解的初始 Method 声明,并切回 Builder 上下文
            String methodName = resolveMethodName(agentName);
            builder = initMethodAndMethodAnnotations(builder, methodName, systemPrompt, userPromptMethod, agentDescription, parameters);

            // 步骤 3:绑定带 @V 的参数注解(使用优雅的 Attribute 焊死,彻底绕过泛型链条中断坑)
            builder = bindParameterAnnotations(builder, methodName, parameters);

            // 步骤 4:编译加载
            Class<?> dynamicInterface = compileAndLoad(builder, agentName);

            // 诊断日志打印
            InterfaceUtil.printDetail(dynamicInterface);

            // 步骤 5:生成最后的 Agent 实例并托管
            return buildFinalAgent(dynamicInterface, model);

        } catch (Exception e) {
            throw new RuntimeException("【架构层异常】全动态接口智能体孵化失败", e);
        }
    }

    /**
     * 🟢 步骤 1:创建接口基础构建器
     */
    private static DynamicType.Builder<?> createInterfaceBuilder(String agentName) {
        return new ByteBuddy().makeInterface().name("com.dynamic.agent.dynamicinterface." + agentName);
    }

    /**
     * 🟢 步骤 2:生成核心方法骨架,并将 @SystemMessage, @UserMessage, @Agent 拍在方法上
     * 技巧:最后通过 .withoutCode() 让 ByteBuddy 结束方法定义,吐出全新的、带有该方法的类 Builder
     */
    private static DynamicType.Builder<?> initMethodAndMethodAnnotations(
            DynamicType.Builder<?> builder,
            String methodName,
            String systemPrompt,
            String userPromptMethod,
            String agentDescription,
            List<ParamMeta> parameters) {

        // 提取物理参数类型数组
        Class<?>[] paramTypes = parameters.stream().map(ParamMeta::getParamType).toArray(Class<?>[]::new);

        return builder.defineMethod(methodName, String.class, Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT)
                .withParameters(paramTypes)
                .withoutCode() // ⚡️ 极度重要:切回类构建上下文,以便后续进行统一的属性/参数追加
                .annotateMethod(AnnotationDescription.Builder.ofType(SystemMessage.class).defineArray("value", new String[]{systemPrompt}).build())
                .annotateMethod(AnnotationDescription.Builder.ofType(UserMessage.class).defineArray("value", new String[]{userPromptMethod}).build())
                .annotateMethod(AnnotationDescription.Builder.ofType(Agent.class).define("value", agentDescription).build());
    }


    /**
     * 🟢 步骤 3:精准绑定带 V 的参数注解(终极无红线版)
     */
    /**
     * 🟢 步骤 3:精准绑定带 V 的参数注解(黄金时机修正版)
     */
    private static net.bytebuddy.dynamic.DynamicType.Builder<?> bindParameterAnnotations(
            net.bytebuddy.dynamic.DynamicType.Builder<?> builder,
            String methodName,
            List<ParamMeta> parameters) {

        net.bytebuddy.asm.AsmVisitorWrapper asmVisitorWrapper = new net.bytebuddy.asm.AsmVisitorWrapper.AbstractBase() {

            @Override
            public net.bytebuddy.jar.asm.ClassVisitor wrap(
                    net.bytebuddy.description.type.TypeDescription instrumentedType,
                    net.bytebuddy.jar.asm.ClassVisitor classVisitor,
                    net.bytebuddy.implementation.Implementation.Context implementationContext,
                    net.bytebuddy.pool.TypePool typePool,
                    net.bytebuddy.description.field.FieldList<net.bytebuddy.description.field.FieldDescription.InDefinedShape> fields,
                    net.bytebuddy.description.method.MethodList<?> methods,
                    int writerFlags,
                    int readerFlags) {

                return new net.bytebuddy.jar.asm.ClassVisitor(net.bytebuddy.jar.asm.Opcodes.ASM9, classVisitor) {

                    @Override
                    public net.bytebuddy.jar.asm.MethodVisitor visitMethod(
                            int access, String name, String descriptor, String signature, String[] exceptions) {

                        // 1. 先抓取到正在构建的底层基础 MethodVisitor
                        net.bytebuddy.jar.asm.MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);

                        // 2. 🎯 精准狙击:如果是目标业务方法,在方法刚创建、参数槽位完全敞开的黄金时机,立刻注入!
                        if (name.equals(methodName) && mv != null) {

                            // 3. ✨ 核心修正:不再等 visitEnd,现在就在这里把所有带 @V 的注解一口气灌进去!
                            for (int i = 0; i < parameters.size(); i++) {
                                ParamMeta meta = parameters.get(i);
                                if (meta.hasVAnnotation()) {
                                    System.out.println("🔥 [ASM时机修正] 正在方法 [" + methodName + "] 创建初期,为第 " + i + " 个参数挂载 @V(\"" + meta.getVAnnotationValue() + "\")");

                                    // 在当前的 MethodVisitor 上直接宣布第 i 个参数的注解
                                    net.bytebuddy.jar.asm.AnnotationVisitor av = mv.visitParameterAnnotation(
                                            i,
                                            "Ldev/langchain4j/service/V;", // @V 注解的 JVM 描述符
                                            true                           // 运行时反射可见
                                    );

                                    // 写入注解的真正数据属性 value
                                    if (av != null) {
                                        av.visit("value", meta.getVAnnotationValue());
                                        av.visitEnd();
                                    }
                                }
                            }
                        }

                        // 返回原本的 MethodVisitor 正常往下走
                        return mv;
                    }
                };
            }
        };

        return builder.visit(asmVisitorWrapper);
    }


    /**
     * 🟢 步骤 4:编译并加载到内存
     */
    private static Class<?> compileAndLoad(DynamicType.Builder<?> builder, String agentName) {
        return builder.make()
                .load(UltraAgentFactoryV2.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                .getLoaded();
    }

    /**
     * 🟢 步骤 5:交由官方 AgenticServices 生成最终托管实例
     */
    private static Object buildFinalAgent(Class<?> dynamicInterface, ChatModel model) {
        return AgenticServices.agentBuilder(dynamicInterface)
                .chatModel(model)
                .build();
    }

    /**
     * 🛠️ 辅助工具:动态计算符合框架偏好的方法名
     */
    private static String resolveMethodName(String agentName) {
        String methodName = agentName.toLowerCase().replace("agent", "");
        return methodName.isEmpty() ? "process" : methodName;
    }

}

main测试

java 复制代码
package com.zishi.ai.shdemo.code.arch;

import com.zishi.ai.shdemo.util.ModelUtil;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {

        // 编排层:高度弹性的参数装配线
        List<ParamMeta> complexParams = new ArrayList<>();

        // 参数 0:要加 @V
        complexParams.add(ParamMeta.withV("user", String.class, "user"));

        // 参数 1:纯净参数,不加 @V
        //complexParams.add(ParamMeta.pure("appContext", Object.class));

        // 参数 2:也要加 @V
        complexParams.add(ParamMeta.withV("amount", Double.class, "amount"));

        // 丢进新工厂,ByteBuddy 会完美地在参数 0 和 2 上加上 @V,自动放过参数 1!
        Object withdrawAgent = UltraAgentFactoryV2.createDynamicAgent(
                "WithdrawAgent",
                "一个从账户中提取美元的银行家",
                "你是一名专业的银行出纳家,只负责从账户中提取美元,拒绝一切其他无聊的闲聊。",
                "从 {{user}} 的账户中提取 {{amount}} 美元,并返回新的余额。",
                complexParams,
                ModelUtil.BASE_MODEL
        );

        java.lang.reflect.Method coreMethod = withdrawAgent.getClass().getInterfaces()[0].getDeclaredMethods()[0];


        //Method withdrawMethod = withdrawAgent.getClass().getMethod("execute", String.class, Double.class);

        System.out.println("🔄 正在触发【财务取款 Agent】的 execute 方法...");
        String withdrawResult = (String) coreMethod.invoke(withdrawAgent, "张三", 8500.25);

        System.out.println("\n🔥 LLM 最终执行响应:");
        System.out.println(withdrawResult);
    }
}
相关推荐
ZFSS6 小时前
Localization Translate API 集成与使用指南
java·服务器·数据库·人工智能·mysql·ai编程
摇滚侠6 小时前
Java 零基础全套教程,集合框架,笔记 153-163
java·开发语言·笔记
nannan12326 小时前
后端技术栈梳理
java
L、2187 小时前
CANN算子开发调试实战:从“Segmentation Fault“到定位根因的完整流程
java·开发语言
索木木8 小时前
NCCL SHARP 和 TREE算法
java·服务器·算法
NiceCloud喜云8 小时前
Claude Files API 深入:从上传、复用到配额管理的工程化指南
android·java·数据库·人工智能·python·json·飞书
超梦dasgg9 小时前
Java 生产环境 MQ 技术选型全解析
java·开发语言·java-rocketmq·java-rabbitmq
霸道流氓气质9 小时前
Spring AI 多工具链式调用(Tool Chain)极简实战
java·人工智能·spring
罗超驿9 小时前
22.深入剖析JDBC架构:从原生API到企业级数据交互核心
java·数据库·mysql·面试