장기 실행 AI Connection 설정하기

장기 실행 AI Connection 설정하기

응답에 몇 분이나 몇 시간이 걸리는 에이전트를, 각 요청을 즉시 승인하고 Confident API로 결과를 돌려보내는 방식으로 평가해요.

출처: 문서

본문

개요

이 가이드는 출력을 만들기까지 초가 아니라 몇 분, 혹은 몇 시간이 걸리는 앱을 가진 팀을 위한 것이에요 — 딥 리서치 에이전트, 다단계 파이프라인, 응답 전에 작업을 큐에 넣는 어떤 것이든요.

기본적으로 AI Connection은 동기식으로 동작해요: Confident AI가 엔드포인트를 호출하고, 응답할 때까지 연결을 유지하며, 출력 key path로 HTTP 응답에서 실제 출력을 바로 파싱해요. 이것은 장기 실행 에이전트에서 무너져요 — 연결이 타임아웃되고, golden 하나마다 연결을 하나씩 잡아두는 것은 확장되지 않아요.

Async Responses 모드는 교환의 후반부 방향을 뒤집어요:

  1. Confident AI가 각 golden을 고유한 testCaseId와 함께 엔드포인트로 보내고, 출력을 기다리지 않고 연결을 닫아요.
  2. 엔드포인트가 빠른 2xx로 요청을 승인하고 실제 작업을 백그라운드에서 시작해요.
  3. 에이전트가 끝나면 POST /v1/test-runs/evaluate/{testCaseId} 엔드포인트로 결과를 돌려보내요.
  4. Confident AI가 결과가 도착하는 대로 각 테스트 케이스를 평가하고, 모든 결과를 받으면 테스트 런을 마무리해요.
sequenceDiagram
    participant CA as Confident AI
    participant EP as Your Endpoint
    participant AG as Your Agent

    loop For each golden in dataset
        CA->>EP: POST payload with testCaseId
        EP-->>CA: 2xx acknowledgement (immediate)
        EP->>AG: Queue the work
    end

    loop When each agent finishes (minutes or hours later)
        AG->>CA: POST /v1/test-runs/evaluate/{testCaseId}
        CA->>CA: Evaluate test case
    end

    CA-->>CA: Finalize test run once all results arrive

Async Responses는 single-turn 평가에서만 사용할 수 있고, streaming 응답 모드와는 결합할 수 없어요 — HTTP Streaming이나 SSE Streaming이 선택된 동안에는 토글이 비활성화돼요.

만들어 보기

AI connection 구성하기

아직 없다면 Project Settings → AI Connections에서 AI connection을 만들고 엔드포인트를 가리키게 해요. 전체 설정은 AI Connections를 보세요.

장기 실행 모드에서 중요한 한 가지: 페이로드에 testCaseId가 포함되어야 해요. 에이전트가 결과를 올릴 때 그것을 다시 보내야 하거든요. JSON payload 모드에서는 testCaseId 변수를 요청 본문에 매핑해요:

{
  "input": golden.input,
  "testCaseId": testCaseId
}

Code 모드에서는 generate_payload가 testCaseId를 파라미터로 받아요 — 같은 방식으로 반환 dict에 포함시키면 돼요.

async connection에는 Actual Output Key Path를 구성할 필요가 없어요. 출력은 엔드포인트의 즉시 응답에서 파싱되는 게 아니라 결과 엔드포인트에서 수집되거든요.

Async Responses 켜기

AI connection의 General 탭을 열고 Async Responses를 켜요.

AI connection General 탭의 Async Responses 토글

켜고 나면 토글 아래 상태 텍스트가 "Results are posted back via the Confident API results endpoint"로 바뀌어요 — 이제 Confident AI는 각 요청을 보낸 후 출력을 기다리는 대신 연결을 닫아요.

스트리밍 응답 모드가 선택된 동안에는 토글을 쓸 수 없어요. 먼저 connection의 응답 모드를 HTTP Response로 되돌리세요.

빨리 승인하고, 백그라운드에서 작업하기

엔드포인트는 즉시 2xx를 반환하고 실제 작업을 백그라운드 작업에 넘겨야 해요. Confident AI는 승인을 "요청 수신됨"으로 취급해요 — 응답 본문의 어떤 것도 파싱되지 않아요.

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()

@app.post("/generate")
async def generate(request: dict, background_tasks: BackgroundTasks):
    background_tasks.add_task(run_agent, request["input"], request["testCaseId"])
    return {"status": "accepted"}

connection에서 Ping Endpoint를 클릭해 확인해요 — async connection에서 성공 ping은 엔드포인트가 요청을 승인하는지만 확인해요.

평가 실행하기

