[AgentShield] 진행 상황 및 개인 공부 정리 (7): LangGraph Judge 안정화, Ollama 메모리 이슈, Docker 실행과 RDS 연결

2026. 5. 4. 23:21·4. [팀] 프로젝트 및 공모전/4-5 AgentShield(보안 플랫폼)

이번 글은 4월 27일 기준으로 AgentShield에서 확인한 이슈와 수정 내용을 정리한 개인 기록입니다.

이번 단계의 핵심은 LangGraph 기반 Judge 구조를 실제로 돌리면서 생긴 메모리 문제, 긴 변형 공격 프롬프트 처리 문제, Docker 기반 실행 흐름, 그리고 RDS/ChromaDB 공유 환경 준비입니다.

특히 Ollama 모델을 여러 개 동시에 띄웠을 때 스왑을 사용하는 문제가 있었고, LangGraph 멀티 에이전트 구조에서 여러 Judge 노드가 병렬로 Ollama를 호출하면서 OOM 위험이 생겼습니다. 그래서 프롬프트 길이 제한, Ollama 동시 호출 제한, 빈 응답 처리, fallback 로직이 중요해졌습니다.


1. 4월 27일에 확인한 주요 이슈

먼저 확인해야 할 문제는 긴 변형 공격 프롬프트입니다.

약 len 10000 길이의 변형 공격 프롬프트가 제대로 저장되고 실제로 동작하는지 확인해야 합니다.

여기서 중요한 점은 로그에만 잘못 출력되는 것인지, 아니면 실제 생성 결과 자체가 잘못되는 것인지 구분해야 한다는 것입니다.

로그 출력이 잘려 보이는 것과 실제 프롬프트가 잘려서 저장되는 것은 완전히 다른 문제입니다.

확인해야 할 지점은 다음과 같습니다.

Red Agent가 생성한 원문 길이
저장 직전 payload 길이
DB 또는 JSON 결과 파일에 저장된 길이
타겟 챗봇으로 실제 전송된 길이
Judge가 받은 target_response와 attack_prompt 길이

두 번째 문제는 Ollama 모델 3개를 동시에 가동했을 때 스왑을 사용한다는 점입니다.

Red, Guard, Judge 또는 Blue까지 동시에 돌면 메모리 사용량이 커질 수 있습니다. 특히 4B 모델이라도 여러 개가 동시에 올라가고 LangGraph 노드가 병렬 호출되면 메모리 압박이 발생할 수 있습니다.

이 경우 모델을 줄이거나, 동시 호출 수를 제한하거나, 컨텍스트 크기를 낮추는 방식이 필요합니다.


2. LangGraph Judge Nodes 수정 방향

LangGraph Judge Nodes는 멀티 에이전트 판정 노드 구조입니다.

기존 단순 Judge 구조를 여러 노드로 나누면서 순환 의존 문제가 생길 수 있었기 때문에, judge_utils.py에서 유틸리티 함수를 글로벌 임포트하는 방식으로 정리했습니다.

또한 병렬 실행 시 각 노드는 전체 상태를 다시 반환하는 것이 아니라, 변경된 필드만 반환하도록 구성해야 합니다.

이렇게 해야 LangGraph 상태 병합이 불필요하게 복잡해지지 않고, 각 노드의 책임이 명확해집니다.

수정 방향은 다음과 같습니다.

judge_utils.py에서 규칙 기반 유틸리티 분리
judge_nodes.py에서 LangGraph 노드 구현
각 노드는 변경된 필드만 반환
Ollama 호출은 안정성을 고려해 제한
예외 발생 시 항상 유효한 판정 결과 반환

3. Ollama 호출 안정화

call_ollama_judge 쪽에서는 메모리 안정성을 위해 컨텍스트와 timeout을 조정했습니다.

초기 수정에서는 다음 값이 사용되었습니다.

num_ctx: 6144
timeout: 90초

이후 4B 모델 안정성을 기준으로 더 보수적인 값도 정리했습니다.

SAFE_CAP_CHARS = 12000
NUM_CTX = 4096
OLLAMA_TIMEOUT = 90
MAX_CONCURRENT_OLLAMA_CALLS = 2

SAFE_CAP_CHARS는 최대 보존 문자 수입니다. 약 3,500 토큰 정도를 목표로 잡고, 4B 모델이 안정적으로 처리할 수 있는 범위 안에 프롬프트를 넣기 위한 값입니다.

NUM_CTX는 컨텍스트 윈도우 크기입니다. 너무 크게 잡으면 메모리 사용량이 늘어나고, 여러 노드가 동시에 호출될 때 OOM 가능성이 커집니다.

