Skip to content
This page has been auto-translated and may contain errors.View in English

에이전트

docs.scrimba.com

**에이전트**는 루프에서 실행되는 모델로, 작업이 완료될 때까지 호출할 다음 도구를 선택합니다. 이 장의 나머지 부분은 그것이 무엇을 의미하는지, 어디서 진정한 가치를 보이는지, 그리고 어디서 조용히 비용을 치르는지에 대해 설명합니다.

에이전트 루프

도구 사용에서 모델은 하나의 함수를 호출했고 당신이 결과를 다시 보냈습니다. 하지만 일부 작업은 여러 단계를 거치며, 미리 단계를 알 수 없습니다. "가장 저렴한 항공편을 찾아서 예약하기"는 검색, 비교, 일정 확인, 예약을 의미할 수 있으며, 모델이 진행하면서 그 순서를 파악해야 합니다.

따라서 단일 도구 호출을 처리하는 대신 전체를 루프에 넣습니다. 이 반복 사이클을 **에이전트 루프**라고 하며, 작업이 완료될 때까지 반복되는 한 번의 턴입니다:

  1. 대화와 사용 가능한 도구를 모델에 보냅니다.
  2. 모델은 도구를 호출하거나 최종 답변을 제공합니다.
  3. 도구를 호출했다면 실행하고, 결과를 대화에 추가한 후 1단계로 돌아갑니다.
  4. 최종 답변을 제공했다면 중단합니다.

매 턴마다 모델은 이전 도구 결과를 포함하여 지금까지 일어난 모든 것을 보고 다음 이동을 결정합니다. 이것이 도구 사용과의 전부 차이입니다: 모델이 완료되었다고 생각할 때까지 루프를 계속합니다.

진정한 변화는 누가 조종하는가입니다. 지금까지의 모든 장에서 당신의 코드가 제어 흐름을 결정했습니다: 모델을 호출할 때, 도구를 실행할 때, 다음에 무엇이 올지를 당신이 선택했습니다. 에이전트는 그 결정을 모델에 맡깁니다. 루프는 계속 "다음에 무엇이 일어나야 하나?"라고 묻고 모델의 예측이 매번 답입니다. 당신은 제어를 유연성으로 바꾸며, 이 거래가 도달 범위와 위험의 원천입니다.

내부적으로는 새로운 일이 일어나지 않습니다. 이것은 성장하는 컨텍스트에 대한 동일한 예측이며, 도구 사용 왕복을 반복한 것입니다. 모델은 계획을 세우고 따르지 않습니다. 매 턴마다 현재 볼 수 있는 것에서 단 하나의 최선의 다음 단계를 예측한 후 루프가 다시 실행됩니다.

그것이 작동할 때는 목표를 향한 추론처럼 보입니다. 그렇지 않을 때는 에이전트가 자신에게 루프를 돌거나 막힌 끝으로 헤맬 수 있습니다. 왜냐하면 시스템의 어느 부분도 전체 계획을 유지하지 않기 때문입니다.

Juno에이전트 루프 에이전트는 도구 사용 루프를 반복하는 것입니다: 대화와 도구를 보내면, 모델이 도구를 호출하거나 최종 답변을 제공하고, 도구가 있으면 실행하고 결과를 다시 피드하고, 답변할 때까지 다시 합니다. 매 턴마다 모델은 모든 이전 결과를 보고 다음 단계를 선택합니다. 일반 도구 사용보다 유일한 새로운 개념은 단일 호출을 처리하는 대신 루프를 도는 것입니다.

도구 사용에서 당신은 한 번의 호출을 처리하고 결과를 반환했습니다. 에이전트는 그 왕복을 루프에 싸서 모델이 완료되었다고 판단할 때까지 실행합니다. 그 루프가 **에이전트 루프**입니다. 여기 핵심이 있습니다: 단계의 순서를 하드코딩하는 것을 멈추고 매 턴마다 모델이 다음 단계를 선택하도록 하십시오.

사이클은 네 단계입니다:

  1. 대화와 도구 목록을 모델에 보냅니다.
  2. 모델은 도구 호출 또는 최종 텍스트 답변으로 응답합니다.
  3. 도구를 요청했다면 실행하고, 각 결과를 대화에 추가한 후 루프를 돕니다.
  4. 답변했다면 중단하고 반환합니다.

이것이 작동하게 하는 메커니즘은 모델이 매 턴마다 전체 성장하는 대화를 봅니다: 원래 요청, 자신이 만든 모든 도구 호출, 반환된 모든 결과. 이 누적된 컨텍스트가 에이전트의 유일한 메모리입니다. 각 예측은 지금까지의 전체 역사에 대해 조건부이므로, 이전 결과에 의존하는 단계를 연결할 수 있습니다.

초반에 이름을 지을 가치가 있는 한 가지 세부 사항: 2단계는 한 번에 여러 도구 호출을 반환할 수 있으며, 하나가 아닙니다. 호출이 서로 의존하지 않을 때 (예: 세 도시의 날씨 확인), 모델은 단일 턴에 세 개 모두를 요청할 수 있고 당신은 함께 실행합니다. 호출이 이전 호출의 결과가 필요할 때, 모델은 그 결과를 기다린 후 다음을 요청해야 하므로 그것들은 별도의 턴에 걸쳐 나타납니다. 루프는 둘 다 처리합니다: 이 턴에 돌아온 모든 호출을 실행하고, 모든 결과를 피드하고, 다시 예측합니다.

