Skip to content
··7 menit baca

Bagaimana Saya Keluar dari Pasir Hisap Coding dengan AI Agent

Saya terjun ke dunia pembuatan chatbot dengan nol kemampuan coding dan penuh antusiasme — hanya untuk menemukan bahwa v0.1 saya adalah bencana database CSV dan chunking primitif, sampai AI agent menyelamatkan saya.

Update (2026): Chatbot ini berevolusi menjadi Sydney! Setelah banyak iterasi, Sydney sekarang ada di /ask/ dan fokus pada konten blog dan produk.


Awal November tahun lalu, saya meluncurkan chatbot DIY saya versi 0.1. Waktu itu, saya menulis, "Meskipun v0.1 merupakan langkah besar pertama sebagai pemula coding, ia memiliki keterbatasan yang signifikan." Nah, ternyata, itu pernyataan yang sangat merendahkan. Saya tidak menyadari betapa kikuknya seluruh proses dan build saya. Kenyataannya, upaya awal saya, meskipun sungguh-sungguh, lebih merupakan prototipe yang dijahit dengan antusiasme tapi pengetahuan yang terbatas.

Postingan ini bukan sekadar kelanjutan; ini adalah penyelaman mendalam ke dalam perjalanan yang terungkap sejak saat itu — perjalanan yang penuh cobaan, kesalahan, dan pelajaran berharga. Saya mengungkap detail-detail perjalanan ini, bukan hanya demi transparansi tapi dengan harapan pengalaman saya, sedetail ini, mungkin bisa beresonansi atau bahkan membantu seseorang di jalur yang sama. (Sebagai konteks tambahan, saya adalah profesional periklanan paruh baya tanpa pengalaman coding sebelumnya.)

Seperti disebutkan, untuk meluncurkan v0.1 chatbot saya, saya terutama mengikuti instruksi dari kursus singkat ini "Building Systems with the ChatGPT API" dan dua cookbook dari OpenAI: Question answering using embeddings-based search dan How to count tokens with tiktoken.

Inilah mengapa chatbot v0.1 saya buruk:

  • Chunking: Saya hanya membagi postingan blog panjang menjadi chunk yang lebih kecil berdasarkan panjang token alias split karakter statis sederhana. Cara paling primitif untuk membagi :D. Kalau mau memahami mengapa ini ide yang buruk, baca tentang 5 level text splitting dari Greg Kamradt di sini.
  • Embedding: Saya kesulitan dengan embedding. Saya menggunakan model embedding OpenAI, tapi terus mentok limit request API, menyebabkan proses embedding gagal di tengah jalan. Kemudian saya belajar untuk melakukan batch request dan menambahkan timeout antar batch untuk menghindari limit. Akhirnya saya menyimpan embedding yang dihasilkan ke file .csv sederhana sebagai "database" darurat saya.
  • Database: Saya tahu CSV bukan optimal untuk database, tapi tidak punya skill untuk alternatif yang lebih baik.
  • Metadata: Awalnya saya tidak menyadari bahwa menyertakan metadata seperti tanggal publikasi dan URL postingan itu penting agar chatbot bisa menjawab pertanyaan pengguna secara akurat. Saya harus mengulang embedding dan penyimpanan untuk menyertakan metadata yang relevan.
  • Retriever: Saya tidak tahu tentang jenis-jenis retriever dan algoritma yang berbeda. Saya hanya menggunakan relevance search dari OpenAI untuk mengambil sejumlah hasil yang di-hardcode.
  • Memory: Untuk bisa bercakap-cakap, chatbot perlu bisa mengingat apa yang dikatakan pengguna sebelumnya. Dan di sinilah dengan context window yang terbatas dari gpt-3.5 (pada saat itu), ada trade off yang jelas antara ukuran chunk, berapa banyak hasil yang ingin kamu ambil.
    • Misalnya, jika ukuran chunk kamu 800 token dan retriever mengembalikan top 8 hasil, itu sudah 6.400 token atau lebih dari 50% dari limit model lama.
    • Di atas itu baru 1 pertanyaan jadi bayangkan percakapan multi-turn dan betapa cepatnya memory bisa terisi.
    • Salah satu cara untuk mengatasi masalah ini adalah dengan ukuran chunk yang lebih kecil dan retriever mengembalikan lebih sedikit hasil tapi dengan retriever dasar (di atas), ini berarti model tidak punya informasi yang komprehensif untuk menjawab pertanyaan.
  • Saya bahkan tidak menggunakan IDE sama sekali. Semua kode diedit menggunakan TextEdit di Mac :D (Sudah saya bilang kalau saya noob kan? :P)
  • Saya bisa terus dan terus tapi saya rasa kamu sudah paham gambarannya.

"Lembah kematian" saya

Bersemangat untuk meningkatkan dari keterbatasan v0.1, saya mencoba beberapa kursus online, berharap mereka akan memberikan bagian yang hilang untuk menaikkan level skill saya. Tapi kemajuan hanya berujung pada jalan buntu.

