This is post #21 in the Ruby for AI series. We've been building everything from scratch — API calls, RAG pipelines, agents, streaming. That's great for understanding. But sometimes you want a framework that handles the plumbing. Enter LangChain.rb — Ruby's port of the popular LangChain library.
What Is LangChain.rb?
LangChain.rb gives you pre-built abstractions for common AI patterns: LLM clients, prompt templates, chains, vector search, agents, and conversation memory. Instead of wiring up OpenAI calls and pgvector queries manually, you get composable building blocks.
Install it:
gem install langchainrb
# or in your Gemfile:
gem "langchainrb"
Basic LLM Usage
Start with a simple LLM call:
require "langchain"
llm = Langchain::LLM::OpenAI.new(
api_key: ENV["OPENAI_API_KEY"],
default_options: { temperature: 0.7, chat_model: "gpt-4o" }
)
response = llm.chat(messages: [{ role: "user", content: "Explain Ruby blocks in one paragraph." }])
puts response.chat_completion
LangChain.rb supports multiple providers out of the box:
# Anthropic
llm = Langchain::LLM::Anthropic.new(api_key: ENV["ANTHROPIC_API_KEY"])
# Ollama (local models)
llm = Langchain::LLM::Ollama.new(url: "http://localhost:11434")
# Google Gemini
llm = Langchain::LLM::GoogleGemini.new(api_key: ENV["GEMINI_API_KEY"])
Swap providers without changing your application code. That's the point.
Prompt Templates
Hardcoded prompts get messy fast. Use templates:
template = Langchain::Prompt::PromptTemplate.new(
template: "You are a {role}. Explain {topic} to a beginner in {style} style.",
input_variables: ["role", "topic", "style"]
)
prompt = template.format(role: "senior Ruby developer", topic: "metaprogramming", style: "conversational")
puts prompt
# => "You are a senior Ruby developer. Explain metaprogramming to a beginner in conversational style."
response = llm.chat(messages: [{ role: "user", content: prompt }])
You can also save and load templates from JSON files:
# Save
template.save(file_path: "prompts/explain.json")
# Load
loaded = Langchain::Prompt.load_from_path(file_path: "prompts/explain.json")
Conversation Memory
Chatbots need to remember context. LangChain.rb provides memory objects that track conversation history:
llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
# ConversationMemory stores full message history
memory = Langchain::Memory::ConversationMemory.new(
llm: llm,
messages: []
)
# Add messages
memory.add_message(role: "user", content: "My name is Sarah.")
memory.add_message(role: "assistant", content: "Nice to meet you, Sarah!")
memory.add_message(role: "user", content: "What's my name?")
# The LLM sees the full history
puts memory.messages
# => [{role: "user", content: "My name is Sarah."}, ...]
For long conversations, use WindowMemory to keep only the last N messages:
memory = Langchain::Memory::ConversationMemory.new(
llm: llm,
messages: [],
strategy: :sliding_window,
window_size: 10 # Keep last 10 messages
)
Vector Search Integration
LangChain.rb wraps vector databases for embedding and search. Here's pgvector:
# Connect to your vector store
pgvector = Langchain::Vectorsearch::Pgvector.new(
url: ENV["DATABASE_URL"],
index_name: "documents",
llm: llm
)
# Create the schema
pgvector.create_default_schema
# Add documents — automatically embeds them
pgvector.add_texts(
texts: [
"Ruby was created by Yukihiro Matsumoto in 1995.",
"Rails is a web framework that follows convention over configuration.",
"ActiveRecord is an ORM that maps database tables to Ruby classes."
]
)
# Semantic search
results = pgvector.similarity_search(query: "Who created Ruby?", k: 2)
results.each { |doc| puts doc.content }
It handles embedding generation, storage, and similarity search in one interface. Also supports Pinecone, Weaviate, Qdrant, and Chroma.
RAG with Ask
Remember the RAG system we built from scratch in post #18? LangChain.rb does it in a few lines:
pgvector = Langchain::Vectorsearch::Pgvector.new(
url: ENV["DATABASE_URL"],
index_name: "knowledge_base",
llm: llm
)
# ask() = retrieve relevant docs + send to LLM with context
answer = pgvector.ask(question: "How does ActiveRecord handle migrations?")
puts answer.chat_completion
The ask method retrieves relevant chunks, constructs a prompt with context, and sends it to the LLM. One method call for the entire RAG pipeline.
Agents with Tools
LangChain.rb includes an agent framework with tool use:
# Define tools the agent can use
search_tool = Langchain::Tool::GoogleSearch.new(api_key: ENV["SERPAPI_KEY"])
calculator = Langchain::Tool::Calculator.new
# Create an agent with tools
agent = Langchain::Agent::ReActAgent.new(
llm: llm,
tools: [search_tool, calculator]
)
# The agent decides which tools to use
response = agent.run(question: "What is the mass of the Earth in kilograms divided by 2?")
puts response
The ReAct agent follows a Reason-Act loop: it thinks about what tool to use, calls it, observes the result, and decides the next step. Just like we built manually in post #19 — but packaged and ready to go.
You can also build custom tools:
class WeatherTool < Langchain::Tool::Base
define_function :get_weather,
description: "Get current weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name" }
},
required: ["city"]
}
def get_weather(city:)
# Your weather API call here
"72°F and sunny in #{city}"
end
end
agent = Langchain::Agent::ReActAgent.new(
llm: llm,
tools: [WeatherTool.new]
)
Using LangChain.rb in Rails
Integrate it as a service object:
# app/services/ai_assistant.rb
class AiAssistant
def initialize
@llm = Langchain::LLM::OpenAI.new(
api_key: Rails.application.credentials.openai_api_key,
default_options: { temperature: 0.7, chat_model: "gpt-4o" }
)
@vectorstore = Langchain::Vectorsearch::Pgvector.new(
url: ENV["DATABASE_URL"],
index_name: "app_knowledge",
llm: @llm
)
end
def chat(user_message, conversation_history: [])
messages = conversation_history + [{ role: "user", content: user_message }]
response = @llm.chat(messages: messages)
response.chat_completion
end
def search_and_answer(question)
@vectorstore.ask(question: question).chat_completion
end
end
Then use it anywhere in your app:
# In a controller
assistant = AiAssistant.new
answer = assistant.search_and_answer("How do I reset my password?")
When to Use LangChain.rb vs. Rolling Your Own
Use LangChain.rb when:
- You need to support multiple LLM providers
- You want quick prototyping with RAG, agents, or memory
- The built-in abstractions fit your use case
Roll your own when:
- You need fine-grained control over prompts and API calls
- Performance is critical and you want to minimize overhead
- Your pattern doesn't fit LangChain's abstractions
There's no shame in using both. Use LangChain.rb for the boring plumbing and custom code where it matters.
What You Learned
LangChain.rb gives you LLM abstraction, prompt templates, conversation memory, vector search, RAG, and agents — all in idiomatic Ruby. It's not magic. It's the same patterns we've been building, packaged into a library.
Next up: image generation in Rails with DALL-E and Stability AI.
Top comments (0)