1. Dasar-dasar LLM (Large Language Model)
Large Language Model (LLM) adalah model AI yang dilatih dengan data teks dalam skala sangat besar sehingga mampu memahami dan menghasilkan teks yang koheren. LLM seperti GPT-4, Claude, dan LLaMA telah mengubah cara kita berinteraksi dengan teknologi.
LangChain adalah framework open-source Python yang menyediakan abstraksi dan tools untuk membangun aplikasi berbasis LLM. Dengan LangChain, Anda bisa dengan mudah menggabungkan LLM dengan data eksternal, memori percakapan, dan tools untuk membuat AI Agent yang cerdas.
Mengapa LangChain?
| Fitur | Penjelasan |
|---|---|
| Model Agnostic | Bisa digunakan dengan OpenAI, Anthropic, Google, Ollama, dan lainnya |
| Chain Composition | Menggabungkan beberapa langkah LLM menjadi pipeline yang kompleks |
| Agent Framework | Membuat agent yang bisa memilih tools dan mengambil keputusan sendiri |
| Memory | Menyimpan dan mengelola konteks percakapan |
| RAG Support | Integrasi dengan vector database untuk retrieval dokumen |
| Ekosistem Luas | Ratusan integrasi dengan database, API, dan layanan cloud |
Cara Kerja LLM
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β CARA KERJA LLM β β β β βββββββββββ ββββββββββββββββ ββββββββββββββββ β β β Input β β Tokenisasi β β Model β β β β Prompt ββββΆβ (pecah kata ββββΆβ Transformer β β β β β β jadi token)β β (attention) β β β βββββββββββ ββββββββββββββββ ββββββββ¬ββββββββ β β β β β βΌ β β βββββββββββ ββββββββββββββββ ββββββββββββββββ β β β Output βββββ Detokenisasiβββββ Prediksi β β β β Teks β β (token β β β token β β β β β β kata) β β berikutnya β β β βββββββββββ ββββββββββββββββ ββββββββββββββββ β β β β Parameter penting: β β β’ temperature: kreativitas (0=konsisten, 1=kreatif) β β β’ max_tokens: panjang maksimal output β β β’ top_p: nucleus sampling β β β’ system prompt: instruksi dasar untuk model β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Untuk tutorial ini, Anda memerlukan API key dari salah satu provider LLM. OpenAI adalah yang paling mudah untuk mulai, tetapi Anda juga bisa menggunakan Ollama (gratis, lokal) atau model lain yang didukung LangChain.
2. Setup LangChain
LangChain memiliki ekosistem modular yang terdiri dari beberapa package. Di tutorial ini kita akan menggunakan langchain (core), langchain-openai (integrasi OpenAI), dan beberapa tools tambahan.
Instalasi
# Instal LangChain dan integrasi OpenAI pip install langchain langchain-openai langchain-community # Untuk RAG (vector store) pip install chromadb # Untuk document loaders pip install pypdf unstructured # Setup API key sebagai environment variable # Linux/Mac: export OPENAI_API_KEY="sk-your-api-key-here" # Windows PowerShell: # $env:OPENAI_API_KEY="sk-your-api-key-here" # Atau gunakan file .env dengan python-dotenv pip install python-dotenv
Panggilan LLM Pertama
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
# Muat API key dari .env file
load_dotenv()
# Inisialisasi LLM
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
max_tokens=500
)
# Panggilan sederhana
response = llm.invoke("Apa itu Machine Learning? Jelaskan dalam 3 kalimat.")
print(response.content)
# Dengan system message
from langchain_core.messages import SystemMessage, HumanMessage
messages = [
SystemMessage(content="Kamu adalah tutor programming yang ramah dan sabar."),
HumanMessage(content="Jelaskan apa itu Python dalam bahasa sederhana.")
]
response = llm.invoke(messages)
print(response.content)
# Streaming response
for chunk in llm.stream("Ceritakan tentang AI dalam 100 kata"):
print(chunk.content, end="", flush=True)
print() # Newline setelah selesai
Jangan pernah menyimpan API key langsung di kode! Gunakan environment variable atau file .env yang tidak di-commit ke repository. Tambahkan .env ke .gitignore Anda.
3. Prompt Templates
Prompt Template memungkinkan Anda membuat prompt yang dinamis dan reusable. Ini sangat penting untuk membangun aplikasi LLM yang fleksibel karena prompt bisa diisi dengan input pengguna secara dinamis.
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
# ===== Chat Prompt Template =====
# Template dengan system dan user message
prompt = ChatPromptTemplate.from_messages([
("system", "Kamu adalah ahli {topik} yang menjelaskan dengan bahasa sederhana."),
("user", "{pertanyaan}")
])
# Format template
messages = prompt.invoke({
"topik": "Python",
"pertanyaan": "Apa itu list comprehension?"
})
print(messages)
# ===== Prompt Template sederhana (untuk LLM text) =====
template = PromptTemplate.from_template(
"Buatkan {jumlah} contoh penggunaan {konsep} dalam bahasa {bahasa}."
)
result = template.invoke({
"jumlah": "5",
"konsep": "dictionary Python",
"bahasa": "Indonesia"
})
print(result.text)
# ===== Few-shot Prompt Template =====
from langchain_core.prompts import FewShotChatMessagePromptTemplate
examples = [
{"input": "hai", "output": "Halo! Ada yang bisa saya bantu?"},
{"input": "terima kasih", "output": "Sama-sama! Senang bisa membantu."},
]
example_prompt = ChatPromptTemplate.from_messages([
("human", "{input}"),
("ai", "{output}"),
])
few_shot_prompt = FewShotChatMessagePromptTemplate(
example_prompt=example_prompt,
examples=examples,
)
final_prompt = ChatPromptTemplate.from_messages([
("system", "Kamu adalah asisten yang ramah dan sopan."),
few_shot_prompt,
("human", "{input}"),
])
# Gabungkan prompt dengan LLM
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
chain = final_prompt | llm
response = chain.invoke({"input": "selamat pagi"})
print(response.content)
# ===== Prompt dengan Partial Variables =====
from datetime import datetime
template = ChatPromptTemplate.from_messages([
("system", "Tanggal hari ini: {tanggal}. Kamu adalah asisten yang informatif."),
("user", "{pertanyaan}")
])
# Partial variable bisa diisi sebelumnya
prompt_with_date = template.partial(
tanggal=datetime.now().strftime("%d %B %Y")
)
# Hanya perlu mengisi 'pertanyaan' saat invoke
chain = prompt_with_date | llm
response = chain.invoke({"pertanyaan": "Apa kejadian penting hari ini?"})
print(response.content)
4. LangChain Chains
Chain adalah konsep inti di LangChain β menggabungkan beberapa langkah (prompt β LLM β output parser) menjadi satu pipeline yang bisa dijalankan. Dengan LCEL (LangChain Expression Language), Anda bisa membuat chain yang kompleks dengan sintaks yang elegan.
Basic Chain dengan LCEL
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
# ===== Simple Chain =====
prompt = ChatPromptTemplate.from_template(
"Jelaskan konsep {topik} dalam 3 paragraf untuk level {level}."
)
# LCEL: prompt | llm | parser
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"topik": "Recursion", "level": "pemula"})
print(result)
# ===== Sequential Chain (berantai) =====
# Chain 1: Buat outline
outline_prompt = ChatPromptTemplate.from_template(
"Buatkan outline 5 poin untuk artikel tentang {topik}."
)
# Chain 2: Kembangkan setiap poin
expand_prompt = ChatPromptTemplate.from_template(
"Kembangkan setiap poin dari outline berikut menjadi paragraf singkat:\n{outline}"
)
# Gabungkan menjadi sequential chain
from langchain_core.runnables import RunnablePassthrough
outline_chain = outline_prompt | llm | StrOutputParser()
expand_chain = expand_prompt | llm | StrOutputParser()
# Menggunakan pipe operator
full_chain = (
{"outline": outline_chain}
| expand_chain
)
result = full_chain.invoke({"topik": "Python Web Framework"})
print(result)
# ===== Chain dengan Structured Output =====
from typing import List
class AnalisisTeks(BaseModel):
"""Struktur output untuk analisis teks"""
sentiment: str = Field(description="sentimen: positif/negatif/netral")
topik_utama: str = Field(description="topik utama teks")
kata_kunci: List[str] = Field(description="3-5 kata kunci")
ringkasan: str = Field(description="ringkasan satu kalimat")
prompt_analisis = ChatPromptTemplate.from_template(
"Analisis teks berikut dan berikan hasil dalam format JSON:\n\n{Teks}"
)
structured_llm = llm.with_structured_output(AnalisisTeks)
analisis_chain = prompt_analisis | structured_llm
result = analisis_chain.invoke({
"Teks": "Python semakin populer di kalangan data scientist karena "
"library-nya yang lengkap dan komunitas yang besar."
})
print(f"Sentimen: {result.sentiment}")
print(f"Topik: {result.topik_utama}")
print(f"Kata Kunci: {result.kata_kunci}")
print(f"Ringkasan: {result.ringkasan}")
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β LANGCHAIN CHAIN ARCHITECTURE β β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β Simple Chain (LCEL) β β β β β β β β Input βββΆ Prompt Template βββΆ LLM βββΆ Parser β β β β β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β Sequential Chain β β β β β β β β Input βββΆ Chain1 βββΆ Chain2 βββΆ Chain3 βββΆ Out β β β β β β β β β β β outline expand summarize β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β Parallel Chain β β β β β β β β ββββΆ Chain A βββ β β β β Input ββ€ ββββΆ Combiner βββΆ Output β β β β ββββΆ Chain B βββ β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β β LCEL Operator: β β chain = prompt | llm | parser β β RunnablePassthrough, RunnableParallel, RunnableLambda β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
5. Memory: Konteks Percakapan
Secara default, LLM tidak memiliki memori β setiap panggilan bersifat independen. Memory di LangChain memungkinkan kita menyimpan riwayat percakapan sehingga model bisa merujuk ke pesan sebelumnya dan menjaga konteks.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, AIMessage
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
# ===== Manual Memory Management =====
# Simpan riwayat secara manual
history = ChatMessageHistory()
# Tambahkan pesan
history.add_user_message("Halo, nama saya Budi")
history.add_ai_message("Halo Budi! Ada yang bisa saya bantu?")
# Akses riwayat
print(history.messages)
# [HumanMessage("Halo, nama saya Budi"), AIMessage("Halo Budi!...")]
# ===== Automatic Memory dengan RunnableWithMessageHistory =====
prompt = ChatPromptTemplate.from_messages([
("system", "Kamu adalah asisten yang ramah. Gunakan riwayat percakapan untuk menjawab."),
MessagesPlaceholder(variable_name="history"),
("human", "{input}"),
])
chain = prompt | llm
# Store untuk menyimpan session history
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# Wrap chain dengan message history
chain_with_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="history",
)
# Konfigurasi session
config = {"configurable": {"session_id": "user_123"}}
# Percakapan berantai
response1 = chain_with_history.invoke(
{"input": "Halo, nama saya Andi"}, config=config
)
print(response1.content)
response2 = chain_with_memory.invoke(
{"input": "Siapa nama saya?"}, config=config
)
print(response2.content)
# Output: "Nama Anda adalah Andi" (ingat dari percakapan sebelumnya)
response3 = chain_with_history.invoke(
{"input": "Saya bekerja sebagai software engineer"}, config=config
)
print(response3.content)
response4 = chain_with_history.invoke(
{"input": "Apa pekerjaan saya?"}, config=config
)
print(response4.content)
# Output: "Anda bekerja sebagai software engineer"
# ===== Window Memory (hanya simpan N pesan terakhir) =====
from langchain_core.chat_history import InMemoryChatMessageHistory
def get_limited_history(session_id: str):
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory(max_messages=10)
return store[session_id]
Untuk produksi, simpan riwayat percakapan di database (Redis, PostgreSQL) bukan di memori. Gunakan RedisChatMessageHistory atau SQLChatMessageHistory untuk persistensi data.
6. Tools untuk Agent
Tools adalah fungsi-fungsi yang bisa dipanggil oleh AI Agent untuk berinteraksi dengan dunia luar β mencari di internet, menghitung matematika, mengakses database, menjalankan kode, dan lainnya. Tools adalah yang membedakan Agent dari chatbot biasa.
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
import requests
import math
# ===== Membuat Custom Tools =====
@tool
def hitung_luas_lingkaran(jari_jari: float) -> str:
"""Hitung luas lingkaran berdasarkan jari-jari. Input: jari-jari dalam satuan meter."""
luas = math.pi * jari_jari ** 2
return f"Luas lingkaran dengan jari-jari {jari_jari}m adalah {luas:.2f} mΒ²"
@tool
def hitung_faktorial(angka: int) -> str:
"""Hitung faktorial dari sebuah bilangan bulat positif."""
if angka < 0:
return "Error: Faktorial tidak didefinisikan untuk bilangan negatif"
hasil = math.factorial(angka)
return f"Faktorial dari {angka} adalah {hasil}"
@tool
def ubah_suhu(nilai: float, dari: str, ke: str) -> str:
"""Konversi suhu antara Celsius, Fahrenheit, dan Kelvin.
Parameter: nilai (float), dari (str: C/F/K), ke (str: C/F/K)."""
# Konversi ke Celsius dulu
if dari.upper() == 'C':
celsius = nilai
elif dari.upper() == 'F':
celsius = (nilai - 32) * 5/9
elif dari.upper() == 'K':
celsius = nilai - 273.15
else:
return f"Error: Unit '{dari}' tidak dikenali. Gunakan C, F, atau K."
# Konversi dari Celsius ke target
if ke.upper() == 'C':
hasil = celsius
elif ke.upper() == 'F':
hasil = celsius * 9/5 + 32
elif ke.upper() == 'K':
hasil = celsius + 273.15
else:
return f"Error: Unit '{ke}' tidak dikenali."
return f"{nilai}Β°{dari.upper()} = {hasil:.2f}Β°{ke.upper()}"
@tool
def cari_arti_kata(kata: str) -> str:
"""Cari arti sebuah kata dalam bahasa Inggris menggunakan Free Dictionary API."""
url = f"https://api.dictionaryapi.dev/api/v2/entries/en/{kata}"
try:
response = requests.get(url, timeout=5)
if response.status_code == 200:
data = response.json()[0]
arti = data['meanings'][0]['definitions'][0]['definition']
return f"Arti '{kata}': {arti}"
else:
return f"Kata '{kata}' tidak ditemukan dalam kamus."
except Exception as e:
return f"Error saat mencari kata: {str(e)}"
# Daftarkan semua tools
tools = [hitung_luas_lingkaran, hitung_faktorial, ubah_suhu, cari_arti_kata]
# Cek deskripsi tools
for t in tools:
print(f"Tool: {t.name} β {t.description[:60]}...")
7. AI Agents
AI Agent adalah sistem LLM yang bisa mengambil keputusan sendiri β memilih tool mana yang akan digunakan, menentukan argumen apa yang diperlukan, dan menggabungkan hasil dari beberapa tool untuk menjawab pertanyaan pengguna.
Membuat Agent dengan Tools
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import create_tool_calling_agent, AgentExecutor
# Inisialisasi LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# Prompt untuk agent
prompt = ChatPromptTemplate.from_messages([
("system", (
"Kamu adalah asisten AI yang cerdas dan membantu. "
"Kamu memiliki akses ke berbagai tools untuk membantu pengguna. "
"Selalu gunakan tools yang tersedia jika pertanyaan memerlukan "
"perhitungan atau pencarian data. Jelaskan hasilnya dengan jelas."
)),
MessagesPlaceholder(variable_name="chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
# Buat agent
agent = create_tool_calling_agent(llm, tools, prompt)
# Buat executor
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # Tampilkan proses berpikir agent
max_iterations=5, # Batasi iterasi
handle_parsing_errors=True, # Tangani error parsing
)
# Jalankan agent
response = agent_executor.invoke({
"input": "Berapa luas lingkaran dengan jari-jari 7 meter?"
})
print(response["output"])
response = agent_executor.invoke({
"input": "Ubah suhu 100 derajat Fahrenheit ke Celsius"
})
print(response["output"])
response = agent_executor.invoke({
"input": "Apa arti kata 'serendipity' dalam bahasa Inggris?"
})
print(response["output"])
# Multi-step reasoning
response = agent_executor.invoke({
"input": (
"Saya punya lingkaran dengan jari-jari 5 meter. "
"Berapa luasnya dalam meter persegi dan centimeter persegi?"
)
})
print(response["output"])
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β AI AGENT DECISION LOOP β β β β ββββββββββββββββ β β β User Input β β β ββββββββ¬ββββββββ β β β β β βΌ β β ββββββββββββββββββββ β β β LLM Reasoning ββββββββββββ β β β (pilih action) β β β β ββββββββββ¬ββββββββββ β β β β β β β βΌ β β β ββββββββββββββββββββββ β β β β Tool Selection β β β β β + Arguments β β β β ββββββββββ¬ββββββββββββ β β β β β β β βΌ β β β ββββββββββββββββββββββ β β β β Execute Tool βββββββββββ β β β β Return Result β (Observation) β β ββββββββββ¬ββββββββββββ β β β β β βΌ β β ββββββββββββββββββββββ β β β Final Answer? βββNoβββΆ Loop kembali β β ββββββββββ¬ββββββββββββ β β β Yes β β βΌ β β ββββββββββββββββββββββ β β β Final Response β β β ββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
8. RAG: Retrieval Augmented Generation
RAG (Retrieval Augmented Generation) adalah teknik yang menggabungkan kemampuan LLM dengan basis pengetahuan eksternal. Alih-alih hanya mengandalkan data pelatihan, RAG mengambil dokumen yang relevan dari database dan memberikannya sebagai konteks kepada LLM.
Mengapa RAG Penting?
| Tanpa RAG | Dengan RAG |
|---|---|
| Pengetahuan terbatas pada data training | Bisa mengakses data terbaru dan spesifik |
| Bisa "mengarang" fakta (hallucination) | Jawaban berdasarkan dokumen nyata |
| Tidak bisa mengakses data perusahaan | Bisa mengakses dokumen internal |
| Sulit memberikan sumber referensi | Bisa menunjukkan sumber jawaban |
Implementasi RAG
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# ===== STEP 1: Load Documents =====
# Dari file PDF
loader = PyPDFLoader("dokumen_perusahaan.pdf")
docs = loader.load()
# Dari file teks
loader = TextLoader("faq.txt", encoding="utf-8")
docs = loader.load()
print(f"Jumlah halaman/dokumen: {len(docs)}")
# ===== STEP 2: Split Documents =====
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # Ukuran setiap chunk (karakter)
chunk_overlap=200, # Overlap antar chunk
separators=["\n\n", "\n", ". ", " ", ""]
)
chunks = text_splitter.split_documents(docs)
print(f"Jumlah chunks: {len(chunks)}")
# ===== STEP 3: Create Embeddings & Vector Store =====
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# Simpan ke ChromaDB (in-memory)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db" # Persist ke disk
)
# Buat retriever
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3} # Ambil 3 dokumen paling relevan
)
# ===== STEP 4: Buat RAG Chain =====
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
rag_prompt = ChatPromptTemplate.from_template("""
Gunakan konteks berikut untuk menjawab pertanyaan.
Jika jawaban tidak ada dalam konteks, katakan bahwa Anda tidak tahu.
Konteks:
{context}
Pertanyaan: {question}
Jawaban:""")
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| rag_prompt
| llm
| StrOutputParser()
)
# ===== STEP 5: Jalankan RAG =====
response = rag_chain.invoke("Apa kebijakan cuti karyawan?")
print(response)
response = rag_chain.invoke("Bagaimana cara klaim asuransi?")
print(response)
Untuk hasil terbaik, pastikan dokumen sudah dibersihkan dari noise (header/footer yang berulang, watermark). Gunakan chunk_overlap yang cukup (10-20% dari chunk_size) agar konteks tidak terpotong. Eksperimen dengan k (jumlah dokumen retrieved) untuk menemukan sweet spot.
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang LangChain dan AI Agent: