10
Caching dan Aplikasi Chat Streamlit
Halaman ini membahas dua backend cache yang disediakan LangChain untuk menekan biaya token, lalu memakai agen ReAct di dalam UI chat Streamlit. Kedua berkas caching menaruh cache pada satu atribut global, langchain.llm_cache, sehingga seluruh pemanggilan llm.invoke di proses yang sama ikut terkena kebijakan itu. Berkas ketiga tidak melakukan caching sendiri; ia menampilkan langkah antara agen lewat StreamlitCallbackHandler. Keduanya bergantung pada langchain-classic==1.0.1 selama API legacy masih dipakai (requirements.txt:60-621).
Callback biaya token sebagai alat ukur
13_caching_memory.py mengimpor get_openai_callback dari langchain_community.callbacks dan InMemoryCache dari langchain_community.cache, lalu memuat .env lewat load_dotenv() (13_caching_memory.py:1-82). LLM dibuat sekali dengan model gpt-3.5-turbo-instruct, dan tepat setelahnya cache global dipasang (13_caching_memory.py:10-133).
Blok pertama membungkus satu llm.invoke di dalam with get_openai_callback() as cb, mencetak hasilnya, lalu mencetak str(cb) bersama durasi end - start dalam detik (13_caching_memory.py:15-214). Blok kedua memakai prompt yang sama persis dua kali di dalam get_openai_callback() as cb2 (13_caching_memory.py:23-315). Karena prompt itu sudah dilewatkan pada blok pertama, kedua panggilan di blok kedua dilayani dari cache. Angka biaya dan waktu yang tercetak di str(cb2) adalah bukti cache hit; nilainya baru muncul saat skrip dijalankan, jadi halaman ini tidak menyalin angka apa pun.
⚠️
InMemoryCachehanya hidup selama proses Python berjalan. Menjalankan ulang skrip mengosongkan cache, sehingga blok kedua kembali menagih token penuh.
SQLiteCache dan bypass pada langkah reduce
13_caching_sqlite.py memakai SQLiteCache(database_path=".langchain.db") sebagai backend cache, dengan komentar bahwa berkas itu sebaiknya tidak ikut di-commit (13_caching_sqlite.py:12-136). Berkas .gitignore di repository ini sudah memuat pola *.db, sehingga .langchain.db terabaikan secara otomatis (.gitignore:6-6).
Di sisi model, berkas ini membuat dua LLM: llm biasa dan no_cache_llm yang diberi argumen cache=False (13_caching_sqlite.py:17-197). Teks berita dibaca dari news/summary.txt, dipecah oleh CharacterTextSplitter, dan tiga potongan pertama dibungkus menjadi Document (13_caching_sqlite.py:21-298). Rantai diringkas dengan load_summarize_chain(..., chain_type="map_reduce", reduce_llm=no_cache_llm) (13_caching_sqlite.py:21-298), sehingga langkah map mengikuti cache global sedangkan langkah reduce selalu memanggil API.
Pemanggilan pertama mencetak result["output_text"] bersama str(cb) (13_caching_sqlite.py:31-379). Pemanggilan kedua mengulang rantai dan dokumen yang sama, lalu mencetak str(cb2) (13_caching_sqlite.py:40-4610). Karena langkah map identik, langkah itu dilayani dari .langchain.db, sementara langkah reduce tetap menghasilkan biaya.
| Berkas | Backend cache | Cakupan | Cara melewati cache |
|---|---|---|---|
13_caching_memory.py | InMemoryCache() | proses Python yang sedang berjalan | tidak ada; semua llm.invoke memakai cache |
13_caching_sqlite.py | SQLiteCache(database_path=".langchain.db") | berkas SQLite, bertahan antar proses | OpenAI(..., cache=False) pada reduce_llm |
Siklus satu request di depan cache
Diagram berikut menunjukkan urutan yang sama untuk kedua berkas. get_openai_callback membuka blok pengukuran, llm.invoke menanyakan langchain.llm_cache lebih dulu, dan hanya cabang miss yang memanggil API. Pemanggilan pada blok kedua tidak pernah mencapai OpenAI API.
sequenceDiagram
participant S as script
participant C as get_openai_callback
participant L as llm.invoke
participant K as langchain.llm_cache
participant A as OpenAI API
S->>C: enter context manager
S->>L: invoke prompt
L->>K: lookup prompt
alt cache hit
rect rgb(45,125,70)
K-->>L: response tersimpan
L-->>S: hasil tanpa request ke API
end
else cache miss
rect rgb(245,158,11)
L->>A: request completion
A-->>L: response
end
L->>K: simpan response
L-->>S: hasil
end
S->>C: str(cb) + durasi
%% fig: Siklus request di depan langchain.llm_cache
%% desc: Request dicek ke cache lebih dulu, cabang hit kembali tanpa biaya dan cabang miss memanggil OpenAI API lalu menyimpan response.Merakit agen chat Streamlit
14_streamlit.py mengimpor AgentType, initialize_agent, dan load_tools dari langchain_classic.agents, StreamlitCallbackHandler dari langchain_community.callbacks, lalu memanggil load_dotenv() (14_streamlit.py:1-811). LLM dibuat dengan temperature=0 dan streaming=True, kuncinya dibaca dari os.getenv("OPENAI_API_KEY"), satu tool ddg-search dimuat lewat load_tools, dan initialize_agent dipanggil dengan agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION (14_streamlit.py:10-1712). Dua contoh prompt ditulis sebagai komentar: nama anak presiden ke-44 Amerika Serikat, dan tiga pemegang saham terbesar Nvidia (14_streamlit.py:19-2013).
Alur UI-nya pendek. st.chat_input() diuji dengan walrus :=, prompt ditulis ke st.chat_message("user"), lalu di dalam st.chat_message("assistant") aplikasi menulis 🧠 thinking..., membuat StreamlitCallbackHandler(st.container()), memanggil agent.invoke({"input": prompt}, callbacks=[st_callback]), dan menulis response["output"] (14_streamlit.py:21-2714). Handler itu yang memindahkan jejak tool dan penalaran agen ke kontainer chat, sehingga pembaca melihat prosesnya dan bukan hanya jawaban akhir.
graph LR
in["st.chat_input()"] --> user["st.chat_message user"]
in --> agent["initialize_agent ZERO_SHOT_REACT_DESCRIPTION"]
llm["OpenAI temperature=0 streaming=True"] --> agent
tools["load_tools ddg-search"] --> agent
agent --> cb["StreamlitCallbackHandler st.container()"]
agent --> out["st.write response output"]
style in fill:#f59e0b,color:#000
style llm fill:#f59e0b,color:#000
style agent fill:#f59e0b,color:#000
style cb fill:#8b5cf6,color:#fff
style tools fill:#336791,color:#fff
style out fill:#2d7d46,color:#fff
%% fig: Perakitan agen chat Streamlit
%% desc: Prompt masuk lewat st.chat_input, dijalankan agen ReAct dengan tool ddg-search, dan jejak langkahnya dialirkan StreamlitCallbackHandler ke kontainer chat.Catatan kompatibilitas dan dependensi
Dua berkas ini adalah bagian dari migrasi ke LangChain 1.x. CHANGES.md mencatat load_summarize_chain dipindah ke langchain_classic.chains.summarize pada 13_caching_sqlite.py, dan initialize_agent dipindah ke langchain_classic.agents pada 14_streamlit.py (CHANGES.md:123-12715). Versi paket yang diuji tercatat di requirements.txt, termasuk langchain, langchain-classic, langchain-community, dan langchain-openai (requirements.txt:60-621).
⚠️
requirements.txttidak memuatstreamlitmaupun paket pencarian DuckDuckGo yang dibutuhkanload_tools(["ddg-search"]). Pasang keduanya terpisah, dan siapkanOPENAI_API_KEYdi.env, sebelum menjalankan14_streamlit.py.
Untuk dasar pembuatan objek OpenAI dan pemilihan model, lihat Dasar LLM dan Embedding. Pola agen lain yang memakai agent.invoke dan tool dibahas di Text-to-SQL dan Analitik Data.