MAX_CONCURRENT_OLLAMA_CALLS는 동시에 Ollama를 호출할 수 있는 최대 개수입니다. 기존에는 5개까지 병렬 호출될 수 있었지만, 이 경우 post 5개가 동시에 Ollama로 들어가면서 OOM 문제가 생겼습니다.

그래서 1~2개 수준으로 제한하는 것이 필요합니다.


4. smart_truncate와 Safe Cap

긴 공격 프롬프트와 긴 타겟 응답을 그대로 Judge에 넣으면 모델 컨텍스트를 초과하거나 메모리 사용량이 커질 수 있습니다.

그래서 smart_truncate에 Safe Cap을 도입했습니다.

초기 기준은 다음과 같습니다.

6,000 토큰
약 20,000자
뒷부분 우선 보존

이후 4B 모델 안정성을 고려해 더 보수적으로 다음 값도 정리했습니다.

최대 12,000자
약 3,500 토큰

여기서 뒷부분을 우선 보존하는 이유는 공격 프롬프트나 응답에서 중요한 지시, tool call, refusal, leakage가 뒤쪽에 등장할 수 있기 때문입니다.

단순히 앞에서부터 자르면 실제 판정에 중요한 부분이 사라질 수 있습니다.


5. Auditor 노드 예외 처리

Auditor 노드는 LLM 기반으로 의도와 문맥을 분석하는 노드입니다.

이 노드가 실패하면 전체 LangGraph 판정이 깨질 수 있기 때문에 예외 처리를 강화해야 합니다.

수정 방향은 다음과 같습니다.

예외 발생 시에도 항상 유효한 결과 반환
빈 응답을 성공으로 처리하지 않음
디버그 로그 추가
Ollama 호출 전 프롬프트 길이 출력

특히 빈 응답 길이가 0일 때 이를 성공으로 보면 안 됩니다.

응답이 비어 있다는 것은 모델 호출 실패, timeout, 네트워크 문제, 서버 오류일 가능성이 있습니다. 따라서 빈 응답은 실패 또는 error로 기록하고, 필요하면 재시도해야 합니다.


6. Consensus 노드 fallback 강화

consensus_node는 여러 판정 노드의 결과를 취합해서 최종 판정을 내리는 역할입니다.

여기서 일부 노드 결과가 None이거나, Auditor가 실패하거나, Scanner 결과가 비어 있을 수 있습니다.

따라서 None 체크 로직을 강화해야 합니다.

또한 모든 노드가 정상 결과를 주지 못했을 때 시스템이 중단되지 않도록 fallback 로직이 필요합니다.

안전한 fallback은 다음 기준으로 정리할 수 있습니다.

명확한 vulnerable 증거가 있으면 vulnerable
명확한 refusal이 있으면 safe
판정 근거가 부족하면 ambiguous
노드 오류는 error로 기록

이렇게 해야 판정 실패가 전체 파이프라인 실패로 이어지지 않습니다.


7. LangGraph 병렬 호출과 OOM 원인

이번 문제의 핵심 원인은 LangGraph 멀티 에이전트 구조에서 post 5개가 병렬로 Ollama를 호출한 것입니다.

각 Judge 노드가 Ollama를 호출하고, 동시에 여러 요청이 들어가면 GPU 또는 메모리 사용량이 급격히 증가합니다.

Ollama 모델 3개를 동시에 가동하는 상황까지 겹치면 스왑을 사용할 가능성이 높아집니다.

그래서 병렬성을 무조건 크게 잡는 것은 위험합니다.

해결 방향은 Semaphore를 사용해서 한 번에 1~2개 이상의 판정이 동시에 일어나지 않도록 제어하는 것입니다.

기존: 최대 5개 동시 Ollama 호출
수정: 최대 1~2개 동시 Ollama 호출

속도는 조금 느려질 수 있지만, OOM으로 전체 실행이 깨지는 것보다 안정적인 편이 낫습니다.


8. 타겟 서버 응답 검증

타겟 서버가 빈 값을 반환하는 경우도 따로 봐야 합니다.

타겟 챗봇이 정상 응답을 주지 않았는데 이를 Judge가 정상 응답으로 판정하면 결과가 오염됩니다.

그래서 타겟 서버 응답이 비어 있으면 즉시 에러 로그를 남기고, 해당 케이스를 정상 성공이나 정상 safe로 처리하지 않아야 합니다.

확인해야 할 기준은 다음과 같습니다.

HTTP status code
response body 존재 여부
messages 필드 존재 여부
content 길이
timeout 여부
JSON parsing 실패 여부

