본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 23. 03:03

나만의 AI 에이전트를 만들며 깨달은 LLM과 프로그램의 책임 분리

요약

개인용 AI 에이전트 'Agent-Sin'을 개발하며 얻은 LLM과 프로그램의 역할 분리 설계 원칙을 다룹니다. LLM은 자연어 판단과 라우팅을 담당하고, 구체적인 실행은 안정적인 Program Skill이 담당하도록 설계하여 실용성을 높이는 방법을 제안합니다.

핵심 포인트

  • LLM은 모호한 자연어 판단과 라우팅에 집중
  • 반복적이고 부작용이 있는 작업은 프로그램으로 고정
  • LLM과 프로그램의 책임 분리를 통한 실행 안정성 확보
  • 비용 절감 및 처리 속도 개선을 위한 설계 전략

최근, 나만의 전용 AI 에이전트로서 Agent-Sin이라는 것을 만들었습니다.

Agent-Sin은 간단히 말해 "대화로 자신만의 스킬을 호출하여 일상이나 업무를 도와주는 AI 에이전트"입니다.

Gmail을 정리하거나, Google Calendar를 확인하거나, GitHub 알림을 보거나, Stripe 매출을 집계하거나, 식사 로그를 기록하거나, 아침에 오늘의 일정이나 뉴스를 정리하기도 합니다.

언뜻 보기에는 흔히 볼 수 있는 AI 에이전트처럼 보일지도 모릅니다.

하지만 만들어가는 과정에서 강하게 느낀 점은, AI 에이전트를 실용적으로 사용하려면 "모든 것을 LLM에 맡기는" 것이 아니라, "LLM에 맡길 부분"과 "프로그램으로서 고정할 부분"을 나누는 것이 매우 중요하다는 것입니다.

이 기사에서는 Agent-Sin을 만들면서 고민했던 개인용 AI 에이전트의 설계에 대해 써 내려가겠습니다.

AI 에이전트는 편리하지만, 그대로는 실용화가 어렵다

LLM에 자연어로 지시할 수 있다는 것은 매우 편리합니다.

  • "오늘 일정 좀 봐줘"
  • "답장해야 할 메일 있어?"
  • "이 포스팅 안 어떻게 생각해?"
  • "아침 브리핑을 보내줘"

이런 의뢰를 자연스럽게 다룰 수 있는 것은 AI 에이전트의 큰 매력입니다.

반면, 실제로 일상 운영을 하려고 하면 몇 가지 문제가 발생합니다.

  • 매번 판단이 조금씩 흔들림
  • 같은 작업이라도 실행 절차가 안정적이지 않음
  • 외부 API 조작을 맡기면 권한 문제가 걱정됨
  • 처리가 느려지기 쉬움
  • 매번 LLM에게 생각하게 하면 비용도 발생함

특히 메일 정리나 일정 등록, 알림, 삭제, 기록과 같은 부작용(Side Effect)이 있는 처리는 단순히 "똑똑한 LLM에 맡기는" 것만으로는 불안함이 있습니다.

그래서 Agent-Sin에서는 대화하는 부분과 실제로 처리하는 부분을 나누고 있습니다.

대화 모드와 Program Skill을 나누기

Agent-Sin에는 **대화 모드 (Conversation Mode)**와 Program Skill이 있습니다.

대화 모드는 사용자의 발화를 읽고 무엇을 하고 싶은지를 판단합니다.

  • "Gmail 정리해줘"라고 하면, Gmail 정리 스킬을 호출
  • "오늘 일정은?"이라고 하면, Google Calendar 일정 확인 스킬을 호출
  • "이 내용 메모해줘"라고 하면, 메모 저장 스킬을 호출

반면, 실제 처리는 Program Skill이 담당합니다.

  • Gmail API를 어떻게 호출할 것인가
  • 어떤 라벨을 붙일 것인가
  • 어떤 메일을 읽음 처리할 것인가
  • 어떤 형식으로 Telegram에 알림을 보낼 것인가
  • 어디에 로그를 남길 것인가

이러한 구체적인 처리는 매번 LLM에게 생각하게 하는 것이 아니라, 스킬 측에 프로그램으로서 구현합니다.

즉, LLM은 "판단과 라우팅 (Routing)"을 담당하고, Program Skill은 "안정적인 실행"을 담당한다는 분담입니다.

이것은 실제로 만들어보니 상당히 효과적입니다.