이 모든 것 안에는 전역 계획이 없습니다. 모델은 현재 볼 수 있는 것에서 최선의 다음 단계를 예측하며, 전체 작업의 지도에서가 아닙니다. 이것이 에이전트와 이전에 작성한 고정 파이프라인 간의 차이이며, 연구 단계를 연결하는 동일한 루프가 자신의 꼬리를 쫓을 수도 있는 이유입니다.

Juno에이전트 루프 에이전트는 루프에서 도구 사용 왕복입니다: 대화와 도구를 보내면, 모델이 도구를 호출하거나 답변하고, 도구를 실행하고 결과를 다시 피드하고, 답변할 때까지 반복합니다. 성장하는 대화가 에이전트의 유일한 메모리이므로 각 단계는 그 이전의 모든 결과에 따라 달라집니다. 한 턴이 독립적인 여러 도구 호출을 한 번에 반환할 수 있으며, 각각이 마지막에 의존할 때 하나씩 반환할 수 있습니다. 어디에도 전역 계획이 없습니다: 모든 단계는 신선한 다음 단계 추측입니다.

에이전트는 도구 사용 왕복을 루프에 배치하고 바퀴를 넘기며, 에이전트 루프: 모델이 매 턴마다 다음 도구를 선택하고, 당신이 실행하고, 결과를 다시 피드하고, 최종 답변 대신 호출을 내보낼 때까지 다시 갑니다. 루프는 네 단계이며 이것이 흥미로운 부분이 아닙니다. 흥미로운 부분은 아키텍처가 당신에게 주는 것과 주지 않는 것입니다.

사이클:

  1. 실행 중인 대화와 도구 스키마를 보냅니다.
  2. 모델은 도구 호출 또는 최종 답변을 반환합니다.
  3. 호출을 실행하고, 결과를 추가하고, 루프를 돕니다.
  4. 최종 답변이 나오면 반환합니다.

단일 턴이 여러 도구 호출을 반환할 수 있습니다. 모델은 독립적이라고 판단할 때 병렬 호출을 내보냅니다 (세 개의 조회가 그 사이에 데이터 의존성이 없음), 그리고 하나가 다른 하나의 출력이 필요할 때 턴 전체에 직렬 호출을 내보냅니다. 외부에서 이를 강제할 수 없습니다: 작업이 병렬화되는지는 작업에서 추론한 것에 따라 모델의 호출이므로, 병렬이라고 생각하는 작업이 직렬화되어 더 느릴 수 있고, 직렬이라고 생각하는 것이 함께 실행해야 할 배치로 돌아올 수 있습니다.

여기 하중을 견디는 사실, 모든 실패 모드가 추적되는 것입니다. 전역 계획이 없습니다. 모델은 작업의 지도를 보유하지 않으며 그에 대해 실행하지 않습니다. 매 턴마다 현재 대화에서 다음 단계를 예측하며, 그 대화는 시스템의 전체 상태이며, 당신의 프롬프트와 지금까지의 모든 호출 및 결과는 누적됩니다.

모든 단계는 이전 단계를 신뢰하며, 전체를 확인하는 것이 없습니다. 이것이 목표를 향한 추론처럼 보이는 에이전트가 실제로 지역적으로 그럴듯한 추측의 순서이며, 작동할 때 더해지는 이유입니다.

지금 내재화해야 할 결과, 가드레일 섹션이 그것을 지불하기 때문: 상태는 성장하는 기록일 뿐이고 각 단계는 그것에 따라 조건부이므로, 초기 잘못된 도구 결과는 수정되지 않고 구축됩니다. 오류는 앞으로 전파되고 복합되며, 루프의 어느 부분도 전체를 알아차리도록 위치하지 않습니다. 에이전트의 신뢰성 작업은 대부분 그 하나의 속성과 싸우는 것입니다.

Juno에이전트 루프 에이전트는 모델이 조종하는 루프에서 도구 사용 왕복입니다: 다음 도구를 선택하고, 당신이 실행하고, 결과를 다시 피드하고, 답변할 때까지 반복합니다. 전체 상태는 성장하는 기록이며 어디에도 전역 계획이 없으며, 마지막의 각각에 따라 조건부인 다음 단계 추측의 사슬만 있습니다. 한 턴은 모델이 독립적이라고 판단할 때 병렬 호출을 반환하거나 서로 의존할 때 직렬 호출을 반환할 수 있으며, 당신은 어느 것인지 제어하지 않습니다. 한 가지를 유지하십시오: 모든 단계는 이전 단계를 신뢰하므로, 초기 잘못된 결과는 전체를 확인하는 것이 없이 앞으로 복합됩니다.

최소 에이전트

여기 루프의 코드입니다. 이것은 도구 사용의 도구 정의 및 구현을 재사용하고 모델이 도구를 요청하는 것을 멈출 때까지 실행하는 루프로 감쌉니다.

python
import json

