본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 26. 10:33

AI 에이전트를 구현하며 깨달은 「가드레일을 설치할 수 있는 설계 역량」의 중요성

요약

AI 에이전트의 자율성으로 인해 발생할 수 있는 보안 및 운영 사고를 방지하기 위한 설계 역량의 중요성을 다룹니다. 프롬프트 지시가 아닌 시스템 레벨의 가드레일 구축을 통해 안전한 에이전트 환경을 만드는 방법을 제안합니다.

핵심 포인트

  • LLM과 AI 에이전트의 핵심 차이는 '자율적 태스크 수행'에 있음
  • 자율적 행동은 기밀 유출이나 시스템 파괴 등 치명적 사고를 유발할 수 있음
  • 프롬프트 기반의 제약은 완벽하지 않으므로 시스템 측면의 차단이 필수적임
  • 안전한 에이전트 설계를 위해 LLM, 프롬프트, 도구의 구성 요소를 이해해야 함

퍼솔 홀딩스(Persol Holdings) 엔지니어링부 Product Engineering실의 R.H.입니다.

제가 이전에 부내에서 개최했던 AI 에이전트 스터디 모임의 내용에 대해 여러분께 소개합니다.

AI 에이전트가 보급되는 가운데, 스크래치(Scratch)로 간이 AI 에이전트를 개발함으로써 얻은 지식과 관점을 정리합니다.

  • AI 에이전트와 LLM의 차이를 이해하고 싶은 사람
  • AI 에이전트의 위험성을 이해하고 싶은 사람
  • AI 에이전트의 구현 이미지를 잡고 싶은 사람

본 기사의 도표나 코드는 스터디 모임용으로 간략화된 부분이 있으니 유의하시기 바랍니다.

LLM의 등장 이후 AI는 급격한 변화를 일으켰으며, 현재는 AI 에이전트가 보급되고 있습니다.

먼저 LLM과 AI 에이전트의 차이에 대해 정리하겠습니다.

  • LLM: 사용자의 질문·지시에 대해 문장 형태의 답변을 출력한다.
  • AI 에이전트: 사용자의 질문·지시에 대해 자율적으로 태스크(Task)를 실시한다. 판단·실행·관찰의 사이클을 스스로 돌리며, 최종적인 결과물을 출력한다.

즉, AI 에이전트의 포인트는 「자율적」이라는 점입니다. AI 에이전트를 개발하려고 하면, 태스크 실행을 위한 처리와 결과물의 판단까지 자율적으로 수행할 수 있도록 환경을 조성해야 합니다.

AI 에이전트는 자율적으로 행동을 수행합니다. 예를 들어 터미널 조작 등도 가능합니다.

이러한 자율적인 행동 때문에 AI 에이전트와 관련된 사고를 자주 목격하게 되었습니다.

예를 들어

  • AI 에이전트 「Main에 푸시해 버렸습니다」
  • AI 에이전트 「기밀 정보를 코드에 포함해 버렸습니다」
  • AI 에이전트 「너무 복잡하게 생각해서 토큰 제한이 걸려 버렸습니다」

이것들은 기업이나 프로덕트(Product)에 있어 치명적인 사고가 되는 경우가 많아, AI 에이전트에 대해 어느 정도 공포심을 가진 분들도 많지 않을까 합니다.

그렇다면, AI 에이전트는 위험한가? 라는 의문이 생길 것입니다. 저는 「시스템 측에서 담보한다면 한없이 안전하게 만들 수 있다」고 생각합니다. 여기서 중요한 것은 「시스템 측」에서 안전성을 담보한다는 점입니다.

흔히 발생하는 실패 사례로, 시스템 프롬프트(System Prompt)에 「rm 커맨드는 금지입니다」라고 지시를 쓰고 만족해하는 패턴이 있습니다. 프롬프트는 AI 에이전트에 대한 구두 지시에 불과하며, 그것을 100% 지킨다는 보장이 없습니다. 사람에 비유하자면 「〇〇하지 마」라고 부하 직원에게 지시하는 것과 같습니다. 부하는 위반하지 않도록 최대한 주의하겠지만, 어찌어찌 실수를 하여 금지 사항을 위반해 버리는 경우도 있을 것입니다.

