在 16G 内存的 Windows 本机跑通 shopkeeper-agent:13 个坑的完整排障笔记
跑了整整两天,才把 shopkeeper-agent 这个 AI 电商问数项目在本机完整跑通——从 uv sync 报错,到 LangChain JSON 解析器被 LLM 的思考块炸掉,再到 Docker Desktop 弹窗黑屏。这篇博客把我踩到的 13 个坑按”问题 → 根因 → 解决”的节奏整理成一条故事线,每一步都附可复现的命令。
如果你也在 Windows + Python 3.14 + Docker + 国内网络这个”地狱四件套”下跑 AI 项目,这篇应该能帮你少走两天弯路。
🎧 文章导读
🎵 背景音乐
前言:为什么要在本机跑这个项目
shopkeeper-agent 是一个 Text-to-SQL 的电商问数 Agent:你用自然语言问”统计华北地区的销售总额”,它会拆解意图、查元数据知识库、检索字段、生成 SQL、执行、返回结果。整套链路是当下最典型的 RAG + Agent 范式——LLM 负责推理,向量库做语义召回,关系库做事实落地。
但官方文档在 macOS / Linux 上跑得很顺,到了 Windows 这边几乎每一步都炸。原因很简单:项目用 Python 3.14(最新)、asyncmy(Cython 扩展,需要 MSVC 编译)、HF 下载(国内网络不稳)、Docker Desktop 4 容器(16G 内存吃紧)。每一项单独看都不致命,叠加在一起就是连环炸。
我把这 13 个坑按”环境 → 依赖 → 中间件 → 后端 → 模型”的顺序重新组织,读起来更像调试日志,而不是 dump 出来的报错清单。