AI 에이전트라고 하면 무심코 "무엇이든 LLM에게 시키는" 방향으로 생각하기 쉽습니다. 하지만 실용적인 면에서는 오히려 반대로, 안정적으로 반복되는 처리일수록 프로그램화하는 것이 좋습니다.

  • LLM은 모호한 자연어를 받아들이는 데 능숙함
  • 프로그램은 정해진 절차를 정확하게 실행하는 데 능숙함

이 두 가지를 너무 섞지 않는 것이 개인용 에이전트를 일상에서 사용할 수 있게 만드는 포인트라고 느끼고 있습니다.

스킬화하면 에이전트의 흔들림이 줄어든다

Agent-Sin에서 최근 제가 만든 스킬은 다음과 같습니다.

  • Gmail 정리 스킬: 읽지 않은 메일을 확인하여 답장이 필요해 보이는 것에 "답장 필요" 라벨을 붙입니다. 그 외의 메일은 정리 대기 상태로 격리합니다.
  • 아침 브리핑 스킬: 오늘의 일정, 답장 필요 메일, ToDo, 뉴스, 건강 로그를 정리하여 Telegram으로 보냅니다.
  • 식사 코칭 스킬: 식사 내용이나 체중, 근력 운동 이력을 기록하고 칼로리나 단백질 기준치를 알려줍니다.
  • SNS 마케팅계 스킬: 과거 포스팅 결과나 배운 점을 저장하여 다음 포스팅 안을 생각하는 재료로 사용합니다.

이것들을 모두 LLM의 자유 실행에 맡기면 매번 조금씩 처리가 달라져 버립니다.

하지만 스킬로 만들어 두면 기본적인 동작은 고정할 수 있습니다.

  • Gmail 정리라면 Gmail 정리 규칙
  • 아침 브리핑이라면 아침 브리핑 구성
  • 식사 로그라면 식사 로그 저장 형식
  • SNS 분석이라면 SNS 분석 이력 관리

그 바탕 위에서 판단이 필요한 부분만 LLM을 사용합니다.

스킬 하나하나의 내부 구조는 다음과 같습니다.

스킬 내부에서 LLM을 사용하고 싶은 부분만 ai_steps로 선언해 두고, ctx.ai.run(...)으로 호출합니다. 그 외의 절차는 일반적인 코드입니다. "전체는 고정되어 있고, 판단이 필요한 일부만 LLM을 사용한다"라는 완급 조절을 하기 쉬운 구조로 되어 있습니다.

이렇게 하면, 자연스럽게 맡길 수 있으면서도 실행은 안정적인 상태에 가까워집니다.

기억은 인간도 읽을 수 있는 형태로 만든다

Agent-Sin에서는 기억의 취급 또한 매우 중요하게 다룹니다.

AI 에이전트에게 기억을 갖게 한다고 하면, 벡터 DB (Vector DB)에 전부 넣는 듯한 설계를 상상하기 쉽습니다.

물론 검색용 인덱스 (Index)는 편리하지만, 그것만으로는 인간이 내용을 파악하기 어려워집니다.

그래서 Agent-Sin에서는 장기 프로필이나 일일 로그, 메모, 토픽별 지식을 Markdown 형식으로 저장하고 있습니다.

  • 사용자의 장기적인 취향이나 업무 방침 → 프로필
  • 일상적인 대화나 작업 로그 → 일일 메모
  • 특정 토픽에 대해 반복적으로 등장한 이야기 → 토픽 지식 (Topic Knowledge)

이렇게 해두면, AI뿐만 아니라 인간도 읽을 수 있습니다. Obsidian으로 열면 자신의 활동 로그나 사고의 축적으로서 그대로 볼 수 있습니다. AI에게 닫힌 블랙박스가 아니라, 인간과 AI가 공유할 수 있는 지식 베이스 (Knowledge Base)가 됩니다.

Obsidian Vault는 실체가 아니라, 워크스페이스의 실제 파일에 대한 심링크 (symlink) 뷰로 구성하고 있습니다.

개인적으로는 이 **"인간도 읽을 수 있는 기억"**이 상당히 중요하다고 생각합니다.

AI 에이전트는 사용하면 사용할수록 자신의 문맥 (Context)을 이해해주길 바랍니다. 하지만 그 기억이 어디에 있고 무엇을 기억하고 있는지 보이지 않으면 두렵습니다.

Markdown으로 남겨두면, 필요할 때 직접 읽을 수 있고, 수정할 수 있으며, 삭제할 수 있습니다.

에이전트의 기억을 자신의 외부 뇌로서 다루기 쉬워집니다.

나아가, 일일 대화 로그 중 "인생과 관련이 있을 법한 정보"만을 야간에 장기 기억으로 승격시키는 메커니즘도 갖추고 있습니다.

