AI Connections용 Async Responses

AI Connections용 Async Responses

몇 분에서 몇 시간이 걸리는 에이전트를 평가할 때, 각 요청을 즉시 승인하고 결과는 나중에 다시 올리는 방식이에요.

출처: 문서

본문

개요 (Overview)

기본적으로 AI Connection은 동기(synchronous) 방식이에요. Confident AI가 엔드포인트가 응답할 때까지 연결을 유지하고, 그 응답에서 실제 출력을 파싱해요. 출력을 만드는 데 몇 분에서 몇 시간이 걸리는 에이전트(심층 리서치 에이전트, 멀티 스텝 파이프라인, 큐(queue)로 처리되는 작업)는 그 연결이 타임아웃돼요.

Async Responses는 교환을 두 단계로 나눠요.

  1. Confident AI가 각 골든을 고유한 testCaseId와 함께 엔드포인트로 보내고, 출력을 기다리지 않고 연결을 닫아요.
  2. 엔드포인트가 빠른 2xx로 승인하고 실제 작업은 백그라운드에서 수행해요.
  3. 에이전트가 끝나면 POST /v1/test-runs/evaluate/{testCaseId} 엔드포인트로 결과를 다시 올려요.

Async Responses는 단일 턴(single-turn) 평가에서만 사용할 수 있고, HTTP Response 모드가 필요해요. 스트리밍 응답 모드가 선택된 동안에는 토글이 비활성화돼요.

Async Responses 활성화하기 (Enabling Async Responses)

Project Settings(프로젝트 설정) → AI Connections로 이동해 연결을 열고, General 탭에서 Async Responses를 켜요.

The Async Responses toggle on the AI connection's General tab

활성화하면 Confident AI는 출력을 기다리는 대신 각 요청을 전송한 후 연결을 닫고, 더 이상 Actual Output Key Path를 요구하지 않아요. 출력은 엔드포인트 응답에서 파싱되는 게 아니라 결과 엔드포인트에서 수집돼요.

testCaseId 포함하기 (Including the testCaseId)

페이로드에 반드시 testCaseId를 포함해야 해요. 에이전트가 결과를 올릴 때 이를 그대로 다시 보내고, 그래야 각 결과가 어떤 테스트 케이스인지 매칭되거든요. JSON 페이로드 모드에서는 testCaseId 변수를 요청 본문에 매핑해요.

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

Code 모드에서는 generate_payload가 testCaseId를 파라미터로 받으니, 반환하는 딕셔너리에 포함시키면 돼요.

요청 승인하기 (Acknowledging Requests)

엔드포인트는 즉시 2xx를 반환하고 작업을 백그라운드 작업으로 넘겨야 해요. Confident AI는 이 승인을 "요청 수신"으로 취급하며, 응답 본문의 내용은 파싱하지 않아요.

...

@app.post("/generate")
def generate(request: dict):
    background_tasks.add_task(
      run_agent, 
      request["input"], 
      request["testCaseId"]
    )

    return {"status": "accepted"}

연결 검증하기 (Verifying the Connection)

연결에서 Ping Endpoint를 클릭해 검증해요. async 연결의 핑은 승인 전용(acknowledgement-only) 이에요. 엔드포인트가 요청을 받아들이고 2xx를 반환하는지만 확인하고, 출력은 검사하지 않아요.

결과 다시 올리기 (Posting Results Back)

에이전트가 테스트 케이스를 끝내면, 그 요청의 testCaseId를 사용해 Project API Key로 인증하며 결과 엔드포인트에 결과를 올려요.

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(())
}

전체 워크스루는 Set Up Long-Running AI Connections 가이드를 참고하세요.

다음 단계 (Next Steps)

Long-Running AI Connections

비동기로 응답하는 에이전트를 평가하는 완전한 엔드투엔드 가이드예요.

Single-Turn Evals Without Code

플랫폼에서 async 연결을 상대로 데이터셋 평가를 실행해요.

더 알아보기 (Learn more)