AI 에이전트 · AI Engineering

반환값이 맞으면 원래 요청도 해결된 걸까요

반환값 검사 5개는 통과해도 함수 재사용 조건을 어길 수 있는 반례로, 선택한 검사와 전체 목표의 차이를 확인해요.

AI AgentPythonSQLiteWorkflow

문제의 출발점

앞 편에서 실행과 기록의 루프를 만들더라도 작업 목록은 설계해야 한다는 문제를 남겼어요. AI에게 목록까지 만들게 하면 이 구간도 자동화할 수 있을 것 같아요. 계산 사례라면 금액 변환을 구현하는 작업과 합계를 구현하는 작업으로 나누고, 앞 작업의 결과를 뒤 작업이 사용하도록 연결할 수 있어요.

Task를 입력과 결과, 검사 조건을 가진 함수처럼 연결한다고 생각해 볼게요. 각 단계의 검사가 통과하면 원래 요청도 해결될 것 같아요. 하지만 검사가 기대한 반환값을 확인했다는 사실과, 요청의 모든 조건을 확인했다는 사실은 달라요. 어떤 목표 조건을 어느 검사로 확인했는지까지 연결해야 해요. 이 편에서는 반환값이 맞아도 함수 재사용 조건을 놓치는 사례로 그 차이를 살펴봐요.

작업을 나누기 전에 요청의 기준을 남기기

VibeMarshal은 요청을 바로 Task 이름으로 바꾸기보다 GoalContractRevision으로 정리해요. 사용자 원문, 관찰할 결과, 반드시 충족할 수용 기준인 Hard AC, 제약과 비목표, 허용 효과를 담아요. 정규화한 목표를 독립적으로 검토하는 경로도 있어요. 목표 준비 구현

계산 사례의 기준을 세 가지로 정리할 수 있어요. 아래 값과 재사용 조건은 실제 fixture의 검사에서 가져온 것이고, 표는 완전한 Engine 입력 JSON이 아닌 설명용 요약이에요.

작업을 나누기 전에 요청의 기준을 남기기 데이터 표
기준확인할 결과
AC-1 금액 변환cents("12.34") == 1234, cents("0.05") == 5, cents("3") == 300
AC-2 합계total(["1.50", "2.25"]) == 375, total([]) == 0
AC-3 함수 재사용두 가격을 합산할 때 invoice에 바인딩된 cents가 두 번 호출됨

Goal은 결과를 판단할 기준이고, Plan은 그 결과를 만들 작업과 순서예요. T1을 cents 구현과 검사, T2를 total 구현과 검사로 나누면 T2는 T1에 의존해요. 이 선후관계를 Task DAG로 표현할 수 있어요. DAG가 있다고 작업을 모두 동시에 실행하는 것은 아니에요. 현재 운영 계약은 같은 프로젝트의 Attempt를 직렬로 제한해요.

반환값은 맞지만 요청은 어기는 경로

total이 cents를 재사용하지 않고 금액을 직접 변환한다고 가정해 볼게요.

from decimal import Decimal
from money import cents

def total(prices: list[str]) -> int:
    return sum(int(Decimal(price) * 100) for price in prices)

이 구현은 예제의 합계 반환값을 맞출 수 있어요. cents 자체도 올바르게 구현돼 있다면 두 함수의 반환값만 보는 검사는 통과할 수 있어요. 그러나 AC-3은 만족하지 않아요. 가격을 변환하는 정책이 두 곳에 생겼고, 기존 함수의 호출도 빠졌어요.

이를 원본 fixture의 격리 복사본에서 확인했어요. 테스트 파일은 그대로 두고, 먼저 반환값을 확인하는 5개 검사만 선택해 실행했어요. 이어서 재사용 조건을 포함한 전체 6개를 실행했어요. 마지막에는 total이 cents를 사용하도록 고친 뒤 같은 전체 검사를 다시 실행했어요.

