Kurs Python · Moduł 12: Projekt końcowy

Planowanie Projektu AI

6 min czytania
W tej lekcji3

Witaj w finale Python Safari! Wyobraź sobie ekspedycję, która rusza w sawannę bez mapy, bez listy zapasów i bez celu. Po trzech dniach ktoś pyta "po co właściwie tu jesteśmy?", a nikt nie umie odpowiedzieć. Z projektami programistycznymi jest dokładnie tak samo: najczęściej nie upadają przez zły kod, tylko przez to, że nikt nie zapisał, jaki problem rozwiązują i kiedy można uznać je za skończone. W tym module połączysz wszystkie zdobyte umiejętności w jeden kompleksowy projekt, a zaczniemy od mapy ekspedycji.

Czego się nauczysz

  • zapisywać problem i miary sukcesu projektu w kodzie Pythona, zamiast trzymać je w głowie
  • formułować user stories z kryteriami akceptacji
  • szacować zadania w story points i układać je w sprinty
  • uzasadniać wybór stosu technologicznego

Metodologia planowania projektu

1. Definiowanie problemu

Zanim napiszemy pierwszą linię aplikacji, nazwijmy rodzaj projektu. Do zamkniętej listy wariantów Python ma typ Enum z modułu enum: każdy członek wyliczenia ma nazwę i wartość, a literówka w nazwie od razu kończy się błędem, zamiast cicho przejść dalej.

1from dataclasses import dataclass
2from enum import Enum
3
4class ProjectType(Enum):
5    WEB_APP = "web_application"
6    API = "api_service"
7    ML_PIPELINE = "ml_pipeline"
8    RAG_SYSTEM = "rag_system"
9    AUTOMATION = "automation"

Na razie to tylko słownik rodzajów wypraw, nic się jeszcze nie dzieje. Teraz opiszmy samą wyprawę. Dekorator @dataclass z modułu dataclasses sam generuje __init__, __repr__ i porównywanie na podstawie pól z adnotacjami typów, więc klasa z danymi mieści się w kilku linijkach.

1@dataclass
2class ProjectDefinition:
3    """Definicja projektu."""
4    name: str
5    problem_statement: str
6    target_users: list[str]
7    success_metrics: list[str]
8    project_type: ProjectType
9    tech_stack: list[str]

Adnotacje typu list[str] to dokumentacja i podpowiedź dla edytora oraz narzędzi takich jak mypy. Python ich nie wymusza w czasie działania, więc dataclass nie odrzuci złego typu. Zobaczmy wypełnioną definicję projektu:

1# Przykład
2capstone_project = ProjectDefinition(
3    name="AI Document Assistant",
4    problem_statement="Użytkownicy tracą czas na szukanie informacji w dokumentach",
5    target_users=["analitycy", "prawnicy", "badacze"],
6    success_metrics=[
7        "Redukcja czasu wyszukiwania o 70%",
8        "Dokładność odpowiedzi > 90%",
9        "Czas odpowiedzi < 2s"
10    ],
11    project_type=ProjectType.RAG_SYSTEM,
12    tech_stack=["Python", "FastAPI", "LlamaIndex", "Qdrant", "React"]
13)

Najważniejsze są tu metryki sukcesu. "Czas odpowiedzi < 2s" da się zmierzyć, a "szybka aplikacja" już nie. To są cele, które sami sobie stawiamy, a nie gotowe dane: przy każdej metryce warto od razu zapisać, jak ją zmierzysz.

2. User Stories

User story to opis funkcji z perspektywy użytkownika, w stałym formacie "jako [kto] chcę [co], aby [po co]". Kryteria akceptacji mówią, kiedy historia jest skończona. W kodzie zapiszemy to kolejną dataclass:

1@dataclass
2class UserStory:
3    """User story w formacie Agile."""
4    as_a: str
5    i_want: str
6    so_that: str
7    acceptance_criteria: list[str]
8    priority: int  # 1-5, 1 = najwyższy

Pole priority to zwykła liczba, a komentarz ustala umowę, że 1 oznacza najwyższy priorytet. Teraz dwie konkretne historie, dla analityka i dla administratora:

1stories = [
2    UserStory(
3        as_a="analityk",
4        i_want="zadać pytanie o dokumenty w języku naturalnym",
5        so_that="szybko znajdę potrzebne informacje",
6        acceptance_criteria=[
7            "System rozumie pytania po polsku",
8            "Odpowiedź zawiera źródła",
9            "Czas odpowiedzi < 3s"
10        ],
11        priority=1
12    ),
13    UserStory(
14        as_a="administrator",
15        i_want="łatwo dodawać nowe dokumenty",
16        so_that="baza wiedzy była aktualna",
17        acceptance_criteria=[
18            "Upload przez drag & drop",
19            "Obsługa PDF, DOCX, TXT",
20            "Automatyczna indeksacja"
21        ],
22        priority=2
23    )
24]