def run_agent(user_text, tools, tool_impls, max_steps=6):
    messages = [{"role": "user", "content": user_text}]
    step = 0

    while step < max_steps:
        response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
        message = response.choices[0].message
        messages.append(message)

        calls = message.tool_calls
        if not calls:
            return message.content  # 도구를 원하지 않음: 이것이 최종 답변입니다

        # 요청된 모든 도구를 실행하고 결과를 다시 피드합니다
        for call in calls:
            args = json.loads(call.function.arguments)
            result = tool_impls[call.function.name](args)
            messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})

        step += 1

    return "Stopped: the agent reached its step limit."  # 안전망

위에서 아래로 읽으십시오: 모델을 호출하고, 메시지를 대화에 밀어넣고, 도구를 요청했다면 모두 실행하고 결과를 추가한 후 다시 루프를 돕니다. 모델이 도구 호출 대신 텍스트를 반환하면 그것이 최종 답변이고 반환합니다.

max_steps 값은 **단계 제한**을 설정하며, 루프가 도망가지 않도록 하는 안전망이며, 다음 섹션이 다루는 것입니다. 여기 모양은 한 공급자의 SDK와 일치합니다; 필드 이름은 공급자마다 다르지만 루프는 모든 곳에서 동일합니다.

Juno최소 에이전트 최소 에이전트는 모델 호출 주위의 루프입니다: 모델을 실행하고, 요청하는 도구를 실행하고, 결과를 다시 피드하고, 반복합니다. 모델이 도구 호출 대신 텍스트를 반환하면 그 텍스트가 최종 답변입니다. 단계 제한은 나중에 추가하는 추가가 아닌 필수 안전망입니다.

여기 루프의 코드이며, 도구 사용의 도구 정의 및 구현을 재사용합니다. 모델이 도구 요청을 멈추거나 단계 한계에 도달할 때까지 실행합니다.

python
import json

def run_agent(user_text, tools, tool_impls, max_steps=6):
    messages = [{"role": "user", "content": user_text}]
    step = 0

    while step < max_steps:
        response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
        message = response.choices[0].message
        messages.append(message)

        calls = message.tool_calls
        if not calls:
            return message.content  # 최종 답변

        for call in calls:
            args = json.loads(call.function.arguments)
            try:
                result = tool_impls[call.function.name](args)
            except Exception as err:
                result = {"error": str(err)}  # 실패를 표면화하고 충돌하지 않음
            messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})

        step += 1

    return "Stopped: the agent reached its step limit."

모양은 한 공급자의 SDK이고 필드 이름은 다르지만 모든 루프에는 이런 부분들이 있습니다: 모델의 메시지를 추가하고, 도구를 요청했는지에 따라 분기하고, 요청된 것들을 실행하고, 각 결과를 그 호출 id에 키된 tool 메시지로 다시 피드합니다.

도구 주위의 try/except를 만드는 것이 기본 버전보다 더 나은 변화입니다. 도구가 실패하면 예외가 루프를 죽이도록 하지 마십시오. 이를 잡고 오류를 도구 결과로 모델에 다시 피드하십시오. 모델은 다음 턴에 "그 조회가 실패했습니다"를 읽고 적응할 수 있습니다: 다른 인수로 다시 시도하거나, 다른 도구를 시도하거나, 사용자에게 작업을 완료할 수 없다고 말합니다. 충돌은 모델에 복구할 기회를 주지 않습니다; 이 결과로서의 오류 패턴은 행동할 정보와 함께 루프에 유지합니다.

이것이 실행되면서 추적할 두 가지. 첫째, 대화는 오직 성장합니다. 매 턴마다 모델의 메시지와 모든 도구 결과를 추가하므로 5단계까지 모델은 1단계부터 4단계까지의 모든 것을 다시 읽습니다. 이것이 에이전트의 메모리이자 그것의 청구서입니다: 각 단계가 전체 누적 컨텍스트를 다시 보내므로 단계당 토큰 비용이 작업이 진행됨에 따라 올라갑니다. 10단계 에이전트는 10개의 저렴한 호출이 아니며, 계속 커지는 기록에 걸친 10개의 호출입니다.

둘째, 그 성장이 max_steps가 무한 루프 중단을 넘어서 중요한 이유입니다. 작업에 대해 캡을 선택하십시오. 날씨 질문은 2-3단계가 필요합니다; 연구 작업은 10을 원할 수 있습니다. 한계를 건강한 실행이 걸리는 것보다 약간 위에 설정하므로 합법적으로 더 긴 작업이 완료될 수 있지만 막힌 것은 예산을 소진하기 전에 여전히 차단됩니다.

Juno최소 에이전트 루프는: 모델의 메시지를 추가하고, 도구를 요청했는지에 따라 분기하고, 실행하고, 각 결과를 tool 메시지로 다시 피드합니다. 도구 호출을 try/except로 감싸고 오류를 충돌하는 대신 다시 피드하므로 모델이 다시 시도하거나 복구할 수 있습니다. 대화가 오직 성장합니다: 매 단계가 전체 기록을 다시 보내므로 단계당 토큰 비용이 작업 실행에 따라 올라갑니다. max_steps를 작업에 대해 설정하되, 건강한 실행보다 약간 위에 설정합니다.

루프이며, 도구 사용의 도구 레이어를 재사용합니다. 한 공급자의 SDK 모양이며, 필드 이름은 다르고 구조는 다르지 않습니다.

