动态创建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);
}
}