← 모든 프로젝트

회계 에이전트

직원이 메시지로 일을 맡기면 WEHAGO 안에서 처리하고, 사람이 허락하기 전에는 아무것도 신고하거나 보내지 않아요

기간
2026년 8월 – 지금
대상
한국의 작은 세무회계 사무소
역할
1인: 설계, 개발, 배포, 운영
기술
TypeScript, Claude Agent SDK, Playwright, Telegram Bot API, SQLite, Docker, Xvfb
규모
커밋 400개, 5.1만 줄, 12칸 인수 테스트
외주 작업, neoworks

다른 언어: english

어떤 프로젝트인가요

세무회계 사무소는 고객사들의 급여, 원천세, 부가세 신고를 한국 회계 웹앱인 WEHAGO에서 처리해요. 이 에이전트는 "A사 8월 급여 해줘" 같은 텔레그램 메시지를 받으면 실제 브라우저로 WEHAGO를 열고 일을 해요. 홈택스 제출, 전표 전송, 카카오 채널 메시지처럼 사무소 밖으로 나가는 일에 닿으면 멈추고, 사람이 승인할 때까지 기다려요.

소리 없는 영상에서 시작했어요

받은 자료는 사무소에서 찍은 소리 없는 화면 녹화 세 개였어요. 급여(5분 26초), 부가세(4분 16초), 카카오 채널 답장(1분 1초). 3초마다 프레임을 뽑아서 한 장씩 읽으면서 메뉴 코드, 클릭 순서, 사람이 판단을 내리는 지점을 전부 적었어요. 여기서 나온 두 가지가 이후 모든 걸 결정했어요. WEHAGO는 ActiveX나 보안 플러그인 없이 완전히 웹으로 돌아가요. 그리고 사무소의 세무 인증서가 이미 WEHAGO 서버에 등록돼 있어서, 네이티브 인증서 모듈은 건드릴 필요가 없었어요.

어려웠던 부분

표가 캔버스예요. WEHAGO는 RealGrid로 캔버스에 표를 그려서, DOM이나 스크린샷만으로는 표가 진짜 비어 있는지 알 수 없어요. 초반에 에이전트가 직원이 두 명 있는 회사에 대해 "급여 데이터 없음"이라고 보고한 적이 있어요. 원인은 기본 지급 구분인 "급여"로 조회했는데, 그 회사 데이터는 "급여 + 상여"로 들어가 있었던 거예요. 같은 세션, 같은 달에 그 필드 하나만 바꿨더니 0행이 2행이 됐어요. 그 뒤로는 0이라는 결과가 나오면 서버 응답 자체를 근거로 확인하게 했어요.

PDF에는 윈도우 프로그램이 필요해요. WEHAGO의 출력은 로컬 데스크톱 에이전트를 거쳐요. 앱의 webpack 청크 866개를 전부 내려받아서 grep으로 뒤졌어요. 알고 보니 서버는 리포트 데이터를 평범한 form POST로 내주고, 그리는 것만 로컬에서 하고 있었어요. 그래서 에이전트가 데이터를 직접 받아서 급여대장과 원천징수이행상황신고서를 스스로 그려요.

너무 많이 묻는 것도 버그예요. 첫 버전은 저장할 때마다, 닫을 때마다 확인을 받았어요. 사무소에서 처음 실제로 써 보니 지시 하나가 질문 네 개로 늘어났고 30분이 걸렸어요. 그중 하나는 "'신고서 목록' 클릭을 진행할까요?"였어요. 그래서 규칙을 뒤집었어요. 사무소 밖으로 나가는 일만 확인을 받아요. 카카오 발송, 홈택스 제출, 전표 전송(F3)이요. WEHAGO 안에 머무는 일은 그냥 진행해요. 모델이 스스로 답할 수 있는 질문을 하면 그 질문을 되돌려 받아요.

아무것도 검증하지 않은 통과. 인수 테스트는 12칸이었어요. 고객사 3곳 x 업무 4개(급여, 사업소득, 일용직, 부가세). 독립 리뷰에서 "12/12"가 여러 번 기각됐는데, 매번 리뷰가 맞았어요. 한 번은 법인에 개인사업자 기준으로 부가세 과세기간을 계산하고 있었어요. 한 번은 지금까지의 "매출" 수치가 전부 사실 매입이었어요. 다섯 번째 리뷰에서는 어떤 칸도 원천세나 부가세 신고 스킬을 아예 호출하지 않았다는 게 드러났어요. 프롬프트가 실제 결과물을 요구한 적이 없었으니, 12/12는 아무것도 요구받지 않았다는 뜻이었어요.

제가 한 일

5일 동안 아키텍처가 세 번 바뀌었어요. 첫날은 게이트가 달린 고정 워크플로 실행기였어요. 둘째 날에는 하드코딩된 매크로 세 개가 맞지 않는 구조라서, 업무 하나하나를 에이전트가 골라 쓸 수 있는 CLI 스킬로 바꿨어요. 확인 토큰은 ed25519로 서명해서, 토큰을 검사하는 코드가 토큰을 만들 수는 없게 했어요. 다섯째 날에는 Claude Agent SDK가 루프를 맡게 됐고, 저장소는 텔레그램 브리지와 도구 네 개로 정리됐어요. WEHAGO 브라우저 도구, 스킬, ask_user, 진행 상황 카드요. 모든 도구 호출은 canUseTool 콜백 하나를 거치고, 사람이 승인하거나 거절하는 곳이 바로 거기예요. 모델이 머릿속으로 계산한 납부세액을 리뷰에서 잡아낸 뒤로는, 그런 금액은 도구가 WEHAGO에서 다시 읽어 온 경우에만 답장에 넣어요.

그 밖에 내린 결정들이에요.

  • 전표 전송은 이제 확인 카드 뒤에 있는 게 아니라 아예 빠졌어요. 그 카드는 사람이 매번 "중지"를 눌러야만 의미가 있었는데, 습관은 통제 수단이 아니에요. 스킬 목록에서 뺐고, 혹시 다시 들어올 때를 대비해 정책 레이어가 그 이름을 계속 걸러내요.
  • 헤드리스로 돌린 WEHAGO 세션은 몇 초 만에 죽어서, 브라우저는 docker 안에서 Xvfb로 화면을 띄운 채 돌려요.
  • 급여 작업 도중에 봇이 한 번 죽었어요. telegraf의 90초 핸들러 타임아웃, bot.catch 누락, 재시작 뒤 남은 X 락 파일 때문이었어요. 세 가지 다 고치고 테스트했어요.
  • 9분 동안 멈춰 있던 건, 실제 화면이 그려지는 데 최대 1991ms가 걸리는데 에이전트가 메뉴를 클릭하고 122ms 만에 메뉴가 없다고 판단했기 때문이었어요. 이제는 기다리고, 재시도해도 진전이 없으면 멈춰요.
  • 테스트용으로 저희 WEHAGO 계정에 버려도 되는 회사 세 개를 만들었고, SMS가 나가기 전에 멈추는 안전장치를 걸어 뒀어요.

지금은

docker에서 돌아가고, 허용 목록에 있는 텔레그램 사용자하고만 대화해요. 10월 9일에는 대표님 허락을 받고 실제 고객사 세 곳의 다음 부가세 예정신고를 저장했어요. 새로 다시 읽어서 확인했을 때 3/3이 맞았고, 이미 마감된 상반기 신고는 건드리지 않았어요. 홈택스 제출과 전표 전송은 당분간 사람이 직접 해요.