python
import json

def run_agent(user_text, tools, tool_impls, max_steps=6):
    messages = [{"role": "user", "content": user_text}]
    step = 0

    while step < max_steps:
        response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
        message = response.choices[0].message
        messages.append(message)

        calls = message.tool_calls
        if not calls:
            return message.content

        for call in calls:
            args = json.loads(call.function.arguments)
            try:
                result = tool_impls[call.function.name](args)
            except Exception as err:
                result = {"error": str(err)}  # 모델이 이것을 읽고 다음 턴에 적응할 수 있습니다
            messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})

        step += 1

    return "Stopped: the agent reached its step limit."

기본 루프는 정확하며 배포 불가능하며, 그 둘 사이의 격차가 이 섹션이 사는 곳입니다. 이 코드의 세 속성이 프로덕션에서 물린다.

오류를 표면화하고 절대 충돌하지 마십시오. try/except는 실패한 도구를 모델이 읽을 수 있는 결과로 피드합니다. 그것이 맞지만, 당신이 무엇을 하는지 이해하십시오: 당신은 모델에 오류 문자열을 건네고 현명하게 복구할 수 있기를 신뢰합니다. 동일한 나쁜 호출을 다시 시도하거나, 다른 접근 방식을 고안하거나, 사과하고 중단할 수 있습니다.

오류를 피드백하는 것은 에이전트를 살아있게 유지합니다; 복구를 신뢰할 수 있게 만들지는 않습니다. 하중을 견디는 모든 것에 대해, 도구당 재시도를 한계하고 반복된 실패를 모델이 느낌으로 해결하도록 하는 루프가 아닌 터미널 상태로 대하십시오.

기록은 오직 성장하며, 그것은 두 개의 복합 비용입니다. 모든 호출이 전체 누적 역사를 다시 보내므로 단계당 토큰 비용이 올라가고, 긴 실행은 각 턴에 평탄한 비용이 아니라 올라가는 청구서를 지불합니다. 기록이 길어질수록, 모델은 다음 이동을 결정하기 위해 오래된 도구 출력을 더 많이 읽고 있으며, 이는 정확히 컨텍스트 윈도우 장의 중간에 잃어버린 효과가 물기 시작하는 곳입니다. 여기서 모델은 긴 컨텍스트의 중간에 묻힌 정보에 적게 주의합니다: 에이전트를 제한해야 하는 초기 명령이 누적된 결과 아래로 빠지고 적게 주의됩니다. 긴 에이전트는 크게 실패하지 않으며, 표류합니다.

max_steps는 버그 가드가 아니라 예산입니다. 모델이 중단할 때를 결정하고 잘못 결정할 수 있기 때문에 존재합니다: 단계에서 루프를 돌거나, 두 도구 사이를 핑퐁하거나, 막힌 끝을 쫓습니다.

숫자는 실제 비용 한계입니다. 최악의 경우 지출은 대략 max_steps를 실행 끝 근처에서 전체 컨텍스트 호출의 비용으로 곱한 것입니다, 그래서 컨텍스트 무거운 작업에 대한 관대한 상한은 관대한 청구서입니다. 가장 긴 합법적인 실행과 여유로부터 설정하고, 실행이 정기적으로 도달할 때 알람하고 (그것은 에이전트가 막혔다는 신호이지, 한계가 너무 낮다는 신호가 아님), 완료할 수 없는 에이전트를 종이로 덮으려고 반사적으로 수를 올리는 것을 저항하십시오.

Juno최소 에이전트 기본 루프는 정확하고 배포 불가능합니다. 모델이 적응할 수 있도록 도구 오류를 피드백하지만, 재시도를 한계하고 반복된 실패를 터미널로 대하십시오. 오류를 표면화하는 것이 신뢰할 수 있는 복구를 만들지 않기 때문입니다. 기록은 오직 성장하므로 단계당 토큰 비용이 올라가고 초기 명령이 오래된 결과 아래로 빠져 에이전트가 표류합니다. max_steps는 비용 한계이지, 버그 가드가 아닙니다: 가장 긴 실제 실행에 여유를 더하여 크기를 조정하고, 실행이 계속 도달할 때 에이전트가 막혔으므로 반사적으로 숫자를 올리지 마십시오.

중단과 가드레일

모델이 중단할 때를 결정하도록 하는 루프는 중단하지 못할 수 있습니다. 도구를 영원히 호출하거나 자신을 반복하거나 막힌 끝을 쫓을 수 있습니다. 모든 단계가 토큰으로 청구되는 모델 호출이므로, 도망가는 에이전트는 빠르게 돈과 시간을 소진합니다.

그래서 가드레일, 루프 주위에 두는 한계는 최종 폴리시 단계가 아닌 설계의 부분입니다:

  • 단계 제한. max_steps가 하는 방식으로 루프의 개수를 한계하십시오. 도달되면 중단하고 반환합니다.
  • 도구 입력 검증. 모델이 인수를 선택하므로, 진정한 결과를 가진 것을 실행하기 전에 확인하십시오.
  • 도구가 할 수 있는 것을 제한하십시오. 에이전트에 필요한 최소 전력을 제공하십시오. 지우는 것보다 읽는 도구가 더 안전합니다.
  • 비용을 감시하십시오. 작업당 여러 모델 호출이 빠르게 곱해지므로 토큰을 추적하고 예산을 설정하십시오.

