D — Runnable Python, Java and React examples | Agentic AI — Chapter 1: Introduction and Foundations
Detailed Chapter 1 of the Agentic AI learning track: all 15 foundation topics, architecture diagrams, runnable Python, Java and React examples, solved questions, 15 exercises, a complete mini-project, interview preparation and revision. Chapters 2–20 are upcoming.
Prerequisites: Beginner-friendly theory. Basic familiarity with functions helps for the Python lab; the runnable example uses only the standard library. No provider credentials are required. This is Chapter 1 of a planned 20-chapter track, not a completion claim for the entire course.
Section D — Coding Examples
Save the following source as agent_demo.py and run it with a supported Python 3 interpreter. It uses only standard-library features; no dependency installation, provider credential or external API is required. The mock policy always searches for Python and then returns observed IDs, so it demonstrates the controller contract rather than general natural-language understanding. The goal is stored and checked for emptiness but is not interpreted by this fixture. A later model adapter must perform that interpretation without bypassing validation.
from dataclasses import dataclass, field
from typing import Any, Callable
import json
JOBS = [
{"id": "job-1", "title": "Python API Developer", "visible_to": ["learner"]},
{"id": "job-2", "title": "Private Python Role", "visible_to": ["other"]},
]
@dataclass
class RunState:
goal: str
status: str = "running"
observations: list[dict[str, str]] = field(default_factory=list)
events: list[str] = field(default_factory=list)
def search_jobs(user_id: str, arguments: dict[str, Any]):
if set(arguments) != {"query"}:
raise ValueError("expected only query")
query = arguments["query"]
if not isinstance(query, str) or not 1 <= len(query.strip()) <= 80:
raise ValueError("query must contain 1–80 characters")
return [{"id": row["id"], "title": row["title"]} for row in JOBS
if user_id in row["visible_to"]
and query.casefold() in row["title"].casefold()]
def mock_policy(state: RunState) -> dict[str, Any]:
# A deterministic teaching fixture, not an LLM or a general planner.
if not state.events:
return {"tool": "search_jobs", "arguments": {"query": "Python"}}
return {"final_ids": [job["id"] for job in state.observations]}
def run_agent(goal: str, user_id: str, policy: Callable = mock_policy,
max_steps: int = 3) -> RunState:
if not goal.strip() or not user_id or not 1 <= max_steps <= 20:
raise ValueError("invalid run configuration")
state = RunState(goal=goal)
tools = {"search_jobs": search_jobs}
for _ in range(max_steps):
try:
proposal = policy(state)
if not isinstance(proposal, dict):
raise ValueError("proposal must be an object")
if "final_ids" in proposal:
ids = proposal["final_ids"]
known = {job["id"] for job in state.observations}
if not isinstance(ids, list) or any(
not isinstance(i, str) or i not in known for i in ids):
raise ValueError("final IDs must reference observed jobs")
state.observations = [job for job in state.observations
if job["id"] in set(ids)]
state.status = "completed"
return state
if set(proposal) != {"tool", "arguments"}:
raise ValueError("invalid tool proposal")
name = proposal["tool"]
if not isinstance(name, str) or name not in tools:
raise PermissionError("tool not allowed")
args = proposal["arguments"]
if not isinstance(args, dict):
raise ValueError("arguments must be an object")
state.observations = tools[name](user_id, args)
state.events.append(name)
except (ValueError, PermissionError):
state.events.append("proposal_rejected")
state.status = "failed"
return state
state.status = "budget_exhausted"
return state
if __name__ == "__main__":
state = run_agent("Find visible Python jobs; do not apply", "learner")
print(json.dumps({"status": state.status, "jobs": state.observations}))
Expected demonstration output
{"status": "completed", "jobs": [{"id": "job-1", "title": "Python API Developer"}]}
Important lines and limitations
The dataclass groups mutable run state, and default_factory gives each run its own lists. The tool registry is a fixed allowlist. search_jobs checks the exact argument keys and validates the query before filtering synthetic rows using the verified user_id passed by the caller. Returned rows exclude visibility metadata. The controller checks proposal shape and final IDs, uses a bounded for-loop and distinguishes rejected proposals from budget exhaustion. The policy sees no credential and cannot request a different tool simply by naming it.
This is a small teaching controller, not a production service or general autonomous agent. user_id is supplied by the demonstration caller; an HTTP service must derive it from verified authentication. It has no durable state, overall deadline, asynchronous cancellation, network timeouts or provider-error handling. Unknown programming defects should fail visibly; the sample only converts expected invalid/denied proposals into a failed run. The in-memory data and fixed mock policy make it suitable for reproducible exercises, not deployment with real resumes.
Unit tests
Save the tests as test_agent_demo.py beside agent_demo.py and run python -m unittest -v. These checks verify visible records, denial of an unregistered tool, a bounded loop and rejection of an invented result ID. Passing them establishes these specific properties of the fixture, not broad factual accuracy or production readiness. Extend them when the tool contracts change and keep model-independent tests even after adding an LLM.
import unittest
from agent_demo import run_agent
class AgentTests(unittest.TestCase):
def test_only_visible_jobs_are_returned(self):
result = run_agent("Find Python jobs", "learner")
self.assertEqual(result.status, "completed")
self.assertEqual([j["id"] for j in result.observations], ["job-1"])
def test_unknown_tool_is_not_executed(self):
result = run_agent("Find jobs", "learner", policy=lambda state:
{"tool": "apply_to_job", "arguments": {"id": "job-1"}})
self.assertEqual(result.status, "failed")
def test_loop_is_bounded(self):
result = run_agent("Find jobs", "learner", max_steps=2, policy=lambda state:
{"tool": "search_jobs", "arguments": {"query": "Python"}})
self.assertEqual(result.status, "budget_exhausted")
def test_fabricated_final_id_is_rejected(self):
result = run_agent("Find jobs", "learner", policy=lambda state:
{"final_ids": ["invented"]})
self.assertEqual(result.status, "failed")
if __name__ == "__main__":
unittest.main()
Java 21 boundary example
package academy.agent;
import java.util.List;
public interface JobSearchTool {
record VisibleJob(String id, String title) {}
// Caller identity must come from verified server authentication.
List<VisibleJob> search(String verifiedUserId, String query);
}
This interface is a Java application port: business code depends on a typed tool contract instead of a model-provider response. A Spring Boot controller should obtain the authenticated principal and call a service that validates limits and ownership. Do not accept a model-supplied user ID as proof of identity. Concrete Spring AI configuration, tool callbacks, persistence and integration tests belong to Chapter 15; adding annotation names here would not make the small contract a complete running backend.
React display example
export function AgentRunStatus({run}) {
return <section aria-label="Agent execution">
<p role="status">Status: {run.status}</p>
<ul>{run.jobs.map(job => <li key={job.id}>
{job.title} <small>Source: {job.id}</small>
</li>)}</ul>
</section>;
}
The component renders text through React and displays actual run status and source IDs. It does not claim a tool ran merely because an animation is visible. Authentication, requests, cancellation and streaming are intentionally absent from this small display example and are developed in Chapter 16. Avoid inserting generated HTML into the document without a deliberate supported-markup sanitization policy.
Illustrative API contract, not an endpoint provided by this academy
POST /agent-runs
Authorization: Bearer <application-user-token>
Content-Type: application/json
{"goal":"Find visible Python jobs; do not apply"}
200 application/json
{"runId":"demo-run","status":"completed","jobs":[{"id":"job-1","title":"Python API Developer"}]}
An implementation would validate the goal, derive identity from authentication and map failures to documented status codes. The request omits user_id because the caller cannot assign its own identity. A background implementation might instead return a run identifier with an accepted status and provide a separate authorized status endpoint. Choose and document one contract; neither pattern is automatically supplied by displaying this example.
Chapter 1 — Introduction to Agentic AI