Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

PheonixHkbxoic/adk-java

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

adk-java

An open-source, code-first Java toolkit for building, evaluating, and running sophisticated AI agents with flexibility and control.
adk-java is a Java implementation of Agent Develop Kit for orchestrating Multi-Agents


✨ Key Features

  • Rich Framework: Utilize any AI frameworks, eg. LangChain4j,SpringAI.

  • Code-First Development: Define agent logic, and orchestration directly in Java for ultimate flexibility, testability, and versioning.

  • Modular Multi-Agent Systems: Design scalable applications by composing multiple specialized agents into flexible hierarchies.

  • Strong Ecosystem: seamlessly integrate with maven, Spring And Easily deploy application on JVM.

Usage

Orchestration Agent node into graph

  • simple: just one agent
AdkAgentProvider qa = AdkAgentProvider.create("qaAssistant", new CustomAdkAgentInvoker());
AgentRunner runner = AgentRunner.of("Assistant", qa);
  • chain: some agent
AdkAgentProvider qa = AdkAgentProvider.create("qaAssistant", new CustomAdkAgentInvoker());
AdkAgentProvider qa2 = AdkAgentProvider.create("qaAssistant2", new CustomAdkAgentInvoker2());
AgentRunner runner = AgentRunner.of("AgentChain", qa, qa2);

AgentChain

  • router: router with some agent
AdkAgentProvider qaRouter = AdkAgentProvider.create("qaRouter", new AdkAgentInvoker() {
    @Override
    public Mono<ResponseFrame> invoke(ExecutableContext context) {
        // mock request llm and response
        Map<String, Object> metadata = Map.of("activeAgent", "echoAgent", "answer", "router self answer..balabala...");
        context.setMetadata(metadata);
        return Mono.empty();
    }

    @Override
    public Flux<ResponseFrame> invokeStream(ExecutableContext context) {
        return this.invoke(context).flux();
    }
});

BranchSelector branchSelector = (edge, index, size, context) -> {
    Object activeAgent = context.getMetadata().get("activeAgent");
    return activeAgent != null && activeAgent.toString().equalsIgnoreCase(edge.getName());
};

AdkAgentProvider qa = AdkAgentProvider.create("echoAgent", new CustomAdkAgentInvoker());
AdkAgentProvider qa2 = AdkAgentProvider.create("mathAgent", new CustomAdkAgentInvoker2());
AdkAgentProvider fallback = AdkAgentProvider.create("fallback", new AdkAgentInvoker() {
    @Override
    public Mono<ResponseFrame> invoke(ExecutableContext context) {
        String answer = (String) context.getMetadata().get("answer");
        ResponseFrame response = new ResponseFrame();
        response.setMessage(answer);
        return Mono.just(response);
    }

    @Override
    public Flux<ResponseFrame> invokeStream(ExecutableContext context) {
        return this.invoke(context).flux();
    }
});
AgentRouterRunner runner = AgentRouterRunner.of("AgentRouter", qaRouter, branchSelector, fallback, qa, qa2);

AgentChain

  • custom
    • Use other existing Runner
    • Custom Runner by extends AbstractRunner like CustomComplexRunner
    • Use low level api about Executor
      public void customAndRun(){
        // define params
        Payload payload;
        Executor executor;
        Graph graph;
        RootContext rootContext = new RootContext(payload);
      
        // execute
        AdkContext ec = executor.execute(graph, rootContext);
        ec.getResponse().subscribe(responseFrame -> log.info("custom responseFrame: {}", responseFrame));
      }

Run it

  • run
List<ResponseFrame> responseFrames = runner.run(payload);
  • runAsync