이처럼 프롬프트로 지시를 하는 것은 완벽하게 위반을 방지할 수 있는 것이 아니기 때문에 주의가 필요합니다. (프롬프트로 지시하는 것이 유용하지 않다는 뜻은 아닙니다)

그렇다면 안전성을 한없이 100%에 가깝게 만들려면 어떻게 해야 할까요? 라는 의문이 생깁니다.

그것은 시스템 측에서 안전성을 담보하도록 하면 됩니다. rm 커맨드의 예를 들면, 애초에 AI 에이전트가 rm 커맨드를 이용할 수 없도록 시스템 측에서 블록(Block)을 해두면 사고는 일어나지 않습니다. 사람에 비유하자면, 누르지 말라고 되어 있는 버튼 그 자체를 삭제하는 행위와 같습니다.

이처럼 프롬프트로 안전성을 담보하는 것이 아니라, 시스템 측에서 안전성을 담보해야 한다는 점을 유념해 주시기 바랍니다. 단, 시스템에 버그가 있는 경우는 치명적이므로 이용자·개발자 관점 모두에서 최대한 주의가 필요합니다.

이 기사의 목표는 다음과 같습니다.

  • AI 에이전트의 구성 요소를 이해한다
  • 안전한 AI 에이전트의 설계 방법을 이해한다
  • AI 에이전트 구현의 감각을 잡는다

AI 에이전트는 다음의 세 가지로 구성되어 있습니다.

  • LLM: AI 에이전트의 두뇌. 도구(Tool)의 이용 판단이나 최종 결과물의 출력을 담당한다.
  • 프롬프트(Prompt): AI 에이전트가 받는 지시.
  • 도구(Tool): AI 에이전트가 이용할 수 있는 도구. 파일을 읽어들이는 함수 등 다양한 도구를 전달할 수 있다.

기존의 LLM 구성 요소는 LLM과 프롬프트뿐이지만, AI 에이전트에서는 거기에 더해 도구도 구성 요소에 포함됩니다. 태스크를 실행하기 위한 도구가 전달됨으로써, AI 에이전트는 자율적인 태스크 실행이 가능해집니다. 이 도구의 제어야말로 AI 에이전트의 핵심이 되는 부분입니다.

여기서 주의해야 할 포인트는 LLM은 어디까지나 언어 처리만을 수행한다는 점입니다. LLM은 문자열을 받아 문자열을 반환하는 것입니다. 따라서 LLM 자체가 도구를 사용하는 것이 아니라, LLM은 「이 도구를 사용해 주길 원한다」는 판단 결과를 문자열로 반환할 뿐입니다.

LLM이 도구를 사용할 때는 Function Calling이라는 메커니즘을 이용합니다. 이는 LLM에게 「사용 가능한 도구 목록」을 JSON으로 전달하면, LLM이 적절한 도구와 인자(Argument)를 선택하여 반환해 주는 방식입니다.

다음은 실제로 LLM에 전달하는 JSON의 예시입니다. 도구와 인자에 대한 설명을 덧붙여 LLM에 전달합니다. 이를 통해 LLM은 이용 가능한 도구를 이해하고, 현재 태스크에 맞는 도구를 선택하여 처리를 수행할 수 있습니다.

