The Pure Functional, Effect-Polymorphic AI Agent & Multi-Agent Graph Engine in Haskell
A strictly typed, effect-polymorphic, AI ecosystem built on pure AST pipelines (
RunnableTree), cyclic state machines (StateGraph), Model Context Protocol (MCP), and production observability.
Modern AI orchestration frameworks often struggle with race conditions, hidden side-effects, fragile dynamic schemas, and uninspectable opaque execution chains. langchain-hs brings mathematical precision and functional programming principles to AI development:
- First-Class Runnable AST Composition (
RunnableTree): Every component—models, prompts, tools, chains, retrievers, and parsers—implements theRunnabletypeclass. Connect components into trees or graphs using type-safe operators:|>>: Sequential composition (data flows from left to right).&>&: Parallel fan-out (concurrent evaluation of independent branches).>>>#: Fallback chains (automatic failover if the primary branch errors).
- LangGraph in Haskell (
StateGraph): Full cyclic state machine engine with pure monoidal state reducers (StateReducer s), thread-safe STM memory checkpointers (TVar), persistent SQLite checkpointers, Human-in-the-Loop (HITL) interrupts, and Time-Travel state replay. - Decoupled Modular Architecture: The core framework is completely lightweight. AI providers (
Ollama,OpenAI,Gemini) and protocol clients (MCP) live in dedicated, standalone sub-packages so you only pull in the dependencies you actually need.
| Package | Directory | Version | Description |
|---|---|---|---|
langchain-hs-core |
langchain-hs-core/ |
0.0.6.0 |
Zero-dependency pure core: RunnableTree, ChatModel, ContentBlock, Tool, and LangchainT. |
langchain-hs-graph |
langchain-hs-graph/ |
0.0.6.0 |
Stateful graph engine: StateGraph s m, checkpointers, HITL, time-travel, and parallel nodes. |
langchain-hs |
./ |
0.0.6.0 |
Core framework: Agents, Output Parsers, Vector Stores, Chains, Memory, Resilience, Observability. |
langchain-hs-ollama |
langchain-hs-ollama/ |
0.0.6.0 |
Dedicated Ollama provider and embeddings integration via ollama-haskell. |
langchain-hs-openai |
langchain-hs-openai/ |
0.0.6.0 |
Dedicated OpenAI & OpenAI-compatible provider, streaming, and embeddings via openai. |
langchain-hs-gemini |
langchain-hs-gemini/ |
0.0.6.0 |
Dedicated Google Gemini provider with function calling and SSE streaming. |
langchain-hs-mcp |
langchain-hs-mcp/ |
0.0.6.0 |
Model Context Protocol client over stdio and HTTP JSON-RPC 2.0. |
examples |
examples/ |
0.1.0.0 |
34 runnable executables covering core components for Ollama and OpenAI. |
site |
site/ |
- | Hakyll documentation website with live provider toggle and component reference. |
| # | Component | Package Layer | Ollama Executable | OpenAI Executable | Documentation |
|---|---|---|---|---|---|
| 1 | Chat Models | Langchain.Core.Model |
stack run simpleollama |
stack run simpleopenai |
Docs |
| 2 | Conduit Streaming | Langchain.Core.Stream |
stack run streamollama |
stack run streamopenai |
Docs |
| 3 | Langchain Monad | Langchain.Core.Monad |
stack run monadollama |
stack run monadopenai |
Docs |
| 4 | Tools & Function Calling | Langchain.Core.Tool |
stack run toolollama |
stack run toolopenai |
Docs |
| 5 | Structured Outputs | Langchain.OutputParser |
stack run jsonollama |
stack run jsonopenai |
Docs |
| 6 | RAG & Embeddings | Langchain.Embedding |
stack run ragollama |
stack run ragopenai |
Docs |
| 7 | Hybrid Retrievers | Langchain.Retriever |
stack run retrieverollama |
stack run retrieveropenai |
Docs |
| 8 | Memory Systems | Langchain.Memory |
stack run memoryollama |
stack run memoryopenai |
Docs |
| 9 | Retrieval QA Chains | Langchain.Chain.RetrievalQA |
stack run retrievalqaollama |
stack run retrievalqaopenai |
Docs |
| 10 | Map-Reduce Processing | Langchain.Chain.MapReduce |
stack run mapreduceollama |
stack run mapreduceopenai |
Docs |
| 11 | ReAct Agent | Langchain.Agent.ReAct |
stack run reactollama |
stack run reactopenai |
Docs |
| 12 | Plan-and-Execute Agent | Langchain.Agent.PlanAndExecute |
stack run planandexecuteollama |
stack run planandexecuteopenai |
Docs |
| 13 | Guardrails & Safety | Langchain.Guardrails |
stack run guardrailollama |
stack run guardrailopenai |
Docs |
| 14 | Resilience & Retries | Langchain.Resilience |
stack run resilienceollama |
stack run resilienceopenai |
Docs |
| 15 | Observability & Tracing | Langchain.Observability |
stack run observabilityollama |
stack run observabilityopenai |
Docs |
| 16 | Model Context Protocol | Langchain.MCP.Client |
stack run mcpollama |
stack run mcpopenai |
Docs |
| 17 | StateGraph Workflows | Langchain.Graph |
stack run stategraphollama |
stack run stategraphopenai |
Docs |
| 18 | Multi-Agent Systems | Langchain.Graph.MultiAgent |
stack run multiagentollama |
stack run multiagentopenai |
Docs |
| 19 | Human-in-the-Loop (HITL) | Langchain.Graph.Checkpointer |
stack run hitlollama |
stack run hitlopenai |
Docs |
| 20 | Runnables & AST Composition | Langchain.Core.Runnable |
stack run runnableollama |
stack run runnableopenai |
Docs |
Compose complex multi-stage pipelines using typed operators without executing any IO until interpretation:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Langchain.Prelude
-- Compose pure AST pipelines with (|>>), (&>&), and (>>>#)
pipeline :: RunnableTree IO Text Text
pipeline =
runLambda (\q -> (q, q)) -- duplicate input query
|>> (fetchDocuments &>& generateFollowup) -- parallel branch fan-out
|>> runLambda (\(docs, fup) -> renderPrompt docs fup) -- pure prompt synthesis
|>> (invokeLLM primaryModel >>># invokeLLM backupModel) -- fallback resilience
|>> parseStructuredResponse -- JSON parser
main :: IO ()
main = do
output <- interpret pipeline "Explain Monads in Haskell"
print output{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.Except (runExceptT)
import qualified Data.Text.IO as T
import Langchain.Prelude
import Langchain.Provider.Ollama
main :: IO ()
main = do
-- Connect to local Ollama instance (DeepSeek, Llama 3, Gemma)
model <- newOllama "gemma3" defaultOllamaConfig
let msg = [userMessage "Write a poem about functional programming"]
res <- runExceptT $ invoke model msg Nothing
case res of
Left err -> T.putStrLn $ errorMessage err
Right m -> T.putStrLn $ extractMessageText mRun: stack --stack-yaml examples/stack.yaml run simpleollama
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.Except (runExceptT)
import qualified Data.Text.IO as T
import Langchain.Prelude
import Langchain.Provider.OpenAI
main :: IO ()
main = do
-- Connect to OpenAI or OpenRouter using environment API key
let model = newOpenAI "sk-..." "gpt-4o"
let msg = [userMessage "Write a poem about functional programming"]
res <- runExceptT $ invoke model msg Nothing
case res of
Left err -> T.putStrLn $ errorMessage err
Right m -> T.putStrLn $ extractMessageText mRun: stack --stack-yaml examples/stack.yaml run simpleopenai
{-# LANGUAGE OverloadedStrings #-}
import Langchain.Graph.StateGraph
import Langchain.Prelude
-- Pure state with a list-append reducer
data AgentState = AgentState { messages :: [Message], loopCount :: Int }
-- Build the graph using pure combinators
workflow :: StateGraph AgentState IO
workflow =
addEdge "reviewer" "planner" -- cyclic feedback loop!
$ addConditionalEdge "executor"
(\s -> pure $ if done s then Right endNodeId else Right "reviewer")
$ addEdge "planner" "executor"
$ addEdge startNodeId "planner"
$ addNode "reviewer" (Node reviewerNode replaceFieldReducer)
$ addNode "executor" (Node executorNode replaceFieldReducer)
$ addNode "planner" (Node plannerNode replaceFieldReducer)
$ emptyStateGraph
main :: IO ()
main = do
checkpointer <- newMemoryCheckpointer
case compileGraph workflow of
Left err -> print err
Right compiled -> do
result <- runGraph compiled initialState (Just checkpointer)
print resultRun: stack --stack-yaml examples/stack.yaml run stategraphollama or stack --stack-yaml examples/stack.yaml run stategraphopenai
Connect Haskell agents to any external MCP server (e.g., Hackage doc search, SQLite, Filesystem, GitHub) over stdio:
{-# LANGUAGE OverloadedStrings #-}
import Langchain.Prelude
import Langchain.MCP.Client
main :: IO ()
main = do
-- Connect to any MCP server via stdio JSON-RPC 2.0
client <- newStdioMcpClient "hackage-doc" "docker" ["run", "-i", "--rm", "mcp/hackage-doc"]
-- Discover available tools from server
mcpTools <- listMcpTools client
let nativeTools = map (mcpToolToLangchainTool client) mcpTools
-- Bind tools to your ReAct or Plan-and-Execute Agent
let agent = defaultReActAgent model nativeTools
res <- runExceptT $ runReActAgent agent [userMessage "Search Hoogle for the signature of 'traverse'"]
case res of
Left err -> putStrLn ("Error: " ++ show err)
Right ans -> putStrLn ("Answer:\n" ++ T.unpack (extractMessageText ans))Run: stack --stack-yaml examples/stack.yaml run mcpollama or stack --stack-yaml examples/stack.yaml run mcpopenai
langchain-hs is modular. Install the core framework and only the provider packages you need:
Add to your stack.yaml:
extra-deps:
- langchain-hs-core-0.0.6.0
- langchain-hs-graph-0.0.6.0
- langchain-hs-0.0.6.0
# Add providers as needed:
- langchain-hs-ollama-0.0.6.0
- langchain-hs-openai-0.0.6.0
- langchain-hs-gemini-0.0.6.0
- langchain-hs-mcp-0.0.6.0Then in your .cabal or package.yaml:
dependencies:
- langchain-hs # Core framework (agents, chains, vector stores, memory, parsers)
- langchain-hs-core # Pure AST & types only (zero network dependencies)
- langchain-hs-graph # StateGraph cyclic orchestration engine
# Add only the providers/clients you use:
- langchain-hs-ollama # Ollama provider
- langchain-hs-openai # OpenAI & OpenAI-compatible provider
- langchain-hs-gemini # Google Gemini provider
- langchain-hs-mcp # Model Context Protocol clientcabal install langchain-hs langchain-hs-ollama langchain-hs-openaiThe repository enforces strict code quality and formatting via make:
# Build the entire monorepo and all 41 example executables
stack build
# Run unit and property-based test suites
stack test
# Run HLint across all source trees (zero hints policy)
make lint
# Check code formatting with Fourmolu
make format-check
# Format all files in-place
make format
# Build the documentation website (Hakyll)
make site-build
# Run live documentation server with auto-reload (port 8000)
make site-watch| Resource | Description |
|---|---|
| Hackage Docs | Full Haddock API reference for all exported modules |
| Whitepaper | Deep technical dive: category theory foundations, algebraic laws, effect-polymorphic design, and advanced multi-agent patterns |
| Documentation Website | Hakyll site with 20 component pages, live provider toggle, and instant search (Cmd+K) |
| Examples | 41 runnable executables covering every component for Ollama and OpenAI |
To build the Haddock API docs locally:
make docs
# Opens in .stack-work/install/.../doc/index.htmlDistributed under the MIT License. See LICENSE for details.