Saya berjuang melalui latihan-latihan tentang vector database (Vector Databases: from Embeddings to Applications with Weaviate), metode RAG evaluatif (Building and Evaluating Advanced RAG Applications dengan Llama-Index dan Truera), dan teknik retrieval lanjutan (Advanced Retrieval for AI with Chroma.) Seberapa pun kerasnya saya mencoba, saya tidak bisa menghubungkan teori dengan aplikasi praktis menggunakan data blog saya sendiri.

Apakah kursus-kursusnya dirancang dengan buruk? Tidak — kekurangannya ada pada kurangnya pengetahuan dasar saya sendiri. Tetap saja, kegagalan demi kegagalan sangat frustasi, belum lagi demoralizing. Saya menemukan diri saya di lembah kiasan, tidak yakin bagaimana melanjutkan.

Kemenangan Kecil Seiring Waktu

Nugget-nugget berharga memang muncul dari kegagalan berulang:

  • Adopsi VS Code menggantikan TextEdit

  • Memanfaatkan ekstensi GitHub Copilot

  • Menghargai Jupyter Notebook untuk lingkungan pengembangan

Kursus terakhir menyebutkan LangChain, framework populer baru untuk membangun chatbot. Sebenarnya saya sudah mencoba tutorial LangChain berbulan-bulan sebelumnya ("LangChain: Chat with Your Data" dan "Functions, Tools and Agents with LangChain") tanpa banyak keberuntungan. Tapi sekarang, dengan pengetahuan yang susah payah diperoleh, mengunjungi kembali dokumentasinya ternyata mencerahkan. Konsep-konsepnya terhubung dan arsitektur modularnya masuk akal secara intuitif.

Saya bisa membayangkan mengadaptasi kemampuan kokoh LangChain untuk proyek passion saya. Akhirnya, jalan ke depan terungkap! Selangkah demi selangkah, saya menyesuaikan diri dengan pipeline-nya untuk data ingestion, embedding, storage, dan retrieval.

Kepercayaan diri saya tumbuh dengan setiap bagian yang berhasil saya implementasikan. V2 mulai terbentuk...

Membangun Ulang Fondasi

Dengan Langchain sebagai panduan, saya mulai merekonstruksi chatbot dari nol:

Mengimpor Ekspor WordPress ke JSON

Setelah beberapa percobaan dan penyesuaian parameter ingestion, JSONLoader dari LangChain berhasil mem-parse postingan ekspor saya. Sekarang input yang tervalidasi bisa menggerakkan pipeline selanjutnya. (Kode untuk langkah ini ada di file "DataIngestionAndIndexing.ipynb" di repo publik Github ini.)

Text Splitting Otomatis

Chunking naif berbasis panjang token saya diganti dengan SentenceTransformers dari LangChain, menggunakan NLP lanjutan untuk membagi unit-unit semantik. Tidak ada lagi kalimat terputus terpotong di tengah! Konfigurasi menjaga chunk tetap berukuran sesuai untuk model dengan keterbatasan memori.

from langchain.text_splitter import SentenceTransformersTokenTextSplitter
# Define the token splitter with specific configurations
token_splitter = SentenceTransformersTokenTextSplitter(
    chunk_overlap=0,  # Overlap between chunks
    tokens_per_chunk=256  # Number of tokens per chunk
)
# Split the documents into chunks based on tokens
all_splits = token_splitter.split_documents(documents)
print(f"Total document splits: \{len(all_splits)\}")

Menghasilkan Embedding dan Indeks

Perjuangan masa lalu dengan limit API OpenAI hilang dengan menggunakan wrapper LangChain untuk embedding OpenAI. Dibundel dalam dua baris kode, embedding dengan bersih mengekstrak fitur menonjol dari teks yang dibagi.

Untuk vector store, saya memilih FAISS (Facebook AI Similarity Search) dibanding Weaviate atau Chroma. FAISS yang teruji industri mencapai keseimbangan yang tepat antara kemampuan dan kompleksitas untuk kebutuhan saya. Versi CPU-nya dengan cepat mengindeks chunk blog, menghasilkan database searchable yang kompak. Saya tidak perlu khawatir tentang batching atau mentok request API ke OpenAI lagi.

Langchain mendukung banyak vector store jadi kamu bisa cek di sini.

# Initialize embeddings and FAISS vector store
embeddings = OpenAIEmbeddings()
db = FAISS.from_documents(all_splits, embeddings)

# Save the vector store locally
db.save_local("path/to/save/faiss_index")  # Placeholder for save path/ index name

Dua baris kode! Itu saja.

Yang juga saya perhatikan adalah jika konten kamu tidak kecil (saya punya 400+ postingan blog), maka kamu tidak boleh langsung menggunakan retriever setelah embedding karena indeks butuh sedikit waktu untuk selesai/menjadi stabil.

Mengevaluasi Retriever