데이터셋으로 single-turn evaluation을 실행하고 async AI connection을 출력 생성 방식으로 선택해요. 평가 대화상자는 이 connection이 비동기로 응답하며 결과가 공개 엔드포인트로 돌려보내져야 한다는 안내를 보여줘요.

테스트 런은 즉시 생성되고, 결과를 기다리는 동안 in progress 상태를 유지해요.

평가용 결과 돌려보내기

에이전트가 테스트 케이스를 끝내면, 그 요청 페이로드의 testCaseId로 결과를 결과 엔드포인트에 올리고 Project API Key로 인증해요. SDK는 CONFIDENT_API_KEY에서 키를 읽어요(deepeval login이나 환경 변수로 설정).

Python

from deepeval import send_test_case_response

send_test_case_response(
    test_case_id="<TEST-CASE-ID>",
    actual_output="The capital of France is Paris.",
)

TypeScript

import { sendTestCaseResponse } from "deepeval";

await sendTestCaseResponse({
  testCaseId: "<TEST-CASE-ID>",
  actualOutput: "The capital of France is Paris.",
});

curL

Request (POST /v1/test-runs/evaluate/{testCaseId}) — API reference

curl -X POST "https://api.confident-ai.com/v1/test-runs/evaluate/{testCaseId}" \
  -H "CONFIDENT_API_KEY: <PROJECT-API-KEY>" \
  -H "Content-Type: application/json" \
  -d '{
  "actualOutput": "The capital of France is Paris."
}'
import requests

response = requests.post(
    "https://api.confident-ai.com/v1/test-runs/evaluate/{testCaseId}",
    headers={
        "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    },
    json={
        "actualOutput": "The capital of France is Paris."
    },
)

print(response.json())
const response = await fetch("https://api.confident-ai.com/v1/test-runs/evaluate/{testCaseId}", {
  method: "POST",
  headers: {
    "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "actualOutput": "The capital of France is Paris."
  }),
});

const data = await response.json();
console.log(data);
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	body := `{
  "actualOutput": "The capital of France is Paris."
}`

	req, err := http.NewRequest("POST", "https://api.confident-ai.com/v1/test-runs/evaluate/{testCaseId}", strings.NewReader(body))
	if err != nil {
		panic(err)
	}
	req.Header.Set("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "actualOutput": "The capital of France is Paris."
            }""";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.confident-ai.com/v1/test-runs/evaluate/{testCaseId}"))
            .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.body());
    }
}
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::Client::new()
        .post("https://api.confident-ai.com/v1/test-runs/evaluate/{testCaseId}")
        .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
        .json(&json!({
          "actualOutput": "The capital of France is Paris."
        }))
        .send()
        .await?;

    println!("{}", response.text().await?);

    Ok(())
}

모든 필드는 선택 사항이에요 — 빼 놓은 것은 golden의 값으로 폴백해요:

  • actualOutput — 에이전트가 만든 출력(string).
  • retrievalContext — RAG 메트릭용 검색된 문서(string[]).
  • toolsCalled — 툴 메트릭용 에이전트가 호출한 툴(ToolCall[]).
  • expectedTools — 기대했던 툴 호출(ToolCall[]).
  • metadata — 테스트 케이스에 붙일 임의의 메타데이터(object).

성공적인 제출은 "status": "accepted"를 반환하고 테스트 케이스가 즉시 평가돼요. 모든 테스트 케이스의 결과가 도착하면 테스트 런이 마무리되고 결과가 평소처럼 대시보드에 나타나요.

규칙과 한계

  • Single-turn 전용. 대화형(다중 턴) 테스트 런은 결과 게시를 400으로 거부해요.
  • 결과 창은 몇 시간. 각 테스트 케이스의 testCaseId는 평가 시작 후 몇 시간 유효해요. 만료 후 게시하면 410 Gone을 반환해요.
  • 제출은 멱등(idempotent)이에요. 같은 testCaseId를 두 번 게시하면 "status": "already_received"를 반환하고 첫 결과가 유지돼요.
  • 완료된 런은 닫혀요. 이미 끝난 테스트 런에 게시하면 409를 반환해요.

다음 단계

이제 응답에 몇 분이나 몇 시간 걸리는 에이전트를 평가할 수 있어요 — 각 요청을 빠르게 승인하고, 실제 작업을 백그라운드에서 하고, 끝나는 대로 결과를 올리면 돼요. 더 나아가려면:

AI Connections

AI connection의 엔드포인트, 페이로드, 출력 파싱, 헤더를 구성해요.

코드 없는 Single-Turn 평가

장기 실행 에이전트 모드를 포함해 플랫폼에서 데이터셋 평가를 실행해요.

트레이스 연결하기

같은 testCaseId로 각 테스트 케이스를 트레이스에 연결해 완전한 관측성을 확보해요.

더 알아보기