Pixeltable + LangGraph

LangChain and LangGraph run the agent loop. Pixeltable is the data plane: media, computed columns, embedding indexes, and HTTP in one Python file. They are not the same job. Use both, or drop the extra store if Pixeltable already holds the chunks.

pip install 'pixeltable[serve]'
See how it works

Different layers

SidePixeltableLangChain
At a glance
  • Tables, computed columns, and embedding indexes in one schema
  • Insert a document or image; chunking and indexes update
  • Tool-calling via computed columns and invoke_tools
  • Same file serves HTTP with FastAPIRouter
  • LangGraph for multi-agent graphs, checkpointers, and interrupts
  • Huge ecosystem: loaders, tools, callbacks, LangSmith tracing
  • The default stack agents already know how to write
  • Persistence and retrieval are still someone else’s job

Data plane vs agent runtime

Pixeltable stores multimodal rows and keeps indexes in sync. LangGraph owns graphs, tools, and tracing. Pixeltable does not replace LangSmith. That split is agent graph vs table — the AI automation workflow category.

FeaturePixeltableLangChain
What it is
Multimodal tables + incremental compute + serving
LLM application SDK; LangGraph is the agent runtime
Where the bytes live
Native Document, Image, Video, Audio columns
You bring a store: Postgres, Chroma, S3, a checkpointer
Chunking and embeddings
document_splitter view + EmbeddingIndex; insert keeps them current
Loaders and splitters you re-run; vectors upserted elsewhere
Agent graphs
Tool-calling columns and @pxt.udf tools; no graph runtime
LangGraph: StateGraph, ToolNode, human-in-the-loop, checkpointers
Tracing and eval
Per-cell errormsg / errortype; no LangSmith
LangSmith, callbacks, and the tracing ecosystem
Multimodal RAG
One schema for PDFs and images; CLIP and text indexes together
Possible; separate loaders, embedders, and stores per modality
Team already using it
Python tables; agents learn TableModel from the skill
Default generated stack; LangGraph Platform if you already pay for it

RAG that stays in sync

Pixeltable: the schema is chunking, the index, and a tool-calling assistant. LangGraph: you still own the store. Apply Pixeltable with pxt schema update app.py app.

Pixeltable

import pixeltable as pxt
from pixeltable.functions.document import document_splitter
from pixeltable.functions.huggingface import sentence_transformer, clip
from pixeltable.functions.openai import chat_completions, invoke_tools
TableModel = pxt.model_base()
embed = sentence_transformer.using(
model_id='sentence-transformers/all-MiniLM-L6-v2'
)
visual = clip.using(model_id='openai/clip-vit-base-patch32')
class Docs(TableModel, name='docs'):
document: pxt.Document
image: pxt.Image
title: pxt.String
__indexes__ = [pxt.EmbeddingIndex(image, embedding=visual)]
class Chunks(
TableModel,
name='chunks',
base=Docs,
iterator=document_splitter(
Docs.document, separators='sentence', limit=512
),
):
__indexes__ = [pxt.EmbeddingIndex(text, embedding=embed)]
@pxt.udf
def search_docs(question: str) -> str:
chunks = pxt.get_table('app.chunks')
sim = chunks.text.similarity(string=question)
rows = (
chunks.order_by(sim, asc=False)
.limit(5)
.select(chunks.text)
.collect()
)
return '\n'.join(r['text'] for r in rows)
tools = pxt.tools(search_docs)
class Assistant(TableModel, name='assistant'):
message: pxt.String
response = chat_completions(
messages=[{'role': 'user', 'content': message}],
model='gpt-4o-mini',
tools=tools,
)
tool_output = invoke_tools(tools, response)
# pxt schema update app.py app

LangChain

from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
docs = PyPDFLoader('handbook.pdf').load()
chunks = RecursiveCharacterTextSplitter(
chunk_size=512, chunk_overlap=50
).split_documents(docs)
store = Chroma.from_documents(
chunks, OpenAIEmbeddings(model='text-embedding-3-small')
)
@tool
def search_handbook(question: str) -> str:
hits = store.similarity_search(question, k=5)
return '\n'.join(d.page_content for d in hits)
agent = create_react_agent(
ChatOpenAI(model='gpt-4o-mini'), tools=[search_handbook]
)
agent.invoke({'messages': [('user', 'What is the refund policy?')]})
# Images, versioning, and re-chunking on insert are still yours.

When each belongs

Use Pixeltable when

  • The data layer is the product

    PDFs, images, video, embeddings, and retrieval that must stay current when a row is inserted. One schema, not a loader plus a vector store plus a re-index job.

  • You want retrieval without a second database

    EmbeddingIndex on the column. similarity(string=query) then order_by. LangGraph can call that as a tool.

Use LangChain when

  • The agent loop is the product

    Multi-agent graphs, interrupts, checkpointers, and LangSmith traces. Pixeltable does not ship that runtime.

  • The team already writes LangGraph

    Keep the graph. Point tools at Pixeltable tables instead of standing up Chroma for every prototype.

Making the right choice

  • Complementary, not a wipe

    • Pixeltable replaces the RAG data path: chunking, embeddings, persistence, incremental insert.
    • LangGraph still fits when the application is a graph. Call Pixeltable from a tool.
    • LlamaIndex is the same split: index/query engine vs tables. No separate vs page.
    • Category page (agent graph vs table): https://pixeltable.com/blog/ai-automation-workflow
    • Longer write-up (notebook-era snippets): https://pixeltable.com/blog/pixeltable-vs-langchain-rag-comparison

Frequently asked questions

One file for the data plane.

Declare tables, indexes, and retrieval. Apply with pxt schema update. LangGraph can still run the agent loop.

pip install 'pixeltable[serve]'
See how it worksGet expert guidance