图 1:shopkeeper-agent 整体架构——FastAPI 后端调度 LLM + 4 个 Docker 中间件
环境速览
先交代舞台。下表是我跑通时的全套环境配置(版本错一位,行为就可能不一样):
| 组件 | 版本 / 配置 | 备注 |
|---|---|---|
| OS | Windows 11 Home China(10.0.22631) | WSL2 + Docker Desktop |
| 内存 | 16 GB | 紧张,但够用 |
| Python | 3.14.3(uv 管理) | 最新版,少数包无 wheel |
| uv | 0.10.9 | 比 pip 快得多,但会扫祖先目录 |
| Docker Desktop | 28.1.1(WSL2 backend) | 存储迁到 E:\dockerimgerdeful\DockerDesktopWSL |
| Docker Compose | v2.35.1 | docker compose(不是 docker-compose) |
| Node | 14.21.3 → 22.20.0 | Node 14 太老,前端起不来,必须切 |
| LLM | MiniMax-M3(OpenAI 兼容) | 用 extra_body 关 think 输出 |
| Embedding | BAAI/bge-large-zh-v1.5 | ModelScope 国内源 |
第一章:Python 环境与依赖——两个温柔的陷阱
坑 1:uv 扫祖先目录,把外层 pyproject.toml 拉下水
1 | uv python install 3.14 |
最后一步炸了:
1 | Failed to parse: ai-application/pyproject.toml |
第一反应是 shopkeeper-agent-main/pyproject.toml 写错了,但真正的问题是外层仓库。我的项目路径是这样的:
1 | F:\bug图\zhioai\ai-application\ai-application\ |
ai-application/pyproject.toml(仓库的”骨架”,教程用的)也有一个 [project] 段,但漏写了 version 字段。uv 0.10 的策略是”沿祖先目录扫所有 pyproject.toml“,PEP 621 又强制 project.version 必须存在,于是解析阶段直接噎死。
解决:外层 ai-application/pyproject.toml 补一行 version = "0.0.0"。一行配置,绕过一个 PEP 621 校验。
💡 教训:用 uv 的同学,别把项目放在”教程仓库”里。要么把外层
pyproject.toml改合规,要么干脆cd进自己的目录再开 terminal。
坑 2:asyncmy 编译——Windows 无 MSVC 的死结
uv sync 接着报:
1 | asyncmy==0.2.11 ... Microsoft Visual C++ 14.0 or greater is required |
asyncmy 是 Cython 扩展,需要在本地编译出 .pyd。Windows 默认没装 MSVC Build Tools,PyPI 上又没有 Python 3.14 的预编译 wheel——3.14 太新了,几乎所有 C 扩展都得自编译。
我有三个选择:装 MSVC(10G+、耗时长)、装 VS Build Tools(一样重)、换驱动。考虑到这只是数据库驱动,纯 Python 的 aiomysql(基于 PyMySQL)完全可以胜任,最终选了第三条路。
改动清单:
1 | # shopkeeper-agent-main/pyproject.toml |
1 | # app/clients/mysql_client_manager.py |
一共改了 3 处连接串 + 几行注释,119 个依赖顺利装好。
第二章:LLM 接入——OpenAI 兼容接口的甜与苦
shopkeeper-agent 用 LangChain 的 init_chat_model(model_provider="openai"),这意味着任何 OpenAI 兼容 provider 都能直接接——只要改 base_url 和 api_key。
我先按官方文档配 MiniMax:
1 | # conf/app_config.yaml |
1 | # .env |
启动、调用,看起来一切正常,直到 LangChain 的 JSON parser 报错。这就是后面会单独讲的”坑 12”——推理模型的思考块输出和 JSON 解析器的兼容性。这章先按住不表,因为它是 13 个坑里最值得单独立传的一个。
第三章:Embedding 模型——国内网络的两条路
坑 3:hf-mirror 看起来配了,其实没生效
1 | HF_ENDPOINT=https://hf-mirror.com uvx --from huggingface_huggingface_cli ... |
报错:
1 | LocalEntryNotFoundError / FileMetadataError |
环境变量确认生效了,但下载依然失败。hf-mirror 维护方在 GitHub Issues 答复过——部分大模型(bge-large-zh-v1.5 这种热门模型)的镜像同步经常滞后。
转 ModelScope(阿里达摩院,国内稳定):
1 | MODELSCOPE_CACHE=F:/modelscope_cache uvx --from modelscope modelscope download \ |
C 盘空间也顺便挪走:HF_HOME=F:/hf_cache。
坑 4(虚惊一场):ModelScope 只有 pytorch_model.bin,没 model.safetensors
下载完成后我下意识去检查 TEI(text-embeddings-inference)的兼容性——它默认吃 safetensors,没有就报错。
结果实测发现:**TEI 的 candle backend 直接吃 pytorch_model.bin**,根本不用 safetensors,也不用装 torch。1.3G 的模型就这么直接用起来了。
⚠️ 这个坑是”我自己吓自己”——遇到报错前先看官方文档说什么,别被搜索结果里的”通用经验”误导。TEI 用 candle 后端时是支持 pytorch_model.bin 的。
第四章:Docker 编排——16G 内存下的容器战争

