0Pricing
AI Agents · Lekcja

Samodoskonalenie na podstawie trajektorii

Uczenie się na podstawie udanych i nieudanych sekwencji działań w celu doskonalenia przyszłego zachowania.

Samodoskonalenie na podstawie trajektorii to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.

Czym jest trajektoria

Trajektoria to pełna sekwencja stanów i działań, które agent wykonał od początku do końca realizacji danego zadania. Zapisuje nie tylko końcową odpowiedź, ale także jak agent do niej doszedł: jakie narzędzia wywołano, w jakiej kolejności, z jakimi parametrami i jakie wyniki pośrednie zaobserwowano.

Rejestrowanie trajektorii

Każde działanie agenta należy opakować rejestratorem, który przechwytuje stan przed działaniem i po nim. Stan obejmuje: bieżący cel, zawartość pamięci i ostatnie obserwacje. Działanie obejmuje: nazwę narzędzia, parametry i wynik.

from dataclasses import dataclass, field
from datetime import datetime
from typing import Any

@dataclass
class TrajectoryStep:
    step_index: int
    state_summary: str    # short description of world state
    action_name: str      # tool or reasoning step name
    action_params: dict
    result: Any
    timestamp: str = ''

    def __post_init__(self):
        if not self.timestamp:
            self.timestamp = datetime.utcnow().isoformat()

@dataclass
class Trajectory:
    trajectory_id: str
    task: str
    steps: list = field(default_factory=list)
    outcome: str = 'unknown'   # 'success', 'failure', 'partial'
    final_score: float = 0.0

    def add_step(self, step: TrajectoryStep):
        self.steps.append(step)

    def mark_success(self, score: float = 1.0):
        self.outcome = 'success'
        self.final_score = score

    def mark_failure(self, reason: str = ''):
        self.outcome = 'failure'
        self.final_score = 0.0

if __name__ == '__main__':
    traj = Trajectory(trajectory_id='t-1', task='Book a flight to Tokyo')
    traj.add_step(TrajectoryStep(
        step_index=0, state_summary='searching flights',
        action_name='search_flights', action_params={'dest': 'NRT'}, result='5 options found'
    ))
    traj.mark_success(score=0.95)
    print(f'Trajectory {traj.trajectory_id}: outcome={traj.outcome}, score={traj.final_score}')
    print('Steps recorded:', len(traj.steps))

Przechowywanie trajektorii

Trajektorie mogą być duże. Należy przechowywać je jako skompresowane pliki JSON, po jednym na trajektorię. Aby szybko je pobierać, należy je indeksować według wyniku i typu zadania. Udane trajektorie stają się przykładami few-shot, a nieudane — sygnałami treningowymi.

import json
import os
from dataclasses import asdict

TRAJECTORY_DIR = 'trajectories'

def save_trajectory(traj: Trajectory):
    os.makedirs(TRAJECTORY_DIR, exist_ok=True)
    filename = f'{TRAJECTORY_DIR}/{traj.trajectory_id}_{traj.outcome}.json'
    data = asdict(traj)
    with open(filename, 'w') as f:
        json.dump(data, f, indent=2)
    print(f'Saved trajectory: {filename}')

def load_successful_trajectories(task_type: str, n: int = 5) -> list:
    results = []
    for fname in os.listdir(TRAJECTORY_DIR):
        if '_success.json' not in fname:
            continue
        with open(os.path.join(TRAJECTORY_DIR, fname)) as f:
            traj = json.load(f)
        if task_type.lower() in traj['task'].lower():
            results.append(traj)
    results.sort(key=lambda t: t['final_score'], reverse=True)
    return results[:n]

Wykorzystywanie udanych trajektorii jako przykładów few-shot

Udana trajektoria jest przykładem rozwiązania: agent może nauczyć się sekwencji kroków, obserwując ją przed podjęciem próby wykonania nowego, podobnego zadania. Należy wstawić trajektorię z najwyższą oceną jako prefiks few-shot w prompt systemowy.

