# 11. Menentukan Model Cadangan (Fallback)

## Cadangan (fallback)

Terdapat berbagai kesalahan/kegagalan dalam aplikasi LLM, termasuk masalah API LLM, kualitas output model yang buruk, dan masalah terkait integrasi lainnya. Fitur `fallback` dapat digunakan untuk menangani dan mengisolasi masalah-masalah ini secara elegan.

Yang penting, fallback dapat diterapkan di seluruh level yang dapat dieksekusi, tidak hanya di level LLM.

## Cara Mengatasi Kesalahan LLM API

Penanganan kesalahan API LLM adalah salah satu kasus yang paling umum untuk menggunakan `fallback`.

Permintaan ke API LLM dapat gagal karena berbagai alasan. API mungkin sedang down, mencapai batas kecepatannya, atau sejumlah masalah lainnya. Menggunakan `fallback` dapat membantu melindungi dari jenis masalah ini.

**Penting**: Secara default, banyak pembungkus LLM yang menangkap kesalahan dan mencoba kembali. Disarankan agar Anda mematikan perilaku default ini ketika menggunakan `fallback`, jika tidak, pembungkus pertama akan terus mencoba ulang dan tidak pernah gagal.

```
%pip install -qU langchain langchain-openai
```

Pertama, mari kita lihat apa yang terjadi ketika `RateLimitError` dilemparkan di OpenAI. `RateLimitError` adalah **kesalahan yang terjadi ketika batas biaya pemanggilan API** OpenAI terlampaui.

Ketika kesalahan ini terjadi, permintaan API dibatasi untuk jangka waktu tertentu, sehingga aplikasi Anda perlu menanganinya dengan tepat. Uji coba memungkinkan Anda untuk melihat bagaimana perilaku aplikasi Anda ketika `RateLimitError` dilemparkan, dan untuk memeriksa logika penanganan kesalahan Anda.

Hal ini akan membantu Anda menghindari masalah yang mungkin muncul dalam produksi nyata dan memastikan bahwa Anda menyediakan layanan yang dapat diandalkan.

```python
from langchain_anthropic import ChatAnthropic
from langchain_openai import ChatOpenAI
```

```python
from unittest.mock import patch

import httpx
from openai import RateLimitError

request = httpx.Request("GET", "/")  # Membuat permintaan GET.
response = httpx.Response(
    200, request=request
)  # Membuat respons dengan kode status 200.
# Membuat RateLimitError dengan pesan "rate limit", respons, dan isi kosong.
error = RateLimitError("rate limit", response=response, body="")
```

Buat objek `ChatOpenAI` di variabel `openai_llm`, dan setel parameter `max_retries` ke 0 untuk **mencegah pengulangan karena batas biaya panggilan API, dll.**

Gunakan metode `with_fallbacks` untuk menetapkan `anthropic_llm` sebagai LLM `fallback`, dan tetapkan ke variabel `llm`.

```python
# Membuat objek openai_llm menggunakan model ChatOpenAI dari OpenAI.
# Mengatur max_retries ke 0 untuk mencegah percobaan ulang akibat pembatasan kecepatan, dll.
openai_llm = ChatOpenAI(max_retries=0)

# Membuat objek anthropic_llm menggunakan model ChatAnthropic dari Anthropic.
anthropic_llm = ChatAnthropic(model="claude-3-opus-20240229")

# Menggunakan openai_llm sebagai default, dan mengatur anthropic_llm sebagai pengganti jika terjadi kegagalan.
llm = openai_llm.with_fallbacks([anthropic_llm])
```

```python
# Menunjukkan kesalahan yang terjadi ketika menggunakan OpenAI LLM terlebih dahulu.
with patch("openai.resources.chat.completions.Completions.create", side_effect=error):
    try:
        # Mengirimkan pertanyaan "Mengapa ayam menyeberang jalan?" ke OpenAI LLM.
        print(openai_llm.invoke("Mengapa ayam menyeberang jalan?"))
    except RateLimitError:
        # Jika terjadi kesalahan, cetak kesalahan.
        print("Terjadi kesalahan")
```

```
Terjadi kesalahan
```

Contoh ini mensimulasikan rate limit API OpenAI dan menguji apa yang terjadi ketika terjadi kesalahan batas laju panggilan API.

Anda dapat melihat bahwa model GPT OpenAI gagal ketika mencoba menggunakannya, dan model fallback, model `Anthropic`, melakukan inferensi sebagai gantinya.

Jika model fallback disetel `with_fallback()` dan model fallback berhasil dijalankan, tidak ada `RateLimitError` yang dilemparkan.

```python
# Kode untuk menggantikan dengan Anthropic jika terjadi kesalahan saat memanggil OpenAI API
with patch("openai.resources.chat.completions.Completions.create", side_effect=error):
    try:
        # Mengirimkan pertanyaan "Apa ibu kota Korea Selatan?" ke model bahasa dan mencetak responsnya.
        print(llm.invoke("Apa ibu kota Indonesia?"))
    except RateLimitError:
        # Jika terjadi RateLimitError, cetak "Terjadi kesalahan".
        print("Terjadi kesalahan")
```

