엔지니어링 노트도구와 실행

서브에이전트 도구를 부모 모델이 고르면 승인이 빠진다, 코드가 정한다

서브에이전트를 붙이는 흔한 방법은 부모 모델에게 서브에이전트를 띄우는 도구를 주고, 프롬프트와 도구와 모델을 띄울 때마다 부모가 정하게 하는 것입니다. 업무 시스템에서는 이 자유 때문에 세 가지가 깨집니다. 서브에이전트는 사람이 지켜보지 않는 곳에서 도는데, 부모가 승인이 필요한 도구를 넘기면 그 도구가 사람 확인 없이 실행됩니다. 부모가 모델 이름을 지어내면 서브에이전트가 단가를 모르는 모델로 돌아 사용량 원장에 비용 0으로 남습니다. 서브에이전트가 끝나지 않으면 부모의 턴도 끝나지 않습니다.

OwlAI에서 서브에이전트를 띄우는 곳은 딥 리서치 하나입니다(「에이전트 루프는 하나만 두고, 기능별 동작은 훅으로 끼운다」). 총괄이 계획을 세우고 조사원과 검증 조사원을 띄워 갈래별 조사를 맡깁니다. 이 서브에이전트의 프롬프트, 보고 형식, 도구를 고르는 규칙, 상한은 코드에 고정된 정의가 정합니다. 총괄 모델이 넘기는 것은 과제와 도구 호출 예산뿐입니다. 이 글은 정의가 무엇을 고정하는지, 조사원의 도구·신원·모델이 어디서 오는지, 총괄이 조사원을 언제까지 기다리는지 다룹니다.

부모가 꾸리는 서브에이전트는 사람이 지켜볼 때 맞는 방식이다

개발 도구의 서브에이전트는 대개 정의 파일에 도구 목록을 적고, 목록을 비우면 부모의 도구를 모두 물려받습니다. 개발자가 화면 앞에서 승인 창을 보고 있으면 이 방식으로 충분합니다. 서브에이전트가 무엇을 부르든 위험한 호출은 사람이 확인하기 때문입니다.

딥 리서치의 조사원은 다릅니다. 조사원 여럿이 동시에 돌고, 각자의 도구 호출은 채팅의 승인 창을 거치지 않습니다. 이 조건에서 부모 모델에게 맡길 수 있는 것과 맡길 수 없는 것을 갈라야 합니다.

부모 모델이 정하면 코드가 정하면
도구 승인 필요 도구도 넘어갈 수 있다 승인 없이 쓸 수 있는 도구만 간다
모델 카탈로그 밖 이름으로 돌 수 있다 대화 모델을 그대로 쓴다
신원 다른 역할로 띄울 인자가 생긴다 질문한 사람 그대로다
대기 언제 끝날지 모델이 정한다 상한이 있고, 넘으면 취소한다

총괄이 넘기는 것은 과제와 도구 예산뿐이다

총괄이 쓰는 서브에이전트 도구는 넷입니다.

도구 하는 일
띄우기 조사원 여럿을 한 번에 띄우고 곧바로 핸들을 돌려준다
지시 보내기 도는 조사원에게 추가 지시를 보낸다. 조사원은 다음 라운드 머리에서 읽는다
기다리기 조사원 하나가 끝날 때까지 기다린다
멈추기 조사원 하나에 취소 신호를 세운다

띄우기 도구가 받는 칸은 조사원마다 다섯입니다. 어느 정의를 띄울지(조사원 또는 검증 조사원), 진행 카드에 보일 짧은 제목, 과제, 도구 호출 예산, 승인된 계획의 어느 항목인지입니다. 프롬프트, 도구 목록, 모델, 추론 강도는 칸이 없습니다. 정의 이름은 두 값 가운데 고르는 칸이라 모델이 다른 이름을 적을 수 없습니다.

