1. AI 코딩 에이전트 구축 가이드: Gemini Flash API 활용 [0]

thumbnail 이 영상은 파이썬과 Gemini Flash API를 사용하여 AI 코딩 에이전트를 구축하는 방법을 안내한다. 에이전트 루프와 도구 호출 기능을 통해 코드를 수정하고 버그를 해결하는 실질적인 코딩 에이전트 개발을 배울 수 있다.

1.1. AI 에이전트 구축의 필요성 및 목표 [0]

  1. AI 에이전트 구축의 동기 [0]
    • AI 기술의 발전 속에서 '골드러시' 현상이 일어나고 있으며, 직접 금을 캐는 대신 '삽을 파는' 즉, AI 도구를 만드는 것이 중요하다. [6]
    • 프로그래머로서 우리가 사용하는 AI 도구의 작동 방식을 깊이 이해하여 더 효과적으로 활용하기 위함이다. [11]
    • AI 에이전트 개발은 재미있는 실습 프로젝트가 될 수 있다. [15]
  2. 학습 목표 [16]
    • LLM API, 특히 Gemini Flash API의 작동 방식을 이해한다. [16]
    • 도구 호출을 활용한 에이전트 루프 구축 경험을 쌓는다. [17]
    • 명령줄 인터페이스(CLI) 기반의 코딩 에이전트를 개발한다. [18]
    • AI 에이전트가 다른 애플리케이션을 구축하는 데 도움을 줄 수 있음을 보여준다. [22]
    • 멀티 디렉토리 파이썬 프로젝트에 대한 이해를 높인다. [90]
    • 개발자로서 AI 도구를 효과적으로 사용하는 방법을 익힌다. [95]
    • 파이썬 및 함수형 프로그래밍 기술을 향상시킨다. [100]
  3. AI 에이전트의 정의 [43]
    • AI 에이전트는 단순한 챗봇과 달리 코딩 작업을 이해하고, 프로젝트의 맥락을 파악하며, 파일 시스템을 스캔하고 코드 파일을 수정할 수 있다. [43]
    • 에이전트는 루프 내에서 작동하며, 단발성 응답이 아닌 여러 번의 반복을 통해 작업을 수행한다. [50]
    • 이러한 에이전트의 핵심은 '도구 호출(Tool Calling)'을 통한 피드백 루프 생성이다. [55]

1.2. 에이전트의 핵심 기능: 도구 호출 [56]

  1. 에이전트가 활용할 네 가지 도구 [56]
    • 디렉토리 내 파일 목록 조회 (ls 명령어와 유사) [58]
    • 파일 내용 읽기 [60]
    • 파일 내용 덮어쓰기 (수정) [63]
    • 파이썬 코드 실행 [64]
  2. 도구 호출을 통한 작업 흐름 [66]
    • "문자열이 분리되지 않는 버그를 고쳐줘"와 같은 요청을 받는다. [66]
    • 에이전트는 파일 시스템을 탐색하고, 잠재적인 문제 파일을 찾아 수정한 후, 코드를 실행하여 결과를 확인한다. [67]
    • 문제가 해결될 때까지 이 과정을 반복한다. [68]

1.3. 개발 환경 설정 및 필수 요소 [77]

  1. 필수 설치 사항 [77]
    • 파이썬 3.10 이상 버전 [78]
    • UV 프로젝트 및 패키지 관리자 (최신 파이썬 프로젝트 관리 방식) [81]
    • Unix 기반 쉘 (zsh 또는 bash) 또는 Windows Subsystem for Linux (WSL) [86]
  2. 프로젝트 구조 [448]
    • 루트 디렉토리에 calculator라는 이름의 새 디렉토리를 생성한다. [448]
    • calculator 디렉토리 안에 main.py와 test.py 파일을 복사한다. [450]
    • calculator 디렉토리 안에 pkg라는 새 디렉토리를 생성한다. [466]
    • pkg 디렉토리 안에 calculator.py와 render.py 파일을 복사한다. [468]
  3. 프로젝트 초기화 및 의존성 설치 [128]
    • uv init <프로젝트 이름> 명령어로 프로젝트를 초기화한다. [133]
    • uv venv 명령어로 가상 환경을 생성한다. [154]
    • 가상 환경을 활성화한다. [160]
    • uv add google-generativeai python-dotenv 명령어로 필요한 라이브러리를 설치한다. [163]
      • google-generativeai: Gemini API SDK [167]
      • python-dotenv: 환경 변수 관리 [168]
    • uv main.py 명령어로 프로젝트를 실행하여 "Hello from AI agent" 메시지를 확인한다. [170]