{% code overflow="wrap" %}
```
content='Ibu kota Indonesia saat ini adalah Jakarta. Jakarta terletak di pesisir utara bagian barat Pulau Jawa. Jakarta merupakan kota terbesar di Indonesia dan menjadi pusat pemerintahan, ekonomi, bisnis, perdagangan dan industri di Indonesia.\n\nNamun, pemerintah Indonesia telah merencanakan untuk memindahkan ibu kota ke Kalimantan Timur, tepatnya di sebagian wilayah Kabupaten Penajam Paser Utara dan Kabupaten Kutai Kartanegara. Ibu kota baru ini rencananya akan diberi nama Nusantara. Pemindahan ibu kota ini dilakukan dengan berbagai pertimbangan seperti pemerataan pembangunan, mengurangi beban Jakarta, serta membangun pusat pemerintahan yang lebih strategis dan berkelanjutan. Proses pemindahan ibu kota ini diperkirakan akan memakan waktu bertahun-tahun hingga dapat sepenuhnya beroperasi.' response_metadata={'id': 'msg_01RqtGGc942irqGvmAYe39MN', 'model': 'claude-3-opus-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 16, 'output_tokens': 261}} id='run-d386b430-492d-4b7f-9ba0-c518093168d6-0' usage_metadata={'input_tokens': 16, 'output_tokens': 261, 'total_tokens': 277}
```
{% endcode %}

Model yang diset ke `llm.with_fallbacks()` juga akan berperilaku sama seperti model yang dapat dijalankan biasa.

Kode di bawah ini juga tidak memberikan “error” karena model fallback telah bekerja dengan baik.

```python
from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Jawablah pertanyaan dengan singkat dan jelas.",  # Deskripsi peran sistem
        ),
        ("human", "Apa ibu kota dari {country}?"),  # Template pertanyaan pengguna
    ]
)
chain = prompt | llm  # Menghubungkan prompt dan model bahasa untuk membuat chain
# chain = prompt | ChatOpenAI() # Jika Anda menghapus komentar pada baris ini dan menjalankannya, pesan "Terjadi kesalahan" akan dicetak.
with patch("openai.resources.chat.completions.Completions.create", side_effect=error):
    try:
        print(chain.invoke({"country": "Indonesia"}))  # Memanggil chain dan mencetak hasilnya
    except RateLimitError:  # Menangani kesalahan batas biaya API
        print("Terjadi kesalahan")
```

```
content='Ibu kota Indonesia saat ini adalah Jakarta.' response_metadata={'id': 'msg_01R97b6PMdjpbf8kgKXtawNo', 'model': 'claude-3-opus-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 31, 'output_tokens': 15}} id='run-4415963c-01c7-46ae-9783-ea235680ee82-0' usage_metadata={'input_tokens': 31, 'output_tokens': 15, 'total_tokens': 46}
```

## Menentukan Beberapa Model Fallback Secara Berurutan

Anda dapat menentukan beberapa model untuk model `fallback`, bukan hanya satu. Bila beberapa model ditentukan, model-model tersebut akan dicoba secara berurutan.

```python
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

# Membuat prompt
prompt_template = (
    "Jawablah pertanyaan dengan singkat dan jelas.\n\nPertanyaan:\n{question}\n\nJawaban:"
)
prompt = PromptTemplate.from_template(prompt_template)
```

Buat dua rantai, satu chain yang melemparkan kesalahan dan satu chain yang berfungsi.

{% code overflow="wrap" %}
```python
# Di sini, kita akan membuat rantai menggunakan nama model yang salah, yang dapat dengan mudah menyebabkan kesalahan.
chat_model = ChatOpenAI(model_name="gpt-fake")
bad_chain = prompt | chat_model
```
{% endcode %}

```python
# Buat chain fallback.
fallback_chain1 = prompt | ChatOpenAI(model="gpt-3.6-turbo") # error
fallback_chain2 = prompt | ChatOpenAI(model="gpt-3.5-turbo") # aman
fallback_chain3 = prompt | ChatOpenAI(model="gpt-4-turbo-preview") # aman
```

```python
# Menggabungkan dua chain untuk membuat chain akhir.
chain = bad_chain.with_fallbacks(
    [fallback_chain1, fallback_chain2, fallback_chain3])
# Memanggil chain yang telah dibuat dan mengirimkan nilai input.
chain.invoke({"question": "Apa ibu kota Korea Selatan?"})
```

{% code overflow="wrap" %}
```
content='Surabaya.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 4, 'prompt_tokens': 43, 'total_tokens': 47}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None} id='run-48264671-a8eb-4246-b5f1-2e956ca4f74a-0' usage_metadata={'input_tokens': 43, 'output_tokens': 4, 'total_tokens': 47}
```
{% endcode %}