def trajectory_to_few_shot(traj: dict) -> str:
    lines = [f'Example task: {traj["task"]}', 'Steps taken:']
    for step in traj['steps']:
        lines.append(
            f'  [{step["step_index"]}] {step["action_name"]}'
            f'({json.dumps(step["action_params"])}) -> {str(step["result"])[:80]}'
        )
    lines.append(f'Outcome: {traj["outcome"]} (score={traj["final_score"]:.2f})')
    return '\n'.join(lines)

def build_few_shot_system_prompt(task_type: str) -> str:
    successful = load_successful_trajectories(task_type, n=2)
    if not successful:
        return 'Complete the following task step by step.'
    examples = '\n\n---\n\n'.join(
        trajectory_to_few_shot(t) for t in successful
    )
    return (
        'Here are examples of successfully completed similar tasks:\n\n'
        + examples
        + '\n\n---\n\nNow complete the new task using the same approach.'
    )

Analizowanie nieudanych trajektorii

Nieudane trajektorie są równie cenne. Należy je analizować, aby znaleźć tryb błędu: który krok się nie powiódł i dlaczego. Typowe tryby błędów to: wybranie niewłaściwego narzędzia, wybranie właściwego narzędzia, ale podanie niewłaściwych parametrów, zhalucynowanie wyniku pośredniego oraz niezakończenie pętli.

def analyze_failure(traj: dict, client) -> dict:
    steps_str = json.dumps(traj['steps'], indent=2)
    prompt = (
        f'Task: {traj["task"]}\n\n'
        f'Agent trajectory (failed):\n{steps_str}\n\n'
        'Identify the failure mode. Return JSON:\n'
        '{"failure_step": 0, "failure_mode": "", "root_cause": "", '
        '"prevention": ""}'
    )
    import anthropic
    client_obj = anthropic.Anthropic(api_key='YOUR_API_KEY')
    result = client_obj.messages.create(
        model='claude-opus-4-5',
        max_tokens=512,
        messages=[{'role': 'user', 'content': prompt}]
    )
    import json
    return json.loads(result.content[0].text)

Taksonomia trybów błędów

Utworzenie taksonomii trybów błędów na podstawie nieudanych trajektorii pomaga dostrzec wzorce. Gdy wystarczająco wiele błędów ma tę samą przyczynę źródłową, jest to sygnał, aby naprawić narzędzie, prompt lub logikę agenta, a nie tylko pojedyncze wykonanie.

from collections import Counter

def build_failure_taxonomy(failed_trajectories: list, client) -> dict:
    failure_modes = []
    for traj in failed_trajectories:
        analysis = analyze_failure(traj, client)
        failure_modes.append(analysis['failure_mode'])

    counts = Counter(failure_modes)
    total = len(failure_modes)

    taxonomy = [
        {
            'failure_mode': mode,
            'count': count,
            'percentage': round(count / total * 100, 1)
        }
        for mode, count in counts.most_common()
    ]

    print('Failure Mode Taxonomy:')
    for entry in taxonomy:
        print(f'  {entry["failure_mode"]}: {entry["count"]} ({entry["percentage"]}%)')

    return {'taxonomy': taxonomy, 'total_failures': total}

Tworzenie par treningowych z trajektorii

W przypadku nadzorowanego dostrajania potrzebne są pary (input, ideal_output). Nieudana trajektoria dostarcza nieprawidłowego wyniku, a analiza błędu określa, co powinno wydarzyć się zamiast tego. Razem tworzą parę treningową.

def trajectory_to_training_pair(
    failed_traj: dict,
    failure_analysis: dict
) -> dict:
    """
    Creates an SFT-ready training pair:
    input = task + context at failure step
    output = what the agent should have done
    """
    fail_step_idx = failure_analysis['failure_step']
    steps = failed_traj['steps']

    # Context up to (but not including) the failure step
    context_steps = steps[:fail_step_idx]
    context_str = '\n'.join(
        f'Step {s["step_index"]}: {s["action_name"]}({s["action_params"]})'
        for s in context_steps
    )
    return {
        'input': f'Task: {failed_traj["task"]}\n\nPrevious steps:\n{context_str}\n\nNext action:',
        'output': failure_analysis['prevention'],  # correct action
        'source': 'failure_trajectory',
        'trajectory_id': failed_traj.get('trajectory_id', 'unknown')
    }