이것들을 묶는 스레드: 모델이 경로를 즉흥하므로, 당신이 그것이 하지 않을 한계를 배치합니다. 에이전트가 적어도 한 번은 잘못된 일을 할 것이라고 가정하고, 그것을 생존할 수 있게 만드십시오.

Juno중단과 가드레일 에이전트는 자신의 중단점을 결정하므로 영원히 루프할 수 있으며, 모든 루프는 토큰이 비용입니다. 항상 단계 제한을 설정하고, 모델이 선택한 인수를 검증하고, 도구에 필요한 최소 전력을 제공하십시오. 비용을 추적하십시오. 작업당 여러 모델 호출이 빠르게 더해집니다.

모델이 중단할 때를 결정하는 루프는 중단하지 못할 수 있으며, 각 단계가 토큰 청구 모델 호출이므로, 막힌 에이전트는 빠르게 예산을 소진합니다. 가드레일은 설계의 부분입니다. 연결할 네 개:

  • 단계 제한. max_steps로 루프를 한계하고 도달되면 깔끔하게 반환합니다. 이것이 도망가는 루프에 대한 당신의 하드 정지입니다.
  • 도구 입력 검증. 모델이 인수를 선택하며, 잘못 선택하거나 잘못 조종될 수 있습니다. 진정한 결과를 가진 도구 실행 전에 확인하십시오: id 없는 삭제, 음수 금액 전송, 허용된 디렉터리 외부의 경로.
  • 최소 전력. 각 도구에 작업이 필요한 가장 좁은 능력을 제공하십시오. 읽기-쓰기가 아니라 읽기 전용이, 원시 데이터베이스 접근이 아니라 범위지정된 쿼리가, 임의 보내기가 아니라 고정 수신자 목록이 더 좋습니다.
  • 비용 한계. 호출당이 아니라 전체 실행에 걸쳐 토큰을 추적하고 실행이 예산을 넘을 때 중단합니다.

이것들을 묶는 점은 도구의 인수가 모델에서 오며, 그것은 모델이 읽은 모든 텍스트에서 간접적으로 온다는 것입니다. 도구가 웹페이지를 가져갔고 그 페이지가 지시를 포함했다면, 그 지시들은 지금 모델의 컨텍스트에 있고 다음 도구 호출을 조종할 수 있습니다. 이것이 **프롬프트 주입**입니다: 신뢰할 수 없는 텍스트가 컨텍스트에서 에이전트가 하는 일을 영향을 미칩니다. 방어는 프롬프트가 아닙니다. 모든 도구 입력을 검증하고 모든 도구의 전력을 범위지정하여 완전히 하이재킹된 인수도 진정한 해를 할 수 없도록 합니다.

Juno중단과 가드레일 네 가드레일, 폴리시가 아닌 설계: max_steps 하드 정지, 모든 중요한 도구에서 입력 검증, 최소 전력 도구 범위지정, 전체 실행에 걸친 토큰 예산. 입력 검증이 중요한 이유: 도구 인수는 모델에서 오며, 그것은 읽은 모든 텍스트에서 오므로, 명령을 수행하는 가져온 페이지는 다음 호출을 조종할 수 있는 프롬프트 주입입니다. 입력을 검증하고 전력을 범위지정하므로 하이재킹된 인수도 여전히 해를 할 수 없습니다.

모델이 중단할 때를 결정하는 루프는 중단하지 못할 수 있으며, 모든 단계가 토큰 청구 호출이므로, 실패 모드는 기본적으로 비쌉니다. 네 가드레일 (단계 제한, 입력 검증, 최소 전력, 비용 한계)은 최소 필수입니다. 주목할 가치가 있는 부분은 입력 검증이 건전성 검사가 아니라 보안 제어인 이유입니다.

도구 인수는 모델 출력이며, 모델 출력은 컨텍스트의 모든 것에 따라 조건부입니다. 제어하지 않는 모델이 가져온 웹페이지, 문서, 이메일, 또는 도구 결과를 포함합니다. 에이전트가 웹페이지, 문서, 이메일, 또는 제어하지 않는 도구 결과를 읽는 순간, 신뢰할 수 없는 텍스트는 그 컨텍스트에 있고 지시를 수행할 수 있습니다. 그것이 **프롬프트 주입**입니다: 입력의 적대적 컨텐츠가 당신이 의도하지 않은 행동으로 모델을 조종합니다. 단일-샷 프롬프트에서 그것은 성가신 일입니다. 에이전트에서 그것은 실시간 이용 불가입니다. 왜냐하면 모델의 다음 이동은 진정한 세계 효과를 가진 도구 호출이고, 주입된 텍스트가 그 호출을 영향을 미칠 수 있기 때문입니다.

