LangChain¶
LangChain is the most widely used framework for building LLM-powered applications. It handles chains, agents, RAG pipelines, and tool calling on top of any OpenAI-compatible model.
Installation¶
pip install langchain langchain-openai langchain-community python-dotenv
# For vector stores (optional — pick one):
pip install chromadb # local, no server needed
pip install faiss-cpu # fast, in-memory
LLM setup¶
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from dotenv import load_dotenv
load_dotenv()
llm = ChatOpenAI(
model="llama-3-1-8b",
openai_api_base=os.environ["PAIS_API_BASE"],
openai_api_key=os.environ["PAIS_API_KEY"],
temperature=0.2,
)
embeddings = OpenAIEmbeddings(
model="qwen3-vl-embedding-8b",
openai_api_base=os.environ["PAIS_API_BASE"],
openai_api_key=os.environ["PAIS_API_KEY"],
)
Simple chain¶
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "You are a research assistant specialising in {domain}."),
("human", "{question}"),
])
chain = prompt | llm | StrOutputParser()
answer = chain.invoke({
"domain": "computational biology",
"question": "What are the main approaches to protein-protein interaction prediction?",
})
print(answer)
RAG chain¶
Build a question-answering chain over your own documents:
from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# 1. Load documents
loader = DirectoryLoader("./papers/", glob="**/*.pdf", loader_cls=PyPDFLoader)
docs = loader.load()
# 2. Split into chunks
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
chunks = splitter.split_documents(docs)
# 3. Create vector store
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db",
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 4. RAG prompt
rag_prompt = ChatPromptTemplate.from_messages([
("system", """Answer the question using only the provided context.
If the context doesn't contain the answer, say so.
Context:
{context}"""),
("human", "{question}"),
])
def format_docs(docs):
return "\n\n".join(d.page_content for d in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| rag_prompt
| llm
| StrOutputParser()
)
answer = rag_chain.invoke("What methods do these papers use for hyperparameter optimisation?")
print(answer)
Agent with tools¶
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import subprocess
@tool
def run_python_code(code: str) -> str:
"""Execute Python code and return stdout. Use for calculations and data analysis."""
result = subprocess.run(
["python3", "-c", code],
capture_output=True, text=True, timeout=30
)
return result.stdout or result.stderr
@tool
def search_vector_store(query: str) -> str:
"""Search the research paper vector store for relevant passages."""
docs = retriever.invoke(query)
return "\n---\n".join(d.page_content for d in docs)
tools = [run_python_code, search_vector_store]
agent_prompt = ChatPromptTemplate.from_messages([
("system", "You are a research assistant. Use tools to answer questions accurately."),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
])
agent = create_tool_calling_agent(llm, tools, agent_prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=5)
result = executor.invoke({
"input": "Search the papers for methods used in hyperparameter tuning, "
"then calculate the average number of trials reported."
})
print(result["output"])
Streaming with LangChain¶
for chunk in chain.stream({"domain": "ML", "question": "Explain attention mechanisms."}):
print(chunk, end="", flush=True)
Memory and conversation history¶
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
chain_with_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="question",
history_messages_key="history",
)
# Use the same session_id to maintain conversation context
chain_with_history.invoke(
{"domain": "NLP", "question": "What is BERT?"},
config={"configurable": {"session_id": "researcher-session-1"}},
)
chain_with_history.invoke(
{"domain": "NLP", "question": "How does it compare to GPT?"},
config={"configurable": {"session_id": "researcher-session-1"}},
)
See also¶
- RAG over Research Papers — end-to-end worked example
- Agentic Literature Review — multi-step agent with arXiv + summarisation