if __name__ == '__main__':
    failed_traj = {
        'task': 'Cancel subscription',
        'trajectory_id': 'traj-7',
        'steps': [
            {'step_index': 0, 'action_name': 'find_account', 'action_params': {'user': 'u1'}},
            {'step_index': 1, 'action_name': 'delete_account', 'action_params': {'user': 'u1'}},
        ]
    }
    failure_analysis = {'failure_step': 1, 'prevention': 'call cancel_subscription(user="u1") instead'}
    pair = trajectory_to_training_pair(failed_traj, failure_analysis)
    print('Training input:')
    print(pair['input'])
    print('Expected output:', pair['output'])

Dostrajanie na podstawie trajektorii

Gdy mają już Państwo wystarczająco dużo wysokiej jakości par treningowych (zwykle 50–500 dla wąskiego zadania), mogą Państwo dostroić mniejszy model, aby przyswoił udane wzorce. API do fine-tuningu OpenAI przyjmuje pliki JSONL w formacie messages.

import json

def export_fine_tuning_jsonl(
    training_pairs: list,
    output_file: str,
    system_prompt: str = 'You are an efficient AI agent.'
):
    with open(output_file, 'w') as f:
        for pair in training_pairs:
            record = {
                'messages': [
                    {'role': 'system', 'content': system_prompt},
                    {'role': 'user', 'content': pair['input']},
                    {'role': 'assistant', 'content': pair['output']}
                ]
            }
            f.write(json.dumps(record) + '\n')
    print(f'Exported {len(training_pairs)} training pairs to {output_file}')

# Upload via OpenAI API (pseudocode):
# client.files.create(file=open('train.jsonl','rb'), purpose='fine-tune')
# client.fine_tuning.jobs.create(training_file='file-id', model='gpt-4o-mini')

if __name__ == '__main__':
    import tempfile, os
    pairs = [{'input': 'Task: Cancel subscription\n\nNext action:', 'output': 'cancel_subscription(user="u1")'}]
    out_path = os.path.join(tempfile.gettempdir(), 'demo_training.jsonl')
    export_fine_tuning_jsonl(pairs, out_path)

Filtrowanie jakości trajektorii

Nie wszystkie udane trajektorie są równie dobre. Trajektoria, która zakończyła się sukcesem po 15 ponowieniach, zawiera więcej szumu niż ta, która zakończyła się sukcesem za pierwszym razem. Należy filtrować je pod kątem efektywności: sukces przy minimalnej liczbie kroków, wysoka ocena końcowa i brak zhalucynowanych wyników pośrednich.

def filter_high_quality_trajectories(
    trajectories: list,
    max_steps: int = 8,
    min_score: float = 0.85
) -> list:
    high_quality = []
    for traj in trajectories:
        if traj['outcome'] != 'success':
            continue
        if traj['final_score'] < min_score:
            continue
        if len(traj['steps']) > max_steps:
            continue
        high_quality.append(traj)

    # Sort by (score DESC, steps ASC)
    high_quality.sort(
        key=lambda t: (-t['final_score'], len(t['steps']))
    )
    print(f'High-quality trajectories: {len(high_quality)} / {len(trajectories)}')
    return high_quality

if __name__ == '__main__':
    trajectories = [
        {'outcome': 'success', 'final_score': 0.92, 'steps': [1, 2, 3]},
        {'outcome': 'failure', 'final_score': 0.10, 'steps': [1]},
        {'outcome': 'success', 'final_score': 0.60, 'steps': [1, 2]},
    ]
    filter_high_quality_trajectories(trajectories)

Porównywanie trajektorii w celu uzyskania wniosków

Porównanie udanej i nieudanej trajektorii dla tego samego typu zadania dokładnie pokazuje, w którym miejscu się rozeszły. Punkt rozbieżności to miejsce, w którym najskuteczniej można poprawić podejmowanie decyzji przez agenta.

