> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

PySpur: The Visual Debugger That AI Agent Development Has Been Waiting For

[ View on GitHub ]

PySpur: The Visual Debugger That AI Agent Development Has Been Waiting For

Hook

Most developers test their AI agents by running the entire workflow from scratch, waiting minutes for LLM responses, only to discover their third node failed—then repeating the process dozens of times. PySpur eliminates this terminal testing nightmare with a visual debugging model that lets you iterate at the node level.

Context

Building AI agents has become a exercise in frustration. The typical development cycle looks like this: write your agent workflow in Python, run it end-to-end, watch it fail three nodes deep because your prompt wasn't quite right, modify the code, and run everything again from the start. Each iteration burns minutes and API credits, multiplied by dozens of test runs. LangChain, LangGraph, and similar frameworks gave us the building blocks for agent workflows, but they inherited the traditional software development model: code, run, debug through logs, repeat.

The problem compounds when you move beyond simple chains into complex agentic workflows with loops, conditional branches, RAG pipelines, and tool integrations. You're essentially flying blind until execution completes, and when something breaks, you're parsing through terminal output trying to reconstruct what happened six nodes ago. PySpur emerged in early 2024 with a different philosophy: what if agent development looked less like traditional programming and more like working with an interactive debugger that never goes away? What if you could see exactly what each node received, what it produced, and modify just that one piece without re-running everything upstream?

Technical Insight

PySpur's architecture splits cleanly between a TypeScript-based visual editor running on port 6080 and a Python execution engine, but the interesting design choice is how it treats workflows as persistent, resumable entities rather than ephemeral scripts. When you execute a workflow, each node's inputs, outputs, and state get stored in either SQLite or PostgreSQL, creating an execution trace that survives beyond the initial run. This architectural decision enables node-level iteration—you can literally click on a node mid-workflow, modify its configuration or code, and re-execute just that node forward using the persisted inputs from the previous run.

The node system itself embraces a file-per-node model. Each node is a self-contained Python file that PySpur discovers and loads dynamically. Here's what a custom RAG retrieval node might look like:

from pyspur import Node, NodeInput, NodeOutput
from typing import List, Dict
import chromadb

class RAGRetriever(Node):
    """
    Retrieves relevant context from vector DB based on query
    """
    
    def setup(self):
        # Initialize once, persists across executions
        self.chroma_client = chromadb.PersistentClient(path="./chroma_db")
        self.collection = self.chroma_client.get_collection("documents")
    
    def execute(self, inputs: Dict) -> NodeOutput:
        query = inputs.get("query")
        top_k = inputs.get("top_k", 5)
        
        # Query vector DB
        results = self.collection.query(
            query_texts=[query],
            n_results=top_k
        )
        
        # Format context for LLM
        context = "\n\n".join([
            f"Source {i+1}: {doc}"
            for i, doc in enumerate(results["documents"][0])
        ])
        
        return NodeOutput(
            outputs={"context": context, "sources": results["metadatas"][0]},
            metadata={"num_chunks": len(results["documents"][0])}
        )

The UI automatically detects this node, generates input/output ports based on the type hints, and lets you wire it into your workflow graph. But here's where persistence becomes powerful: after running this node once, you can click into the execution trace, see exactly what context it retrieved, and if it's not quite right—maybe you need top_k=10 instead of 5—you modify that parameter and re-execute just this node and everything downstream. The query embedding from the previous run gets reused automatically.

The human-in-the-loop feature takes this further with explicit breakpoints. You can mark any node as requiring human approval before proceeding:

class ContentModerator(Node):
    requires_approval = True  # Workflow pauses here
    
    def execute(self, inputs: Dict) -> NodeOutput:
        generated_content = inputs.get("content")
        
        # Run automated checks
        toxicity_score = self.check_toxicity(generated_content)
        
        return NodeOutput(
            outputs={"content": generated_content},
            approval_context={
                "toxicity_score": toxicity_score,
                "needs_review": toxicity_score > 0.3
            }
        )

When execution hits this node, the workflow pauses and waits. A human reviewer sees the generated content and metadata in the UI, can approve or reject with feedback, and the workflow resumes from exactly that point. For production AI systems where you can't afford hallucinated content or policy violations, this transforms workflows into quality gates rather than fire-and-forget processes.

The provider abstraction layer is refreshingly simple—rather than building yet another adapter system, PySpur leverages LiteLLM under the hood, giving you access to 100+ LLM providers through a unified interface. The same workflow can swap between GPT-4, Claude, DeepSeek, or local Ollama models by changing a dropdown in the UI. This works because nodes don't call providers directly; they declare their LLM requirements and PySpur handles routing:

class Summarizer(Node):
    def execute(self, inputs: Dict) -> NodeOutput:
        text = inputs.get("text")
        
        # Provider-agnostic LLM call
        response = self.call_llm(
            messages=[{"role": "user", "content": f"Summarize: {text}"}],
            model=self.config.get("model", "gpt-4"),
            temperature=0.3
        )
        
        return NodeOutput(outputs={"summary": response.content})

The call_llm method is provided by the Node base class and handles provider routing, retries, rate limiting, and cost tracking automatically. The execution trace captures not just what the LLM returned, but token counts, latency, and cost per call—critical data when you're optimizing production workflows.

PySpur's RAG pipeline implementation deserves attention because it bundles what typically requires integrating 4-5 separate libraries. Document parsing (PDF, DOCX, HTML), chunking strategies, embedding generation, and vector DB operations are all exposed as composable nodes. You can visually construct a RAG pipeline by wiring together Parse → Chunk → Embed → Store nodes, see the actual chunks and embeddings in the trace, and iterate on chunking parameters while keeping embeddings cached. This is substantially faster than the usual "modify chunking code, re-embed entire corpus, test retrieval" cycle.

Gotcha

The Unix-only development requirement is a hard blocker if you're on Windows. The documentation explicitly states development is not supported on Windows systems, which immediately excludes a significant portion of developers and creates friction for teams with mixed OS environments. While you could theoretically run it in WSL2, the fact that this isn't officially supported suggests you'll be on your own if things break. For a tool targeting rapid iteration, this platform limitation is ironic—it slows down iteration for anyone not on macOS or Linux.

The project's youth shows in rough edges. Features marked "coming soon" like workflow self-improvement indicate that the vision isn't fully realized yet. The evaluation system exists but appears limited compared to mature offerings from Weights & Biases or LangSmith. You can trace executions, but sophisticated A/B testing, dataset management, or automated regression detection aren't there yet. Documentation is functional but sparse—you'll find yourself reading source code more than you'd like. The community is growing (5,700+ stars) but Stack Overflow questions are scarce, meaning you're largely dependent on GitHub issues for troubleshooting. If you need battle-tested stability or comprehensive documentation, PySpur's early-stage status will frustrate you.

Verdict

Use if: You're building production AI agents that need human oversight, working with complex multi-step workflows where debugging is painful, or developing RAG systems where iteration speed matters more than having every possible feature. PySpur shines when you're past the prototype phase but not yet at massive scale—that middle ground where you need more than print statements but less than enterprise observability platforms. The visual debugging and persistent execution model will genuinely make you faster if you're iterating on agent behavior daily. Skip if: You need Windows development support, prefer pure code-first approaches without UI dependencies (LangGraph is better here), are building simple single-shot LLM calls that don't need workflow orchestration, or require enterprise-grade features like SSO, audit logs, and compliance certifications that only mature platforms provide. Also skip if you're allergic to early-stage tools—wait six months for the ecosystem to mature if you need extensive documentation and community support.