도구 호출 예산은 요청일 뿐입니다. 서버가 설정 범위(agentLoop.researcherToolBudgetMin·researcherToolBudgetMax, 기본 3~25)로 잘라 조사원 루프의 도구 호출 상한으로 겁니다. 예산을 빠뜨리면 범위의 가운데인 14를 씁니다. 하한을 쓰면 넓은 과제가 몇 번 만에 잘리고, 상한을 쓰면 예산을 빠뜨린 조사원이 가장 비싸지기 때문입니다. 자른 숫자는 과제 뒤에 한 줄로 붙어 조사원이 믿을 값이 됩니다. 반복 횟수 상한은 예산보다 한 라운드 크게 둡니다. 예산을 다 쓴 뒤 도구 없이 보고서를 쓸 라운드가 남아야 하기 때문입니다.

한 번의 띄우기에 담긴 항목 가운데 하나라도 과제가 비었거나 정의 이름이 틀렸거나 권한이 부모를 넘으면, 하나도 띄우지 않고 사유를 돌려줍니다. 일부만 띄우면 총괄이 나머지를 고쳐 다시 보내면서 이미 뜬 조사원을 한 번 더 띄웁니다.

조사원 하나를 이루는 세 출처: 총괄의 과제, 코드의 정의, 부모의 신원과 모델
그림 1. 조사원 하나를 이루는 세 출처: 총괄의 과제, 코드의 정의, 부모의 신원과 모델

조사원의 도구는 총괄의 도구에서 걸러 낸다

조사원의 도구 목록은 띄우는 순간 총괄이 지금 가진 도구에서 고릅니다. 남기는 것은 조사에 쓰는 도구입니다. 웹 검색과 페이지 본문 읽기, 데이터 조회 분류에 속한 도구 전부, 제품 도움말, 그리고 커넥터의 원격 MCP 도구입니다. 그 가운데 두 부류를 뺍니다.

  • 사용자 PC를 거쳐 실행되는 도구
  • 실행 정책이 그냥 허용하지 않는 도구(승인 필요, 차단)

두 번째 판정은 채팅이 도구를 실행하기 전에 쓰는 권한 확인과 같은 코드로 합니다. 그 확인이 허용으로 답한 도구만 조사원에게 갑니다. 사용자에게 되묻는 도구, 최종 답을 내는 도구, 서브에이전트를 띄우는 도구는 처음부터 고르는 대상이 아닙니다.

데이터 조회 도구는 이름을 골라 적지 않고 분류 전체를 넘깁니다. 조사원도 채팅과 같은 도구 사용 규칙을 받는데, 그 규칙이 조인 전에 조인 경로 도구를 먼저 부르라고 적습니다. 이름을 골라 나열하다 그 도구가 빠지면, 규칙을 지키는 모델은 SQL을 한 번도 실행하지 않고 "조회할 수 없다"로 끝냅니다.

원격 도구는 처음부터 요청에 싣지 않고 이름과 요약만 보이다가 필요할 때 켭니다(「도구 185개 정의는 9만 9천 토큰, 이름만 싣고 고른 도구만 펼친다」). 총괄은 계획과 파견이 일이라 원격 도구를 거의 켜지 않습니다. 그래서 총괄이 아직 켜지 않은 원격 도구도 같은 판정을 거쳐 조사원의 목록에 이름으로 올리고, 조사원이 스스로 켭니다. 켠 도구는 그 조사원 안에만 남습니다.

정의 쪽에도 검사가 하나 더 있습니다. 서브에이전트 정의가 가진 외부 원천이 부모의 외부 원천 안에 있는지, 정의가 승인 필요 도구를 가질 때 부모도 그런 도구를 갖는지 띄우기 전에 봅니다. 넘으면 띄우기를 거절합니다. 비교하는 축은 도구 이름이 아니라 외부 원천과 승인 필요 여부입니다. 부모에게 없는 데이터 도구를 서브에이전트에게 주는 것이 정의를 따로 두는 이유 가운데 하나이기 때문입니다.

신원과 모델은 총괄의 것을 그대로 쓴다