이 검증이 있어야 긴 프롬프트, 서버 오류, 모델 오류를 공격 성공/실패 판정과 구분할 수 있습니다.


9. Docker 실행 명령 정리

메인 Docker는 다음 명령으로 실행합니다.

docker compose up -d

빌드까지 다시 해야 할 때는 다음 명령을 사용합니다.

docker compose up -d --build

테스트베드 Docker는 별도 compose 파일로 실행합니다.

docker compose -f docker-compose.testbed.yml up -d

Docker를 완전히 종료해야 할 때는 macOS에서 다음 명령을 사용할 수 있습니다.

osascript -e 'quit app "Docker"'

백엔드만 재시작할 때는 다음 명령을 사용합니다.

docker compose restart backend

전체를 재시작하려면 다음 명령을 사용합니다.

docker compose restart

10. Docker 챗봇 테스트

테스트베드 챗봇이 정상 동작하는지 확인하려면 /chat 엔드포인트에 직접 요청을 보냅니다.

curl -s -X POST "http://localhost:8010/chat" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role":"user","content":"안녕? 지금 모드가 뭐야?"}
    ]
  }' | jq

이 테스트는 파이프라인을 돌리기 전에 타겟 챗봇이 정상 응답하는지 확인하는 용도입니다.

타겟 서버가 정상 응답하지 않으면 이후 Phase 1~4 결과도 신뢰하기 어렵습니다.


11. 챗봇 대상 파이프라인 실행

챗봇을 대상으로 Phase 1~4 smoke 파이프라인을 실행할 수 있습니다.

python scripts/run_phase1_to_4_smoke.py --shuffle --seed 57 --verbose-trace --save-full \
  --target-url http://localhost:8010/chat

공격 패턴 파일을 지정해서 전체 파이프라인을 실행할 수도 있습니다.

ATTACK_PATTERN_PATH=data/curated_attack_sets/testbed_manual_mixed_10.json \
python scripts/run_phase1_to_4_smoke.py --shuffle --seed 57 --verbose-trace --save-full \
  --target-url http://localhost:8010/chat

시간을 줄이려면 Phase 2 라운드를 제한합니다.

ATTACK_PATTERN_PATH=data/curated_attack_sets/testbed_manual_mixed_10.json \
python scripts/run_phase1_to_4_smoke.py --shuffle --seed 57 --verbose-trace --save-full \
  --target-url http://localhost:8010/chat \
  --phase2-rounds 1

Phase 2는 Red Agent 변형 공격이 들어가므로 시간이 오래 걸릴 수 있습니다. 그래서 smoke test에서는 라운드 수를 줄이는 것이 유용합니다.


12. RDS 연결 확인

RDS까지 연결해서 파이프라인을 실행할 때는 DATABASE_URL을 RDS 주소로 지정합니다.

DATABASE_URL='postgresql+asyncpg://postgres:<RDS_PASSWORD>@agentshield-db.clomome4gm5w.ap-northeast-2.rds.amazonaws.com:5432/agentshield?ssl=require' \
ATTACK_PATTERN_PATH=data/curated_attack_sets/testbed_manual_mixed_10.json \
python scripts/run_phase1_to_4_smoke.py --shuffle --seed 57 --verbose-trace --save-full \
  --target-url http://localhost:8010/chat

이 명령은 로컬 testbed 챗봇을 타겟으로 쓰면서, 파이프라인 결과 저장은 RDS PostgreSQL로 보내는 흐름입니다.

여기서 중요한 것은 ssl=require입니다.

RDS에 붙을 때 SSL 설정이 맞지 않으면 연결 오류가 날 수 있습니다.


13. RDS에 공격 패턴 넣기

수동 검수된 공격 패턴을 RDS에 넣을 때는 ingest_attack_patterns.py를 사용합니다.

테스트용 10개 데이터를 넣는 명령은 다음과 같습니다.

DATABASE_URL='postgresql+asyncpg://postgres:<RDS_PASSWORD>@agentshield-db.clomome4gm5w.ap-northeast-2.rds.amazonaws.com:5432/agentshield?ssl=require' \
venv/bin/python scripts/ingest_attack_patterns.py \
  --path data/curated_attack_sets/testbed_manual_mixed_10.json \
  --source manual_reviewed_830_testbed10 \
  --replace-source

전체 830개 데이터를 넣을 수도 있습니다.

DATABASE_URL='postgresql+asyncpg://postgres:<RDS_PASSWORD>@agentshield-db.clomome4gm5w.ap-northeast-2.rds.amazonaws.com:5432/agentshield?ssl=require' \
venv/bin/python scripts/ingest_attack_patterns.py \
  --path data/curated_attack_sets/manual_reviewed_830_normalized.json \
  --source manual_reviewed_830 \
  --replace-source

