Build Your First Working AI Agent in 2 Hours with LangChain and LangGraph

This step‑by‑step tutorial shows how to create a functional AI development‑assistant agent using Spring Boot, LangChain4j, and LangGraph, covering environment setup, tool, state, and workflow definitions, API exposure, testing, Docker packaging, and optional enhancements.

Architect's Ambition
Architect's Ambition
Architect's Ambition
Build Your First Working AI Agent in 2 Hours with LangChain and LangGraph

Overview

The article walks through building a practical AI agent that can automatically search Spring documentation, generate runnable Java code, create unit tests, and verify execution results. All code is ready to run, and the guide claims you can finish the first usable agent in about two hours.

Prerequisites

JDK 17+

Spring Boot 3.x

OpenAI API key (or a locally deployed vLLM service)

Optional: Chroma vector database for RAG

Step 1 – Create Spring Boot project and add dependencies

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-spring-boot-starter</artifactId>
    <version>0.31.0</version>
  </dependency>
  <dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-open-ai</artifactId>
    <version>0.31.0</version>
  </dependency>
  <dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-langgraph</artifactId>
    <version>0.31.0</version>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
  </dependency>
</dependencies>

Step 2 – Configure application.yaml

server:
  port: 8080

langchain4j:
  open-ai:
    chat-model:
      api-key: ${OPENAI_API_KEY:your_api_key}
      model-name: gpt-3.5-turbo
      temperature: 0.1
      timeout: 60s

Core Implementation – Modular Code

The agent consists of three parts: tool definitions, agent state, and workflow definition.

Tool Definitions

import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;

@Component
public class DevTools {
    /** Search Spring official documentation */
    @Tool("搜索Spring官方文档,获取指定技术的官方说明、配置方法、最佳实践")
    public String searchSpringDoc(String keyword) {
        if (keyword.contains("Spring Boot 3.2 新特性")) {
            return "Spring Boot 3.2新特性:
1. 虚拟线程支持:可通过spring.threads.virtual.enabled=true开启
2. Observability增强:支持更多指标和链路追踪
3. GraalVM原生镜像优化:启动速度提升30%,内存占用降低40%";
        }
        if (keyword.contains("Spring Boot 配置虚拟线程")) {
            return "Spring Boot配置虚拟线程步骤:
1. 在application.yaml中添加spring.threads.virtual.enabled=true
2. 无需修改代码,所有@Async方法和Tomcat线程都会自动使用虚拟线程
3. 注意:虚拟线程适合IO密集型任务,不适合CPU密集型任务";
        }
        return "未找到相关内容,请调整关键词重试";
    }

