AlfaCan Defence Group — Alfa-Coder의 개발팀
대부분의 AI 코딩 도구는 채팅창 안에서 단일 모델로 작동합니다. 지난 몇 달 동안 저희는 다른 것을 구축해 왔습니다. 모든 좌석이 AI 모델인 작은 엔지니어링 팀입니다. 그리고 어떤 좌석도 자신의 작업을 검토하도록 허용되지 않습니다.
Architect가 설계를 작성합니다. Code가 이를 구현하며, 작업을 병렬 에이전트들로 분할합니다. Breaker는 적대자처럼 결과물을 공격합니다. Skeptic은 Breaker의 발견 중 어느 것이 실제인지 확인합니다. Reviewer는 확인된 내용을 수정 계획으로 만듭니다. Validator는 모든 수용 기준(acceptance criterion)에 대한 증거가 있을 때만 승인합니다. Curator는 사양(spec)으로부터의 이탈(drift)을 감시합니다. 각 좌석은 서로 다른 연구소의 모델로 구동됩니다. 코드를 작성하기 전에, 이 좌석들은 회의를 개최합니다. 각자는 독립적으로 답변하고, 의견이 다를 때만 논쟁하며, 인간이 계획을 승인합니다.
가장 중요한 규칙은 다른 어떤 것보다도 중요하며, 프로그램은 프롬프트가 아닌 이것을 강제합니다: 코드는 빌드가 'green' 상태이고 테스트가 통과할 때까지 단계를 넘겨줄 수 없습니다.
이번 주에는 출판 서비스의 OAuth 및 webhook 핵심 기능을 약 90분 만에 구축했으며, 실제 Postgres를 대상으로 하는 46개의 테스트와 1,800 라인의 코드를 완성했고, 심지어 새로운 연결마다 500 에러를 반환했을 SQL 타입 오류를 포함하여 아무도 찾아보라고 요청하지 않은 네 가지 버그까지 수정했습니다.
이곳에 도달하는 과정에서 저희는 예상치 못한 방식으로 문제가 발생했습니다. 공유할 가치가 있는 문제들을 소개합니다.
1. 계속 같은 말을 하던 Reviewer
Curator는 각 단계의 작업을 읽고 다음과 같은 메모를 올립니다: “요구사항 X가 충족되지 않았음”, “이 숫자는 사양에 없음”. 이 Curator는 미묘하게 다른 단어로 계속해서 동일한 메모를 제기했고, 그 단계는 이 문제로 인해 루프(loop)에 빠졌습니다.
모델 자체가 문제는 아니었습니다. 저희는 모델에게 오직 마지막 40개 노트의 번호와 제목만을 제공했을 뿐이었습니다. 상태 정보도, 단계에서의 답변도, 이의 제기도 없었죠. 모델은 그 노트가 수정되었다는 사실을 솔직히 알지 못했습니다. 또한 삭제된 라인을 볼 방법도 없어서, 코드를 제거한 수정 사항은 전혀 변화가 없는 것처럼 보였습니다.
저희는 상태 정보와 답변이 포함된 노트의 전체 기록과, 제거된 라인이 포함된 실제 차이(diff)를 제공했습니다. 그리고 중복 감지 기능까지 추가했습니다. 즉, 다른 단어로 작성된 노트는 새로운 노트가 아니며, 하나의 노트는 최대 두 번의 라운드만 거치도록 제한한 것입니다.
그 수정 사항은 정반대의 버그를 만들어냈습니다. 'Migration 0019 is not applied'와 'Migration 0020 is not applied'가 같은 노트처럼 보였고, 심지어 두 번째 노트는 조용히 사라져버렸습니다. 'no tests for the payment block'과 'no tests for the delivery block'도 마찬가지였습니다. 제목의 유사성만으로는 충분하지 않습니다. 번호, 식별자(identifiers), 경로(paths), HTTP 메서드 등이 노트들을 구별해야 하며, 단지 아마도 반복되는 것일 뿐인 것은 절대 조용히 누락되어서는 안 됩니다. 이는 인간에게 한 번 전달되어, '이것들은 이전 노트처럼 보이는데 — 스테이지에 넘겨줄까요, 아니면 제거할까요?'라는 질문을 받게 됩니다.
한 모듈에 대해서만 적대적 검토(adversarial review)를 네 차례 거치고 나서야 어느 방향으로도 아무것도 빠짐없이 처리하게 되었습니다.
2. 자체 에이전트를 거부한 사고 모델
코드는 읽기 및 쓰기를 위한 병렬 에이전트(parallel agents)를 실행합니다. 저희가 이를 프로덕션 환경에서 처음 가동했을 때, 모든 에이전트는 두 번째 요청에서 HTTP 400 오류와 함께 실패했습니다: "The reasoning_content in the thinking mode must be passed back to the API."
메인 Code 좌석(seat)은 같은 모델을 상대로는 아무 문제 없이 작동했습니다. 차이는 단 한 줄이었습니다. 도구 결과(tool results) 처리 후, 메인 루프는 항상 환경 세부 정보 블록을 추가합니다. 따라서 모델에게 다음 요청은 새로운 턴으로 인식됩니다. 하지만 에이전트 루프는 도구 결과만 보내고 아무것도 보내지 않았기 때문에, 모델은 이를 자신의 턴의 연속으로 간주하고 추론 내용(reasoning) 반환을 요구했던 것입니다.
라이브 API에 두 가지 요청으로 확인했습니다: 트레일링 라인(trailing line)이 없는 경우 400 에러가 발생했고, 있는 경우 200 OK를 받았습니다. 해결책은 도구 결과 후에 짧은 "Step N of M" 라인을 추가하고, 해당 추론 과정(reasoning)을 필요한 제공업체(providers)의 에이전트 턴(agent's turn)에 유지하는 것입니다.
불편했던 점은 이 요청 형태에 대한 테스트가 있었는데, 이것이 모의(mocks)를 대상으로 실행되었기 때문에 깨진 형태를 고착화했다는 것입니다. 이제 모든 릴리스에서는 배포되기 전에 좌석(seats)에서 사용하는 모든 모델에 대해 에이전트를 라이브로 실행합니다.
3. "계획을 승인하지만..."
사람들은 고정된 문구로 계획을 승인합니다. 사람들은 이 문구보다 더 많은 것을 쓰기 때문에, 우리는 그 문구와 설명(explanation)을 함께 받아들이기로 했습니다. 이것이 구멍을 만들었습니다: _"I approve the plan, because it looks fine, but do stage 2 on Redis"_는 승인으로 간주되었고, 수정 사항은 사라졌습니다.
우리는 수정 사항을 표시하는 단어들 — "but", "only", "instead", "later" 등 — 목록에 대해 다섯 번의 검토 라운드를 거쳤습니다. 매번 리뷰어는 통과되는 새로운 표현을 발견했습니다: "because ok with Redis instead of Postgres", "because tests on the client", "because ok (stage 2 on Redis)". 임의의 어휘(open vocabulary)로든 수정 사항이 조립될 수 있습니다.
유지된 버전은 다음과 같습니다: 승인 문구 뒤에는 폐쇄 목록에 있는 전체 구문 중 짧은 이유만 계산됩니다 — "all good", "the plan is clear", "no issues", "it's urgent". 그 외의 모든 것은 수정 사항이며, 프로그램이 다시 묻습니다. 잘못된 승인은 아무도 요청하지 않은 작업을 시작하게 하고; 추가 질문은 한 번의 답변 비용을 발생시킵니다.
4. 자체 게이트웨이가 클라이언트가 요청한 것보다 30배를 사용하고 있었다
게이트웨이는 에디터와 모델 제공업체 사이에 위치합니다. 이는 모든 답변을 32,768 토큰으로 제한했으며, 요청에서 제한을 설정하지 않으면 이 한도를 채웠습니다. 우리가 이 한도를 높였을 때, 적대적 검토(adversarial review)를 통해 게이트웨이가 클라이언트가 설정했던 예산까지 재작성하고 있다는 것을 발견했습니다: Curator는 최대 4,096 토큰만 요청했지만, 제공업체는 131,072 토큰을 받았습니다. 루핑 모델의 경우, 이는 의도된 비용과 시간보다 서른 배에 달하는 것입니다.
우리가 이제 적용하는 규칙은 다음과 같습니다: 클라이언트가 보낸 가장 작은 유효 예산(budget)이 우선하며, 상한선(cap)은 이를 낮출 뿐이고, 상한선은 클라이언트가 아무것도 설정하지 않았을 때만 채워집니다.
동일한 검토 과정에서 131,072 토큰 분량의 답변이 초당 약 45 토큰 속도로 진행되는 경우 25분 동안 완료될 수 없다는 점이 발견되었습니다. 이 경우 작업은 잘리는 스트림(cut-off stream)으로 끝나며 완전히 지불된 실패로 이어집니다. 실제 최대치는 마감 시간 내에 들어가는 양입니다.
5. 제공업체(provider)가 한 시간 동안 다운될 때
어느 날 오후, 한 제공업체가 약 한 시간 동안 모든 요청에 대해 500으로 답변하기 시작했고, 이를 사용하는 좌석이 중단되었습니다. 따라서 각 외부 모델은 이제 예비 시스템을 갖추게 되었습니다: 즉, 제공업체 장애가 발생하거나 모든 좌석이 15초 이상 바쁠 경우, 요청은 다른 연구소의 모델로 전달됩니다. 이는 답변의 첫 번째 바이트가 나오기 전이며, 스트림 중간에는 절대 일어나지 않습니다.
죽은 제공업체를 계속해서 때리는 것을 막는 최초의 회로 차단기(circuit breaker) 버전조차 자체적인 버그를 가지고 있었습니다: 그것은 우리의 큐(queue)—긴 작업으로 바쁜 좌석—를 제공업체 장애로 계산했습니다. 최대 부하 상황에서는 주 모델이 유휴 상태인 동안 모든 요청을 예비 시스템으로 보내어 처리량(throughput)을 절반으로 떨어뜨릴 수 있었습니다. 우리 자체의 부하는 그들의 서비스 중단과는 다릅니다.
우리가 얻은 교훈
- 대부분의 버그는 모델 자체에 있는 것이 아니었습니다. 그것들은 우리가 모델에게 보여준 것, 그리고 우리가 답변을 읽어들이는 방식에 있었습니다.
- 모호한 규칙(fuzzy rule)에 대한 모든 수정은 새로운 경계(edge)를 만들어냈기 때문에, 각각은 적대적 사이클(adversarial cycle)을 거쳤습니다: 공격자 모델, 회의론자 모델, 수정 작업, 그리고 통제 라운드까지 진행되었고, 마침내 아무것도 심각하지 않다는 것을 발견한 라운드가 나올 때까지 반복되었습니다.
- 모의 객체(mocks)에 대한 테스트는 코드가 제공업체 자체에 대한 가정이 아니라, 제공업체에 대한 우리의 가정과 일치함을 증명합니다. 실제 환경에서 테스트하십시오.
사용해 보기
Alfa-Coder는 VS Code 확장 프로그램 또는 브라우저 작업 공간에서 실행됩니다. 토큰이 아닌 작업 시간에 대해 비용을 지불하며, 모델 제공업체를 기다리는 시간은 청구되지 않습니다.
개발자들이 이것을 망가뜨려 주기를 찾고 있습니다. 무료로 사용할 수 있는 시간이 10시간 제공되니, [https://ai-studio.alfa-can.com/alfa-coder]에서 등록하고 대시보드의 테스트 버튼을 눌러주세요. 저희는 솔직한 피드백을 중요하게 생각합니다. 무엇이 고장 났는지, 무엇이 짜증 났는지, 그리고 절대 사용하지 않을 것인지를 알려주시면 감사하겠습니다. 댓글에서 뵙겠습니다.