무엇을 승격시켰는지에 대한 diff도 남기고 있기 때문에, 나중에 "언제·왜 AI가 이것을 기억했는가"를 추적할 수 있도록 했습니다.

스킬별로 상태를 가진다

또 하나 중요한 것이 스킬별 상태 관리 (State Management) 입니다.

Agent-Sin에서는 스킬마다 SQLite DB를 가질 수 있도록 하고 있습니다.

  • 식단 코치라면: 식단 로그, 체중, 근력 운동 이력
  • SNS 마케팅 스킬이라면: 포스팅 결과, 배움, 프로젝트 정보
  • Gmail 정리 스킬이라면: 과거 처리 규칙이나 분류 결과
  • ToDo 스킬이라면: 미완료 태스크나 기한

이러한 이력은 전체의 장기 기억에 전부 섞는 것보다, 스킬 측에 가두어 두는 것이 다루기 쉽습니다.

전체 프로필에는 "사용자가 무엇을 선호하는가", "어떤 방침을 가지고 있는가"를 둡니다. 스킬 고유의 세세한 이력은 해당 스킬의 DB에 둡니다.

이러한 분리를 통해 기억이 어지러워지는 것을 방지할 수 있습니다.

AI 에이전트의 기억이라고 하면 무엇이든 한곳에 넣고 싶어지지만, 실제로는 "전체 문맥"과 "업무별 이력"은 나누는 것이 좋다고 느낍니다.

스킬별로 DB를 나누는 장점은 또 하나 있는데, 바로 스킬 단위로 포터블(Portable)하며 삭제 가능하다는 점입니다. 스킬을 다른 환경으로 가져가고 싶다면 DB째로 옮깁니다. 필요 없게 되면 DB째로 지웁니다. 공유 DB로 만들어 버리면 이 작업이 매우 번거로워집니다.

스킬 측에서는 다음과 같은 한 줄로 이력을 기록할 수 있도록 했습니다.

