G — Complete chapter mini-project guide | 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 G — Practical Assignment: Read-Only Job Discovery Agent
Build a bounded assistant that returns only visible synthetic job records and reports why a run stopped. The user goal is to find Python opportunities without applying. The deliverable includes a working controller, scripted decision policies, a catalogue tool, tests and a README explaining the difference between this mock and an LLM agent. Do not connect real candidate data or submission tools for this chapter. You may use the Section D implementation as a starting point, but must add and document your own boundary tests.
Requirements and technology stack
Python standard library; no provider account or external infrastructure.
Typed or documented tool inputs, one read-only search tool and a fixed tool registry.
Per-run state, a step budget and explicit completed, failed and budget_exhausted outcomes.
Verified-user propagation contract; the CLI demonstration uses a fixed caller, not production authentication.
No unknown final IDs, visibility metadata or unauthorized job records in results.
A normal fixture, invalid proposals, unknown tools, empty results and looping-policy tests.
Architecture and folder structure
agentic-chapter-1/
agent_demo.py # Controller, state and synthetic catalogue
test_agent_demo.py # Unit tests and additional boundary cases
README.md # Setup, contracts, measured local results, limitations
CLI goal -> run_agent -> mock policy -> validation -> search_jobs
^ |
+---- observations -------+
Implementation steps
Create the three files above and copy the Section D source and tests.
Run the normal fixture and record its actual output, keeping the mock label.
Add tests for blank/non-string queries, extra argument keys and unknown users.
Add a policy returning an unknown final ID and show that it fails.
Add a policy that loops and show budget_exhausted rather than completed.
Document how authenticated identity, timeouts, persistence and cancellation would be added in later chapters.
Save your code or repository/evaluation notes in the matching Practice assignment.
Sample source
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}))
Test cases and expected outcomes
["Case","Expected result"]
["learner searches Python","completed with job-1 only"]
["other searches Python","completed with job-2 only"]
["unknown user searches Python","completed with no visible jobs"]
["apply_to_job proposal","failed; no side effect"]
["invented final ID","failed"]
["invalid argument shape","failed or direct tool ValueError"]
["endless permitted search policy","budget_exhausted"]
Acceptance criteria
The program is reproducible offline, the test report contains actual local results and every returned ID is present in the current permitted observations. Denied actions are distinguishable from empty searches. No external application is sent and no credentials are included. The README must state that the goal is not interpreted by the mock and must identify missing production controls. A manual trace is acceptable for a learner without Python, but it must be labeled as analysis rather than an executed test result.
Chapter 1 — Introduction to Agentic AI