def compare_trajectories(success_traj: dict, failure_traj: dict) -> dict:
    s_steps = {s['step_index']: s for s in success_traj['steps']}
    f_steps = {s['step_index']: s for s in failure_traj['steps']}

    divergences = []
    for idx in sorted(set(s_steps) & set(f_steps)):
        s_action = s_steps[idx]['action_name']
        f_action = f_steps[idx]['action_name']
        if s_action != f_action:
            divergences.append({
                'step': idx,
                'success_action': s_action,
                'failure_action': f_action
            })
            break  # First divergence is most important

    return {
        'first_divergence': divergences[0] if divergences else None,
        'success_steps': len(s_steps),
        'failure_steps': len(f_steps)
    }

# Usage:
# comparison = compare_trajectories(good_traj, bad_traj)
# print('First divergence:', comparison['first_divergence'])

if __name__ == '__main__':
    good_traj = {'steps': [{'step_index': 0, 'action_name': 'search'}, {'step_index': 1, 'action_name': 'summarize'}]}
    bad_traj = {'steps': [{'step_index': 0, 'action_name': 'search'}, {'step_index': 1, 'action_name': 'delete'}]}
    comparison = compare_trajectories(good_traj, bad_traj)
    print('First divergence:', comparison['first_divergence'])

Koło zamachowe doskonalenia na podstawie trajektorii

Pełne koło zamachowe wygląda następująco: uruchomienie agenta → rejestracja trajektorii → ocena wyniku → zapisanie w bibliotece trajektorii → analiza błędów → utworzenie par treningowych → dostrojenie lub aktualizacja promptów → uruchomienie udoskonalonego agenta → rejestracja nowej trajektorii. Każdy cykl udoskonala agenta.

class TrajectorySelfImprovement:
    def __init__(self, agent_id: str):
        self.agent_id = agent_id
        self.trajectory_lib = []

    def run_and_record(self, task: str, agent_fn) -> Trajectory:
        traj = Trajectory(
            trajectory_id=f'{self.agent_id}_{len(self.trajectory_lib)}',
            task=task
        )
        result = agent_fn(task, traj)  # agent_fn appends steps to traj
        # Evaluate result (e.g., via reflection or user rating)
        score = evaluate_result(result)
        if score >= 0.7:
            traj.mark_success(score)
        else:
            traj.mark_failure('Low quality score')
        save_trajectory(traj)
        self.trajectory_lib.append(traj)
        return traj

    def improvement_cycle(self, client):
        failed = [t for t in self.trajectory_lib if t.outcome == 'failure']
        if len(failed) >= 10:
            taxonomy = build_failure_taxonomy([vars(t) for t in failed], client)
            print('Improvement cycle complete:', taxonomy)

def evaluate_result(result: str) -> float:
    return 0.8  # placeholder

Sprawdzenie wiedzy

Jaki jest główny cel analizowania nieudanych trajektorii?

Podsumowanie: samodoskonalenie na podstawie trajektorii

Doskonale! Najważniejsze wnioski z tej lekcji:

  • Trajektoria: sekwencja kroków (stan, działanie, wynik) od początku do końca
  • Udane trajektorie: przykłady few-shot wstawiane przed podobnymi zadaniami
  • Nieudane trajektorie: analizowane pod kątem trybów błędów → pary treningowe → dostrajanie
  • Filtrowanie jakości: preferowanie krótkich, wysoko ocenionych trajektorii zakończonych sukcesem
  • Koło zamachowe: uruchom → zarejestruj → przeanalizuj → dostrój → uruchom udoskonalonego agenta

Następnie: co może pójść nie tak podczas samodoskonalenia — manipulowanie funkcją nagrody, przesunięcie rozkładu i mechanizmy ochronne.

Często zadawane pytania

Czy lekcja „Samodoskonalenie na podstawie trajektorii” jest bezpłatna?

Tak — pełny tekst „Samodoskonalenie na podstawie trajektorii” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.

Co nauczysz się w „Samodoskonalenie na podstawie trajektorii”?

Uczenie się na podstawie udanych i nieudanych sekwencji działań w celu doskonalenia przyszłego zachowania. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Agents?

Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Samodoskonalenie na podstawie trajektorii”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?

Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Gromadzenie i przechowywanie informacji zwrotnych
  2. Pętle refleksji i samokrytyki
  3. Samodoskonalenie na podstawie trajektorii
  4. Gdy samodoskonalenie idzie źle
← Powrót do AI Agents