图 2:4 个核心容器(mysql/es/qdrant/TEI)+ 可选 kibana
坑 5:OOM 黑屏
docker compose up 拉起 5 个容器(含 kibana)时,整机黑屏 3 秒。重启后发现 ES 镜像 675MB 那层 extract 时直接把内存撑爆——16G 只剩 988MB,IDEA + Chrome 已经把可用内存吃干抹净。
1 | # 关 IDEA |
只起 4 容器就稳了。kibana 是可选的 UI 工具,不影响主链路。
坑 6 + 7:容器名 + 端口双重冲突
启动 mysql 时又报:
1 | container name "mysql" already in use |
本机装了 mysqld.exe(Windows MySQL 服务)占着 3306,还有个旧的 mysql 容器(别的项目残留)占着容器名。
两步解决:
1 | docker rm mysql # 删旧容器 |
1 | # docker/docker-compose.yaml |
1 | # conf/app_config.yaml |
容器内还是 3306(不用动),只把宿主端口错开。
第五章:元数据知识库——aiomysql 的回旋镖
坑 8:aiomysql ping 签名 bug(异步适配器的副作用)
跑 build_meta_knowledge.py(构建元数据知识库的脚本)时:
1 | AsyncAdapt_aiomysql_connection.ping() missing 1 required positional argument: 'reconnect' |
SQLAlchemy 开了 pool_pre_ping=True(每次取连接前 ping 一下),会调 connection.ping()。但 aiomysql 的 async 适配器要求 reconnect 参数(asyncmy 没这问题,是换驱动后的副作用)。
最干净的解决:**关掉 pool_pre_ping**。
1 | # app/clients/mysql_client_manager.py |
💡 这个坑很隐蔽:换驱动的隐性代价只有运行时才暴露。**生产环境别只看”功能等价”,要看”配置参数签名等价”**。
坑 9:build 重跑主键冲突
第一次失败残留了部分数据,重跑就:
1 | Duplicate entry 'dim_region.region_id' for key 'PRIMARY' |
清表后重跑:
1 | docker exec mysql mysql -u didilili -pdili123 meta -e \ |
小细节:mysql 命令在密码告警时会输出到 stderr,传统的 cmd 2>&1 | grep -v Warning 加上 && 链会因为 grep 在”空匹配时返回 1”而断链。改用 ; 分隔命令最稳。
最终:5 表 / 24 字段 / 2 指标 落库,元数据知识库就位。
第六章:后端启动——GBK 编码下的 emoji 血案
坑 10:fastapi dev 因 rich emoji 崩
按 README 跑:
1 | uv run fastapi dev main.py |
报错:
1 | UnicodeEncodeError: 'gbk' codec can't encode character '\U0001f680' |
fastapi-cli 用 rich 打印 banner,里面有 🚀 这样的 emoji。Windows 终端默认 GBK,根本编码不了 unicode。rich 的 win32 渲染器直接崩。
两条路:
- 绕开:用 uvicorn 直接起(没有 rich banner)
- 配环境:告诉 Python 用 UTF-8
我两条都做了:
1 | PYTHONUTF8=1 PYTHONIOENCODING=utf-8 \ |
后来(2026-07-18 复启)发现:只要带上 PYTHONUTF8=1,fastapi dev 也能跑。这条经验也写进了 README。
后端起来后还会有一堆 jieba 的 SyntaxWarning: invalid escape sequence——无害,是 jieba 旧正则在 Python 3.14 下的告警,不影响功能。
第七章:模型选型心路——这是最长的一章
这是整篇博客最值得读的部分。前 10 个坑都是工程问题,坑 12 是 LLM 应用的核心架构问题。