적대적으로 설계하십시오. 왜냐하면 그것이기 때문입니다. 세 규칙이 유지됩니다:

  • 모든 도구 인수를 blocklist가 아닌 allowlist에 대해 검증하십시오. 유효한 인수가 보이는 방식을 정의하고 (이 id 형식, 이 수신자 집합, 이 디렉터리) 나머지 모두를 거부합니다. 공격자가 만들 모든 나쁜 입력을 열거할 수 없습니다; 좋은 것들을 열거할 수 있습니다.
  • 도구 전력을 범위지정하여 완전히 하이재킹된 호출이 생존 가능합니다. 인수가 공격자 제어 하에 있다고 가정하고 도구가 무엇을 할 수 있는지 물으십시오. 실제 답변이 "누구든지 이메일"이거나 "모든 행을 삭제"라면, 얼마나 신중한 프롬프트가 있든지 도구가 너무 광범위합니다. 최소 전력은 모델이 손상될 때 유지하는 제어이며, 프롬프트는 제어가 아닙니다.
  • 되돌릴 수 없는 행동에 인간을 루프에 두십시오. 돈 보내기, 데이터 삭제, 고객 이메일: 확인에서 이것들을 게이트하십시오. 에이전트가 제안하고, 사람 또는 더 엄격한 검사가 처분합니다.

이것이 모두 프롬프트에 없는 더 깊은 이유: 시스템 프롬프트의 지시와 가져온 컨텐츠의 주입된 지시는 텍스트로서 같은 종류의 것으로 컨텍스트에 도달하고, 모델은 하나를 다른 것 위에 신뢰할 수 있는 방법이 없습니다. 그래서 당신은 프롬프트 방식으로 안전에 도달할 수 없습니다. 유지하는 경계는 모델 외부입니다: 검증된 입력과 범위지정된 도구. 이것은 안전과 한계의 더 넓은 치료와 연결됩니다.

Juno중단과 가드레일 네 가드레일은 최소 필수입니다; 생각을 얻는 것은 입력 검증이며, 왜냐하면 그것은 보안 제어이기 때문입니다. 도구 인수는 읽은 모든 것에 대해 조건부인 모델 출력이므로, 가져온 페이지나 이메일은 주입된 지시를 수행할 수 있고 에이전트에서 그 호출은 진정한 세계 효과를 가집니다. allowlist에 대해 검증하고, 모든 도구를 범위지정하여 하이재킹된 인수도 여전히 생존할 수 있으며, 인간이 되돌릴 수 없는 행동을 게이트합니다. 프롬프트 방식으로 안전에 도달할 수 없습니다: 주입된 지시와 시스템 지시는 모델에 같은 종류의 텍스트이므로, 경계는 그것 외부에 있어야 합니다.

워크플로우가 에이전트를 이길 때

에이전트는 흥미로우므로 필요하지 않을 때 하나를 잡도록 유혹합니다. 일반 규칙: 이미 단계를 알고 있으면 에이전트를 사용하지 마십시오. 당신이 제어하는 경로 대신 모델이 찾기를 바라는 일반 코드, **워크플로우**로 기록하십시오.

워크플로우는 더 신뢰할 수 있고, 더 저렴하고, 디버깅이 훨씬 더 쉬우며, 경로를 제어하기 때문입니다. 에이전트를 열린 작업을 위해 절약하십시오. 단계가 발견된 것에 의존합니다.

많은 에이전트처럼 보이는 기능은 실제로 구조화된 출력 호출이나 RAG 조회이며 손으로 연결됩니다. 에이전트를 잡으십시오. 단계를 미리 기록할 수 없을 때만.

Juno워크플로우가 에이전트를 이길 때 이미 단계를 알고 있으면 에이전트 대신 워크플로우로 하드코드하십시오: 더 저렴하고, 더 신뢰할 수 있으며, 디버깅이 더 쉽습니다. 단계가 발견된 것에 의존하는 열린 작업을 위해 에이전트를 절약하십시오. 많은 "에이전트" 기능은 정말로 손으로 연결된 구조화된 출력 또는 RAG 호출입니다.

에이전트는 흥미로운 도구이며, 정확히 그것이 잘못되었을 때 잡기 위해 이유입니다. 결정 규칙은 하나의 질문입니다: 단계를 미리 기록할 수 있습니까? 예인 경우, 당신은 **워크플로우**를 원합니다. 단계는 고정된 순서의 일반 코드입니다.

워크플로우는 에이전트가 투쟁하는 모든 것에서 이깁니다. 에이전트 루프 대신 하나 또는 둘의 모델 호출을 하므로 더 저렴합니다. 재결정되지 않고 고정된 경로이므로 더 신뢰할 수 있습니다. 그리고 검사할 수 있는 알려진 단계가 있지 않고 모델이 선택한 이동의 사슬에서 실패가 어디에서 일어나지 않으므로 디버깅이 훨씬 더 쉽습니다.

여기 실제로 테스트가 있습니다. "지원 티켓을 요약하고, 긴급성을 분류하고, 큐로 경로하십시오"는 알려진 순서의 알려진 단계가 있으므로 워크플로우입니다: 요약, 카테고리, 긴급성을 얻기 위한 하나의 구조화된 출력 호출, 그 후 결과에 경로하는 일반 코드. "이 고객의 마지막 세 주문이 실패한 이유를 조사하십시오"는 알려진 단계가 없으며, 경로는 각 조회가 드러내는 것에 의존하므로 에이전트입니다.