OwlAI에서 에이전트 실행의 신원은 네 가지를 한 값으로 묶습니다. 사용량 한도를 따지는 사람, 사용량 원장에 적히는 사람, 샌드박스를 가르는 사용자, 데이터 권한을 정하는 역할입니다. 이 값은 만들 때 빈 칸을 거절합니다. 조사원은 총괄의 신원을 그대로 받고, 덮어쓰는 인자는 없습니다. 조사원이 실행하는 SQL도 질문한 사람의 역할이 열어 둔 테이블 안에서 돌고, 조사원이 쓴 토큰도 그 사람의 한도에서 깎입니다.

모델과 추론 강도도 대화가 고정한 것을 물려받습니다. 띄우기 인자로 모델을 받는다면, 총괄이 지어낸 모델 이름이 그대로 호출 대상이 됩니다. 카탈로그에 없는 모델은 단가가 없어 원장 비용이 0으로 남고, 그 모델이 도구 호출을 받아 주는지도 알 수 없습니다.

조사원은 자기 스레드에서 돌고, 띄울 때 총괄의 실행 컨텍스트를 복사해 갑니다. 사용량을 어느 대화·사용자에게 매길지, 어느 자격증명(개인 키 또는 공용 키)으로 부르는지, 턴 마감이 언제인지가 그 안에 있습니다. 복사하지 않으면 조사원의 모델 호출이 원장에 한 행도 남지 않거나, 개인 키로 쓴 토큰이 공용 키로 적힙니다. 조사원의 원장 행에는 서브에이전트 단위라는 표시, 부모 단위, 깊이 1이 함께 붙어 어느 턴의 어느 조사원이 쓴 사용량인지 거슬러 갈 수 있습니다.

서브에이전트를 다루는 도구 넷은 깊이 0인 총괄에게만 실립니다. 조사원에게는 거절하는 대신 도구 자체를 보여 주지 않습니다. 조사원의 요청 앞머리에 쓸 수 없는 도구가 실리지 않고, 조사원이 하위 조사원을 띄우려다 거절당하는 라운드도 생기지 않습니다.

총괄은 기다리기 도구로 기다리고, 기다림에는 상한이 있다

띄우기 도구는 조사원을 전용 스레드 풀에 넣고 곧바로 돌아옵니다. 한 총괄이 동시에 돌리는 조사원은 기본 4명이고(agentLoop.maxParallelSubagents), 나머지는 진행 카드에 대기 중으로 보이다가 자리가 나면 출발합니다. 스레드로 띄우는 것은 총괄의 도구 실행이 라운드마다 이벤트 루프를 열고 닫기 때문입니다. 비동기 태스크로 띄우면 라운드가 끝날 때 조사원도 함께 죽습니다. 풀을 따로 두는 것은 스레드 기아 교착을 피하려는 것입니다. 총괄과 조사원과 도구가 공용 풀을 나눠 쓰면, 조사원을 기다리는 쪽이 스레드를 쥔 채 막혀 조사원이 돌 스레드가 남지 않습니다.

조사원의 결과는 총괄의 메일박스(밖에서 온 입력을 다음 라운드 머리에 싣는 큐)에 들어오고, 도구 결과 한 쌍의 형태로 총괄의 다음 라운드 입력에 실립니다(「에이전트 루프는 하나만 두고, 기능별 동작은 훅으로 끼운다」). 결과가 아직 실리지 않은 조사원이 남아 있으면 총괄의 최종 답은 실행 전에 거절됩니다. 그대로 받으면 그 조사원의 결과는 누구에게도 닿지 않습니다.

기다림을 도구로 둔 것은 모델의 행동 때문입니다. 기다림이 "도구를 부르지 않고 응답하기"라는 암묵적인 동작이면, 일부 모델은 그 대신 지시 보내기나 멈추기를 부릅니다. 실측에서 한 모델은 띄운 지 30초 만에 멈추기로 살아 있는 조사원을 끊어 세 번 중 두 번 실패했습니다. 기다리기 도구는 조사원이 끝날 때까지 막고 있다가 상태만 돌려줍니다. 한 번의 기다림은 도구 하나의 실행 시간 상한보다 짧게 끊고 "아직 도는 중이니 다시 기다려라"를 돌려줍니다. 실행 시간 상한에 잘려 오류를 받으면 모델은 다시 조급한 호출로 돌아가기 때문입니다. 기다리는 동안 사용자가 새 메시지를 보내면 기다림을 풀어 그 말을 먼저 읽게 합니다(「400으로 막힌 대화, 도구 결과는 저장할 때·모양은 보낼 때 맞춘다」).