다만 전체 830개를 넣으면 Phase 1이 830개를 전부 돌 수 있습니다.

따라서 smoke test나 기능 확인 단계에서는 10개짜리 테스트셋을 먼저 쓰는 것이 안전합니다.


14. 과거 로컬 DB 구조

과거 로컬 DB 구조는 크게 두 개로 나뉘었습니다.

하나는 AgentShield 파이프라인 결과 저장소입니다.

PostgreSQL localhost:5432
└── agentshield DB
    ├── test_sessions
    └── test_results

당시 기준으로 test_sessions에는 29개 세션이 있었고, test_results에는 14,340개 결과가 있었습니다.

오늘 취소된 세션에는 447건이 있었고, vulnerable 4건은 Phase 1 seed에서 나온 것으로 정리되었습니다.

다른 하나는 testbed DB입니다.

PostgreSQL localhost:5432
└── testbed DB
    ├── customers
    ├── orders
    └── support_tickets 등

testbed DB는 타겟 챗봇이 사용하는 가짜 고객 데이터입니다.

당시 기준으로 customers는 110건, orders는 320건이 있었습니다.


15. ChromaDB 상태

ChromaDB는 성공 공격 벡터 저장소로 사용됩니다.

경로는 다음과 같습니다.

./chromadb_data

컬렉션은 attack_results입니다.

당시 기준으로 219건이 저장되어 있었습니다.

오늘 추가된 것은 0건으로 정리되었습니다. 취소 세션에서 Phase 2 vulnerable이 0이었기 때문에 새로 쌓인 성공 공격 사례가 없었습니다.

이 점은 중요합니다.

취소된 세션이 있었더라도 ChromaDB에 오염된 성공 사례가 새로 들어가지 않았다는 뜻입니다.


16. 공유 DB와 ChromaDB 준비

RDS는 이미 올렸고, 남은 작업은 스키마와 테이블 정의입니다.

보안 그룹은 IP 주소 0.0.0.0으로 열어둔 상태로 정리되어 있습니다.

다음으로는 ChromaDB를 EC2에 열어서 팀이 공유할 수 있게 해야 합니다.

공유 구조가 완성되면 팀원들이 같은 PostgreSQL과 같은 ChromaDB를 기준으로 결과를 쌓고 검수할 수 있습니다.

다만 공유 DB에서는 raw와 cleaned를 더 엄격하게 구분해야 합니다.

RDS에 원본 실행 결과가 계속 들어가고, 사람이 검수한 cleaned 데이터만 학습과 보고서에 반영되어야 합니다.


17. 이번 정리에서 잡은 기준

이번 단계에서 가장 중요한 기준은 안정성입니다. LangGraph 멀티 에이전트 구조는 판정 품질을 높일 수 있지만, Ollama 호출이 병렬로 늘어나면 OOM과 timeout 문제가 생길 수 있습니다.

따라서 컨텍스트 크기, 프롬프트 길이, 동시 호출 수, timeout, 빈 응답 처리, fallback 로직을 같이 잡아야 합니다.

정리하면 다음 흐름이 필요합니다.

긴 프롬프트 저장 여부 확인
→ smart_truncate로 입력 길이 제한
→ Ollama 동시 호출 수 제한
→ 빈 응답은 error로 처리
→ 타겟 서버 응답 검증
→ Consensus fallback 강화
→ RDS/ChromaDB 공유 구조 준비

 

AgentShield는 이제 단순히 로컬에서 파이프라인이 한 번 도는 단계를 넘어서고 있습니다.

여러 모델, Docker testbed, LangGraph Judge, RDS, ChromaDB가 함께 연결되기 때문에 성능보다 먼저 안정성과 데이터 오염 방지가 중요합니다.

'4. [팀] 프로젝트 및 공모전 > 4-5 AgentShield(보안 플랫폼)' 카테고리의 다른 글