Zwróć uwagę, że historie nie mówią nic o technologii. Analityk nie wie, czym jest wektorowa baza danych, i nie musi. Kryteria typu "Odpowiedź zawiera źródła" staną się później testami, więc pisz je tak, by dało się je sprawdzić.

3. Estymacja i planowanie sprintów

W Scrumie pracę dzieli się na sprinty, czyli odcinki o stałej długości, najwyżej miesiąc. W praktyce zespoły najczęściej wybierają dwa tygodnie. Zadania szacuje się w story points, czyli względnej złożoności, a nie w godzinach. Popularna skala to ciąg Fibonacciego (1, 2, 3, 5, 8, 13), bo rosnące przerwy między liczbami przypominają, że duże zadania są coraz mniej przewidywalne.

1from datetime import datetime, timedelta
2
3@dataclass
4class Task:
5    name: str
6    story_points: int  # Fibonacci: 1, 2, 3, 5, 8, 13
7    dependencies: list[str]
8    assigned_to: str = ""

Task pamięta też zależności, czyli nazwy zadań, które muszą być gotowe wcześniej. Sprint dostaje dwie właściwości liczone w locie dzięki @property: datę końca i sumę punktów.

1@dataclass
2class Sprint:
3    number: int
4    start_date: datetime
5    tasks: list[Task]
6    velocity: int = 20  # Story points per sprint
7
8    @property
9    def end_date(self) -> datetime:
10        return self.start_date + timedelta(days=14)
11
12    @property
13    def total_points(self) -> int:
14        return sum(t.story_points for t in self.tasks)

velocity to liczba punktów, którą zespół realnie domyka w jednym sprincie. Zakładamy 20, ale prawdziwą wartość poznasz dopiero po kilku sprintach. Zobaczmy plan dwóch pierwszych etapów wyprawy:

1# Plan projektu
2sprints = [
3    Sprint(1, datetime(2024, 1, 1), [
4        Task("Setup projektu", 2, []),
5        Task("Konfiguracja CI/CD", 3, ["Setup projektu"]),
6        Task("Podstawowe API", 5, ["Setup projektu"]),
7        Task("Integracja Vector DB", 5, ["Podstawowe API"]),
8    ]),
9    Sprint(2, datetime(2024, 1, 15), [
10        Task("RAG Pipeline", 8, ["Integracja Vector DB"]),
11        Task("Chat interface", 5, ["Podstawowe API"]),
12        Task("Document upload", 5, ["RAG Pipeline"]),
13    ])
14]

Kolejność wynika z zależności: najpierw setup, potem API, dopiero potem RAG. Pierwszy sprint ma 15 punktów, drugi 18, więc oba mieszczą się w założonym velocity i zostaje zapas na niespodzianki.

Wybór stosu technologicznego

Ostatni element mapy to decyzje technologiczne. Zapisz przy każdym wyborze powody, bo za pół roku ktoś (może Ty) zapyta, dlaczego akurat Qdrant.

1tech_decisions = {
2    "backend": {
3        "choice": "FastAPI",
4        "reasons": ["Async", "Type hints", "Auto docs", "Performance"]
5    },
6    "vector_db": {
7        "choice": "Qdrant",
8        "reasons": ["Self-hosted", "Filtering", "Rust performance"]
9    },
10    "llm": {
11        "choice": "OpenAI API",
12        "reasons": ["Quality", "Function calling", "Reliable"]
13    },
14    "rag_framework": {
15        "choice": "LlamaIndex",
16        "reasons": ["Flexibility", "Integrations", "Community"]
17    }
18}

Przy modelu językowym celowo zapisaliśmy dostawcę, a nie konkretny model. Nazwy modeli zmieniają się co kilka miesięcy, a starsze wersje są wycofywane, więc aktualny model wybierz z listy w dokumentacji dostawcy w dniu startu projektu. Moja rada: trzymaj tę listę decyzji w repozytorium obok kodu, a nie w notatkach, bo wtedy żyje razem z projektem.

Cały cykl życia projektu to kolejno planowanie, implementacja, testowanie, deployment i monitoring. Instalację zależności z pliku requirements.txt robi się poleceniem pip install -r requirements.txt, a konkretną wersję pakietu przypinasz zapisem nazwa==wersja, na przykład pip install numpy==1.26.4. W następnej lekcji zaprojektujemy architekturę systemu, która zamieni ten plan w strukturę kodu.

Pamiętaj: dobry plan ekspedycji to połowa sukcesu, bo mapa narysowana przed wyjazdem oszczędza tygodni błądzenia po sawannie.

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Co to jest User Story w metodologii Agile?

  2. 2. Ile trwa typowy Sprint w metodyce Scrum?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Układanie w pionie

    Uporządkuj kroki:

  • Edytor kodu

    Zaimplementuj ProjectDefinition dataclass

  • Układanie w poziomie

    Ułóż elementy:

  • Klikanie w kolejności

    Kliknij w kolejności:

Przydatne artykuły