AI & Data Science

LangChain & AI Agent untuk Developer

Tutorial lengkap membangun AI Agent dengan LangChain β€” dari dasar LLM, chains, agents, memory, tools, hingga Retrieval Augmented Generation (RAG)

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 AgnosticBisa digunakan dengan OpenAI, Anthropic, Google, Ollama, dan lainnya
Chain CompositionMenggabungkan beberapa langkah LLM menjadi pipeline yang kompleks
Agent FrameworkMembuat agent yang bisa memilih tools dan mengambil keputusan sendiri
MemoryMenyimpan dan mengelola konteks percakapan
RAG SupportIntegrasi dengan vector database untuk retrieval dokumen
Ekosistem LuasRatusan integrasi dengan database, API, dan layanan cloud

Cara Kerja LLM

Diagram: 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         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
πŸ’‘ Tips

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

Bash
# 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

Python β€” LLM Pertama dengan LangChain
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
⚠️ Keamanan API Key

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.

Python β€” Prompt Templates
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

Python β€” LangChain Chains
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}")
Diagram: Arsitektur LangChain Chain
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              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.

Python β€” Memory di LangChain
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]
πŸ’‘ Tips Memory

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.

Python β€” Membuat Custom Tools
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

Python β€” AI Agent
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"])
Diagram: Agent Decision Loop
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                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 trainingBisa mengakses data terbaru dan spesifik
Bisa "mengarang" fakta (hallucination)Jawaban berdasarkan dokumen nyata
Tidak bisa mengakses data perusahaanBisa mengakses dokumen internal
Sulit memberikan sumber referensiBisa menunjukkan sumber jawaban

Implementasi RAG

Python β€” RAG dengan LangChain
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)
πŸ’‘ Tips RAG

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:

Pertanyaan 1: Apa fungsi utama dari LangChain?

a) Melatih model LLM dari awal
b) Framework untuk membangun aplikasi berbasis LLM dengan integrasi tools dan data
c) Database penyimpanan model AI
d) Compiler untuk kode Python

Pertanyaan 2: Apa yang dilakukan oleh operator pipe (|) dalam LCEL?

a) Menghapus data
b) Menggabungkan output dari satu langkah ke input langkah berikutnya
c) Membuat parallel execution
d) Mengkonversi tipe data

Pertanyaan 3: Mengapa Memory penting dalam aplikasi chatbot?

a) Agar model berjalan lebih cepat
b) Agar model bisa merujuk percakapan sebelumnya dan menjaga konteks
c) Agar model tidak membutuhkan API key
d) Agar model bisa menghasilkan gambar

Pertanyaan 4: Apa yang dimaksud dengan RAG dalam konteks LLM?

a) Teknik untuk melatih ulang model LLM
b) Teknik mengambil dokumen relevan dari basis pengetahuan eksternal sebagai konteks untuk LLM
c) Metode untuk mengkompres model LLM
d) Framework untuk deployment model

Pertanyaan 5: Apa yang membedakan AI Agent dari chatbot biasa?

a) Agent menggunakan bahasa yang lebih formal
b) Agent bisa memilih dan menggunakan tools secara mandiri untuk menyelesaikan tugas
c) Agent tidak memerlukan LLM
d) Agent hanya bisa menjawab pertanyaan ya/tidak
πŸ” Zoom
100%
🎨 Tema