Understanding AI Agents and Using Them to Generate Code
AI agents are autonomous or semi-autonomous systems designed to interact with their environment, make decisions, and perform tasks to achieve goals. In the context of software development, AI agents can be particularly useful in generating code, automating repetitive programming tasks, or even helping to debug or optimize code. With the advancement of language models like GPT-3.5 (which powers ChatGPT), AI agents have become capable of understanding natural language requests and generating structured outputs such as code snippets, configuration files, or even entire scripts.
AI models like ChatGPT, Codex, and others generate code by learning from vast amounts of data, including public code repositories, documentation, and tutorials. They can then synthesize this information to produce code that meets the user’s requirements.
For example, if you ask, “How do I write a function to calculate the factorial of a number in Typescript?”, an AI agent would generate code that fulfils this request:
Let’s dive into a step-by-step approach for creating an AI agent that can generate code in response to user input. We’ll leverage the OpenAI API with Node.js and Express to build a code generation agent.
1. Create a directory for your project and initialize the Node.js project:
2. Install the necessary dependencies:
3. Create a .env file to store your OpenAI API key:
Inside the .env file, add:
Create a server.js file that will handle user requests for code generation.
Add the following code to server.js:
Start the server by running:
2. You can now send POST requests to the /generate-code endpoint with a prompt and language to get code generated by the AI agent. Here’s an example of how to make a request using Postman or cURL.
Example Request using cURL:
Sample Response:
How AI Agents Generate Code
AI models like ChatGPT, Codex, and others generate code by learning from vast amounts of data, including public code repositories, documentation, and tutorials. They can then synthesize this information to produce code that meets the user’s requirements.
For example, if you ask, “How do I write a function to calculate the factorial of a number in Typescript?”, an AI agent would generate code that fulfils this request:
function factorial(n: number): number {
if (n < 0) {
throw new Error("Factorial is not defined for negative numbers.");
}
if (n === 0) {
return 1;
}
let result = 1;
for (let i = 1; i <= n; i++) {
result *= i;
}
return result;
}
if (n < 0) {
throw new Error("Factorial is not defined for negative numbers.");
}
if (n === 0) {
return 1;
}
let result = 1;
for (let i = 1; i <= n; i++) {
result *= i;
}
return result;
}
Creating an AI Agent for Code Generation Using ChatGPT
Let’s dive into a step-by-step approach for creating an AI agent that can generate code in response to user input. We’ll leverage the OpenAI API with Node.js and Express to build a code generation agent.
Step 1: Set Up the Node.js Project
1. Create a directory for your project and initialize the Node.js project:
mkdir code-gen-agent
cd code-gen-agent
npm init -y
cd code-gen-agent
npm init -y
2. Install the necessary dependencies:
npm install express axios dotenv
3. Create a .env file to store your OpenAI API key:
touch .env
Inside the .env file, add:
OPENAI_API_KEY=your_openai_api_key_here
Step 2: Build the Express Server
Create a server.js file that will handle user requests for code generation.
touch server.js
Add the following code to server.js:
const express = require('express');
const axios = require('axios');
require('dotenv').config();
const app = express();
const PORT = 3000;
// Middleware to parse JSON requests
app.use(express.json());
// Endpoint to handle code generation requests
app.post('/generate-code', async (req, res) => {
const { language, entityName, dbName } = req.body;
if (!language || !entityName || !dbName) {
return res.status(400).json({ error: 'Please provide details.' });
}
try {
// Make a request to OpenAI API to generate code
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: 'gpt-3.5-turbo', // Specify the model
messages: [
{ role: 'system', content: `You are a helpful assistant that generates ${language} code.` },
{ role: 'user', content: `generate a web api microservice with separation of concerns and layered architecture in ${language} language having crud operations for entity name as ${entityName} with ${dbName} db. Keep the constants and other environment specific information configurable`}
],
max_tokens: 150, // Set the token limit (you can increase this if you expect longer code)
},
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
}
);
const generatedCode = response.data.choices[0].message.content;
return res.status(200).json({ code: generatedCode });
} catch (error) {
console.error('Error generating code:', error);
return res.status(500).json({ error: 'Error generating code.' });
}
});
// Start the server
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
const axios = require('axios');
require('dotenv').config();
const app = express();
const PORT = 3000;
// Middleware to parse JSON requests
app.use(express.json());
// Endpoint to handle code generation requests
app.post('/generate-code', async (req, res) => {
const { language, entityName, dbName } = req.body;
if (!language || !entityName || !dbName) {
return res.status(400).json({ error: 'Please provide details.' });
}
try {
// Make a request to OpenAI API to generate code
const response = await axios.post(
'https://api.openai.com/v1/chat/completions',
{
model: 'gpt-3.5-turbo', // Specify the model
messages: [
{ role: 'system', content: `You are a helpful assistant that generates ${language} code.` },
{ role: 'user', content: `generate a web api microservice with separation of concerns and layered architecture in ${language} language having crud operations for entity name as ${entityName} with ${dbName} db. Keep the constants and other environment specific information configurable`}
],
max_tokens: 150, // Set the token limit (you can increase this if you expect longer code)
},
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
}
);
const generatedCode = response.data.choices[0].message.content;
return res.status(200).json({ code: generatedCode });
} catch (error) {
console.error('Error generating code:', error);
return res.status(500).json({ error: 'Error generating code.' });
}
});
// Start the server
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Step 3: Running the AI Agent
Start the server by running:
node server.js
2. You can now send POST requests to the /generate-code endpoint with a prompt and language to get code generated by the AI agent. Here’s an example of how to make a request using Postman or cURL.
Example Request using cURL:
curl -X POST http://localhost:3000/generate-code \
-H "Content-Type: application/json" \
-d '{"language": "Java", "entityName": "policy", "dbName": "Oracle"}'
-H "Content-Type: application/json" \
-d '{"language": "Java", "entityName": "policy", "dbName": "Oracle"}'
Sample Response:
// pom.xml (Maven)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>policy-service</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.2.0</spring-boot.version>
<oracle-jdbc.version>23.2.0.0</oracle-jdbc.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>${oracle-jdbc.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>${spring-boot.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
</project>
// application.properties
spring.datasource.url=jdbc:oracle:thin:@//localhost:1521/ORCLCDB
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.hibernate.ddl-auto=update
server.port=8080
// Policy.java (Entity)
package com.example.policy.entity;
import jakarta.persistence.*;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
@Entity
@Data
public class Policy {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Policy name is mandatory")
private String policyName;
private String description;
// Add other fields as needed
}
// PolicyRepository.java (Data Access Layer)
package com.example.policy.repository;
import com.example.policy.entity.Policy;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PolicyRepository extends JpaRepository<Policy, Long> {
}
// PolicyService.java (Service Layer)
package com.example.policy.service;
import com.example.policy.entity.Policy;
import com.example.policy.repository.PolicyRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class PolicyService {
private final PolicyRepository policyRepository;
@Autowired
public PolicyService(PolicyRepository policyRepository) {
this.policyRepository = policyRepository;
}
public List<Policy> getAllPolicies() {
return policyRepository.findAll();
}
public Optional<Policy> getPolicyById(Long id) {
return policyRepository.findById(id);
}
public Policy createPolicy(Policy policy) {
return policyRepository.save(policy);
}
public Policy updatePolicy(Long id, Policy policy) {
policy.setId(id);
return policyRepository.save(policy);
}
public void deletePolicy(Long id) {
policyRepository.deleteById(id);
}
}
// PolicyController.java (Controller Layer)
package com.example.policy.controller;
import com.example.policy.entity.Policy;
import com.example.policy.service.PolicyService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/policies")
public class PolicyController {
private final PolicyService policyService;
@Autowired
public PolicyController(PolicyService policyService) {
this.policyService = policyService;
}
@GetMapping
public ResponseEntity<List<Policy>> getAllPolicies() {
return ResponseEntity.ok(policyService.getAllPolicies());
}
@GetMapping("/{id}")
public ResponseEntity<Policy> getPolicyById(@PathVariable Long id) {
Optional<Policy> policy = policyService.getPolicyById(id);
return policy.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Policy> createPolicy(@Valid @RequestBody Policy policy) {
return ResponseEntity.status(HttpStatus.CREATED).body(policyService.createPolicy(policy));
}
@PutMapping("/{id}")
public ResponseEntity<Policy> updatePolicy(@PathVariable Long id, @Valid @RequestBody Policy policy) {
Optional<Policy> existingPolicy = policyService.getPolicyById(id);
if (existingPolicy.isPresent()) {
return ResponseEntity.ok(policyService.updatePolicy(id, policy));
} else {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePolicy(@PathVariable Long id) {
policyService.deletePolicy(id);
return ResponseEntity.noContent().build();
}
}
// PolicyServiceApplication.java (Main Application)
package com.example.policy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PolicyServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PolicyServiceApplication.class, args);
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>policy-service</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.2.0</spring-boot.version>
<oracle-jdbc.version>23.2.0.0</oracle-jdbc.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>${oracle-jdbc.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>${spring-boot.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
</project>
// application.properties
spring.datasource.url=jdbc:oracle:thin:@//localhost:1521/ORCLCDB
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.hibernate.ddl-auto=update
server.port=8080
// Policy.java (Entity)
package com.example.policy.entity;
import jakarta.persistence.*;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
@Entity
@Data
public class Policy {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Policy name is mandatory")
private String policyName;
private String description;
// Add other fields as needed
}
// PolicyRepository.java (Data Access Layer)
package com.example.policy.repository;
import com.example.policy.entity.Policy;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PolicyRepository extends JpaRepository<Policy, Long> {
}
// PolicyService.java (Service Layer)
package com.example.policy.service;
import com.example.policy.entity.Policy;
import com.example.policy.repository.PolicyRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class PolicyService {
private final PolicyRepository policyRepository;
@Autowired
public PolicyService(PolicyRepository policyRepository) {
this.policyRepository = policyRepository;
}
public List<Policy> getAllPolicies() {
return policyRepository.findAll();
}
public Optional<Policy> getPolicyById(Long id) {
return policyRepository.findById(id);
}
public Policy createPolicy(Policy policy) {
return policyRepository.save(policy);
}
public Policy updatePolicy(Long id, Policy policy) {
policy.setId(id);
return policyRepository.save(policy);
}
public void deletePolicy(Long id) {
policyRepository.deleteById(id);
}
}
// PolicyController.java (Controller Layer)
package com.example.policy.controller;
import com.example.policy.entity.Policy;
import com.example.policy.service.PolicyService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/policies")
public class PolicyController {
private final PolicyService policyService;
@Autowired
public PolicyController(PolicyService policyService) {
this.policyService = policyService;
}
@GetMapping
public ResponseEntity<List<Policy>> getAllPolicies() {
return ResponseEntity.ok(policyService.getAllPolicies());
}
@GetMapping("/{id}")
public ResponseEntity<Policy> getPolicyById(@PathVariable Long id) {
Optional<Policy> policy = policyService.getPolicyById(id);
return policy.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Policy> createPolicy(@Valid @RequestBody Policy policy) {
return ResponseEntity.status(HttpStatus.CREATED).body(policyService.createPolicy(policy));
}
@PutMapping("/{id}")
public ResponseEntity<Policy> updatePolicy(@PathVariable Long id, @Valid @RequestBody Policy policy) {
Optional<Policy> existingPolicy = policyService.getPolicyById(id);
if (existingPolicy.isPresent()) {
return ResponseEntity.ok(policyService.updatePolicy(id, policy));
} else {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePolicy(@PathVariable Long id) {
policyService.deletePolicy(id);
return ResponseEntity.noContent().build();
}
}
// PolicyServiceApplication.java (Main Application)
package com.example.policy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PolicyServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PolicyServiceApplication.class, args);
}
}
Enhancing the AI Agent for Code Generation
You can extend this basic setup to create a more sophisticated code generation AI agent:- Multi-Language Support: Expand the system to support multiple programming languages, such as Python, JavaScript, C++, etc.
- Code Execution: Integrate code execution environments (like Node.js, Python interpreters) to allow users to test generated code.
- Syntax Highlighting: If you integrate this with a front-end, you can add syntax highlighting using libraries like Prism.js or Highlight.js to improve code readability.
- Error Handling: Provide code linting and error feedback by integrating with tools like ESLint for JavaScript, pylint for Python, etc.
- Learning-Based Refinement: Collect user feedback on generated code and fine-tune the agent to improve responses over time.
Advantages of AI Agents for Code Generation
- Speed: AI can generate boilerplate code or solve common problems quickly, saving developers time.
- Assistance for Beginners: For novice programmers, AI agents can act as real-time tutors, helping them understand how to write certain functions or algorithms.
- Code Consistency: AI-generated code can follow consistent patterns and best practices (if prompted correctly).
- Cross-Language Support: AI agents can generate code in different languages based on a single prompt, making it easy to translate between programming languages.