await ctx.history.append({
"time": ctx.now(),
"meta": {"action": "label_added", "label": "要返信"},
...

스키마는 id / time / meta(JSON) / content 뿐입니다. meta를 JSON으로 해두면 스킬마다 자유로운 구조화 정보를 넣을 수 있습니다. 공유 스키마를 정하려고 하면 반드시 파탄 나기 때문에, 처음부터 자유롭게 두었습니다.

부작용이 있는 조작은 구조화한다

Agent-Sin에서는 대화 모드의 답변만으로는 부작용 (Side Effect)이 일어나지 않도록 하고 있습니다.

예를 들어, 사용자에게 "Gmail을 정리하겠습니다"라고 문장으로 답하는 것만으로는 실제로는 아무 일도 일어나지 않습니다.

정말로 Gmail을 정리하려면, Gmail 정리 스킬을 구조화된 skill_call로서 호출해야 합니다.

이것은 사소해 보이지만 상당히 중요합니다.

AI 에이전트에서 무서운 점은, "그럴듯하게 답변했을 뿐인데, 사용자에게는 실행된 것처럼 보이는" 것입니다. 대화 모드(Conversation Mode)의 LLM이 "해두었습니다"라고 쓰면, 사용자는 실제로 수행되었다고 믿게 됩니다. 하지만 실제로는 API가 호출되지 않았다면—이런 일이 발생하면 에이전트 전체에 대한 신뢰가 한순간에 무너집니다.

Agent-Sin에서는 부작용(Side Effect)이 동반되는 스킬(메모 저장, ToDo 추가, 메일 조작, 외부 전송 등)에는 side_effect: true를 설정합니다. 이 플래그가 붙은 스킬이 호출된 턴에서는, LLM이 반환한 대화문은 버리고 스킬의 실행 결과만을 이력(History)에 남기도록 하고 있습니다.

즉, "한 것 같은 기분(Narrative)"을 남기지 않도록 구조적으로 보장하는 것입니다.

  • 부작용 스킬이 호출됨 → 스킬의 실행 결과(사실)만 남김
  • 스킬이 호출되지 않음 → LLM의 대화문만 남김
  • 스킬이 에러로 종료됨 → 에러가 올바르게 남음

LLM의 프롬프트로 "거짓말하지 마"라고 제약하는 것보다, 애초에 내러티브(Narrative)를 물리적으로 제거해 버리는 것이 훨씬 확실합니다.

전체적으로 어떻게 연결되는가

지금까지의 설계를 한 장으로 요약하면 다음과 같습니다.

포인트는 세 가지입니다.

  • 입구는 LLM, 실행은 Program Skill. LLM은 라우팅과 판단을, Skill은 안정적인 실행을 담당합니다.
  • 기억은 Markdown과 SQLite로 분리. 인간이 읽는 것과, 스킬이 구조화된 형태로 가지는 것을 분리합니다.
  • Obsidian Vault는 기억의 뷰(View). 실체는 별도의 디렉토리에 있으며, 사용자가 직접 읽고 쓸 수 있습니다.

멀티 채널에서도 동일한 에이전트

입구는 CLI, Discord, Telegram, 스마트 글래스 등 여러 가지가 있지만, 뒷단의 Chat Engine, 스킬, 기억은 모두 공통입니다.

외출 중에는 스마트폰의 Discord로 말을 걸고, 집에서는 CLI로 내용을 이어 쓰며, 이동 중에 떠오른 메모는 스마트 글래스로 말합니다—어디서 접속하든 동일한 스킬과 동일한 기억을 공유합니다.

"동일한 에이전트를 여러 입구에서 호출할 수 있다"는 점은 개인용 에이전트의 실용성을 상당히 높여준다고 생각합니다.

스킬 자체도 AI가 만들게 하기 (Build Mode)

평소에는 대화 모드로 동작하지만, /build로 모드를 전환하면 스킬 자체를 AI가 생성하게 할 수 있습니다.

생성되는 것은 일반적인 파일(skill.yaml + main.py)이므로, AI가 작성한 코드를 나중에 인간이 편집할 수도 있습니다.

AI 에이전트의 능력 확장이 AI 스스로의 코드 생성으로 이루어지는 재귀적 구조를 가지고 있으며, 이 부분은 아직 실험적인 영역이지만 개인용 에이전트와는 특히 궁합이 좋다고 느낍니다.

만들며 깨달은 점

좋았던 점

  • LLM과 Program의 책임을 분리함으로써, 에이전트가 일상적으로 운용될 수 있는 안정성까지 높아졌다.
  • 스킬이 Git으로 diff를 낼 수 있는 "자산"으로 남는다. 반년 전의 스킬이 지금도 문제없이 동작한다.
  • Obsidian에서 AI의 기억을 직접 읽을 수 있어, 기괴함이 사라지고 신뢰감이 높아진다.
  • 스킬 단위의 SQLite는 생각보다 부작용이 적어 쾌적하다. 스킬 삭제 = DB 통째로 휴지통으로 보내기,로 완결된다.

어려웠던 점

  • "이것이 대화로 끝낼 일인지, 스킬을 호출해야 할 일인지"에 대한 판정은 모델에 따라 정밀도가 다르다. Intent Router를 별도 레이어로 두길 잘했다.
  • side_effect 개념을 LLM의 프롬프트만으로 제약하는 데는 한계가 있다. 내러티브(Narrative)를 물리적으로 버리는 구조로 만들고 나서야 비로소 안정되었다.
  • 멀티 채널 통합은 편리하지만, "어디서 보낸 메시지인지"를 로그에 남겨두지 않으면 나중에 혼란스럽다.

앞으로 하고 싶은 것

  • 스킬 간의 워크플로우(어떤 스킬의 결과를 다른 스킬로 전달하는 것)를 LLM을 거치지 않고 선언적으로 작성할 수 있는 메커니즘.
  • 토픽 단위의 의미 검색 (메모뿐만 아니라 모든 레이어를 횡단하는 검색).
  • Build Mode의 생성 품질을 한 단계 더 높이기.

요약

Agent-Sin을 만들며 가장 강하게 느낀 점은, AI 에이전트는 "LLM 단독"이 아니라 "LLM + 프로그램"으로 생각해야 압도적으로 실용적이 된다는 것입니다.

  • LLM은 모호한 자연어를 읽고, 판단하고, 라우팅(Routing)한다 -
  • **프로그램 스킬 (Program Skill)**은 안정적인 절차를 정확하게 실행한다 -
  • **기억 (Memory)**은 인간도 읽을 수 있는 Markdown과 스킬 고유의 SQLite로 나눈다 -
  • **부작용 (Side Effect)**은 구조화된 skill_call을 통해서만 발생하도록 한다

「무엇이든 LLM에게 시키는 AI 에이전트」가 아니라, LLM을 하나의 강력한 도구로서 프로그램 안에組み込んで(組み込んで, 포함시켜) 사용하는 것.

개인용 AI 에이전트의 실용성은 이러한 책임 분리(Separation of Concerns)를 철저히 함으로써 크게 달라진다고 느끼고 있습니다.

Discussion

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0