1.4. Gemini API 활용 [175]

  1. Gemini API 소개 [175]
    • Gemini는 대규모 언어 모델(LLM)로, ChatGPT, Claude 등과 유사하게 텍스트 입력을 받아 텍스트 출력을 생성한다. [176]
    • LLM의 핵심은 다음 텍스트를 예측하는 능력이며, 이를 통해 다양한 애플리케이션 구축이 가능하다. [182]
    • Gemini는 무료 티어에서도 상당한 기능을 제공하여 에이전트 개발에 적합하다. [186]
  2. 토큰(Tokens)의 개념 [188]
    • AI API는 대부분 토큰 사용량을 기반으로 작동한다. [189]
    • 토큰은 글자나 단어와 정확히 일치하지 않으며, 대략 4글자 정도의 길이를 가진다. [190]
    • 이 프로젝트에서는 Gemini의 무료 티어 한도 내에서 토큰을 사용한다. [195]
  3. API 키 발급 및 설정 [196]
    • Google AI Studio에서 계정을 생성하고 API 키를 발급받는다. [196]
    • 발급받은 API 키를 .env 파일에 API_KEY=<발급받은 키> 형식으로 저장한다. [217]
    • .env 파일은 Git에 커밋되지 않도록 .gitignore 파일에 추가한다. [219]
  4. Gemini API 호출 및 응답 확인 [225]
    • main.py 파일을 업데이트하여 Gemini 클라이언트를 초기화한다. [225]
    • client.models.generate_content() 함수를 사용하여 API 호출을 수행한다. [231]
    • 프롬프트로 "Why is bootdev such a great place?"를 사용하여 응답을 받는다. [253]
    • 응답 객체의 text 속성을 출력하여 결과를 확인한다. [257]
    • 응답과 함께 사용된 토큰 수(프롬프트 토큰, 응답 토큰)를 출력하여 API 사용량을 확인한다. [267]

1.5. 동적 프롬프트 처리 및 대화 관리 [318]

  1. 명령줄 인자(Argument)로 프롬프트 전달 [318]
    • 하드코딩된 프롬프트 대신 sys.argv를 사용하여 명령줄에서 프롬프트를 입력받는다. [319]
    • sys.argv는 스크립트 이름과 함께 전달된 인자들의 리스트이다. [325]
    • 프롬프트가 제공되지 않으면 오류 메시지를 출력하고 프로그램을 종료한다. [340]
    • 명령줄에서 "What color is the sky? Answer in one word"와 같은 프롬프트를 입력하여 테스트한다. [348]
  2. 대화 기록 관리 (메시지 리스트) [363]
    • LLM은 이전 대화의 맥락을 기억해야 하므로, 대화 기록을 관리하는 것이 중요하다. [363]
    • 각 메시지는 role (user, model, tool)과 content를 가진다. [372]
    • 대화는 user, model, user, model 순서로 메시지들의 배열(리스트)로 구성된다. [377]
    • google.genai.types.content를 사용하여 메시지 리스트를 생성하고, 초기 메시지로 사용자의 입력을 추가한다. [380]
    • 이전 대화 기록을 generate_content 함수에 전달하여 모델이 맥락을 유지하도록 한다. [389]
  3. 상세 출력 제어: Verbose 플래그 추가 [404]
    • 개발 중에는 상세한 디버깅 정보가 필요하지만, 사용자에게는 깔끔한 출력을 제공해야 한다. [404]
    • 명령줄 인자로 -d verbose 플래그를 추가하여 상세 출력 여부를 제어한다. [410]
    • verbose 플래그가 활성화되면 사용자 프롬프트, 토큰 수, 응답 토큰 수를 출력한다. [424]
    • 플래그가 없으면 모델의 응답만 출력한다. [431]

