Level: Intermediate
Table of Contents
- Module 1 — Understanding Agentic AI
- Module 2 — Increasing Developer Productivity
- Module 3 — Improving Operational Efficiency and Innovating Products
Module 1 — Understanding Agentic AI
1.1 Machine Learning and NLP
Machine Learning (ML) is a branch of artificial intelligence focused on systems that can learn from experience and improve over time, without being explicitly programmed for each scenario.
ML use cases:
- Fraud detection: An ML model can analyze millions of transactions and flag suspicious ones.
- Computer vision: Analyzing X-rays to detect anomalies (fractures, etc.).
The natural language challenge:
Natural language → Unstructured data → Hard for machines
Example:
"Add a big red apple, two bananas, and a carton of milk to cart"
↓
NLP (Natural Language Processing)
↓
┌──────────┬──────────┬─────────────┬──────────┐
│ Item │ Quantity │ Description │ Category │
├──────────┼──────────┼─────────────┼──────────┤
│ apple │ 1 │ big, red │ fruit │
│ banana │ 2 │ - │ fruit │
│ milk │ 1 │ carton │ dairy │
└──────────┴──────────┴─────────────┴──────────┘
NLP (Natural Language Processing) bridges human language and structured data. It works in both directions:
- Natural language → structured data (comprehension)
- Structured data → natural language (generation)
NLP use cases:
┌──────────────────────────────────────────────┐
│ NLP │
│ │
│ • Automatic translation (EN → FR, JP...) │
│ • Chatbots │
│ • Sentiment analysis │
│ • SPAM detection │
└──────────────────────────────────────────────┘
1.2 Large Language Models (LLMs)
A language model is a specific type of ML model trained to understand and generate human language. Its operation is based on probabilistic prediction:
Prediction example:
"The weather today is very ___"
┌──────────────┬─────────────┐
│ Candidate │ Probability │
├──────────────┼─────────────┤
│ sunny │ 42% │
│ hot │ 27% │
│ nice │ 13% │
│ cloudy │ 11% │
│ cold │ 7% │
└──────────────┴─────────────┘
With added context: "I live in Antarctica."
┌──────────────┬─────────────┐
│ Candidate │ Probability │
├──────────────┼─────────────┤
│ cold │ 66% │
│ freezing │ 21% │
│ snowy │ 8% │
│ harsh │ 3% │
│ windy │ 2% │
└──────────────┴─────────────┘
Token: Basic unit of LLM processing. A token can be a whole word or part of a word.
What makes an LLM “large”?
| Characteristic | Classic Language Model | Large Language Model |
|---|---|---|
| Training data size | Modest | Billions/trillions of tokens (books, websites, code…) |
| Architecture | Varied | Transformer (attention over the entire input) |
| Compute power | 1 CPU / 1 GPU | GPU clusters, weeks of training |
| Capabilities | Narrow tasks (sentiment, spam) | Complex reasoning, text and code generation |
| Scope | Task-specific | General purpose (same model for hundreds of tasks) |
LLMs and code:
LLMs understand code because code is text. They can:
- Read, write, debug, and refactor code
- Explain code in natural language
// Example code understood and generated by an LLM
function computeOrderTotal(lineItems, taxRate) {
let subtotal = 0;
lineItems.forEach(item => {
subtotal += item.price;
});
return subtotal + subtotal * taxRate;
}
The 3 domains where AI can help:
mindmap
root((AI for Developers))
Engineering Productivity
Code generation
Automated testing
Code review
Log analysis
Operational Efficiency
Intelligent ticket routing
Automatic incident summaries
Self-healing infrastructure
Product Innovation
New user experiences
AI integration in products
1.3 Agentic AI
Evolution of AI systems:
flowchart LR
subgraph LLM_simple["1 — Single LLM"]
A1[Input] --> B1[LLM] --> C1[Output]
end
subgraph Workflow["2 — Workflows"]
A2[Inputs] --> B2[LLM 1]
A2 --> C2[LLM 2]
B2 --> D2[Aggregator]
C2 --> D2
D2 --> E2[Output]
end
subgraph Agent["3 — Agents"]
A3[Input] --> B3[LLM / Reasoning Engine]
B3 --> C3{Action}
C3 --> D3[Environment]
D3 --> E3{Feedback}
E3 -->|Done| F3[Output]
E3 -->|Continue| B3
end
The 4 key characteristics of an AI agent:
┌─────────────────────────────────────────────────────────┐
│ AI Agent │
│ │
│ 1. AUTONOMOUS EXECUTION — Completes tasks on its own │
│ 2. PROACTIVITY — Takes initiative │
│ 3. GOAL-ORIENTED — Works toward an objective │
│ 4. COLLABORATION — Interacts with humans, │
│ other agents and systems │
└─────────────────────────────────────────────────────────┘
Agent lifecycle:
flowchart LR
P["Perceive\n(Sense the environment)"] --> R["Reason\n(Process with the LLM)"] --> A["Act\n(Execute)"] --> L["Learn\n(Learn from results)"]
L --> P
Simplified summary: Plan → Act → Adapt
The 3 main components of an AI agent:
graph TD
Agent["AI Agent"]
Agent --> RE["Reasoning Engine\n(LLM - the brain)"]
Agent --> Mem["Memory\n(Short and long term)"]
Agent --> Tools["Tools\n(Available actions)"]
1.4 Memory and Tools
Detailed agent architecture with Memory and Tools:
flowchart TD
Input["Input\n(prompt, API call, schedule)"]
RE["Reasoning Engine (LLM)"]
Mem["Memory"]
Tools["Tools"]
Output["Output"]
Input --> RE
RE <--> Mem
RE --> Tools
Tools --> RE
RE --> Output
Memory types:
| Type | Description | Examples |
|---|---|---|
| Files | Simple, readable, easy to manage | JSON files |
| Relational databases | Structured, queryable | PostgreSQL |
| NoSQL / Cache | Fast access, temporary state | Redis |
| Dedicated AI solutions | Context management and long-term storage | Zep |
Tool types:
┌───────────────────────────────────────────────────────┐
│ Tools │
│ │
│ Code executor │ Create file │ Delete file │
│ Database query │ API call │ PDF parser │
│ Search web │ Send email │ ... │
└───────────────────────────────────────────────────────┘
1.5 MCP — Model Context Protocol
Problem with tools without MCP:
Without MCP, each agent has its own non-reusable tools:
Agent A : [create_file] [update_file] [delete_file]
Agent B : [create_file] [update_file] [delete_file] ← Duplication!
Agent C : [create_dir] [list_dir] [remove_dir]
Definition:
MCP is an open protocol that standardizes how applications provide context and tools to LLMs.
— Source: modelcontextprotocol.io
MCP architecture:
graph LR
subgraph Host["MCP Host\n(Claude Desktop, IDEs, Frameworks)"]
Client["MCP Client"]
LLM["LLM"]
LLM <--> Client
end
subgraph Servers["MCP Servers"]
FS["MCP Server\nFilesystem\n[create/update/delete file]\n[create/list/delete dir]"]
PG["MCP Server\nPostgreSQL\n[query/insert/update/delete]"]
Git["MCP Server\nGit\n[clone/commit/push/branch]"]
end
Client <--> FS
Client <--> PG
Client <--> Git
FS --> FSsrc["Filesystem"]
PG --> PGsrc["PostgreSQL"]
Git --> Gitsrc["Git Repo"]
MCP components:
| Component | Role |
|---|---|
| MCP Server | Interface between the LLM and a data source. Exposes tools grouped by type. |
| MCP Client | Intermediary that maintains the connection to the MCP server. |
| MCP Host | The environment hosting the client (Claude Desktop, IDEs, agentic frameworks). |
1.6 RAG — Retrieval Augmented Generation
The cutoff problem:
Every LLM is trained on data up to a certain point in time (the cutoff). It knows nothing about events occurring after that date.
Timeline:
──────────────────────────────────────────────►
│
Cutoff time
(e.g.: Jan 2025)
│
[LLM Knowledge] │ [Unknown to LLM ✗]
Definition:
RAG is a technique that allows LLMs to retrieve and incorporate new information at runtime.
— Source: Wikipedia
How RAG works:
flowchart TD
Q["User question"]
LLM["LLM"]
Check{{"Does the LLM\nknow the answer?"}}
Retrieve["Search\nexternal sources\n(web, files, DB...)"]
Answer["Enriched answer"]
Q --> LLM
LLM --> Check
Check -->|"No"| Retrieve
Retrieve --> LLM
Check -->|"Yes"| Answer
LLM --> Answer
RAG data sources:
- Local files (PDF, Word, TXT)
- Private databases
- Real-time web search
- Vector databases
Vector Databases:
Vector databases store numerical representations (embeddings) of data for semantic similarity search:
Object: Shape Color Shininess Material Weight
Ball: [1.0, 1.0, 1.0, 1.0, 1.0 ]
[1.0, 0.7, 0.9, 0.4, 0.9 ]
[1.0, 0.5, 0.7, 0.1, 0.8 ]
Query: "Show me something round, yellow and shiny"
→ Vector search → Most similar result
RAG challenges:
| Challenge | Description |
|---|---|
| Irrelevant retrieval | Fetching documents unrelated to the question |
| Long/complex documents | Difficulty extracting the essentials |
| Security and access control | Avoiding exposure of sensitive data |
1.7 Data Security and Compliance
Main Risks
1. Uncontrolled autonomous access
AI agents can become attack targets. Real-world example:
Shortly after the GitHub MCP server was developed, researchers at Invariant Labs discovered a serious vulnerability. The exploit allowed an agent to access private repositories, inject malicious code, and leak sensitive data — without any direct user command.
GitHub MCP exploit:
┌─────────────────────────────────┐
│ 1. Access private repos │
│ 2. Inject malicious code │
│ 3. Leak sensitive data │
└─────────────────────────────────┘
2. Non-compliant behavior
Agents have been reported as:
- Deleting important project files
- Attempting to wipe entire directories or critical OS components
EU AI Act
graph TD
EU["EU AI Act\n(effective August 2026)"]
EU --> R1["Unacceptable risks\n→ PROHIBITED"]
EU --> R2["High risks\n→ Strict regulation"]
EU --> R3["Limited risks\n→ Transparency regulation"]
EU --> R4["Minimal risks\n→ No regulation"]
Module 2 — Increasing Developer Productivity
2.1 Agentic AI in the SDLC
The software development lifecycle (SDLC) can be improved by AI at each phase:
flowchart LR
subgraph Plan
P1["Estimates\n(historical velocity)"]
P2["Requirements\nanalysis\n(PRD → user stories)"]
P3["Communication\nplans"]
end
subgraph Design
D1["Rapid\nprototyping"]
D2["Architecture\ndiagrams"]
D3["API contracts"]
end
subgraph Implement
I1["Code\ngeneration"]
I2["Assisted\ncode review"]
I3["Reverse\nengineering"]
end
subgraph Test
T1["Test\ngeneration"]
T2["Gap analysis\n(coverage)"]
T3["Legacy\ncode"]
end
subgraph Deploy
Dep1["Release report"]
Dep2["Anomaly\ndetection"]
Dep3["Automatic\ndocumentation"]
end
subgraph Maintain
M1["Internal\nchatbots"]
M2["Security\nscanning"]
M3["Postmortems"]
end
Plan --> Design --> Implement --> Test --> Deploy --> Maintain
2.2 Agentic Coding
The 3 approaches to AI-assisted code generation:
graph LR
A["1 — Direct LLMs\n(ChatGPT, Claude)\nManual copy-paste"] -->|Evolution| B["2 — AI Coding Assistants\n(IDE-integrated)\nAutocomplete + Chat"] -->|Evolution| C["3 — Agentic Coding\n(autonomous agent)\nFull task execution"]
Agentic coding tool modes:
| Mode | Tools |
|---|---|
| IDE Plugin | GitHub Copilot, Tabnine, Amazon Q, JetBrains AI |
| Full IDE | Cursor, Windsurf |
| CLI | Claude Code, Aider, OpenAI Codex CLI, Gemini CLI |
| Web Interface | GitHub Copilot Workspace, OpenAI Codex, Jules by Google |
How an AI coding assistant works:
┌─────────────────────────────────────┐
│ AI Coding Assistant │
│ │
│ Autocomplete │
│ ├─ Monitors the active file │
│ ├─ Predicts next line/block │
│ └─ Real-time suggestion │
│ │
│ Chat │
│ ├─ Access to the entire project │
│ ├─ Complex questions │
│ └─ Full block generation │
└─────────────────────────────────────┘
Autocomplete: optimized for speed and small context (current file).
Chat: access to the entire project, better for complex tasks.
Key concepts:
| Concept | Definition |
|---|---|
| Vibe Coding | Approach where you describe what you want in natural language and let AI generate the code. Ideal for rapid prototyping, but risky in production. |
| Context Engineering | Providing the right information to the LLM in the right format for it to complete the task efficiently. |
Context Engineering — components:
mindmap
root((Context Engineering))
Prompt design
State / History
Long-term memory
RAG
Structured outputs
Agentic coding use cases:
- Reverse Engineering
- Code generation
- Test generation
- Documentation
- Refactoring
2.3 Global Rules
Global Rules define the rules that an agent must follow. They are critical for security, compliance, and code consistency.
Why global rules?
┌─────────────────────────────────────────────┐
│ Global Rules │
│ │
│ Security and compliance │
│ → Which tools the agent can use │
│ → Which actions are forbidden │
│ │
│ Code standards │
│ → Style consistent with the codebase │
│ → Naming conventions │
│ │
│ Guardrails │
│ → Input protection │
│ → Output quality │
└─────────────────────────────────────────────┘
Configuration levels:
graph TD
E["Enterprise policy\n(entire organization)"]
G["Global personal policy\n(all your projects)"]
PT["Project policy (team)\n(entire project)"]
PI["Project policy (individual)\n(you + this project)"]
E --> G --> PT --> PI
2.4 Demo: Global Rules
Tool used: Claude Code (CLI). Workflow for Global Rules:
# 1. Open a terminal and navigate to the project
cd orders-service
# 2. Start Claude Code
claude
# 3. Run the initialization command
/init
This command automatically generates a CLAUDE.md file — the project “rulebook.”
Prompt used to generate Global Rules:
Please generate a CLAUDE.md file that defines universal development standards
applicable to all projects.
The document should include:
- Code Style Guidelines: naming conventions, indentation, commenting, file organization
- Version Control Practices: branching strategies, commit message formats, merge protocols
- Testing Requirements: unit testing, integration testing, code coverage expectations
- Documentation Standards: code comments, README files, API documentation
- Security Best Practices: input validation, authentication, dependency management
- CI/CD: automated build, test, deployment processes
- Code Review Procedures: peer reviews, approval processes, code quality checks
- Licensing and Compliance: licensing requirements and compliance
Example generated DEVELOPMENT_STANDARDS.md:
# Naming Conventions
- Variables/Functions: camelCase (getUserName, calculateTotal)
- Classes: PascalCase (UserService, OrderController)
- Constants: UPPER_SNAKE_CASE (MAX_RETRY_COUNT, DEFAULT_TIMEOUT)
- Packages: lowercase (com.example.service)
# Branching Strategy
- main/master: production-ready code
- develop: integration branch
- feature/: new features (feature/user-authentication)
- hotfix/: critical fixes (hotfix/security-patch)
- release/: release preparation (release/v1.2.0)
# Commit Message Format
<type>(<scope>): <description>
Types: feat, fix, docs, style, refactor, test, chore
Example: feat(auth): add OAuth2 integration
# Testing
- Minimum coverage: 80% for new code
- Pattern: Arrange-Act-Assert
- Naming: shouldThrowExceptionWhenUserNotFound
2.5 Demo: One-shot Prompt
Step 1: Create the PRD (Product Requirements Document)
Prompt 1 — PRD generation:
Please create a Product Requirements Document (PRD) in Markdown format for a
lightweight web service that enables incident reporting and management.
The service should be written in Java with Spring Boot and support the following:
Key Functional Requirements:
- Users or systems can report incidents (app crashes, failed jobs, customer complaints)
- Incidents are stored in a relational database
- Endpoints: listing, viewing by ID, marking as resolved, assigning a category
Non-Functional Requirements:
- Lightweight and easy to run locally
- RESTful API conventions
- Standard Spring Boot practices
Technical Constraints:
- Language: Java | Framework: Spring Boot | Database: H2 (in-memory)
Generated PRD — API specification:
Base URL: http://localhost:8080/api/v1
POST /incidents → Create an incident
GET /incidents → List (with pagination + filters)
GET /incidents/{id} → View incident by ID
PATCH /incidents/{id}/status → Update status
PATCH /incidents/{id}/category → Update category
Data model:
erDiagram
INCIDENT {
Long id PK
String title
String description
IncidentCategory category
IncidentSeverity severity
IncidentStatus status
LocalDateTime createdAt
LocalDateTime updatedAt
}
Model enums:
public enum IncidentStatus {
OPEN, IN_PROGRESS, RESOLVED, CLOSED
}
public enum IncidentSeverity {
LOW, MEDIUM, HIGH, CRITICAL
}
public enum IncidentCategory {
DATABASE, APPLICATION, INFRASTRUCTURE, SECURITY, OTHER
}
Step 2: Implement the service
Prompt 2 — Code generation:
Using the content of the file PRD.md as the authoritative product requirements,
implement a Java + Spring Boot project for an Incident Reporting Web Service.
The service should:
- Allow reporting, listing, resolving, and categorizing incidents
- Use an in-memory H2 database for persistence
- Be lightweight and demo-ready
Please generate:
- Java source code: models, controllers, services, repositories (Spring Data JPA)
- A minimal application.properties for Spring Boot configuration
- A README.md explaining how to run the project
Constraints:
- Skip test implementation for now
- Follow idiomatic Spring Boot conventions
- Maven layout: src/main/java, src/main/resources
Generated architecture — incident-manager-service:
src/main/java/com/example/incidentmanager/
├── IncidentManagerApplication.java
├── controller/
│ ├── IncidentController.java
│ └── GlobalExceptionHandler.java
├── service/
│ └── IncidentService.java
├── repository/
│ └── IncidentRepository.java
├── model/
│ ├── Incident.java
│ ├── IncidentStatus.java
│ ├── IncidentSeverity.java
│ └── IncidentCategory.java
└── dto/
├── CreateIncidentRequest.java
├── UpdateStatusRequest.java
├── UpdateCategoryRequest.java
└── ErrorResponse.java
Incident.java entity (with Lombok after refactoring):
@Entity
@Table(name = "incidents")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Incident {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Title is required")
@Size(max = 255)
@Column(nullable = false)
private String title;
@Size(max = 2000)
@Column(length = 2000)
private String description;
@NotNull
@Enumerated(EnumType.STRING)
private IncidentCategory category;
@NotNull
@Enumerated(EnumType.STRING)
private IncidentSeverity severity;
@Enumerated(EnumType.STRING)
private IncidentStatus status = IncidentStatus.OPEN;
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
LocalDateTime now = LocalDateTime.now();
createdAt = now;
updatedAt = now;
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}
IncidentService.java:
@Service
@Transactional
public class IncidentService {
private final IncidentRepository incidentRepository;
@Autowired
public IncidentService(IncidentRepository incidentRepository) {
this.incidentRepository = incidentRepository;
}
public Incident createIncident(Incident incident) {
return incidentRepository.save(incident);
}
@Transactional(readOnly = true)
public Page<Incident> getAllIncidents(
IncidentStatus status, IncidentCategory category, Pageable pageable) {
return incidentRepository.findByOptionalStatusAndCategory(status, category, pageable);
}
@Transactional(readOnly = true)
public Incident getIncidentById(Long id) {
return incidentRepository.findById(id)
.orElseThrow(() -> new IncidentNotFoundException(id));
}
public Incident updateIncidentStatus(Long id, IncidentStatus status) {
Incident incident = getIncidentById(id);
incident.setStatus(status);
return incidentRepository.save(incident);
}
public Incident updateIncidentCategory(Long id, IncidentCategory category) {
Incident incident = getIncidentById(id);
incident.setCategory(category);
return incidentRepository.save(incident);
}
}
IncidentRepository.java:
@Repository
public interface IncidentRepository extends JpaRepository<Incident, Long> {
Page<Incident> findByStatus(IncidentStatus status, Pageable pageable);
Page<Incident> findByCategory(IncidentCategory category, Pageable pageable);
Page<Incident> findByStatusAndCategory(
IncidentStatus status, IncidentCategory category, Pageable pageable);
@Query("SELECT i FROM Incident i WHERE " +
"(:status IS NULL OR i.status = :status) AND " +
"(:category IS NULL OR i.category = :category)")
Page<Incident> findByOptionalStatusAndCategory(
@Param("status") IncidentStatus status,
@Param("category") IncidentCategory category,
Pageable pageable
);
}
application.properties:
spring.application.name=incident-manager-service
server.port=8080
# H2 Database
spring.datasource.url=jdbc:h2:mem:incidentdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
# JPA/Hibernate
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# H2 Console (development only)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# OpenAPI/Swagger
springdoc.api-docs.path=/api-docs
springdoc.swagger-ui.path=/swagger-ui.html
# Actuator
management.endpoints.web.exposure.include=health,info,metrics
2.6 Demo: Reverse Engineering
Reverse Engineering is a safe operation since it modifies no files. It is the best entry point for exploring a new codebase.
The 3 analysis levels:
graph LR
L1["1 — Overview\n(High level)"] --> L2["2 — Architecture\n(Components)"] --> L3["3 — Deep dive\n(Specific feature)"]
Prompt 1 — Overview:
Summarize what this project does.
Include main features, tech stack, and key components.
Prompt 2 — Architecture:
Outline the project's architecture.
List main controllers, services, and how data flows through them.
Prompt 3 — Specific feature:
Find and explain the /api/incidents/{id}/resolve endpoint.
Show which controller handles it, what it does,
and how it interacts with services and DB.
Result — Data flow of the GET /incidents/{id} endpoint:
sequenceDiagram
participant Client
participant IncidentController
participant IncidentService
participant IncidentRepository
participant H2DB as "H2 DB"
Client->>IncidentController: GET /api/v1/incidents/{id}
IncidentController->>IncidentService: getIncidentById(id)
IncidentService->>IncidentRepository: findById(id)
IncidentRepository->>H2DB: SELECT * FROM incidents WHERE id=?
H2DB-->>IncidentRepository: Incident row
IncidentRepository-->>IncidentService: Optional~Incident~
alt Incident found
IncidentService-->>IncidentController: Incident
IncidentController-->>Client: 200 OK + Incident JSON
else Incident not found
IncidentService-->>IncidentController: throw IncidentNotFoundException
IncidentController-->>Client: 404 Not Found
end
2.7 Demo: Test Creation
Prompt:
Create unit tests for the getIncidentById method from the IncidentService class.
Claude Code process:
- Checks test dependencies (JUnit, Mockito)
- Detects missing dependencies and requests permission to add them
- Generates the test file
- Runs the tests to verify they pass
- If a test fails, analyzes and auto-corrects
Generated IncidentServiceTest.java:
@ExtendWith(MockitoExtension.class)
@DisplayName("IncidentService - getIncidentById Tests")
class IncidentServiceTest {
@Mock
private IncidentRepository incidentRepository;
@InjectMocks
private IncidentService incidentService;
private Incident sampleIncident;
@BeforeEach
void setUp() {
sampleIncident = new Incident();
sampleIncident.setId(1L);
sampleIncident.setTitle("Database Connection Timeout");
sampleIncident.setDescription("Unable to connect to user database after 30 seconds");
sampleIncident.setCategory(IncidentCategory.DATABASE);
sampleIncident.setSeverity(IncidentSeverity.HIGH);
sampleIncident.setStatus(IncidentStatus.OPEN);
sampleIncident.setCreatedAt(LocalDateTime.now());
sampleIncident.setUpdatedAt(LocalDateTime.now());
}
@Test
@DisplayName("Should return incident when valid ID is provided")
void getIncidentById_WithValidId_ReturnsIncident() {
// Arrange
when(incidentRepository.findById(1L)).thenReturn(Optional.of(sampleIncident));
// Act
Incident result = incidentService.getIncidentById(1L);
// Assert
assertThat(result).isNotNull();
assertThat(result.getId()).isEqualTo(1L);
assertThat(result.getTitle()).isEqualTo("Database Connection Timeout");
verify(incidentRepository, times(1)).findById(1L);
}
@Test
@DisplayName("Should throw IncidentNotFoundException when incident does not exist")
void getIncidentById_WithInvalidId_ThrowsIncidentNotFoundException() {
// Arrange
when(incidentRepository.findById(999L)).thenReturn(Optional.empty());
// Act & Assert
assertThatThrownBy(() -> incidentService.getIncidentById(999L))
.isInstanceOf(IncidentNotFoundException.class)
.hasMessage("Incident with ID 999 not found");
verify(incidentRepository, times(1)).findById(999L);
}
}
Best practice: Incremental approach — test method by method rather than the entire project at once.
2.8 Demo: Documentation
Prompt 1 — Review and improvement:
Review README.md and suggest improvements.
Check for clarity, completeness, outdated steps,
and missing setup or usage info.
Prompt 2 — External documentation:
Rewrite the API documentation to be suitable for external partners.
Make it concise, formal, and remove internal implementation notes.
Example generated external documentation (EXTERNAL_DOCUMENTATION.md):
# Incident Management API — v1
**Base URL**: https://api.example.com/api/v1
**Content-Type**: application/json
## POST /incidents
Creates a new incident.
Request Body:
{
"title": "string (required, max 255 chars)",
"description": "string (optional, max 2000 chars)",
"category": "DATABASE|APPLICATION|INFRASTRUCTURE|SECURITY|OTHER",
"severity": "LOW|MEDIUM|HIGH|CRITICAL"
}
Response 201:
{
"id": 123,
"title": "Database Connection Timeout",
"category": "DATABASE",
"severity": "HIGH",
"status": "OPEN",
"createdAt": "2024-01-15T10:30:00Z"
}
Use case: Documentation is particularly useful after adding new features or onboarding external partners.
2.9 Demo: Refactoring
Golden rule: Always have a complete test suite before refactoring. Tests are your safety net.
Recommended approach:
- Small, incremental changes
- One component at a time
- Validate tests after each change
Prompt:
Refactor all Java classes to remove boilerplate getters, setters, constructors,
and toString. Use Lombok annotations like @Getter, @Setter, @AllArgsConstructor,
@NoArgsConstructor, and @ToString.
Before refactoring (simplified Incident.java):
public class Incident {
private Long id;
private String title;
// ...
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
// ... dozens of additional lines
}
After refactoring (with Lombok):
@Entity
@Table(name = "incidents")
@Data // Replaces getters, setters, toString, equals, hashCode
@NoArgsConstructor
@AllArgsConstructor
public class Incident {
private Long id;
private String title;
// ... much cleaner!
}
Performance note: Refactoring a small codebase took Claude Code ~11 minutes. For larger projects, plan accordingly.
2.10 CI/CD Pipelines
AI can be integrated as a standard step in CI/CD pipelines via an SDK:
flowchart LR
subgraph CI Pipeline
Build["Build"] --> Test["Tests"]
Test -->|Tests fail| AIAgent["AI Agent Step\n(via SDK)"]
AIAgent -->|Analyzes & fixes| Test
Test -->|Tests pass| Deploy["Deploy"]
end
AI use cases in CI/CD:
| Phase | Use Case |
|---|---|
| Build | Anomaly detection in logs |
| Test | Auto-correction of failing tests |
| Test | Generating new tests if coverage is low |
| Implementation | Generating missing code (PR context) |
| Code Review | Automatic review + PR comments |
| Release | Release report generation |
| Documentation | Automatic documentation of changes |
SDK architecture in the pipeline:
Pipeline Step
│
▼
┌─────────────────────────────────────┐
│ LLM SDK │
│ ├─ LLM connection │
│ ├─ Access to CI/CD tools │
│ ├─ Build context │
│ └─ Actions: read, write, test │
└─────────────────────────────────────┘
│
▼
LLM (Claude, GPT, etc.)
Module 3 — Improving Operational Efficiency and Innovating Products
3.1 Agentic Frameworks
Main AI patterns for operational efficiency:
graph LR
subgraph Patterns
W["1 — AI Workflows\n(Step orchestration)"]
A["2 — AI Agents\n(Autonomous, reasoning)"]
M["3 — Multi-agent Systems\n(Agent collaboration)"]
end
W --> A --> M
Types of agentic frameworks:
┌──────────────────────────────────────────────────────────┐
│ Agentic Frameworks │
│ │
│ No-code platforms Coding frameworks │
│ ──────────────── ───────────────── │
│ • n8n • LangGraph │
│ • Relevance AI • PydanticAI │
│ • Flowise • Microsoft AutoGen │
└──────────────────────────────────────────────────────────┘
Product innovation:
graph LR
ML["Machine Learning\n(Predictive models)"] --> Prod["Product\nInnovation"]
GenAI["Generative AI\n(LLMs, generation)"] --> Prod
3.2 Demo: Incident Enrichment
Tool: n8n (no-code, visual interface)
Goal: Automatically enrich incident data with an AI agent.
Workflow architecture:
flowchart LR
Trigger["Chat Trigger\n(or Webhook)"] --> Agent["AI Agent\n(Incident Triage)"]
Agent --> Output["Enriched Report"]
Enrichment agent prompt:
You are an AI agent that performs incident triage.
When given incident details, immediately:
1. Summarize the incident briefly.
2. Classify it by:
- Severity (Critical, High, Medium, Low)
- Source/system involved (e.g., database, network, auth)
3. Suggest likely causes if technical context is available.
Do not ask for more input or wait for confirmation.
Always respond directly with analysis.
Input: {{ $json.chatInput }}
Input JSON payload:
{
"id": 123,
"title": "Database Connection Timeout",
"description": "Unable to connect to user database after 30 seconds",
"category": "DATABASE",
"status": "OPEN",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
n8n agent configuration:
| Parameter | Description |
|---|---|
| Chat model | The reasoning engine (required) |
| Tools | Available actions for the agent |
| Memory | Context persistence between executions |
3.3 Demo: Memory
Goal: Allow the agent to relate linked incidents to each other.
Memory configuration in n8n:
- Type: Simple memory (window buffer)
- Window: 10 last interactions (default: 5)
Prompt with memory:
You are an AI agent that performs incident triage. When given incident details:
1. Summarize the incident briefly.
2. Review recent incidents from memory and check for any that:
- Affect the same or related systems (database, auth, frontend)
- Appear to be recurring or escalating versions of a past issue
- May share a root cause (timeouts, DB latency, systemic slowdowns)
3. If related incidents are found, link them together and include:
- Reference to the past incident(s) (e.g., "similar to Incident #123")
- Assessment of escalation, recurrence, or widening scope
- Updated severity if recurrence indicates increased impact
4. Classify by: Severity + Source/system involved
5. Suggest likely causes.
Input: {{ $json.chatInput }}
Memory in action demonstration:
Run 1: Incident #123 - Database Connection Timeout
→ Result: "No related incidents found in recent memory"
Run 2: Incident #124 - User Service Fails to Retrieve Profiles
(mentions database latency issues)
→ Result: "⚠️ Linked to Incident #123 — same DB problem"
Run 3: Incident #125 - Multiple Microservices Failing Due to DB Latency
(auth, billing, user — timeout >35s)
→ Result: "🚨 Escalation detected — Incidents #123 and #124 linked"
Test payloads:
// Incident #124
{
"id": 124,
"title": "User Service Fails to Retrieve Profiles",
"description": "User service is experiencing latency retrieving profile data from the database. Requests take over 25 seconds.",
"category": "DATABASE",
"status": "OPEN",
"createdAt": "2024-01-16T08:42:00Z"
}
// Incident #125
{
"id": 125,
"title": "Multiple Microservices Failing Due to DB Latency",
"description": "Several microservices (auth, billing, user) are timing out when querying the database. Latency exceeds 35s.",
"category": "DATABASE",
"status": "OPEN",
"createdAt": "2024-01-17T11:15:00Z"
}
Impact: Memory enables much faster root cause analysis by connecting incidents that appear isolated.
3.4 Demo: Tools
Goal: Use external data (Google Sheets) for severity classification and send automatic email notifications (Gmail).
Workflow architecture with tools:
flowchart LR
Input["Incident\n(JSON)"] --> Agent["AI Agent"]
Agent --> GS["Google Sheets\n(Severity Mapping)"]
Agent --> GM["Gmail\n(Send email)"]
GS --> Agent
Agent --> Report["Final report\n+ Email sent"]
Prompt with tools:
[Includes same triage + memory tasks, then:]
4. Classify by:
- Severity: Refer to the Google Sheet of severity mappings for guidance.
Consider keywords, patterns, or context that match known mappings to assign:
Critical, High, Medium, or Low.
- Source/system involved.
After analysis, immediately send an email:
Subject: New Incident: *Summary* [*Severity*]
Body:
<p>Hello Team,</p>
<p>A new incident has been reported:</p>
<ul>
<li><strong>Summary:</strong> *Summary*</li>
<li><strong>Severity:</strong> *Severity*</li>
<li><strong>Source:</strong> *Source*</li>
<li><strong>Suggested Cause:</strong> *Suggested Cause*</li>
</ul>
<p>This notification was auto-generated by the AI Incident Enrichment Agent.</p>
3.5 Demo: Guardrails
Goal: Protect the workflow from invalid, dangerous, or malformed inputs.
Guardrail types:
| Type | Role |
|---|---|
| Relevance classifier | Verifies that input is within the expected scope |
| Safety classifier | Detects prompt injections and malicious inputs |
| PII filter | Filters personally identifiable data |
| Moderation | Flags harmful or inappropriate content |
| Tool safeguard | Dynamically evaluates and restricts tool access |
Workflow architecture with guardrail:
flowchart LR
Input["User\ninput"] --> Guard["Guardrail Agent\n(Validation)"]
Guard -->|ValidIncident| Enricher["Incident\nEnricher Agent"]
Guard -->|InvalidIncident| Reject["Rejection\n+ Explanation"]
Enricher --> Output["Final\nReport"]
Guardrail Agent prompt:
You are an AI guardrail agent protecting the incident enrichment workflow.
Tasks:
1. Determine if input qualifies as a valid incident:
- Clear description of a system failure, error, or issue
- Affected system or area (login service, database, etc.)
2. Reject non-incident inputs:
- Random messages ("hi", "check this out")
- Feature requests, opinions, support tickets unrelated to system errors
- Blank or ambiguous messages
3. Sanitize and normalize the input:
- Trim excessive whitespace
- Infer category/source if unclear
4. Classify as: ValidIncident OR InvalidIncident
Output format (JSON):
{
"status": "<ValidIncident | InvalidIncident>",
"sanitizedInput": "<cleaned version>",
"reason": "<short explanation>"
}
Input: {{ $json.chatInput }}
3.6 Multi-Agent Systems
Definition: Multiple AI agents collaborate, hand off tasks, and automate complex processes end to end.
The 4 conversational patterns:
Pattern 1: Two-agent Chat
flowchart LR
A1["Agent A"] <--> A2["Agent B"]
Ideal for simple scenarios requiring minimal coordination.
Pattern 2: Sequential Chat
flowchart LR
CW["Content\nWriter"] --> SEO["SEO\nOptimizer"] --> IG["Image\nGenerator"] --> PUB["Publisher"]
Example: Content publishing pipeline. Each agent completes its task before passing to the next.
Pattern 3: Group Chat
flowchart TD
GCM["Group Chat\nManager\n(Orchestrator)"]
GCM --> TL["Timeline\nAgent"]
GCM --> BA["Budget\nAgent"]
GCM --> RM["Resource\nManagement Agent"]
GCM --> RA["Risk\nAssessment Agent"]
TL --> GCM
BA --> GCM
RM --> GCM
RA --> GCM
Example: Project management — parallel tasks coordinated by an orchestrator.
Pattern 4: Nested Chat
flowchart TD
CM["Campaign\nManager"]
subgraph Production["Production Team"]
CC["Content\nCreator"]
HO["Hashtag\nOptimizer"]
end
subgraph Strategy["Strategy Team"]
TA["Trend\nAnalyzer"]
AI["Audience\nInsights"]
end
CM --> Production
CM --> Strategy
CC & HO --> CM
TA & AI --> CM
Example: Social media campaign — hierarchical structure with independent teams.
Pattern comparison:
| Pattern | Use Case | Complexity |
|---|---|---|
| Two-agent chat | Simple coordination | Low |
| Sequential chat | Linear pipeline | Medium |
| Group chat | Parallel tasks | High |
| Nested chat | Complex hierarchies | Very high |
3.7 Agent Observability and Evaluation
Difference between Testing and Evaluation:
Testing Evaluation
─────────────────────── ─────────────────────────────────
Checks the OUTPUT Analyzes the entire REASONING PROCESS
• Does the code compile? • Which internal prompts were used?
• Are requirements met? • How many tokens were consumed?
• Is it what was requested? • Was memory used correctly?
• Were the right tools called?
Evaluation methods:
graph TD
E["Agent Evaluation"]
E --> H["Human-in-the-loop\n(Pause after each step)"]
E --> L["LLM-as-a-judge\n(Another LLM evaluates)"]
E --> T["Task completion\n(Metrics across multiple runs)"]
E --> O["Observability frameworks\n(Detailed traces)"]
Observability frameworks:
| Framework | Description |
|---|---|
| Opik (Comet) | Open-source. Traces the reasoning process step by step. |
| Langfuse | Detailed analytics, prompt tracking, decision flow visualization. |
Evaluation metrics (Task Completion):
• Success rate
• Execution time
• Tool usage
• Token consumption
• Number of iterations required
Complete observability cycle:
flowchart LR
Agent["Agent in production"]
OFW["Observability Framework\n(Opik / Langfuse)"]
Metrics["Metrics"]
Eval["Evaluation"]
Improve["Prompt/tool\nimprovement"]
Agent -->|Traces| OFW
OFW --> Metrics
Metrics --> Eval
Eval --> Improve
Improve --> Agent
General Summary
Course Overview
mindmap
root((Agentic AI for Developers))
Module 1 - Understanding
ML and NLP
LLMs and tokens
Agents
Perceive - Reason - Act - Learn
Memory
JSON, PostgreSQL, Redis, Zep
Tools
MCP Protocol
RAG
Security and EU AI Act
Module 2 - Dev Productivity
Complete SDLC
Agentic Coding
Claude Code, Cursor, Copilot
Global Rules
CLAUDE.md
Demos
PRD - Code - Tests - Docs - Refactor
CI/CD with AI
Module 3 - Operations and Products
Frameworks
n8n, LangGraph, AutoGen
Incident Enrichment
Memory in workflows
External tools
Google Sheets, Gmail
Guardrails
Multi-agent Systems
Observability
Opik, Langfuse
Key Terminology
| Term | Definition |
|---|---|
| ML | Machine Learning — systems that learn from data |
| NLP | Natural Language Processing — bridge between human language and machines |
| LLM | Large Language Model — large-scale language model (transformer) |
| Token | Basic unit of an LLM (word or part of a word) |
| Agentic AI | AI that perceives, reasons, acts and learns autonomously |
| MCP | Model Context Protocol — standardizes tool access for LLMs |
| RAG | Retrieval Augmented Generation — enriching the LLM with external data |
| Vibe Coding | Generating code by natural language description |
| Context Engineering | Art of providing the right context to the LLM to maximize quality |
| Guardrails | Security mechanisms to control agent inputs/outputs |
| Cutoff | Training data cutoff date for an LLM |
| Hallucination | LLM generation of incorrect but plausible information |
Search Terms
agentic · ai · developers · agents · orchestration · artificial · intelligence · generative · chat · pattern · global · memory · rules · tools