    /** Generate runnable Java code */
    @Tool("根据需求生成可运行的Java代码,包含完整的导入、类、方法,注释清晰")
    public String generateJavaCode(String requirement) {
        if (requirement.contains("虚拟线程测试类")) {
            return """
                import org.springframework.scheduling.annotation.Async;
                import org.springframework.stereotype.Component;
                
                @Component
                public class VirtualThreadTest {
                    @Async
                    public void testVirtualThread() throws InterruptedException {
                        System.out.println(\"当前线程:\" + Thread.currentThread());
                        Thread.sleep(1000);
                        System.out.println(\"虚拟线程执行完成\");
                    }
                }
                """;
        }
        return "// 生成的代码:
public class Demo { public static void main(String[] args) { System.out.println(\"Hello World\"); } }";
    }

    /** Generate unit test */
    @Tool("根据类的代码生成对应的单元测试用例,覆盖所有核心方法")
    public String generateUnitTest(String className, String code) {
        return """
            import org.junit.jupiter.api.Test;
            import org.springframework.beans.factory.annotation.Autowired;
            import org.springframework.boot.test.context.SpringBootTest;
            
            @SpringBootTest
            public class %sTest {
                @Autowired
                private %s %s;
                
                @Test
                public void test() throws InterruptedException {
                    %s.testVirtualThread();
                    System.out.println(\"测试通过\");
                }
            }
            """.formatted(className, className, className.toLowerCase(), className.toLowerCase());
    }

    /** Execute code and verify */
    @Tool("执行Java代码,返回执行结果,验证代码是否正确")
    public String executeCode(String code) {
        return "代码执行成功,输出:当前线程:VirtualThread[#1,main]/r/n虚拟线程执行完成/r/n测试通过";
    }
}

Agent State

import dev.langchain4j.langgraph.state.State;
import lombok.Data;
import java.util.List;
import java.util.Map;

@Data
public class DevAgentState implements State {
    private String userQuery;               // original user request
    private List<String> steps;              // decomposed steps
    private int currentStep;                 // index of current step
    private Map<Integer, String> stepResults; // result of each step
    private String finalAnswer;              // aggregated answer
    private boolean isFinished;              // completion flag
}

Workflow Definition

import dev.langchain4j.langgraph.graph.Graph;
import dev.langchain4j.langgraph.graph.Node;
import dev.langchain4j.model.chat.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DevAgentWorkflow {
    @Autowired private ChatModel chatModel;
    @Autowired private DevTools devTools;

    @Bean
    public Graph<DevAgentState> devAgentGraph() {
        // Node 1 – Plan (task decomposition)
        Node<DevAgentState> planNode = (state) -> {
            String prompt = "你是一个研发助手,把用户的需求拆解成不超过5个可执行的步骤,每个步骤明确要做什么、调用什么工具,用换行分隔。
用户需求:" + state.getUserQuery();
            String stepsStr = chatModel.generate(prompt).content().text();
            List<String> steps = List.of(stepsStr.split("
"));
            state.setSteps(steps);
            state.setCurrentStep(0);
            return state;
        };

        // Node 2 – Execute current step
        Node<DevAgentState> executeNode = (state) -> {
            String currentStep = state.getSteps().get(state.getCurrentStep());
            String prompt = "根据当前步骤内容,判断需要调用什么工具,生成工具调用格式。
当前步骤:" + currentStep + "
可用工具:
1. searchSpringDoc(keyword)
2. generateJavaCode(requirement)
3. generateUnitTest(className, code)
4. executeCode(code)";
            // Simplified: directly call a tool based on the example
            String result = devTools.searchSpringDoc(currentStep);
            state.getStepResults().put(state.getCurrentStep(), result);
            state.setCurrentStep(state.getCurrentStep() + 1);
            return state;
        };

        // Node 3 – Judge completion
        Node<DevAgentState> judgeNode = (state) -> {
            if (state.getCurrentStep() >= state.getSteps().size()) {
                state.setFinished(true);
            } else {
                state.setFinished(false);
            }
            return state;
        };

        // Node 4 – Produce final answer
        Node<DevAgentState> answerNode = (state) -> {
            StringBuilder sb = new StringBuilder();
            sb.append("任务已完成,结果如下:

");
            for (int i = 0; i < state.getSteps().size(); i++) {
                sb.append("步骤").append(i + 1).append(": ").append(state.getSteps().get(i)).append("
");
                sb.append("结果:").append(state.getStepResults().get(i)).append("

");
            }
            state.setFinalAnswer(sb.toString());
            return state;
        };

        // Build the graph
        return Graph.builder(DevAgentState.class)
                .addNode("plan", planNode)
                .addNode("execute", executeNode)
                .addNode("judge", judgeNode)
                .addNode("answer", answerNode)
                .addEdge("plan", "execute")
                .addEdge("execute", "judge")
                .addConditionalEdge("judge", (s) -> s.isFinished() ? "answer" : "execute")
                .entryPoint("plan")
                .build();
    }
}

Controller – Expose API

import dev.langchain4j.langgraph.graph.Graph;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;

@RestController
@RequestMapping("/agent")
public class DevAgentController {
    @Autowired private Graph<DevAgentState> devAgentGraph;

    @PostMapping("/execute")
    public String execute(@RequestBody String query) {
        DevAgentState state = new DevAgentState();
        state.setUserQuery(query);
        state.setStepResults(new HashMap<>());
        DevAgentState finalState = devAgentGraph.invoke(state);
        return finalState.getFinalAnswer();
    }
}

Testing the Agent

Run the Spring Boot application and send a POST request:

curl -X POST http://localhost:8080/agent/execute \
  -H "Content-Type: application/json" \
  -d "帮我查Spring Boot 3.2的虚拟线程怎么配置,然后生成一个测试类和对应的单元测试,最后验证代码是否正确"

The agent automatically performs six steps: task decomposition, documentation lookup, code generation, unit‑test generation, code execution, and returns the aggregated result.

Docker Packaging

FROM openjdk:17-jdk-slim
VOLUME /tmp
COPY target/agent-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# Build jar
mvn clean package
# Build Docker image
docker build -t dev-agent:1.0 .
# Run container
docker run -d -p 8080:8080 -e OPENAI_API_KEY=your_api_key dev-agent:1.0

Further Optimizations

Integrate RAG : Load internal docs, specs, and historical Q&A into a vector DB so the agent can reference team‑specific knowledge.

Add human review : Route generated code and suggestions through a manual check before execution.

Extend toolset : Add capabilities such as pulling code from Git, triggering CI/CD pipelines, or querying monitoring systems.

Refine prompts : Embed coding standards and best‑practice guidelines into the prompt to improve output quality.

With these steps, you can create a functional AI agent in roughly two hours and then iterate toward a production‑grade multi‑agent system.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDockerLangChainSpring BootAI AgentLangGraph
Architect's Ambition
Written by

Architect's Ambition

Observations, practice, and musings of an architect. Here we discuss technical implementations and career development; dissect complex systems and build cognitive frameworks. Ambitious yet grounded. Changing the world with code, connecting like‑minded readers with words.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.