Build Your First AI Agent with Python and LangChain: A Command-Line File Organizer
Buka terminal, bikin folder baru. Ini bukan pengenalan AI. Kita langsung terjun ke real project: CLI file organizer yang dikontrol oleh agent LangChain. Udah ada 37 lain tentang file organizer โ tapi semuanya manual, pake if-else atau pattern matching. Kali ini, agentnya paham konteks. Lu bilang "pindahin semua PDF ke folder Documents/PDF", dia ngerti. Lu bilang "bersihin desktop tapi jangan sentuh gambar", dia ngerti.
Gue udah riset: LangChain yang ada di database kebanyakan basic agents untuk chatbot atau API. Belum ada yang beneran bikin CLI tool dengan file system tools. Ini celah kita.
Sebelum nulis kode, gue desain dulu. Ini arsitekturnya:
User Input (CLI) โ LangChain Agent โ Tools (FileSystem) โ Response โโโ Conversation Memory (Buffer) โโโ Tool Definitions (move, rename, list, search) โโโ LLM (GPT-4 or Claude)
Kenapa pake LangChain? Karena udah handle tool calling, memory, dan prompt chaining out of the box. Gue bisa pake framework lain, tapi LangChain mature dan punya ecosystem tools yang pas buat masalah ini.
Setelah proyek selesai, strukturnya bakal gini:
file-organizer/ โโโ agent/ โ โโโ init.py โ โโโ tools.py โ file system tools โ โโโ memory.py โ conversation memory โ โโโ agent.py โ agent setup & execution โโโ cli/ โ โโโ init.py โ โโโ main.py โ CLI entry point โโโ tests/ โ โโโ testtools.py โ โโโ testagent.py โโโ .env โ API keys โโโ requirements.txt โโโ README.md
Bikin virtual environment dulu. Gue pake Python 3.11+.
python -m venv venv
urce venv/bin/activate # or venv\Scripts\activate on Windows
Bikin file `requirements.txt`:
# requirements.txt
langchain>=0.1.0
langchain-openai>=0.0.2
python-dotenv>=1.0.0
click>=8.1.7
Install: `pip install -r requirements.txt`
Bikin `.env`:
OPENAI_API_KEY=sk-your-key-here
Gue mulai dari agent paling sederhana dulu. Cuma bisa ngobrol, tapi udah integrate memory.agent/agent.py
from langchainopenai import ChatOpenAI from langchain.agents import AgentExecutor, createopenaifunctionsagent from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.memory import ConversationBufferMemory
Initialize LLM
llm = ChatOpenAI(model="gpt-4", temperature=0)
Prompt template dengan memory
prompt = ChatPromptTemplate.frommessages([ ("system", "You are a file organizer AI assistant. Help the user organize their files by understanding their intent."), MessagesPlaceholder(variablename="chathistory"), ("human", "{input}"), MessagesPlaceholder(variablename="agent_scratchpad"), ])
memory = ConversationBufferMemory(memorykey="chathistory", return_messages=True)
Buat agent (sementara tanpa tools)
agent = createopenaifunctionsagent(llm, [], prompt) agentexecutor = AgentExecutor( agent=agent, tools=[], memory=memory, verbose=True, handleparsingerrors=True )
if name == "main": while True: userinput = input("You: ") if userinput.lower() == "exit": break response = agentexecutor.invoke({"input": userinput}) print(f"Agent: {response['output']}")
Coba jalanin. Kalo muncul error ModuleNotFoundError: No module named 'langchain_openai' โ jangan panik, itu karena kita pake package langchain-openai, bukan langchain.llms.openai. Udah beda di versi baru.
Setelah jalan, agent bakal panggil balik percakapan sebelumnya. Tapi belum bisa ngapa-ngapain. Waktunya nambah tools.
Bikin tools yang beneran bisa akses file system. Gue pake pathlib dan shutil.
# agent/tools.py
from pathlib import Path
import shutil
from typing import Optional, List
from langchain.tools import tool
@tool
def list_files(directory: str = ".") -> List[str]:
"""List all files in a given directory. Returns relative paths."""
path = Path(directory)
if not path.exists():
return [f"Directory '{directory}' does not exist."]
return [str(p.relative_to(path)) for p in path.iterdir() if p.is_file()]
@tool
def move_file(source: str, destination: str) -> str:
"""Move a file from source to destination. Creates parent directories if needed."""
src = Path(source)
dst = Path(destination)
if not src.exists():
return f"Source file '{source}' not found."
if dst.exists() and dst.is_dir():
dst = dst / src.name
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(src), str(dst))
return f"Moved {source} -> {destination}"
@tool
def rename_file(path: str, new_name: str) -> str:
"""Rename a file. New name must not include path."""
p = Path(path)
if not p.exists():
return f"File '{path}' not found."
new_path = p.parent / new_name
p.rename(new_path)
return f"Renamed {path} -> {new_path}"
@tool
def search_files(pattern: str, directory: str = ".") -> List[str]:
"""Search for files matching a glob pattern (e.g., '*.pdf', '**/*.txt')."""
path = Path(directory)
if not path.exists():
return [f"Directory '{directory}' does not exist."]
return [str(p.relative_to(path)) for p in path.glob(pattern)]
Perhatikan docstring-nya โ itu penting buat LangChain, karena LLM pake deskripsi tool untuk milih tool mana yang dipanggil.
Sekarang update `agent.py` biar pake tools ini:agent/agent.py (updated)
from langchainopenai import ChatOpenAI from langchain.agents import AgentExecutor, createopenaifunctionsagent from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.memory import ConversationBufferMemory from agent.tools import listfiles, movefile, renamefile, searchfiles
llm = ChatOpenAI(model="gpt-4", temperature=0)
prompt = ChatPromptTemplate.frommessages([ ("system", "You are a file organizer AI assistant. Use the provided tools to help the user organize their files. Always confirm before executing destructive actions like moving or renaming if the user didn't explicitly ask for it."), MessagesPlaceholder(variablename="chathistory"), ("human", "{input}"), MessagesPlaceholder(variablename="agent_scratchpad"), ])
memory = ConversationBufferMemory(memorykey="chathistory", return_messages=True)
tools = [listfiles, movefile, renamefile, searchfiles]
agent = createopenaifunctionsagent(llm, tools, prompt) agentexecutor = AgentExecutor( agent=agent, tools=tools, memory=memory, verbose=True, handleparsingerrors=True, max_iterations=5 # prevent infinite loops )
if name == "main": while True: userinput = input("You: ") if userinput.lower() == "exit": break response = agentexecutor.invoke({"input": userinput}) print(f"Agent: {response['output']}")
Coba test: "list files in current directory". Agent harusnya panggil list_files dan balikin daftar. Kalo error, cek verbose output โ biasanya karena LLM gagal parsing output tool.
Gue sengaja pake max_iterations=5 biar gak looping terus. Dulu pernah agent stuck karena bilang "I need to check the directory again" โ loop 20 kali. Fixed.
Selanjutnya, bungkus jadi CLI proper. Pake click biar ada argumen opsional.
# cli/main.py
import click
from agent.agent import agent_executor
@click.command()
@click.option("--verbose", "-v", is_flag=True, help="Show agent internals")
@click.argument("command", nargs=-1, required=False)
def main(verbose: bool, command: tuple):
"""AI-powered file organizer. Run with no arguments for interactive mode, or pass a single command."""
if command:
user_input = " ".join(command)
response = agent_executor.invoke({"input": user_input})
click.echo(response["output"])
else:
click.echo("Interactive mode. Type 'exit' to quit.")
while True:
user_input = click.prompt("You")
if user_input.lower() == "exit":
break
response = agent_executor.invoke({"input": user_input})
click.echo(f"Agent: {response['output']}")
if __name__ == "__main__":
main()
Bikin entry point di `setup.py` atau `pyproject.toml`:pyproject.toml
[project] name = "file-organizer" version = "0.1.0"
[project.scripts] file-organizer = "cli.main:main"
Install package: pip install -e .
Sekarang tinggal ketik file-organizer "pindahin semua file .txt ke folder Teks" โ agent bakal jalankan.
Masalah pertama: agent bisa disuruh move file yang gak ada. Tool move_file udah handle case file not found, tapi agent kadang gak ngecek dulu. Kita tambahin pre-check.
# agent/tools.py (updated)
from pathlib import Path
import shutil
from typing import List, Optional
from langchain.tools import tool
@tool
def safe_move_file(source: str, destination: str) -> str:
"""Safely move a file. Validates source exists and destination is writable. Never overwrite existing files."""
src = Path(source)
dst = Path(destination)
if not src.exists():
return f"Error: Source '{source}' does not exist."
if not src.is_file():
return f"Error: '{source}' is not a file."
if dst.exists():
return f"Error: Destination '{destination}' already exists. Use rename or delete first."
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(src), str(dst))
return f"Moved '{source}' -> '{destination}'"
Ganti tool di `agent.py`:tools = [listfiles, safemovefile, renamefile, search_files]
Masalah lain: memory buffer bisa nambah terus. Kalo sesi panjang, context window penuh. Tambahin ConversationSummaryMemory atau ConversationBufferWindowMemory.
# agent/memory.py
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(
memory_key="chat_history",
return_messages=True,
k=5 # keep last 5 exchanges
)
[adsterra-button: Relevant Resource | https://www.effectivecpmnetwork.com/zer141uh?key=c5ddf6386f731a5ddf7ff968178ed982]
Gue gak fanatik coverage, tapi core logic harus dites.tests/test_tools.py
import tempfile from pathlib import Path from agent.tools import listfiles, safemovefile, searchfiles
def testlistfilesempty(): with tempfile.TemporaryDirectory() as tmpdir: result = listfiles(tmpdir) assert result == []
def testlistfileswithfiles(): with tempfile.TemporaryDirectory() as tmpdir: (Path(tmpdir) / "a.txt").touch() (Path(tmpdir) / "b.pdf").touch() result = list_files(tmpdir) assert len(result) == 2 assert "a.txt" in result
def testsafemovefile(): with tempfile.TemporaryDirectory() as tmpdir: src = Path(tmpdir) / "src.txt" src.writetext("hello") dst = Path(tmpdir) / "subdir" / "dst.txt" result = safemovefile(str(src), str(dst)) assert "Moved" in result assert dst.exists() assert not src.exists()
Jalankan: pytest tests/
Di production, jangan simpan API key di .env version control. Tambahin .gitignore. Juga pertimbangin rate limiting: agent bisa panggil tools berkali-kali dalam satu permintaan, dan tiap tool call kena biaya API.
Gue pake maxiterations=5 tadi, tapi bisa juga tambahin earlystopping_method="generate" biar agent berhenti lebih awal.
Agent ini bisa dikembangin: tambah tool deletefile, copyfile, readfilecontent buat analisis konten. Atau integrasi cron job supaya jalan otomatis tiap jam. Tapi dasar agent udah jalan. Yang penting, lu sekarang paham gimana cara bikin tool custom, integrasi memory, dan handle error di LangChain.
Coba deh file-organizer "pindahin semua file gambar dari Downloads ke Pictures" โ agent bakal panggil searchfiles("*.png", "~/Downloads") terus safemove_file satu per satu. Keren kan?

Memuat komentar...