함정은 많은 기능이 열려 있어 보이고 아닌 것입니다. 문서에서 필드를 뽑아내고, 문서에서 답변하고, 들어오는 메일을 정렬합니다: 이것들은 에이전트 작업처럼 읽지만 구조화된 출력 호출이나 코드 주위의 RAG 조회로 해결합니다. 워크플로우로 기본 설정하고 작업이 당신이 스크립트할 수 없는 방식에서 예측 불가능함으로써 에이전트를 얻도록 하십시오.

Juno워크플로우가 에이전트를 이길 때 결정은 하나의 질문입니다: 단계를 미리 기록할 수 있습니까? 예인 경우, 워크플로우를 구축하십시오. 단계는 일반 코드입니다. 더 저렴하고, 더 신뢰할 수 있으며, 알려진 점에서 디버깅 가능하기 때문입니다. "요약, 분류, 경로 티켓"은 워크플로우입니다: 하나의 구조화된 출력 호출과 경로 코드. "이 주문이 실패한 이유를 조사하십시오"는 에이전트이며, 경로가 각 단계가 찾는 것에 의존하기 때문입니다. 워크플로우로 기본 설정하고 작업이 에이전트를 얻도록 하십시오.

에이전트는 사용하고 싶은 도구이며, 어디에서 속하지 않는지입니다. 결정 규칙은 짧습니다: 단계를 미리 기록할 수 있으면 기록하십시오. 그것이 **워크플로우**입니다. 단계는 고정된 순서의 코드이며, 형태를 알고 있는 모든 작업에 대해 에이전트를 이깁니다.

이유는 아키텍처로 직선적입니다. 에이전트는 전역 계획이 없고 오류를 앞으로 복합하므로, 그 신뢰성은 모든 단계가 전체를 확인하는 것이 없이 올바르게 가는 것의 산물입니다. 당신이 쓴 워크플로우는 정의에 의해 계획을 가지며, 격리된 상태로 각 단계를 검사, 테스트, 유효성 검사할 수 있습니다. 워크플로우에서 무언가가 깨지면 한 줄에서 깨집니다. 에이전트에서 무언가가 깨지면 모델이 선택한 이동의 기록에서 어딘가에서 깨지고, 추적에서 그것이 생각한 것을 재구성합니다.

지금 더 어려운 경우이며, "단계를 알고 있는가"는 직선적인 반입니다. 에이전트는 작업이 열려 있을 때도 잘못된 도구이지만, 잘못된 단계의 비용이 너무 높아서 다음 단계 추측자를 취하도록 할 수 없습니다. 여행을 예약하고, 돈을 이동하거나, 프로덕션 데이터를 수정하는 에이전트는 열려 있으며 또한 계획되지 않고, 주입 가능하고, 오류 복합 루프를 제어하도록 원하지 않는 곳입니다. 올바른 설계는 종종 하이브리드입니다: 모델이 계획하거나 제안하지만, 고정 워크플로우와 인간 게이트가 실행하므로 예측 불가능한 부분은 되돌릴 수 없는 부분을 직접 만지지 않습니다.

그래서 질문은 두 질문입니다. 단계를 알고 있습니까, 그리고 잘못된 단계의 비용은 무엇입니까?

알려진 단계는 워크플로우로 갑니다. 저렴하고 되돌릴 수 있는 행동이 있는 알려지지 않은 단계는 에이전트가 생기는 곳입니다. 비싸고 되돌릴 수 없는 행동이 있는 알려지지 않은 단계는 자유 흐르는 루프가 아닌 제한된 설계를 원합니다.

에이전트는 저렴하고 되돌릴 수 있는 단계와 함께 열린 작업에 맞으며, 별로 더 이상은 아닙니다. "에이전트" 기능의 놀라운 공유는 코드 주위의 구조화된 출력 호출이나 RAG 조회입니다, 그리고 그것들은 그렇게 유지하는 것이 더 좋습니다.

Juno워크플로우가 에이전트를 이길 때 두 질문, 하나가 아닙니다: 단계를 알고 있습니까, 그리고 잘못된 단계의 비용은 무엇입니까? 알려진 단계는 워크플로우로 갑니다. 비용, 신뢰성, 디버깅에서 에이전트를 이깁니다. 왜냐하면 계획이 있고 에이전트는 없기 때문입니다. 저렴하고 되돌릴 수 있는 행동이 있는 알려지지 않은 단계는 에이전트가 그 자리를 얻는 곳입니다. 비싸고 되돌릴 수 없는 행동이 있는 알려지지 않은 단계는 하이브리드를 원합니다: 모델은 제안하고, 고정 워크플로우와 인간 게이트가 실행하므로 오류 복합 루프는 절대 되돌릴 수 없는 부분을 만지지 않습니다.

실제로

위의 run_agent 함수는 전부입니다. 질문, 도구 집합, 그들의 구현을 제공하고, 완료될 때까지 또는 단계 제한에 도달할 때까지 루프합니다. 작은 작업은 루프가 그 자리를 얻는 곳을 보여줍니다:

python
answer = run_agent(
    "What's the weather in Oslo and should I bring an umbrella?",
    tools,        # 도구 사용 장의 get_weather를 포함합니다
    tool_impls,   # {"get_weather": ...}
)
# 에이전트가 get_weather를 호출하고 "흐림"을 읽은 후 하나의 최종 턴에서 답변합니다:
# "It's cloudy in Oslo at 18 degrees, so an umbrella is a sensible idea."