@Test
public void testAgentChainRunnerAsync() {
    AdkAgentProvider qa = AdkAgentProvider.create("qaAssistant", new CustomAdkAgentInvoker());
    AdkAgentProvider qa2 = AdkAgentProvider.create("qaAssistant2", new CustomAdkAgentInvoker2());
    AgentRunner runner = AgentRunner.of("AgentChain", qa, qa2);

    Payload payload = Payload.builder().userId("1").sessionId("2").message("hello").stream(true).build();

    // runAsync
    runner.runAsync(payload)
            .subscribe(responseFrame -> log.info("agent runAsync responseFrame: {}", responseFrame));

    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

Payload

User payload support some key properties:

  • userId
  • sessionId
  • stream When stream is true, Runners will use sse by the method invokeStream of AdkAgentInvoker to invoke LLM
  • messages
    Supports AdkTextMessage,AdkImageMessage,AdkVideoMessage,AdkAudioMessage,AdkFileMessage
  • metadata
 AdkPayload payload = AdkPayload.builder()
        .userId("1")
        .sessionId("2")
        .taskId(AdkUtil.uuid4hex())
        .messages(List.of(AdkTextMessage.of("hello")))
        .build();

How to interact with LLM

Implements AdkAgentInvoker interface to interact with LLM by any framework, eg.LangChain4j,a2a4j

@Slf4j
public class CustomAdkAgentInvoker implements AdkAgentInvoker {

    @Override
    public Mono<ResponseFrame> invoke(ExecutableContext context) {
        ResponseFrame response = new ResponseFrame();
        response.setMessage("ok");
        return Mono.just(response);
    }

    @Override
    public Flux<ResponseFrame> invokeStream(ExecutableContext context) {
        return Flux.create(sink -> {
            for (int i = 0; i < 10; i++) {
                ResponseFrame frame = new ResponseFrame();
                frame.setMessage("message" + i);
                sink.next(frame);
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            sink.complete();
        });
    }
}

Executor

Runner use new Executor(new InMemorySessionService(), new InMemoryEventService()) as default executor.
Your also can use custom executor by initExecutor of Runner.

AgentRunner runner = AgentRunner.of("Assistant", qa).initExecutor(executor);

Runner

Runner is high level api for Orchestrating your agents and run it synchronously or asynchronously.

  • AgentRunner
    AgentRunner
  • AgentRouterRunner
    AgentRouterRunner
  • AgentLoopRunner
    AgentLoopRunner
  • AgentParallelRunner
    AgentParallel
  • Custom yourself Runner like CustomComplexRunner
    CustomComplexRunner

More Runners is oncoming!

Support output image of Graph with PlantUML

  • generate kinds of images with PlantUmlGenerator and Graph by FileFormat
public void testGenerateUmlPng() {
    AgentRouterRunner runner;
    // gen uml png
    try {
        PlantUmlGenerator generator = runner.getPlantUmlGenerator();
        Graph graph = runner.getGraph();
        FileOutputStream file = new FileOutputStream("target/" + graph.getName() + ".png");
        generator.generate(graph, file, FileFormat.PNG);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
  • generate png image with Runner directly
public void generatePngImage() {
    Runner runner;
    // gen uml png
    try {
        FileOutputStream file = new FileOutputStream("target/" + appName + ".png");
        runner.generatePng(file);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
  • generate svg image with Runner directly
public void generateSvgImage() {
    Runner runner;
    // gen uml svg
    try {
        FileOutputStream file = new FileOutputStream("target/" + appName + ".svg");
        runner.generateSvg(file);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

AgentChain

  • generate png image with Runner Payload Task directly
public void generatePngImage() {
    Runner runner;
    // gen task uml png
    try {
        FileOutputStream file = new FileOutputStream("target/" + appName + "_" + payload.getTaskId() + ".png");
        runner.generateTaskPng(payload, file);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Agent Router

Example Log

RunnerTests.testAgentRouterRunner()

20:09:50.624 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- before event: execute, start
20:09:50.629 [main] DEBUG reactor.util.Loggers -- Using Slf4j logging framework
20:09:50.630 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- after event: execute, start
20:09:50.630 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- before event: execute, qaRouter
20:09:50.630 [Thread-0] INFO io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- before event: invoke, qaRouter
20:09:50.666 [Thread-0] INFO io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- after event: invoke, qaRouter
20:09:50.666 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- before event: route, qaRouter
20:09:50.666 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- after event: route, qaRouter
20:09:50.666 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- after event: execute, qaRouter
20:09:50.666 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- before event: execute, echoAgent
20:09:50.666 [Thread-0] INFO io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- before event: invoke, echoAgent
20:09:50.672 [Thread-0] INFO io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- after event: invoke, echoAgent
20:09:50.672 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- after event: execute, echoAgent
20:09:50.672 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- before event: execute, end
20:09:50.672 [Thread-0] DEBUG io.github.pheonixhkbxoic.adk.event.LogInvokeEventListener -- after event: execute, end
20:09:50.689 [main] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- before runAsync
20:09:50.703 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message0)
20:09:50.808 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message1)
20:09:50.920 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message2)
20:09:51.031 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message3)
20:09:51.143 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message4)
20:09:51.255 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message5)
20:09:51.366 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message6)
20:09:51.474 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message7)
20:09:51.583 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message8)
20:09:51.691 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- agent router runAsync responseFrame: ResponseFrame(message=message9)
20:09:51.801 [boundedElastic-1] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- after runAsync
20:09:53.698 [main] INFO io.github.pheonixhkbxoic.adk.test.RunnerTests -- taskContextChain: [{"id": "606e06fb3b4f444090bd73dfc4ab77a3", "name": "root", "node": null, "metadata": {}, "activeParentId": "", "activeChildId": "9dc083f4d8754dbd9658b7dcb1417c1e"}
, {"id": "9dc083f4d8754dbd9658b7dcb1417c1e", "name": "start", "node": {"type": "start", "status": {"state": "success"}}, "metadata": {}, "activeParentId": "606e06fb3b4f444090bd73dfc4ab77a3", "activeChildId": "19eac3a325764c93b11e68d7a7455426"}
, {"id": "19eac3a325764c93b11e68d7a7455426", "name": "qaRouter", "node": {"type": "agent_router", "status": {"state": "success"}}, "metadata": {}, "activeParentId": "9dc083f4d8754dbd9658b7dcb1417c1e", "activeChildId": "35f1409202f646328005ed32b076d686"}
, {"id": "35f1409202f646328005ed32b076d686", "name": "echoAgent", "node": {"type": "agent", "status": {"state": "success"}}, "metadata": {}, "activeParentId": "19eac3a325764c93b11e68d7a7455426", "activeChildId": "09af97ff99ba4f71a872c93b0311ba3c"}
, {"id": "09af97ff99ba4f71a872c93b0311ba3c", "name": "end", "node": {"type": "end", "status": {"state": "success"}}, "metadata": {}, "activeParentId": "35f1409202f646328005ed32b076d686", "activeChildId": ""}
]
@startuml
start
partition **AgentRouter** {
  : start;
  switch( qaRouter? )
    case ( echoAgent )
      : echoAgent;
    case ( mathAgent )
      : mathAgent;
    case ( fallback )
      : fallback;
  endswitch
  : end;
}
stop
@enduml
@startuml
start
partition **AgentRouter** {
  -[#red]->
  #green: <color:red><b>start;
  -[#red]->
  switch( <color:red><b>qaRouter? )
    case ( <color:red><b>echoAgent )
      -[#red]->
      #green: <color:red><b>echoAgent;
      -[#red]->
    case ( mathAgent )
      : mathAgent;
    case ( fallback )
      : fallback;
  endswitch
  -[#red]->
  #green: <color:red><b>end;
  -[#red]->
}
stop
@enduml

Preference

adk-core test

Star History

Star History Chart

About

An open-source, code-first Java toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages