shopkeeper-agent: a LangGraph reference build for text-to-SQL over an e-commerce warehouse
📊 电商数仓智能问数 AI Agent,最适合用于系统学习 LangGraph 的实战项目:基于 LangGraph、FastAPI、Qdrant、Elasticsearch、MySQL 与 React,完整实现元数据知识库、混合检索、自然语言生成 NL2SQL 生成校验、SQL 执行与流式查询展示。前后端完整代码全栈可跑,Docker 环境一键部署,配套 ai-agents-from-zero 免费教程与章节代码分支。适合系统学习大模型应用、数据分析 Agent 和企业级 AI 工程落地。
At a glance
- What is it?
- The repository is a full-stack, Docker-deployable teaching project that turns a natural-language question into a validated SQL query against a simulated e-commerce warehouse. Its real value is the retrieval layer in front of the model, not the model call itself.
- Who is it for?
- Adopt it if you want a runnable end-to-end example of a LangGraph agent where retrieval, SQL validation and streaming delivery are all present in the codebase, and you are willing to follow the companion tutorial to understand the branches. Do not adopt it as a production query layer for a real warehouse: the README describes a teaching warehouse, not an integration path for existing schemas, permissions or SQL review.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 121 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem it targets: business questions that never reach SQL
The README frames the problem in operational terms. Business users do not write SQL, and analysts cannot hold every table structure, field meaning, metric definition and field value in their heads. Sending the raw question to a model produces a predictable set of failures: the wrong table, the wrong column, a misread metric, or a fabricated query. shopkeeper-agent addresses that gap by inserting a retrieval stage before generation. The system recalls relevant fields, metrics and field values, hands that context to the model, and only then asks for SQL. The intended audience is stated plainly in the README: people who want to learn LangGraph beyond toy nodes, and who want MySQL, Qdrant, Elasticsearch and an LLM inside one business scenario rather than four disconnected demos. It is a learning project with a working application attached, not a product.
Two pipelines: building the metadata knowledge base, then querying it
The README splits the system into two main lines. The first is knowledge base construction: extract tables, fields, metrics and field values from the teaching warehouse, then write them into three different stores. MySQL holds the structured, authoritative metadata through SQLAlchemy models. Qdrant holds vectors for fields and metrics, which supports semantic recall. Elasticsearch holds the real field values, which supports keyword and value-domain lookup. The second line is the query path: a user question triggers recall, context assembly, SQL generation, validation and execution, with the process streamed back to the frontend. The architecture diagram in the repository shows the frontend connecting over FastAPI and SSE, with LangGraph orchestrating recall, SQL work and result return on top of Jieba, MySQL, Qdrant, Elasticsearch and the LLM. The split matters because it explains why three data stores exist instead of one. Each store answers a different kind of lookup, and the README treats that division as the core design decision rather than an implementation detail.
Why three stores instead of one vector index
A single vector index over table schemas is the common shortcut in text-to-SQL demos, and it tends to fail on exact values. If a user asks about a specific city, brand or order status, semantic similarity is a poor tool for matching a literal string that already exists in the data. shopkeeper-agent routes those lookups to Elasticsearch, which the README describes as handling keyword and value-domain retrieval over real field values. Fields and metrics go to Qdrant for semantic recall, because a question phrased as revenue or sales volume should reach the metric definition even when the wording differs. MySQL keeps the complete structured metadata, which is what the other two indexes are derived from. The README claims this three-way recall is closer to real enterprise analysis than table-level or field-level retrieval alone. That claim is reasonable on its face, but the repository does not present retrieval accuracy numbers, so the benefit is argued from design rather than measured.
Getting it running: the actual command sequence
The README gives a linear setup path. Prerequisites are Python >= 3.14, uv, Docker with Compose, and Node.js with pnpm. Clone the repository, then run uv sync to install backend dependencies. Copy .env.example to .env and replace LLM_API_KEY with a real key. The default configuration points at an OpenAI-compatible endpoint: model_name is Pro/zai-org/GLM-5.1 and base_url is https://api.siliconflow.cn/v1, both in conf/app_config.yaml. To use another compatible platform, change model_name and base_url there. The embedding model is BAAI/bge-large-zh-v1.5 served through TEI, and because the files are large the README instructs you to download them into the Docker mount with uv run hf download BAAI/bge-large-zh-v1.5 --local-dir docker/embedding/bge-large-zh-v1.5. Then start the infrastructure with docker compose -f docker/docker-compose.yaml up -d. Default ports are MySQL 3306, Elasticsearch 9200, Kibana 5601, Qdrant 6333 and Embedding 8081. Finally, build the knowledge base with uv run python -m app.scripts.build_meta_knowledge -c conf/meta_config.yaml, which writes field metadata to MySQL, field and metric vectors to Qdrant, and field values to Elasticsearch. The README notes that docker/mysql/meta.sql and docker/mysql/dw.sql initialize the metadata database and teaching warehouse automatically on first container start.
The Python 3.14 requirement is the first thing that will stop you
The README lists Python >= 3.14 as a prerequisite, and the badge repeats it. That is a demanding floor. Anyone on a managed runtime, a locked container base image, or a machine where the system Python is pinned by other tooling will hit this before touching any application code. The README does not describe a fallback, a lower supported version, or a compatibility matrix, so the requirement should be read as stated. The embedding model is the second obstacle: the README is explicit that the model files are too large to commit and must be downloaded into docker/embedding/bge-large-zh-v1.5, either with the hf download command or by manual extraction. Skip that step and the TEI service has nothing to load. A third constraint is scope. The warehouse is a teaching dataset initialized from docker/mysql/dw.sql, and the README presents expansion toward permission control, SQL review and result visualization as future work rather than existing features. There is no described path for pointing the agent at an existing production schema, and no described SQL review gate beyond the generation and validation steps inside the graph.
Compared with a plain LangChain SQL agent
The obvious alternative is a LangChain SQL agent that hands the model a schema dump and lets it iterate against the database. The difference is where the intelligence sits. A schema-dump agent depends on the model to notice which columns and values matter, and it usually has no index of metric definitions or literal field values. shopkeeper-agent moves that work into retrieval: Qdrant for fields and metrics, Elasticsearch for values, MySQL as the source of truth, with Jieba in the pipeline for Chinese text handling. The trade-off is operational weight. You run MySQL, Qdrant, Elasticsearch, Kibana and a TEI embedding service before the agent answers a single question. A single-process LangChain agent needs a database and an API key. If your goal is to understand how retrieval changes text-to-SQL quality, the extra services are the point. If your goal is a weekend prototype, they are overhead you will spend an afternoon wiring up.
Maintenance, licence and what the repository commits to
The project is MIT licensed, which permits commercial use, modification and redistribution provided the licence text is retained. That is a permissive baseline, and nothing in the supplied material suggests additional restrictions; it is still worth reading the LICENSE file in the repository rather than relying on the metadata alone. On maintenance, the repository is not archived and the last push recorded is 2026-05-18. No releases were retrieved, so there is no versioned artifact to pin against and no changelog to consult. The README states that the practical project has been completed and that the companion tutorial, chapter branches and frontend and backend code are all available for study. Upgrading therefore means tracking the main branch and the chapter branches rather than bumping a version number. The dependency surface is broad: LangGraph, LangChain, FastAPI, SQLAlchemy, Qdrant, Elasticsearch, TEI, uv and pnpm all appear in the stack table, and each moves on its own schedule. For a project whose stated purpose is learning, that is acceptable. For anything you intend to run unattended, the absence of tagged releases is the concrete thing to weigh.
Editorial conclusion
Adopt it if you want a runnable end-to-end example of a LangGraph agent where retrieval, SQL validation and streaming delivery are all present in the codebase, and you are willing to follow the companion tutorial to understand the branches. Do not adopt it as a production query layer for a real warehouse: the README describes a teaching warehouse, not an integration path for existing schemas, permissions or SQL review. Before committing, verify that your Python toolchain can satisfy the stated >= 3.14 requirement, confirm the TEI embedding model directory is populated at docker/embedding/bge-large-zh-v1.5, and check whether your LLM provider is reachable through the OpenAI-compatible settings in conf/app_config.yaml.
Community notes