图 3:从 MiniMax-M2.7 到 M3 + extra_body 的四步选型路径
shopkeeper-agent 用 LangChain 的 PydanticOutputParser(一种 JSON 输出解析器)来约束 LLM 输出。解析器期望:
1 | {"intent": "aggregation", "metric": "销售总额", ...} |
而推理模型(reasoner)默认会先输出一段思考过程:
1 | 用户问的是华北地区的销售总额,我需要先找到对应区域字段, |
LangChain 的 JSON 解析器撞上 `` 标签就直接 OUTPUT_PARSING_FAILURE,整个 recall_value / recall_metric 节点失败。
查 MiniMax 官方文档说”M 系列不能关闭 think”——这个结论不准确:
- M2.x:确实不能关闭(即使传
thinking: {"type": "disabled"}也会被忽略) - M3:支持
thinking: {"type": "disabled"}关掉 think 输出(内部思考能力保留,只是不返回 think 块)
解决(M3 + extra_body)
app/agent/llm.py 的 init_chat_model(...) 加一行参数:
1 | extra_body={"thinking": {"type": "disabled"}}, |
配合:
1 | # conf/app_config.yaml |
效果:M3 关 think 后输出纯 JSON,项目 LangChain 解析正常,端到端跑通(简单查询 + 复杂日期 JOIN 查询都过)。M3 内部推理能力保留(关的是输出,不是推理)。
模型选型的完整演进
| 阶段 | 模型 | 结果 | 备注 |
|---|---|---|---|
| 1 | MiniMax-M2.7-highspeed | ❌ JSON 解析炸 | think 输出无法关闭 |
| 2 | MiniMax-M3(默认) | ❌ JSON 解析炸 | 同样 think 炸 |
| 3 | qwen-plus / qwen-max | ✅⚠️ 偶发错 | 简单查询过,复杂日期 JOIN 偶错 |
| 4 | MiniMax-M3 + extra_body | ✅ 完美 | M3 内部强推理 + 输出纯 JSON |
最终选了第 4 条路:用最强推理能力的模型 + 关掉其思考输出——既拿到 M3 的复杂推理能力,又拿到干净的 JSON 解析。
坑 13:Git Bash 自带 curl 坏了
端到端测试时碰到个小坑:
1 | curl -X POST http://localhost:8000/api/query -d '{"query":"..."}' |
curl 无任何输出(连错误都没有),后端日志也无请求记录。Git Bash 自带的 curl 坏了:
1 | C:/Program Files/Git/mingw64/bin/curl.exe: error while loading shared libraries |
换成 Windows 自带 curl:
1 | /c/Windows/System32/curl.exe -N -X POST http://127.0.0.1:8000/api/query \ |
返回:
1 | {"type": "result", "data": [{"销售总额": 41099.5}]} |
✅ 端到端跑通。
额外细节:用 127.0.0.1 不用 localhost(避免 IPv6 解析到 ::1,后端只听 IPv4)。
重启后端(换 LLM 后)
换完模型需要重启后端:
1 | netstat -ano | grep ":8000 " # 找 PID |
第八章:前端补完 + 2026-07-18 复启
坑 11:node 14 太老 + pnpm 缺失(最终解法)
之前卡在 node 14 + pnpm 缺失。本次确认:
nvm.exe use 22.20.0(vite 6 + react 19 需 node 18+;本机 nvm 已有 18.20.8 / 20.18.0 / 22.20.0 / 24.9.0)- 不用装 pnpm:
frontend/node_modules早已存在,Git Bash 里 pnpm 又不在 PATH,直接用本地 vite:
1 | cd frontend && npx --no-install vite |
vite v6.4.2,端口 5173,/api 已代理到 8000(vite.config.ts 的 VITE_DEV_PROXY_TARGET,默认 http://127.0.0.1:8000)。
本次完整启动顺序(5 步)
- 启 Docker Desktop(GUI,轮询
docker ps直到通) docker compose -f docker/docker-compose.yaml up -d(5 容器;内存紧可docker stop kibana,restart: unless-stopped下不会自动重起)- 后端
PYTHONUTF8=1 PYTHONIOENCODING=utf-8 uv run fastapi dev main.py(lifespan 自动连 4 个中间件;元数据在 docker volume 里,**无需重跑build_meta_knowledge**) - 前端
nvm use 22.20.0 && cd frontend && npx --no-install vite - 浏览器开 http://localhost:5173 问数
排障时间线复盘

图 4:13 个坑按启动顺序排列的时间线
把 13 个坑按启动顺序拉成一条线,能看清几件事:
- 前 1/3 是环境问题(Python、依赖、Embedding),全部是”装东西”阶段的
- 中间 1/3 是中间件问题(Docker、Mysql、知识库构建),全部是”起服务”阶段的
- 最后 1/3 是 LLM 和前端问题(模型选型、curl、node 版本),全部是”用起来”阶段的
每一阶段的失败都跟前一阶段强相关——这就是排障的真相:bug 不会单独出现,总是连环来。
经验总结:13 个坑的根因分类
写完所有坑之后回头看,我把它们归到 5 个根因桶里:
1. Windows 适配(3 个)
- asyncmy 编译失败(无 MSVC)
- rich emoji 编码崩(GBK 终端)
- Git Bash curl 共享库丢失
对策:项目一开始就走”纯 Python 优先 + UTF-8 强制”路线,能避开一大半 Windows 适配坑。
2. 国内网络(1 个)
- hf-mirror 不稳 → ModelScope
对策:直接用 ModelScope 作为默认 HF 替代,别再赌 hf-mirror。
3. 环境冲突(2 个)
- 端口 3306、容器名 mysql
对策:用 docker ps -a + netstat 先做端口/容器名扫描,再起新项目。
4. Python 3.14 太新(1 个)
- asyncmy 无 wheel
对策:要么锁 Python 3.12,要么默认走纯 Python 驱动(aiomysql/PyMySQL)。
5. 内存紧张(1 个)
- 16G 跑 5 容器 + 桌面程序 → OOM,跳 kibana 缓解
对策:docker compose 文件要分层——必选 vs 可选。kibana 这种纯可视化工具拆出来,内存不够时直接 skip。
6. LLM 架构(2 个)
- 推理模型 think 输出与 JSON parser 不兼容
- extra_body 关闭 think 是解药
对策:用 LangChain 的输出解析器时,默认假设模型会输出额外内容——优先选非推理模型,或者用 extra_body 关 think。
7. Node 生态(1 个)
- node 14 EOL + pnpm 不在 PATH
对策:用 nvm 切 node 20+,项目根放个 .nvmrc 锁版本。
关键改动文件最终汇总
| 文件 | 改动 | 根因 |
|---|---|---|
ai-application/pyproject.toml |
加 version = "0.0.0" |
uv 扫祖先解析 |
shopkeeper-agent-main/pyproject.toml |
asyncmy → aiomysql |
Windows 无 MSVC |
app/clients/mysql_client_manager.py |
asyncmy→aiomysql + pool_pre_ping=False |
驱动替换 + ping bug |
conf/app_config.yaml |
LLM = MiniMax-M3 + mysql 端口 3307 | 模型切换 + 端口避让 |
docker/docker-compose.yaml |
mysql 端口 3307:3306 |
端口避让本机 mysql |
app/agent/llm.py |
extra_body={"thinking":{"type":"disabled"}} |
关 M3 think 输出 |
.env |
MiniMax API Key | LLM 鉴权 |
下次启动(快速恢复)
同机器(数据/模型都在,3 步)
1 | cd F:/bug图/zhioai/ai-application/ai-application/源码项目/shopkeeper-agent-main |
浏览器开 http://localhost:5173 即可问数。
关 Docker(数据安全)
- 直接关 Docker Desktop 即可,数据安全:mysql/es/qdrant 数据在 docker volume(
mysql_data/es_data/qdrant_data),关 Docker 只是停容器,不删 volume,下次docker compose up数据自动恢复 - embedding 模型在本地
docker/embedding/bge-large-zh-v1.5/(1.3G,gitignore 不传 GitHub),只要不删这个目录,下次不用重下 - ⚠️ 别用「Reset Docker to factory defaults」或手动删 volume,那会清空 mysql/es/qdrant 数据,要重跑 build_meta_knowledge
结语
写到这里回头看,13 个坑单独看都不大,但叠加起来就是两天的工作量。这也正是”AI 应用项目”的真实写照——核心代码可能只占 20%,剩下 80% 全在和”环境、依赖、网络、模型版本”做斗争。
如果这篇博客能帮你省下哪怕一个小时的排障时间,那这 4000 字就值了。
欢迎在评论区分享你的踩坑故事——我相信每个跑过本地 LLM 项目的人都有一个”坑 12”故事。