한 도구, 한 루프, 한 답변. 동일한 구조는 형태를 변경하지 않고 여러 도구와 여러 단계로 확장합니다. 일단 이렇게 능력 있는 것을 구축할 수 있으면, 다음 질문은 어떻게 실제로 작동하는지 알 수 있으며, 이것이 evals입니다.

Juno실제로run_agent 루프는 전체 패턴입니다: 질문, 도구, 그들의 구현을 제공하고, 모델이 도구 요청을 멈추거나 한계에 도달할 때까지 루프합니다. 한 도구와 한 루프는 형태를 변경하지 않고 많은 것으로 확장합니다. 다음 질문은 실제로 작동하는지 아는 방법이며, 그것이 evals입니다.

run_agent 함수는 전체 패턴입니다. 질문, 도구, 그들의 구현을 제공하고, 모델이 요청을 멈추거나 한계에 도달할 때까지 루프합니다.

python
answer = run_agent(
    "What's the weather in Oslo and should I bring an umbrella?",
    tools,        # 도구 사용 장의 get_weather를 포함합니다
    tool_impls,   # {"get_weather": ...}
)
# 에이전트가 get_weather를 호출하고 "흐림"을 읽은 후 하나의 최종 턴에서 답변합니다.

한 도구, 한 루프, 한 답변, 그리고 동일한 형태는 여러 도구와 여러 단계로 확장합니다. 무료로 확장하지 않는 것은 그것이 작동하는 신뢰입니다. 워크플로우를 단계별로 테스트할 수 있습니다; 에이전트는 입력마다 다른 경로를 가져가므로, 어제 작동한 실행은 오늘 실패한 실행에 대해 거의 알려줍니다.

이것이 에이전트와 함께 옆으로 구축할 다음 것이 그것을 측정하는 방법인 이유입니다: 대표 작업의 집합을 실행하고 신뢰하지 않고 결과를 확인하십시오 하나의 좋은 데모. "그것이 나를 시도했을 때 작동했습니다"를 추적할 수 있는 숫자로 바꾸는 것이 정확히 evals가 다루는 것입니다.

Juno실제로run_agent 루프는 전체 패턴이며, 한 도구와 한 루프는 형태를 변경하지 않고 많은 것으로 확장합니다. 포함은 신뢰입니다: 에이전트는 입력에 따라 달라지므로, 입력당 테스트는 본적이 없는 입력에 대해 거의 알려줍니다. 신뢰하지 않고 단일 성공이 아닌 대표 작업을 실행하고 읽는 방법을 측정하는 다음 것을 구축하십시오. 이것이 evals입니다.

run_agent 함수는 전체 패턴이며 아래의 예제는 최소 최소입니다: 한 도구, 한 루프, 한 답변.

python
answer = run_agent(
    "What's the weather in Oslo and should I bring an umbrella?",
    tools,        # 도구 사용 장의 get_weather를 포함합니다
    tool_impls,   # {"get_weather": ...}
)
# get_weather는 "흐림"을 반환하고, 모델은 하나의 최종 턴에서 답변합니다.

형태는 많은 도구와 많은 단계로 확장되고, 코드는 여전히입니다. 함정은 실패 모드가 그것과 함께 확장되는 반면 코드는 가만히 있다는 것입니다. 에이전트는 경로 의존적입니다: 다른 입력은 다른 모델이 선택한 단계의 순서를 가져가므로, 입력당 테스트는 본적이 없는 입력에 대해 거의 알려줍니다. 샘플링의 비결정성은 동일한 입력도 두 번 다른 경로를 취할 수 있음을 의미합니다.

그래서 당신이 에이전트 다음에 구축할 것은 측정이며, 워크플로우가 필요로 하는 것과는 다른 종류의 측정입니다. 하나의 예상 출력을 주장하지 않습니다; 당신은 많은 대표 작업을 샘플하고 결과의 분포를 읽습니다: 얼마나 자주 예산에서 끝나는지, 얼마나 자주 올바른 답변에 도달하는지, 루프가 어디에 있는지, 비용이 무엇인지 꼬리에. 그것이 에이전트 모양의 evals이며, 위의 신뢰성, 비용, 안전 문제가 당신이 얻을 수 있는 숫자로 바뀌는 유일한 방법입니다 당신이 운이 좋은 데모 대신 방어할 수 있습니다.

Juno실제로run_agent는 전체 패턴이며 형태가 확장되는 동안 실패 모드가 확장됩니다. 에이전트는 경로 의존적이고 샘플링이 비결정성을 더하므로 입력당 테스트는 거의 알려줍니다 그리고 좋은 데모는 더 적게 증명합니다. 에이전트 방법으로 측정하십시오: 많은 대표 작업을 샘플하고 분포를 읽으십시오. 얼마나 자주 예산 내에서 끝나는지, 루프가 어디에 있는지, 꼬리가 비용이 무엇인지. 이것이 evals입니다, 그리고 위의 신뢰성과 비용 문제가 당신이 방어할 수 있는 숫자로 바뀌는 방법입니다.