0Pricing
AI Agents · 강의

궤적 기반 자기 개선

성공하거나 실패한 작업 순서에서 학습해 이후 행동을 개선합니다.

궤적 기반 자기 개선은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

궤적이란 무엇인가요?

궤적은 특정 작업에서 에이전트가 시작부터 끝까지 거친 상태와 행동의 전체 순서입니다. 최종 답변뿐 아니라 에이전트가 그 결과에 도달한 과정도 기록합니다. 즉 어떤 도구를 어떤 순서로 어떤 매개변수와 함께 호출했는지, 어떤 중간 결과를 관찰했는지를 기록합니다.

궤적 기록

각 에이전트 행동을 기록기로 감싸 전후 상태를 포착합니다. 상태에는 현재 목표, 메모리 내용, 최근 관찰 내용이 포함됩니다. 행동에는 도구 이름, 매개변수, 결과가 포함됩니다.

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))

궤적 저장

궤적은 클 수 있습니다. 궤적마다 하나씩 압축된 데이터 파일로 저장합니다. 빠르게 검색할 수 있도록 결과와 작업 유형을 기준으로 색인화합니다. 성공한 궤적은 퓨샷 예시가 되고, 실패한 궤적은 학습 신호가 됩니다.

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]

성공한 궤적을 퓨샷 예시로 사용하기

성공한 궤적은 풀이 예시입니다. 에이전트는 새로운 유사 작업을 시도하기 전에 그 예시를 보고 단계 순서를 학습할 수 있습니다. 점수가 가장 높은 궤적을 시스템 프롬프트의 퓨샷 접두사로 삽입합니다.

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.'
    )

실패한 궤적 분석

실패 궤적도 똑같이 중요합니다. 실패 궤적을 분석해 실패 유형을 찾습니다. 즉 어떤 단계가 잘못되었고 그 이유가 무엇인지 확인합니다. 흔한 실패 유형으로는 잘못된 도구 선택, 올바른 도구를 선택했지만 잘못된 매개변수 사용, 환각으로 만들어 낸 중간 결과, 종료되지 않은 반복 과정이 있습니다.

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)

실패 유형 분류 체계

실패한 궤적에서 실패 유형의 분류 체계를 만들면 패턴을 파악하는 데 도움이 됩니다. 충분히 많은 실패가 같은 근본 원인을 공유한다면, 이는 한 번의 실행만이 아니라 에이전트의 도구나 프롬프트 또는 논리를 수정해야 한다는 신호입니다.

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}

궤적에서 학습 쌍 만들기

지도 미세 조정을 위해서는 (입력, 이상적인 출력) 쌍이 필요합니다. 실패한 궤적은 잘못된 출력을 제공하고, 실패 분석은 대신 어떤 결과가 나왔어야 했는지를 알려 줍니다. 이 둘을 합치면 학습 쌍이 됩니다.

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'])

궤적을 활용한 미세 조정

고품질 학습 쌍이 충분히 확보되면(좁은 작업의 경우 일반적으로 50~500개), 더 작은 모델을 미세 조정해 성공적인 패턴을 내재화할 수 있습니다. OpenAI의 미세 조정 인터페이스는 messages 형식의 JSONL 파일을 허용합니다.

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)

궤적 품질 필터링

성공한 궤적이 모두 똑같이 좋은 것은 아닙니다. 15번 재시도한 끝에 성공한 궤적은 한 번에 성공한 궤적보다 잡음이 많습니다. 효율성을 기준으로 필터링합니다. 즉 최소 단계로 성공했는지, 최종 점수가 높은지, 환각으로 만들어 낸 중간 결과가 없는지를 확인합니다.

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)

통찰을 위한 궤적 비교

같은 작업 유형의 성공 궤적과 실패 궤적을 비교하면 두 궤적이 정확히 어디에서 갈라졌는지 알 수 있습니다. 분기 지점은 에이전트의 의사 결정을 개선할 때 가장 큰 효과를 낼 수 있는 지점입니다.

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'])

궤적 기반 개선 선순환

전체 선순환 과정은 다음과 같습니다. 에이전트 실행 → 궤적 기록 → 결과 평가 → 궤적 라이브러리에 저장 → 실패 분석 → 학습 쌍 만들기 → 미세 조정 또는 프롬프트 업데이트 → 개선된 에이전트 실행 → 새 궤적 기록. 각 주기가 에이전트를 개선합니다.

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

이해도 확인

실패한 궤적을 분석하는 주된 목적은 무엇인가요?

복습: 궤적 기반 자기 개선

잘하셨습니다! 이 단원의 핵심 내용은 다음과 같습니다.

  • 궤적: 시작부터 끝까지의 (상태, 행동, 결과) 단계 순서
  • 성공한 궤적: 유사한 작업 전에 삽입하는 퓨샷 예시
  • 실패한 궤적: 실패 유형 분석 → 학습 쌍 만들기 → 미세 조정
  • 품질 필터링: 짧고 점수가 높은 성공 궤적을 우선
  • 선순환: 실행 → 기록 → 분석 → 미세 조정 → 개선된 에이전트 실행

다음 내용에서는 자기 개선에서 발생할 수 있는 문제인 보상 해킹, 분포 이동, 안전하지 않은 자기 수정, 그리고 안전장치를 살펴봅니다.

자주 묻는 질문

“궤적 기반 자기 개선” 강의는 무료인가요?

네 — “궤적 기반 자기 개선” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

“궤적 기반 자기 개선”에서 뭘 배우나요?

성공하거나 실패한 작업 순서에서 학습해 이후 행동을 개선합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Agents을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“궤적 기반 자기 개선” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 피드백 수집 및 저장
  2. 성찰 및 자기 비평 루프
  3. 궤적 기반 자기 개선
  4. 자기 개선이 잘못될 때
← AI Agents(으)로 돌아가기