[AgentShield] 프로젝트 후 코드 분석 및 연구 (2): 레드 에이전트 강화 일지  (0) 2026.05.24
[AgentShield] 프로젝트 후 코드 분석 및 연구 (1): 모델 학습  (0) 2026.05.22
[AgentShield] 진행 상황 및 개인 공부 정리 (6): Adaptive Red Agent, 세션 기반 공격 전략, LangGraph 보안 판정 구조  (0) 2026.05.02
[AgentShield] 진행 상황 및 개인 공부 정리 (5): Judge와 Guard 구조, Ollama 모델 역할, Fine-tuning 모델 연결  (0) 2026.04.30
[AgentShield] 진행 상황 및 개인 공부 정리 (4): Testbed RAG 연결, Fine-tuning 파이프라인, Prompt Injection 실험 구조  (0) 2026.04.29
'4. [팀] 프로젝트 및 공모전/4-5 AgentShield(보안 플랫폼)' 카테고리의 다른 글
  • [AgentShield] 프로젝트 후 코드 분석 및 연구 (2): 레드 에이전트 강화 일지
  • [AgentShield] 프로젝트 후 코드 분석 및 연구 (1): 모델 학습
  • [AgentShield] 진행 상황 및 개인 공부 정리 (6): Adaptive Red Agent, 세션 기반 공격 전략, LangGraph 보안 판정 구조
  • [AgentShield] 진행 상황 및 개인 공부 정리 (5): Judge와 Guard 구조, Ollama 모델 역할, Fine-tuning 모델 연결
고니3000원
고니3000원
프로젝트의 구현 과정과 기술적 노하우를 담았습니다. AI 모델 연구와 매일의 학습 기록을 차곡차곡 공유하고 있습니다. [ 매너 & 태도 ] * 항상 겸손해라.
늘 자신을 낮추고 겸손함을 잃지 마라.
 * 나이 불문 예의를 지켜라.
나이와 지위를 막론하고, 누구에게나 변함없는 예의를 갖추어라.
 * 행복하겠다는 생각을 버려야 행복하다.
행복에 대한 강박과 집착을 내려놓을 때, 비로소 진정한 행복이 찾아온다.
 [ 리더십 & 실행 ] * 통찰력, 결단력,
  • 고니3000원
    곤이의 공부 블로그
    고니3000원
  • 전체
    오늘
    어제
    • 분류 전체보기 (211) N
      • 1. AI 논문 + 모델 분석 (21)
        • AI 논문 분석 (13)
        • AI 모델 분석 (8)
      • 2. 자료구조와 알고리즘 (16)
        • 2-1 자료구조와 알고리즘 (13)
        • 2-2 강화학습 알고리즘 (3)
      • 3. 자습 & 메모(실전, 실습, 프로젝트) (27)
        • 3-1 문제 해석 (4)
        • 3-2 메모(실전, 프로젝트) (14)
        • 3-3 배포 실전 공부 (7)
        • 3-4 최신 기술 분석 (2)
      • 4. [팀] 프로젝트 및 공모전 (31)
        • 4-1 팀 프로젝트(메모, 공부) (1)
        • 4-2 Meat-A-Eye (6)
        • 4-3 RL-Tycoon-Agent (3)
        • 4-4 구조물 안정성 물리 추론 AI 경진대회(D.. (4)
        • 4-5 AgentShield(보안 플랫폼) (17)
      • 5. [개인] 프로젝트 및 공모전 (20) N
        • 4-1 귀멸의칼날디펜스(자바스크립트 활용) (5)
        • 4-2 바탕화면 AI 펫 프로그램 (4)
        • 4-3 개인 프로젝트(기타) (3)
        • 4-4 공모전 (4) N
        • 4-5 나만의 로컬 LLM 멀티 에이전트 구축 (4)
      • 개념 정리 step1 (32)
        • Python 기초 (7)
        • DBMS (1)
        • HTML | CSS (3)
        • Git | GitHub (1)
        • JavaScript (5)
        • Node.js (5)
        • React (1)
        • 데이터 분석 (6)
        • Python Engineering (3)
      • 개념 정리 step2 (60)
        • Machine | Deep Learning (15)
        • 멀티모달(Multi-modal) (23)
        • 강화 학습 (10)
        • AI Agent (9)
        • 메디컬 이미지 (3)
      • 개인 공부 - 내가 공부하고 싶은 모든 것 (2) N
        • 1. 인프라 (2) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Notion-포트폴리오
    • Github
  • 공지사항

    • ‘박영곤’ 나의 핵심 가치
  • 인기 글

  • 태그

    Lora
    논문 리뷰
    보안
    Python
    API
    공모전
    데이터분석
    구현
    강화학습
    Ai
    OCR
    transformer
    Vision
    파이썬
    전처리
    프로젝트
    인공지능
    알고리즘
    자료구조
    Agent
    RAG
    Ollama
    pandas
    자바스크립트
    paddleocr
    github
    학습
    파인튜닝
    llm
    ViT
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
고니3000원
[AgentShield] 진행 상황 및 개인 공부 정리 (7): LangGraph Judge 안정화, Ollama 메모리 이슈, Docker 실행과 RDS 연결
상단으로

티스토리툴바