1.6. 에이전트의 도구 개발: 파일 시스템 접근 [489]

  1. 도구 #1: get_files_info (디렉토리 내용 조회) [489]
    • 기능: 지정된 디렉토리의 파일 목록과 각 파일의 크기 정보를 문자열 형태로 반환한다. [490]
    • 구현:
      • functions 디렉토리에 get_files_info.py 파일을 생성한다. [495]
      • 작업 디렉토리(working_directory)와 조회할 디렉토리 경로(directory)를 인자로 받는다. [500]
      • 조회할 디렉토리가 작업 디렉토리 외부로 벗어나는 경우 오류 문자열을 반환하여 보안을 강화한다. [508]
      • os.listdir() 함수로 디렉토리 내용을 가져오고, os.path.getsize()로 파일 크기를 얻는다. [545]
      • 결과를 "파일 이름, 크기, 디렉토리 여부" 형식의 문자열로 포맷하여 반환한다. [577]
    • 테스트: test.py 파일에서 get_files_info 함수를 호출하여 다양한 디렉토리 경로(루트, 하위 디렉토리, 외부 디렉토리)에 대한 결과를 확인한다. [688]
  2. 도구 #2: get_file_content (파일 내용 읽기) [758]
    • 기능: 지정된 파일의 내용을 문자열로 반환한다. 파일이 너무 클 경우 최대 문자 수만큼만 읽어온다. [759]
    • 구현:
      • functions 디렉토리에 get_file_content.py 파일을 생성한다. [760]
      • 작업 디렉토리(working_directory)와 파일 경로(file_path)를 인자로 받는다. [764]
      • 파일이 작업 디렉토리 외부 경로를 가리키거나 존재하지 않는 경우 오류 문자열을 반환한다. [770]
      • config.py에 정의된 MAX_CHARS (10,000자)를 기준으로 파일 내용을 읽어온다. [819]
      • 파일 내용이 MAX_CHARS에 도달하면 "파일이 10,000자로 잘렸습니다."라는 메시지를 추가한다. [839]
      • 파일 읽기 중 발생하는 예외를 처리하고 오류 메시지를 반환한다. [849]
    • 테스트: test.py 파일에서 get_file_content 함수를 호출하여 일반 파일, 큰 파일(lauram.txt), 존재하지 않는 파일, 외부 경로 파일에 대한 결과를 확인한다. [853]
  3. 도구 #3: write_file (파일 쓰기/덮어쓰기) [900]
    • 기능: 지정된 파일에 내용을 쓰거나 덮어쓴다. 필요한 경우 상위 디렉토리를 생성한다. [901]
    • 구현:
      • functions 디렉토리에 write_file.py 파일을 생성한다. [909]
      • 작업 디렉토리(working_directory), 파일 경로(file_path), 쓸 내용(content)을 인자로 받는다. [913]
      • 파일 경로가 작업 디렉토리 외부인 경우 오류 문자열을 반환한다. [930]
      • 파일이 존재하지 않으면 os.makedirs()를 사용하여 필요한 상위 디렉토리를 생성한다. [950]
      • 파일을 쓰기 모드('w')로 열어 내용을 작성하고, 성공 시 "파일 경로에 성공적으로 작성됨, X자 작성됨" 메시지를 반환한다. [985]
      • 파일 쓰기 중 발생하는 예외를 처리하고 오류 메시지를 반환한다. [992]
    • 테스트: test.py 파일에서 write_file 함수를 호출하여 기존 파일 덮어쓰기, 새 파일 생성, 새 디렉토리 생성 후 파일 쓰기 등의 시나리오를 테스트한다. [1007]
  4. 도구 #4: run_python_file (파이썬 코드 실행) [1089]
    • 기능: 지정된 파이썬 파일을 실행하고, 표준 출력(stdout)과 표준 에러(stderr)를 캡처하여 반환한다. 최대 30초의 타임아웃을 설정한다. [1090]
    • 보안: 이 기능은 임의의 파이썬 코드를 실행하므로 보안에 유의해야 한다. 프로젝트는 교육용이며, 배포 시에는 주의가 필요하다. [1095]
    • 구현:
      • functions 디렉토리에 run_python_file.py 파일을 생성한다. [1120]
      • 작업 디렉토리(working_directory), 실행할 파일 경로(file_path), 선택적 CLI 인자(args)를 인자로 받는다. [1124]
      • 파일 경로가 작업 디렉토리 외부이거나, 파일이 .py 확장자로 끝나지 않거나, 파일이 존재하지 않는 경우 오류 문자열을 반환한다. [1127]
      • subprocess.run() 함수를 사용하여 파이썬 파일을 실행한다. [1161]
      • timeout=30으로 30초의 타임아웃을 설정한다. [1175]
      • capture_output=True로 stdout과 stderr를 캡처한다. [1187]
      • 실행 결과(stdout, stderr, 반환 코드)를 포맷하여 반환한다. [1200]
      • 실행 중 예외 발생 시 오류 메시지를 반환한다. [1261]
    • 테스트: test.py 파일에서 run_python_file 함수를 호출하여 정상적인 파이썬 파일 실행, 인자 전달, 외부 경로 실행, 존재하지 않는 파일 실행 등의 시나리오를 테스트한다. [1266]