Menyiapkan retriever ini hanya butuh 1 baris kode :D, menggunakan FAISS sebagai vector store.

retriever = db.as_retriever(search_type="mmr")

Dengan indeks dan retriever saya terkunci, saya punya pipeline data yang siap menggerakkan chatbot cerdas!

Merancang Agent Percakapan

Saya memutuskan untuk menggunakan framework agent dari langchain untuk membangun chatbot ini. Apakah berlebihan saat ini? Ya memang. Tapi harapan saya adalah seiring waktu, saya bisa mengevolusi chatbot ini dan memberinya lebih banyak "tools" alias fungsionalitas. Langchain membuat pengaturan agent dan memberikan tools sangat mudah.

embeddings = OpenAIEmbeddings()
db = FAISS.load_local("path/to/your/faiss_index_file", embeddings)  # Replace the path with your actual FAISS index file path
retriever = db.as_retriever(search_type="mmr")
tool = create_retriever_tool(
    retriever,
    "search_your_blog",  # Replace "search_your_blog" with a descriptive name for your tool
    "Your tool description here"  # Provide a brief description of what your tool does
)

tools = [tool]
prompt_template = ChatPromptTemplate.from_messages([
    # Customize the prompt template according to your chatbot's persona and requirements
])

llm = ChatOpenAI(model_name="gpt-3.5-turbo-1106", temperature=0)
llm_with_tools = llm.bind_tools(tools)
agent = (
    \{
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
        "chat_history": lambda x: x["chat_history"],
    \}
    | prompt_template
    | llm_with_tools
    | OpenAIToolsAgentOutputParser()
)

agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

Kode python final lengkap untuk agent

Terakhir, kalau kamu mau mencoba chatbot v2, ini dia.

Apakah aneh kalau chatbot-nya tidak tahu apa-apa tentang kamu Chandler?

P.S: Terima kasih kepada beberapa dari kamu yang menghubungi untuk memberitahu bahwa chatbot tidak tahu apa-apa tentang saya. Dan kamu benar! Itu karena saya lupa mengekspor halaman "About" dan hanya mengekspor "published posts". Ini kedua kalinya saya lupa melakukan ini jadi saya akan memasukkan pertanyaan dasar tentang saya dalam daftar pertanyaan eval. Pelajaran berharga!

Terima kasih semuanya yang sudah berbagi feedback konstruktif. Terus berikan ya. Dan ya saya tahu chatbot-nya sangat lambat untuk memulai jadi saya sedang mengerjakan itu juga. :| (Sudah saya bilang kan kalau saya noob? :P)

Update singkat

Masalah chatbot yang tidak tahu apa-apa tentang saya sekarang sudah diperbaiki. Ini yang saya lakukan dan pelajari:

  • Ekspor halaman "About me" dari Wordpress ke .XML sesuai langkah di atas.
  • Melakukan text splitting dan menghasilkan embedding menggunakan FAISS seperti di atas. Menyimpan vector store dengan nama berbeda secara lokal untuk menguji retriever
# save the vector store to local machine
db.save_local("faiss_index_about")
# set up the retriever to test the new vector store about Chandler
from langchain.retrievers.multi_query import MultiQueryRetriever
llm = ChatOpenAI(temperature=0)
retriever_from_llm = MultiQueryRetriever.from_llm(
    retriever=db.as_retriever(), llm=llm
)
# test retriever
question = "Who is Chandler Nguyen?"
results = retriever_from_llm.get_relevant_documents(query=question, top_k=8)
for doc in results:
    print(f"Content: {doc.page_content}, Metadata: {doc.metadata}")

  • Ternyata proses menggabungkan dua FAISS vector store sangat sederhana, sesuai dokumentasi di sini.
# Try to merge two FAISS vector stores into 1
# load the vector store from local machine
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
db_1 = FAISS.load_local("faiss_index_about", embeddings)
db_2 = FAISS.load_local("faiss_index", embeddings)
db_2.merge_from(db_1)
# save the vector store to local machine
db_2.save_local("faiss_index_v2")
# test the new vector store to confirm correct retrieved documents
retriever = db_2.as_retriever(search_type="mmr")
results = retriever.get_relevant_documents("Who is Chandler Nguyen?")
for doc in results:
    print(f"Content: {doc.page_content}, Metadata: {doc.metadata}")
  • Setelah itu, prosesnya kurang lebih sama seperti di atas, menggunakan vector store baru

Update 14 Feb Chatbot v2.10 Diluncurkan

Dua minggu setelah deployment chatbot ini, saya memperkenalkan versi 2.10 yang meningkatkan User Experience dengan Kecepatan, Skalabilitas, dan Kesederhanaan yang Lebih Baik. Kamu bisa baca lebih lanjut di sini.

Update 25 Mar: Dari Upgrade Frontend ke Perjuangan dan Terobosan Docker

Kamu bisa baca lebih lanjut di sini.

Lanjutkan Membaca

Perjalanan Saya
Terhubung
Bahasa
Preferensi