[
// 파일·폴더 목록을 가져오는 함수
{
...

LLM이 이용하고 싶은 도구를 선택한 경우, 다음과 같이 응답 내의 tool_calls를 통해 도구 호출이 실행됩니다.

{
"role": "assistant",
"content": "",
...

그 후, 도구 호출에 따라 서버 측에서 처리가 수행됩니다. 해당 처리 결과에 대해서는 다음과 같이 roletool로 설정하여 LLM에 반환하면, LLM은 이를 도구를 실행한 결과로 인식할 수 있습니다.

{
"role": "tool",
"content": "{\"success\":true,\"data\":{\"directory\":\"sample\",\"totalItems\":5,\"files\":[{\"name\":\"index.ts\",\"path\":\"sample/index.ts\",\"type\":\"file\",\"size\":1234,\"category\":\"code\"}]}",
...

이 결과를 바탕으로 LLM이 다음 태스크를 판단함으로써 최종적인 태스크 완료를 목표로 합니다.

AI 에이전트는 에이전트 루프 (Agent Loop)라는 흐름으로 태스크를 실행합니다. 우선 주어진 태스크와 도구 정의를 바탕으로 처음에 이용할 도구를 생각합니다. 그 후, 실제로 도구를 이용한 결과로부터 다음에 이용할 도구를 생각하여 이용하는 루프가 반복됩니다. 도구를 여러 번 이용하여 태스크 해답에 필요한 정보를 모두 수집하면, 수집한 정보를 바탕으로 최종 답변을 반환하며 루프를 종료합니다.

실제 사례를 생각하면 다음과 같습니다.

「sample 폴더에서 지난주 회의록을 정리해줘」라고 지시했을 경우의 실제 루프 흐름은:

  • LLM: list_files({ directory: "sample" })를 호출 - 결과: 파일 목록이 반환됨
  • LLM: read_file({ filePath: "sample/20260101회의록.txt" })를 호출 - 결과: 파일 내용이 반환됨
  • LLM: (다른 파일도 읽음…)
  • LLM: 최종적으로 지난주 분량의 회의록 리포트를 생성

각 요소는 다음과 같은 역할을 가집니다. 에이전트가 중심이 되어 LLM 및 도구 호출을 수행합니다.

  • UI: 사용자 입력 수신 및 결과 표시
  • API: HTTP 요청 처리
  • Agent Loop: 에이전트 루프 제어 및 LLM 호출
  • LLM: LLM API 제공
  • Tool: 도구 목록 관리 및 개별 도구 처리

시스템을 제어함에 있어서는 세 가지 관점에서의 제어가 필요합니다.

  • AI가 사용하는 도구의 제한
    • 어떤 도구를 사용할지 선택하는 것은 LLM이므로, 틀려도 괜찮은 도구만 전달한다.
    • 위험한 도구는 처음부터 전달하지 않는다.
  • AI가 사용하는 파라미터(Parameter)의 검증
    • 인자 값을 선택하는 것은 LLM이므로, 잘못된 파라미터를 걸러낼 필요가 있다.
  • 루프의 상한 설정
    • 에이전트에서는 일반적인 LLM에 비해 도구 실행 계열의 대화 이력도 쌓이기 때문에 토큰 소비에 주의해야 한다.

이러한 관점들을 고려하며 설계 및 구현해야 합니다.

특정 폴더 workspace의 내용을 확인하고 리포트를 작성하는 AI 에이전트를 개발했습니다.

샘플로서 workspace 폴더 안에 2주 치의 회의록을 저장해 두었으며, 이 회의록을 바탕으로 리포트를 작성하는 데모를 가정합니다. 또한, 회의록 이외의 자신의 문서를 지정된 폴더 내에 저장함으로써 그에 대한 리포트를 작성하는 것도 가능합니다.

  • Next.js 16
  • TypeScript 5
  • Tailwind CSS v4
  • OpenAI SDK
  • LM Studio
  • Docker / Docker Compose
  • react-markdown + react-syntax-highlighter

간략화된 코드로 전체상을 파악해 보겠습니다.

이 에이전트 루프를 빠져나오는 조건은 LLM이 최종 응답을 출력했을 때 또는 최대 루프 횟수에 도달했을 때입니다.

루프 내의 처리는 두 가지 패턴으로 나뉩니다.

  • LLM이 도구 호출 (Tool Call)을 하는 경우 (responseToolCalls가 있는 경우): 도구 실행을 시스템 측에 요청하고 결과를 저장
  • LLM이 도구 호출을 하지 않는 경우: 최종 응답으로서 LLM의 결과를 표시
while (iteration < maxIterations) {
iteration++;
// LLM의 응답을 가져옴
...

도구 정의의 4가지 요소는 다음과 같습니다. 이것들을 정의함으로써 LLM이 설명문을 보며 적절한 도구를 선택할 수 있게 됩니다.

  • name: 도구의 이름·식별자
  • description: 도구의 설명문
  • parameters: 도구에서 사용하는 인자 (Arguments) 및 그 설명문
  • execute: 도구의 처리 부분
const readFileTool = {
// 도구·인자 설명 (LLM용)
neme: "read_file",
...

이번에는 OpenAI API를 이용합니다. OpenAI API의 ChatCompletions에서는 지금까지의 메시지 이력을 LLM에 전달하여 다음 답변을 얻습니다. 이번에는 여기에 더해 태스크 수행을 위해 도구 정의도 LLM에 제공하여, LLM이 도구 사용 여부를 판단하도록 합니다.

const response = await fetch(`${baseURL}/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json" },
...

이번에는 파일 조작을 할 수 있는 에이전트를 상정하고 있습니다. 따라서 안전한 파일 조작이 되도록 목표로 합니다.

제약을 걸지 않으면 폴더를 거슬러 올라가 기밀 정보를 취득하는 등의 사고가 발생할 수 있으므로 주의가 필요합니다.

먼저, 앞서 언급한 「안전성 설계에서 확인해야 할 3가지 관점」에 대해 재확인하고 실제로 구현에 들어갑니다.

  • AI가 사용하는 도구의 제한
  • AI가 사용하는 파라미터의 검증
  • 루프의 상한 설정

먼저 「AI가 사용하는 도구의 제한」에 관해 확인하겠습니다. 이번 에이전트는 폴더 내의 자료를 읽어 들여, 웹 애플리케이션 상에서 프롬프트에 대한 답변을 표시하는 것입니다. 따라서 파일의 생성이나 파일의 수정은 불필요합니다. 스크래치(Scratch)부터 개발하는 경우, 이러한 불필요한 처리에 대해서는 구현하지 않으면 사고가 발생하지 않습니다. 그러므로 이번에는 파일 이름 목록 취득과 파일 내용을 읽는 도구만을 구현하여 LLM에 전달하도록 하겠습니다.

export const allTools: ToolDefinition[] = [listFilesTool, readFileTool];

다음으로 「AI가 사용하는 파라미터의 검증」에 대해 확인하겠습니다. 이번 구현에서는 폴더 경로 등이 파라미터가 됩니다. LLM이 예기치 않은 폴더까지 탐색하여 기밀 정보를 취득하는 등의 사고를 방지하기 위해서도 파라미터 검증은 필수적입니다. 아래의 검증 함수가 API 엔드포인트를 호출할 때와 LLM이 도구를 이용할 때 실행됩니다.

export function resolveAndValidatePath(userPath: string): string {
// 1. 사용자가 지정한 상대 경로를 절대 경로로 변환
const resolved = path.resolve(baseDir, userPath);
...

마지막으로 「루프의 상한 설정」에 대해 확인합니다. AI 에이전트는 도구 등의 컨텍스트도 프롬프트를 통해 LLM에 전달하기 때문에, 일반적인 LLM보다 컨텍스트 사용량이 많습니다. 또한, AI 측에서 무한 루프에 빠질 경우 API 이용료가 계속해서 늘어날 가능성도 있습니다. 따라서 이러한 사고를 줄이기 위해 미리 상한을 시스템 측에서 설정할 필요가 있습니다.

다음과 같이 에이전트 루프의 상한 횟수를 정해두면, 설령 사고 루프(Thinking Loop)에 빠지더라도 루프를 종료시킬 수 있어 LLM이 무제한으로 이용되는 것을 방지할 수 있습니다.

// 상한 횟수를 고려한 루프
while (iteration < maxIterations) {
iteration++;
...

다음과 같은 데일리 미팅의 의사록이 2주 치 텍스트 파일로 저장되어 있을 때, 엔지니어 C의 진척 상황에 대해 정리해 달라고 요청합니다.

데일리 미팅 2026/01/19(월) 10:00-10:15
PO: 안녕하세요. 이번 주부터 스프린트 3가 시작됩니다. 각자 담당 태스크(Task) 확인과 현재 진행 상황을 공유해 주세요.
엔지니어 A: 안녕하세요. 이번 스프린트에서는 사용자 인증 기능 구현에 들어갑니다. 우선 로그인 화면의 UI 제작부터 착수할 예정입니다. 오늘 중으로 와이어프레임(Wireframe) 확인을 마치고, 내일부터 구현에 들어가고자 합니다.
...

먼저, "1/19부터 1/23까지의 엔지니어 C의 진척 상황을 정리해 주세요"라는 프롬프트(Prompt)로 AI 에이전트에게 태스크를 의뢰합니다.

image.png

그러면, 첫 번째 요청에서 시스템 프롬프트(System Prompt), 사용자 프롬프트(User Prompt), 툴 정의(Tool Definition)를 LLM에 전송합니다. (화면은 LMStudio)

image.png

프롬프트와 툴 정의를 바탕으로, 다음 행동을 LLM이 판단합니다. 첫 번째로는 의사록 파일이 저장되어 있는 디렉토리의 파일을 탐색하는 list_files라는 툴을 호출하고 있습니다. 이 이후부터는 툴의 결과로부터 다음 행동을 LLM이 판단합니다.

image.png

툴 이용이 불필요해진 시점에서 최종 결과물을 LLM이 출력합니다. 간단한 AI 에이전트이자 로컬 LLM(Local LLM)이기에 정밀도는 그 정도로 제한적이지만, 자율적으로 태스크를 완수할 수 있는 AI 에이전트입니다.

image.png

오늘의 목표로부터 정리하자면, 다음과 같은 내용을 설명했습니다.

  • AI 에이전트의 구성 요소를 이해한다

  • LLM, 툴, 프롬프트의 3요소가 있다

  • 기존의 LLM에 더해 툴을 이용할 수 있게 되었다고 인식하고 있다면 문제없다

  • AI 에이전트의 안전한 설계 방법을 이해한다

  • AI가 사용하는 툴의 처리 과정에서 안전성을 담보한다

  • AI가 판단한 파라미터(Parameter)를 시스템에서 검증한다

  • 루프(Loop) 상한을 설정하여 무한히 API를 호출하는 것을 방지한다

  • AI 에이전트 구현 사례의 감각을 익힌다

  • 에이전트 루프(Agent Loop)를 통해 툴을 사용하며 자율적으로 태스크를 실행한다

  • 펑션 콜링(Function Calling) 메커니즘을 사용하여 LLM이 툴을 이용하게 한다

  • 에이전트의 안전성 담보는 툴의 처리 부분에서 수행한다.

결국 AI 에이전트를 개발할 때 어떤 능력이 필요한가?라고 생각했을 때, "가드레일을 설치할 수 있는 설계 역량"이라고 느꼈습니다. 예를 들어, 리스크를 파악하고 대책을 세울 수 있는가? 그 대책은 리스크에 대해 포괄적인가?를 생각할 수 있는 스킬입니다.

저는 엔지니어로서 신입이며, AI와 함께 가드레일을 설치했습니다. 하지만 포괄적인지에 관해서는 아직 경험 부족으로 인해 판단할 수 없었습니다. 따라서 앞으로는 보안이나 안전한 시스템을 학습함으로써, 더욱 포괄적으로 대처할 수 있도록 성장할 필요가 있다고 실감했습니다.

AI는 계속해서 진화하고 있으며, 프롬프트를 공들여 작성하지 않아도 충분한 성능이 나오게 되었습니다. 게다가 LLM 개발은 빅테크(Big Tech) 기업들이 수행하고 있어 솔직히 통제할 수 있는 상태는 아니라고 생각합니다. 그렇기 때문에 앞으로는 어떻게 안전하게 다룰 것인가? 누가 사용해도 안전한 시스템을 만들고 있는가?라는 부분에 초점을 맞추어 생각하고 개발하는 것이 중요해질 것이라고 생각합니다. 그 관점을 계속 고민함으로써 엔지니어로서의 가치를 발휘할 수 있도록 정진해 나가겠습니다.

마지막으로, 이번 기사가 여러분의 활동에 참고가 되기를 바랍니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0