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.
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: 60sCore 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.0Further 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
