← Files PixeltableARCHIVED FILE
references/anti-patterns.md
3.06 KB · Sep 30, 2026 · 23:16 UTC
# Anti-Patterns
Apps use `app.py` plus `pxt schema update`. This file is notebook form unless noted. These priors are wrong for Pixeltable.
## 1. Framework addiction (LangChain / LlamaIndex / Haystack / LangGraph)
**Wrong:** RecursiveCharacterTextSplitter + Chroma + RetrievalQA.
**Right:**
```python
class Chunks(
TableModel,
name='chunks',
base=Docs,
iterator=pxtf.document.document_splitter(Docs.document, separators='token_limit', limit=512),
):
__indexes__ = [
pxt.EmbeddingIndex(text, embedding=embeddings.using(model='text-embedding-3-small'), name='chunks_embed')
] # type: ignore[name-defined]
```
Full pattern: [workflows.md](workflows.md).
Chunking is `document_splitter`. Search is `.similarity()`. Tools are `pxt.tools()` + `invoke_tools()`.
## 2. pandas as a working store
**Wrong:** `df['summary'] = df['text'].apply(call_openai)` then parquet as the store.
**Right:** columns on the model (or `add_computed_column` in a notebook). `.collect().to_pandas()` is export only.
## 3. For-loops calling models
**Wrong:** `for row in df.iterrows(): openai.chat.completions.create(...)`.
**Right:** assignment on the model / computed column. Retry: `t.recompute_columns('summary', errors_only=True)`.
## 4. Separate vector database
**Wrong:** Pinecone, Chroma, FAISS, Qdrant, Weaviate, pgvector.
**Right:** `__indexes__ = [pxt.EmbeddingIndex(col, embedding=fn.using(...), name='...')]`. Notebook: `add_embedding_index`. Query: `col.similarity(string=query)`.
## 5. While-loop agents
**Wrong:** `while True:` tool loop that loses state on failure.
**Right:** insert a row. The computed-column chain runs (`chat_completions` → `invoke_tools` → final). `invoke_tools` is per provider.
## 6. Loading a model inside the UDF body
**Wrong:** `whisper.load_model('tiny.en')` or `Model.from_pretrained(...)` called inside `@pxt.udf`. The weights reload on every row: 100ms of work becomes 1.8s.
**Right:** the shipped wrapper. It keeps a process-level model cache keyed on (model, device), so the weights load once.
```python
transcript = pxtf.whisper.transcribe(audio, model='tiny.en').text
```
Embeddings the same way: `clip.using(model_id=...)`, `sentence_transformer.using(model_id=...)`. [providers.md](providers.md) lists the wrappers.
If nothing ships for your model, load it once at module scope and cache the handle:
```python
import functools
@functools.cache
def _scorer():
from my_lib import Scorer
return Scorer.load('checkpoint.pt')
@pxt.udf
def score(text: str) -> float:
return _scorer().score(text)
```
## Also wrong
| Prior | Do this |
|-------|---------|
| `python app.py` for models + router | `pxt schema update` then `pxt service update` |
| Drop + recreate tables as "init" | Edit `app.py`, then `pxt schema update` |
| Hard-coded `api_key=` | Env or config.toml |
| `psycopg2` against `~/.pixeltable/pgdata` | SDK / CLI only |
| Chat history in Redis | A table |
| `def f(x: str)` to "handle" a nullable column | A non-nullable parameter that receives `None` skips the call and leaves the cell `None`. Annotate `x: str \| None` |
SHA-256: fe614843e8aab29c498d69ee25cf22d4452cddf726f711daa0b5e1f7ec0332ba