动态创建Agent01

通过ByteBuddy动态创建Agent

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

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.agentic.AgenticServices;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import net.bytebuddy.jar.asm.Opcodes;

import java.util.Map;

public class UltraAgentFactory {

    /**
     * 【全动态智能体孵化核心】
     * 在内存中凭空雕刻出带有 LangChain4j 原生注解的"伪预设接口",并托管给官方生态
     *
     * @param agentName        动态生成的 Agent 接口类名 (例如 "WithdrawAgent")
     * @param systemPrompt     大模型的 System Prompt (扮演的角色职责)
     * @param userPromptMethod 大模型的 User Prompt 模板 (例如 "从 {{user}} 账户提取 {{amount}} 元")
     * @param paramDefinitions 参数定义映射表(必须用 LinkedHashMap 保证参数顺序,Key 为占位符名,Value 为类型)
     * @param model            LangChain4j 基础大模型实例
     * @return                 LangChain4j 官方代理生成的强类型 Agent 实例
     */
    public static Object createDynamicAgent(
            String agentName,
            String agentDescription,
            String systemPrompt,
            String userPromptMethod,
            Map<String, Class<?>> paramDefinitions,
            ChatModel model) {
        try {
            // 1. 开启 ByteBuddy 纯接口(Interface)雕刻模式
            var builder = new ByteBuddy().makeInterface().name("com.dynamic.agent.dynamicinterface." + agentName);

            // 2. 将传入的参数 Class 类型提取为数组 (例如 [String.class, Double.class])
            Class<?>[] paramTypes = paramDefinitions.values().toArray(new Class<?>[0]);


            // 🛠️ 核心修正:不要再叫无名之辈 "execute" 了!
            // 我们直接将方法名动态雕刻为更符合业务的名称,例如小写的 agentName("withdrawAgent" -> "withdraw")
            // 或者直接约定一个 AgenticServices 绝对放行的通用标准行为方法名。
            String methodName = agentName.toLowerCase().replace("agent", "");
            if (methodName.isEmpty()) {
                methodName = "process"; // 降级兜底名
            }

            System.out.println("🤖 [框架层注入] 正在为 AgenticServices 雕刻核心触发方法: " + methodName);

            // 3. 现场雕刻接口方法(统一硬编码方法名为 "execute",返回值统一为 String 方便上层反射抓取)
            // 1. 开启方法定义,并紧跟 .withParameters 指定参数
            DynamicType.Builder.MethodDefinition<?> methodDefinition =
                    builder.defineMethod(methodName, String.class, Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT)
                    .withParameters(paramTypes)

                    // 2. 🎯 核心修正:在 1.18.x 版本中,直接调用 .annotateMethod 之前,
                    // 必须先调用 .withoutCode() 表明这是接口的抽象方法。
                    // 调用后,API 的上下文会切换为 MethodDefinition.ExceptionDeclarable,此时核心注解方法全部现身!
                    .withoutCode()

                    // 3. 动态将 LangChain4j 的 @SystemMessage 刻在方法上
                    .annotateMethod(AnnotationDescription.Builder.ofType(SystemMessage.class)
                            .defineArray("value", new String[]{systemPrompt}).build())

                    // 4. 动态将 LangChain4j 的 @UserMessage 刻在方法上
                    .annotateMethod(AnnotationDescription.Builder.ofType(UserMessage.class)
                            .defineArray("value", new String[]{userPromptMethod}).build())// ⚡️ 见证奇迹:在这里!把 @Agent 注解稳稳地刻在接口类的头上!
                    .annotateMethod(AnnotationDescription.Builder.ofType(Agent.class)
                            .define("value", agentDescription) // 对应 @Agent("xxxx")
                            .build());


            // 4. 遍历参数定义表,将 LangChain4j 的 @V 注解精准刻在对应的参数位置上
            int index = 0;
            for (String paramName : paramDefinitions.keySet()) {
                methodDefinition = methodDefinition
                        .annotateParameter(index, AnnotationDescription.Builder.ofType(V.class).define("value", paramName).build());
                index++;
            }

            // 5. 将这个纯动态接口编译并加载到当前 JVM 内存中
            Class<?> dynamicInterface = methodDefinition.make()
                    .load(UltraAgentFactory.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                    .getLoaded();

            printDetail(dynamicInterface);


            // 6. 🎉 天衣无缝的降维打击:直接塞给 LangChain4j 官方工厂
            // 官方会认为这是一个你开发期手写的标准接口,高高兴兴地接管了拼装 Prompt 和请求 LLM 的全部脏活
            // 6. 🎉 修正 2:切换为 AgenticServices 建造者工厂进行托管
            return AgenticServices.agentBuilder(dynamicInterface)
                    .chatModel(model)
                    .build();

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

    public static void printDetail(Class<?> dynamicInterface) {
        // ==================== 🛠️ 接口元数据显微镜 (放入工厂中) ====================
        System.out.println("\n=======================================================");
        System.out.println("🔍 [字节码探测] 动态生成的接口全限定名: " + dynamicInterface.getName());
        System.out.println("🔍 [字节码探测] 是否真的是接口: " + dynamicInterface.isInterface());

        java.lang.reflect.Method[] methods = dynamicInterface.getDeclaredMethods();
        System.out.println("🔍 [字节码探测] 接口内查找到的方法总数: " + methods.length);

        for (java.lang.reflect.Method m : methods) {
            System.out.println("------------ 方法明细 ------------");
            System.out.println("👉 方法名: " + m.getName());
            System.out.println("👉 返回值类型: " + m.getReturnType().getName());

            // 打印方法上的注解
            java.lang.annotation.Annotation[] annos = m.getAnnotations();
            System.out.println("👉 方法上的注解数量: " + annos.length);
            for (java.lang.annotation.Annotation a : annos) {
                System.out.println("   [Method Anno] -> " + a.toString());
            }

            // 打印参数及参数上的注解
            java.lang.reflect.Parameter[] params = m.getParameters();
            System.out.println("👉 方法参数数量: " + params.length);
            for (int i = 0; i < params.length; i++) {
                java.lang.reflect.Parameter p = params[i];
                System.out.println("   [Param " + i + "] 类型: " + p.getType().getName() + ", 名字: " + p.getName());
                for (java.lang.annotation.Annotation pa : p.getAnnotations()) {
                    System.out.println("     [Param Anno] -> " + pa.toString());
                }
            }
        }
        System.out.println("=======================================================\n");
        // ====================================================================
    }
}

使用

java 复制代码
import com.zishi.ai.shdemo.util.ModelUtil;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;

import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

public class Main02 {
    public static void main(String[] args) throws Exception {
        
        // 0. 初始化底层的原子大模型(此处使用 OpenAI 示例,生产环境可替换为任意模型)
        ChatModel baseModel = ModelUtil.BASE_MODEL;

        System.out.println("====== 🎯 场景一:运行时动态派生【财务取款 Agent】 ======");

        // 1. 动态定义参数字典(!!! 必须使用 LinkedHashMap 确保反射时参数的严格顺序 !!!)
        Map<String, Class<?>> withdrawParams = new LinkedHashMap<>();
        withdrawParams.put("user", String.class);     // 第一个参数叫 user
        withdrawParams.put("amount", Double.class);   // 第二个参数叫 amount

        // 2. 召唤工厂,一键捏出强类型实例
        Object withdrawAgent = UltraAgentFactory.createDynamicAgent(
                "WithdrawAgent",
                "一个从账户中提取美元的银行家", // 👈 这一行会被完美注入到接口头顶的 @Agent 注解中!
                "你是一名专业的银行出纳家,只负责从账户中提取美元,拒绝一切其他无聊的闲聊。",
                "从 {{user}} 的账户中提取 {{amount}} 美元,并返回新的余额。",
                withdrawParams,
                baseModel
        );

        // 3. 运行时反射抓取固定的 "execute" 方法进行调用
        // 这一步彻底摆脱了硬编码接口,参数类型在运行时完美对齐

        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);


        System.out.println("\n\n====== 🎯 场景二:同一套代码,现场捏一个完全不同的【大额合同审计 Agent】 ======");

        // 1. 重新定义一套完全不同的业务参数
        Map<String, Class<?>> auditParams = new LinkedHashMap<>();
        auditParams.put("companyName", String.class);
        auditParams.put("contractValue", Integer.class);
        auditParams.put("riskLevel", String.class);

        // 2. 再次孵化一个新的纯动态接口 Agent
        Object contractAuditAgent = UltraAgentFactory.createDynamicAgent(
                "ContractAuditAgent",
                "你是一名严谨的跨国集团法务风控总监,精通商业合同合规性审查。",
                "你是一名严谨的跨国集团法务风控总监,精通商业合同合规性审查。",
                "正在审计 [{{companyName}}] 的合同,标的额 {{contractValue}} 万元,风控等级定为 [{{riskLevel}}]。请给出防范意见。",
                auditParams,
                baseModel
        );

        // 3. 反射抓取它专属的 execute 方法进行调用(参数变成了 String, Integer, String)
        //Method auditMethod = contractAuditAgent.getClass().getMethod("execute", String.class, Integer.class, String.class);
        java.lang.reflect.Method auditMethod = contractAuditAgent.getClass().getInterfaces()[0].getDeclaredMethods()[0];
        
        System.out.println("🔄 正在触发【合同审计 Agent】的 execute 方法...");
        String auditResult = (String) auditMethod.invoke(contractAuditAgent, "阿里巴巴网络技术有限公司", 5000, "HIGH_RISK");

        System.out.println("\n🔥 LLM 最终执行响应:");
        System.out.println(auditResult);
    }
}
相关推荐
彦为君12 小时前
Java文件处理效率库Commons-IO(速览)
java·开发语言·mfc
她的男孩13 小时前
后台权限不只是菜单隐藏:Forge Admin 的 RBAC 权限链路拆解
java·后端·架构
不会编程的懒洋洋13 小时前
VisionPro 中 图像预处理工具
图像处理·笔记·c#·视觉检测·visionpro
Slow菜鸟13 小时前
Maven 仓库下载机制
java·数据库·maven
一个诺诺前行的后端程序员13 小时前
rag+springai
java·eclipse
Hexian258013 小时前
SpringAI+RAG
java·spring·ai
冰小忆13 小时前
类变量在继承场景下的初始化规则是怎样的?
java·前端·数据库
AI人工智能+电脑小能手14 小时前
【大白话说Java面试题 第71题】【Mysql篇】第1题:索引是什么?
java·开发语言·b树·mysql·面试
AC赳赳老秦14 小时前
OpenClaw碎片时间利用:设置轻量化自动化任务,高效利用职场碎片时间
java·大数据·运维·服务器·数据库·自动化·openclaw