기다림의 상한은 기본 1,800초입니다(agentLoop.subagentJoinTimeoutSeconds). 상한이 없으면 죽어서 아무것도 보내지 않는 조사원 하나가 턴 전체를 붙잡습니다. 상한을 600초로 두면 복잡한 SQL을 34라운드, 11분에 걸쳐 쓰는 정상 작업도 잘립니다. 같은 값이 조사원 한 명의 실행 시간 상한이기도 해서, 조사원은 1,800초와 "턴의 남은 시간에서 보고서 작성 몫을 뺀 시간" 가운데 짧은 쪽 안에서 돕니다. 작성 몫은 기본 600초입니다(agentLoop.researcherWriterReserveSeconds). 이 시간을 떼어 두지 않으면 조사원이 턴 시간을 다 쓰고 보고서가 비어 나갑니다. 마감은 띄운 시각을 기준으로 정하므로, 풀에서 자리를 기다리다 마감이 지난 조사원은 출발하지 않고 시간 부족으로 끝납니다.

멈추기는 취소 신호를 세우고, 조사원은 라운드 머리와 도구 실행 중 양쪽에서 그 신호를 봅니다. SQL 실행 도구는 이 신호를 DB 쪽 쿼리 취소로 잇습니다(「읽기 전용과 쿼리 취소는 DB 서버에 닿아야 한다」). 사용자가 턴을 중지하면 도는 조사원을 모두 멈추고 기다리던 총괄을 곧바로 깨웁니다.

설계에서 고른 것

부모 모델의 자유 대신 코드의 정의를 골랐습니다. 대가는 새로운 종류의 서브에이전트가 필요할 때마다 코드에 정의를 더해야 한다는 점입니다. 총괄이 과제에 맞춰 조사원의 도구를 그때그때 좁히거나 넓힐 수도 없습니다. 그 대신 조사원이 무엇을 할 수 있는지는 배포된 코드와 관리자의 실행 정책만 보고 답할 수 있습니다.

조사원에게는 승인 없이 쓸 수 있는 도구만 줍니다. 메일 발송이나 티켓 생성처럼 승인이 필요한 일은 딥 리서치 안에서 할 수 없고, 사용자는 딥 리서치를 끄고 일반 질문으로 요청해야 합니다. 조사원마다 승인 창을 띄우는 방법은 승인을 지키지만, 조사원 넷이 동시에 승인을 기다리면 사용자는 어느 조사의 어느 호출인지 판단하기 어렵습니다.

일은 한 겹으로만 나눕니다. 조사원이 다시 조사원을 띄우면 더 큰 조사를 잘게 쪼갤 수 있지만, 갈래마다 도구 예산과 마감이 더 잘게 나뉘고 계획 검토가 여러 곳으로 흩어집니다(「딥 리서치 조사원은 원문 대신 보고서를 넘긴다, 조사 범위와 빈칸까지」). 신원·모델·권한 검사도 한 겹에만 같은 규칙으로 걸면 됩니다.

대기 상한은 실패를 끊는 장치이고, 잘린 조사원의 몫은 총괄이 보고서 전 검토에서 빈칸으로 보고 다시 맡기거나 보고서에 밝힙니다. 상한을 더 늘리면 느린 정상 조사는 덜 잘리지만 죽은 조사원 하나가 턴을 붙잡는 시간도 같이 늘어납니다.

서브에이전트에게 맡기는 일이 서로 독립이고 사람이 그 실행을 지켜보지 않는다면, 서브에이전트의 도구와 모델과 신원은 부모 모델의 인자가 아니라 코드의 정의와 관리자가 정한 정책에서 오게 하는 편이 맞습니다.

어떤 문제를 해결하나요?

도구와 실행의 다른 글