에이전트 플랫폼 · Spark
누가 말하든 일은 CEO 에이전트의 작업지시서로 시작하고, 위임과 도구 실행은 서버가 통제합니다
에이전트가 서로 이슈를 넘기며 일하는 사내 플랫폼입니다. Paperclip(에이전트 이슈 관리 오픈소스)을 포크했고, 회사 기능은 코어 밖 서비스 폴더 아래 HTTP 서비스로 붙였습니다. 예외는 Work OS(업무 상태·승인·실행 기록 앱)와 Spark뿐입니다. 직원은 Spark로 업무 DB 4개를 읽기 전용으로 묻고, 일은 CEO 에이전트에게 맡깁니다. 위임이 길어지면 요청이 바뀌고, 도구 하나가 돈이나 외부 노출로 이어집니다. 그래서 통제는 프롬프트가 아니라 서버 코드에 뒀습니다.
- 작업지시서 원문은 해시로 잠급니다. 위임이 단계를 거칠수록 요청 문장이 바뀝니다. 원문을 NFC 정규화하고 줄바꿈을 맞춘 뒤 sha256과 함께 잠급니다. 클라이언트가 보낸 해시는 서버가 덮어씁니다. 중간 요약 대신 원문을 넘길 때 supervisor 오류가 줄었다는 외부 벤치마크를 따랐습니다.
- 위임 루프는 서버가 끊습니다. 플러그인은 이슈가 생긴 뒤에 지켜볼 수만 있어 서버 코어를 고쳤습니다. 깊이 2, 지시서당 5회, A→B→A 금지입니다. 깊이는 값을 바꿀 수 있는 열 대신 실제 부모 이슈를 거슬러 올라가며 셉니다. 6번째 위임과 깊이 위조는 테스트에서 거절됩니다.
- Jev가 추리고 Gemini가 고릅니다. Jev가 100여 개 전부를 채점해 상위 5개로 추리면 Gemini가 그 중 1개를 고릅니다. 실패, 시간 초과, 차단기(2회 실패 후 300초) 중 하나면 Jev 단계를 건너뛰고 Gemini가 전체 목록에서 바로 고릅니다. 롤아웃 대상은 sha256 앞 8자리로 나눕니다.
- 승인은 정확히 한 번만 씁니다. 승인 행을 FOR UPDATE로 잠그고, 부분 유니크 인덱스가 한 번 더 막습니다. 실행이 실패해도 승인은 소진됩니다. 중복 방지 키는 ON CONFLICT DO NOTHING 뒤 재조회라 경쟁이 없습니다. 동시 요청 10개에 실행 1개를 테스트로 확인했습니다.
- 쓰기 승인은 미리보기에 서명합니다. 입력 해시, 미리보기 해시, 요청자, 실행 ID, 만료 시각을 정규 JSON으로 묶어 HMAC-SHA256으로 서명합니다. DB 쓰기 권한이 있어도 키 없이는 통과하지 못합니다. 키에 시각을 넣지 않아 모델이 재시도해도 승인이 늘지 않습니다.
- Spark DB 조회는 질의마다 새 연결을 엽니다. 읽기 전용 역할만으론 부족했습니다. set_config 한 번이면 타임아웃과 읽기 전용이 풀리고, 재사용 연결이 그 상태를 다음 사람에게 넘깁니다. 위험 함수 23개와 세미콜론, 쓰기 CTE를 거른 뒤 연결 1개를 열고 끝나면 닫습니다.
- 비싼 질의는 돌리기 전에 거절합니다. EXPLAIN 비용이 100만을 넘으면 실행하지 않습니다. 10초 제한은 DB 설정과 앱의 소켓 종료 두 곳에 겁니다. 결과는 500행, 100KB에서 자릅니다. RLS가 결과를 조용히 비우지 않게 역할에 BYPASSRLS를 줬고, 감사 로그엔 SQL 해시만 남깁니다.
- 유료 상한 초과는 429가 아니라 403입니다. OpenAI 계열 SDK는 429와 5xx를 알아서 재시도합니다. 그래서 하루 3만원을 넘기면 403에 x-should-retry: false를 붙입니다. 장부는 KST 날짜별 Redis 키입니다. 서비스 키는 추론 경로만 부를 수 있고, 겹치는 키는 버립니다.