1.7. 시스템 프롬프트와 함수 선언 [1315]

  1. 시스템 프롬프트의 역할 [1315]
    • 시스템 프롬프트는 LLM의 행동을 제어하는 데 사용되며, 일반 사용자 프롬프트보다 더 큰 가중치를 가진다. [1316]
    • AI의 성격 설정, 행동 지침 제공, 대화 맥락 설정, 규칙 정의 등에 활용된다. [1330]
    • 부트캠프의 챗봇 '부츠(Boots)'는 시스템 프롬프트를 통해 학생들에게 직접적인 답 대신 질문을 유도하는 방식으로 작동한다. [1325]
  2. 함수 선언 (Function Declaration) [1360]
    • LLM이 사용할 수 있는 함수 목록과 각 함수의 사용법을 텍스트 형태로 제공한다. [1360]
    • Gemini SDK의 types.FunctionDeclaration을 사용하여 함수 스키마를 정의한다. [1380]
      • 함수 이름, 설명, 매개변수(이름, 타입, 설명)를 포함한다. [1401]
      • run_python_file 함수의 경우, args 매개변수를 문자열 배열로 정의하기 위해 items 키를 사용한다. [1519]
    • 정의된 함수 스키마들을 types.Tool 객체로 묶어 generate_content 함수의 tools 매개변수에 전달한다. [1408]
    • 시스템 프롬프트에 사용 가능한 함수들의 기능과 사용법을 명확하게 명시한다. [1421]
      • "파일 목록 조회", "파일 내용 읽기", "파일 쓰기", "파이썬 파일 실행" 등의 기능을 설명한다. [1425]
  3. 함수 호출 로직 구현 [1625]
    • LLM이 함수 호출을 결정하면, response.candidates의 function_calls 속성을 통해 함수 이름과 인자를 얻는다. [1625]
    • call_function 함수는 LLM이 결정한 함수 호출 정보를 받아 실제 함수를 실행한다. [1630]
    • call_function 함수는 함수 이름에 따라 해당 함수(get_files_info, get_file_content, write_file, run_python_file)를 호출하고, 인자를 전달한다. [1644]
    • 함수 실행 결과를 types.Content 객체로 변환하여 반환한다. [1660]
    • main.py에서 generate_content 호출 후, 응답에 function_calls가 있는지 확인하고, 있다면 call_function을 호출하여 결과를 처리한다. [1701]
    • verbose 플래그를 사용하여 함수 호출 과정과 결과를 상세하게 확인할 수 있다. [1739]

1.8. 에이전트 루프 구축: 자율적인 문제 해결 [1973]

  1. 에이전트 루프의 작동 방식 [1973]
    • 에이전트는 도구 호출과 루프를 결합하여 사용자 요청을 만족시킬 때까지 작업을 반복한다. [1973]
    • 대화는 user, model, tool 역할의 메시지들로 구성된다. [1979]
      • user: 사용자의 초기 요청. [1984]
      • model: LLM이 다음에 수행할 작업(함수 호출 또는 최종 텍스트 응답)을 결정한다. [1985]
      • tool: 에이전트가 실행한 함수의 결과. 이 결과는 다음 model 결정에 사용된다. [1987]
    • 루프는 최대 20번 반복되며, 각 반복마다 LLM은 다음 단계를 결정하고, 함수가 호출되면 그 결과를 다시 LLM에게 전달한다. [1990]
  2. 에이전트 루프 구현 [1999]
    • main.py에서 generate_content 호출을 루프 안에 넣는다. [2001]
    • 루프는 최대 20번 반복되도록 설정한다 (max_iterations = 20). [2004]
    • 각 반복마다 LLM의 응답을 확인하여, 텍스트 응답이면 루프를 종료하고 결과를 반환한다. [2010]
    • LLM이 함수 호출을 결정하면, 해당 함수를 실행하고 그 결과를 tool 역할의 메시지로 messages 리스트에 추가한다. [2016]
  3. 버그 수정 시나리오 데모 [2023]
    • 계산기 앱의 calculator.py 파일에서 연산자 우선순위 버그를 의도적으로 발생시킨다. [2030]
    • 에이전트에게 버그 수정을 요청한다 ("Hey, my calculator is broken. 3 + 7 * 2 shouldn't be 20"). [2045]
    • 에이전트는 파일 시스템을 탐색하고, 코드를 분석하며, 버그를 수정하는 함수(write_file, run_python_file)를 호출한다. [2050]
    • 수정된 코드를 실행하여 버그가 해결되었음을 확인하고 최종 응답을 생성한다. [2057]