반환값은 맞지만 요청은 어기는 경로 데이터 표
구현과 검사 범위실행 결과해석
직접 변환하는 total, 반환값 검사 5개 선택5개 통과, 종료 코드 0선택한 반환값은 맞음
같은 구현, 전체 6개 검사5개 통과·1개 실패, 종료 코드 1함수 재사용 조건을 어김
cents 재사용으로 수정, 전체 6개 검사6개 통과, 종료 코드 0fixture의 모든 검사 충족

실패한 검사는 mock.patch.object(invoice, "cents", wraps=invoice.cents)로 합계 모듈에 바인딩된 함수를 감시해요. 기대 호출 수는 2지만 직접 변환하는 구현의 호출 수는 0이었어요. ‘함수 반환값이 맞다’와 ‘요청한 방식으로 동작한다’가 다른 기준임을 보여줘요. 변환 검사, 재사용 검사

처음 선택한 5개 검사는 AC-1과 AC-2의 반환값만 확인했어요. AC-3을 확인하는 재사용 검사는 포함하지 않았어요. 따라서 이 실행은 불완전한 검사 범위를 비교하기 위한 관측이며 작업의 완료 근거로 채택하지 않았어요. 실제 Core에서 모든 Task가 완료됐거나 잘못된 Plan이 승인됐다는 기록도 아니에요. 확인한 것은 선택한 검사들의 통과를 전체 Goal의 충족으로 확대할 때 놓칠 수 있는 조건이에요.

Task의 성공과 Goal의 완료를 나누기

Engine은 개별 Task validation과 Plan 수준의 integration validation, 전체 Goal의 판정을 구분해요. complete_task는 해당 Task의 필수 검사 ID 집합과 최신 결과를 대조해요. 빠진 검사나 실패가 있으면 완료를 거부하고, 미확정 실행이나 효과도 확인해요. Task 완료 조건

전체 목표는 별도의 기준으로 확인해요. runtime은 최신 Goal 검사 근거를 모아 판정에 사용하고, record_goal_verdict는 제출된 판정이 같은 Goal·Plan에 연결됐는지와 criterion coverage, Task 완료, integration 결과 및 실제 evidence를 대조해요. 개별 작업이 끝났다는 사실을 전체 목표 달성으로 바로 바꾸지 않아요. Goal 결과 집계, Goal 판정 수용 조건

그렇다고 이 구조가 원래 의도를 완벽히 알아낸다는 뜻은 아니에요. Goal 정규화에서 중요한 요구를 빠뜨리거나 의미 검사가 잘못 판단하면, 기록과 연결이 정확해도 문제는 남아요. 코드가 강제하는 것은 계약과 근거의 일치이고, 계약의 내용까지 항상 옳다는 보장은 아니에요.

Task를 함수처럼 연결하는 생각은 작업 사이의 입출력을 설계하는 데 도움이 돼요. 하지만 파일 수정이나 외부 효과를 가진 실행은 순수 함수가 아니며, 이 사례가 함수 합성의 수학적 보증을 입증하는 것도 아니에요. 원래 목표와 제약을 전체 경로에 다시 대조해야 해요. 다음 편에서는 Task 목록을 상세히 만들기 전에 어떤 해결 경로를 검토할지 살펴봐요.

공통 검증 부록

TECHNICAL SERIES

AI에게 문제 해결 과정을 맡기는 설계

개발 과정의 저장에서 시작해 작업과 목표의 차이, 해결 경로 비교, 역할 분담과 실패 복구를 VibeMarshal의 구현으로 살펴보는 다섯 편의 연재예요.

시리즈 전체 보기

현재 2/5편

  1. 1개발 과정을 저장하면 다음 프로젝트에도 쓸 수 있을까요
  2. 2반환값이 맞으면 원래 요청도 해결된 걸까요
  3. 3AI가 제안한 해결 경로는 무엇으로 고를까요
  4. 4AI의 계획과 실행은 누가 승인하고 검증할까요
  5. 5실패한 작업을 목표에 맞게 다시 이어 가려면