1. 项目概述:一场不靠宣传稿、只看代码输出的硬核对决
最近两周,我把自己关在书房里没怎么出门,就为了干一件事:把三款当前最被程序员圈内人认真讨论的大模型——智谱的GLM-5.1、通义千问最新发布的Qwen3.6-Plus,还有Anthropic刚推出来的Claude Opus 4.6——拉到同一个编程考场里,用真实项目任务当考卷,一题一题地批改它们交上来的“代码作业”。不是跑个Hello World,也不是调个API接口就完事;而是从零开始,完成一个带前后端交互、含数据库设计、有真实业务逻辑、还要能本地跑起来的完整小系统:一个轻量级的「技术文档协作平台」。它要支持Markdown编辑、版本对比、权限分级、离线缓存,最后还得生成可部署的Docker镜像。整个过程我全程录屏、保存每轮输出、手动执行每段代码、记录报错细节和修复耗时。这不是评测,是陪练——我把自己当成那个被AI辅助的真实开发者,看谁真能让我少写30%的样板代码、少查50%的文档、少踩80%的坑。
核心关键词已经非常明确: 国产大模型、GLM-5.1、Qwen3.6-Plus、Claude Opus 4.6、编程能力、代码生成、工程落地、技术文档平台、Docker部署 。如果你是每天和IDE、Git、CI/CD打交道的前端/后端/全栈工程师,或者正考虑把AI编程助手引入团队开发流程的技术负责人,这篇内容就是为你写的。它不讲参数量、不比训练数据规模、不谈“理解力”这种虚词,只聚焦一个最朴素的问题:当我坐在电脑前,面对一个真实需求,敲下第一行提示词(prompt)之后,接下来的20分钟里,谁能让我的键盘敲得更少、终端报错更少、浏览器里看到效果更快?答案不在厂商白皮书里,而在你复制粘贴就能跑起来的那几行命令里。
2. 内容整体设计与思路拆解:为什么选这个题目、这套流程、这三款模型?
2.1 选题逻辑:拒绝“玩具级”测试,直击工程交付痛点
很多公开的编程模型评测,喜欢用HumanEval、MBPP这类标准数据集打分。它们确实有参考价值,但有个致命缺陷:题目太“干净”。比如“写一个函数,输入两个整数,返回最大公约数”,输入确定、边界清晰、输出唯一。现实开发中,你永远不会接到这种需求。你接到的是:“老板说下周要上线一个内部知识库,产品经理画了三张低保真图,要求支持历史版本回溯,权限按部门隔离,最好能导出PDF,服务器资源有限,别搞Java全家桶。”——这才是常态。
所以我把整个评测锚定在
一个可交付、可验证、有真实约束条件的最小可行产品(MVP)
上。技术文档协作平台这个选题,恰好覆盖了现代Web开发的典型技术栈:前端用React + Vite(兼顾开发体验与构建速度),后端用FastAPI(轻量、异步、类型提示友好),数据库选SQLite(零配置、易嵌入、适合单机协作场景),部署用Docker(工业界事实标准)。它不追求高并发或分布式,但对代码结构合理性、模块职责划分、错误处理健壮性、环境配置一致性,提出了非常实际的要求。模型如果只是堆砌语法正确的代码,却在数据库连接初始化时漏掉
check_same_thread=False
,或者在React组件里忘了加
useEffect
依赖数组,那它在真实项目里就会立刻暴露——而这些,恰恰是HumanEval永远测不到的“工程债”。
2.2 模型选择依据:不是“最强”,而是“最可能被你用上”
GLM-5.1和Qwen3.6-Plus入选,根本原因只有一个:它们是目前 国内开发者最容易、最合规、最稳定接入生产环境的两个顶尖开源/开放模型 。GLM-5.1已通过Hugging Face提供量化版,支持4-bit加载,一张3090显存就能跑;Qwen3.6-Plus虽为闭源API,但阿里云百炼平台提供了极低延迟、高稳定性的调用入口,且有完善的私有化部署方案。它们代表了国产模型在代码能力上的真实水位,不是实验室里的Demo,而是你明天开会就能提议接入的选项。
Claude Opus 4.6的加入,则是为了提供一个 国际一线水平的参照系 。它不是为了证明“国产不如人”,而是为了看清差距落在哪里:是API响应慢导致开发节奏卡顿?是中文注释生成质量差影响团队协作?还是对Python 3.12新语法支持滞后?Opus 4.6在长上下文、复杂逻辑推理上确有优势,但它在中文技术语境下的“本土化适配”,比如对“钉钉机器人”、“飞书多维表格API”、“微信小程序云开发”的理解深度,天然存在信息差。这场对比,本质是“全球通用能力”与“中国开发者工作流深度适配能力”的较量。
2.3 流程设计原则:模拟真实开发流,杜绝“开卷考试”
整个评测流程,严格遵循一个核心原则: 所有操作必须在我自己的MacBook Pro M2(16GB内存)上本地完成,禁用任何预设模板、脚手架或Copilot插件,所有代码均由模型一次性生成并经我手审查后执行 。具体分为四个阶段:
-
需求澄清阶段 :我向模型提供一份简明的PRD(产品需求文档),包含功能列表、用户角色、非功能需求(如“首次加载时间<1s”、“支持离线编辑”)。模型需先输出一份技术方案概要,说明将采用什么技术栈、各模块如何划分、关键难点及应对思路。这一步筛掉只会写代码、不会做架构设计的“代码打印机”。
-
骨架搭建阶段 :模型生成项目根目录结构、基础配置文件(
pyproject.toml,vite.config.ts,Dockerfile)、以及最核心的几个空模块(如api/routers/docs.py,frontend/src/components/Editor.tsx)。我检查结构合理性后,才允许进入下一步。这一步检验模型对现代工程规范的理解。 -
功能实现阶段 :按模块逐个击破。例如,先让模型实现“Markdown实时预览”功能,它需生成前端组件+后端API+单元测试。我执行代码,记录首次运行是否成功、报错类型、修复所需时间。此阶段不许模型“重写”,只允许基于报错信息进行针对性修正(即真正的Debug辅助)。
-
集成与部署阶段 :所有模块完成后,模型需生成完整的
docker-compose.yml,并指导我如何构建镜像、启动服务、验证端到端流程。最终以“在本地浏览器打开http://localhost:5173,成功看到登录页并能创建第一条文档”为通关标志。
这个流程的设计,就是为了把模型从“答题机器”还原成“协作者”。它考验的不仅是代码生成准确率,更是 上下文保持能力、错误归因能力、调试引导能力、以及对工程交付闭环的理解深度 。
3. 核心细节解析与实操要点:从Prompt设计到环境配置的魔鬼细节
3.1 Prompt不是咒语,是需求翻译器:我的三层提示词结构
很多人以为,给模型丢一句“写个博客系统”就够了。实测下来,这是效率最低的做法。真正高效的Prompt,是一套 需求翻译协议 ,它需要把模糊的业务语言,精准转译成模型能理解的工程指令。我采用的是三层结构,每一层都解决一个关键问题:
-
第一层:角色定义(Role)
明确告诉模型它此刻的身份和约束。“你是一位有8年经验的Python全栈工程师,专注于FastAPI和React技术栈,熟悉Docker容器化部署。你习惯编写类型安全、有详细docstring、包含单元测试的代码,并优先选用成熟稳定的第三方库(如SQLModel而非纯SQLAlchemy Core)。你不会使用尚未进入PyPI正式版的实验性特性。”提示:角色定义不是套话。它直接框定了模型的知识边界和风格偏好。比如强调“SQLModel”,就规避了它生成一堆原始SQL字符串的风险;强调“类型安全”,就大幅降低了后续TypeScript类型错误的概率。
-
第二层:任务分解(Task Breakdown)
把大需求切分成原子化、可验证的小任务。“请分步完成以下任务:1. 在backend/main.py中添加一个/api/v1/docs/{doc_id}/diff端点,接收两个版本ID,返回JSON格式的diff结果(使用difflib.unified_diff);2. 在frontend/src/lib/api.ts中创建对应的fetchDocDiff函数,使用AbortSignal支持取消请求;3. 在frontend/src/components/VersionDiffView.tsx中实现一个展示diff的UI组件,高亮插入/删除行,并支持折叠/展开上下文。”
这样拆解,模型输出的代码块边界清晰,我审查时能快速定位到具体文件和函数,避免了“一大坨代码里混着三个功能”的混乱局面。 -
第三层:约束与示例(Constraints & Examples)
给出硬性规则和正向范例。“约束:所有API端点必须返回ResponseModel泛型类;所有React组件必须使用React.memo包裹;数据库操作必须在async with上下文中完成。示例:ResponseModel定义如下:class ResponseModel(BaseModel): success: bool = True; data: Any = None; message: str = ""。”
这一步是质量控制的最后防线。模型对“泛型类”“React.memo”的理解可能有偏差,但给出具体代码示例,就等于给了它一个可复刻的模板,极大提升了输出的一致性。
3.2 环境配置:为什么我坚持用M2 Mac+Docker Desktop,而不是云GPU
选择本地M2 Mac作为评测环境,绝非为了“情怀”或“省钱”,而是基于三个不可妥协的工程现实:
-
开发体验一致性 :90%以上的国内中小团队,日常开发机就是MacBook。他们不会为了用一个AI工具,专门去租一台A100服务器。模型在云端跑得再快,如果生成的代码在本地
npm run dev时一堆EACCES权限错误,或者Docker构建时因为glibc版本不匹配而失败,那它对真实用户的帮助就归零。我在M2上遇到的所有坑,大概率就是你的团队明天会踩的坑。 -
可观测性与调试深度 :在本地,我能随时
ps aux | grep python看进程、lsof -i :8000查端口占用、docker logs -f盯住容器日志流。这些是云环境里被封装掉的“黑盒”。正是通过反复查看uvicorn启动时的warning日志,我才发现了Qwen3.6-Plus在生成main.py时,漏掉了--reload-dir参数的配置,导致热重载失效——这个细节,在API响应时间的评测报告里永远看不到,但它直接决定了开发者每天要多按多少次Ctrl+C和Enter。 -
Docker Desktop的“真实世界”模拟 :M2芯片对Docker的支持并非完美。它会触发一些x86_64镜像的兼容层问题,比如某些Python包在
arm64下编译失败。这恰恰是国产模型需要面对的现实挑战。GLM-5.1在生成Dockerfile时,默认用了python:3.11-slim基础镜像,结果在M2上构建时报错No module named '_curses'。我让它修正为python:3.11-slim-bookworm,问题解决。这个过程,本质上是在训练模型理解“目标运行环境”的硬件约束,而不是只顾着写逻辑正确的代码。
3.3 代码审查清单:我手把手检查的12个关键项
模型生成的代码,我从不直接
git add . && git commit
。我会用一份自己整理的《AI生成代码审查清单》逐项核验。这份清单不是教条,而是过去三年踩坑总结出的“高频雷区”。以下是其中最关键的12项,每项都附带一个真实案例:
-
环境变量注入方式 :检查是否用
os.getenv("DB_URL", "sqlite:///./app.db"),而非硬编码。 案例:Claude Opus 4.6首次生成的settings.py里,数据库URL是写死的sqlite:///tmp/test.db,导致部署时无法切换环境。 -
异步I/O的正确await :在FastAPI路由函数里,所有
async函数调用前必须有await。 案例:GLM-5.1生成的get_doc_by_id函数,调用session.get()时漏了await,导致返回<coroutine object>对象,前端收到500错误。 -
React状态更新的不可变性 :检查
useState更新时是否用了...spread或immer,而非直接arr.push()。 案例:Qwen3.6-Plus生成的文档列表组件,用docs.push(newDoc)修改状态,导致UI不刷新。 -
Docker多阶段构建的中间镜像清理 :
build阶段安装的gcc、make等编译工具,必须在final阶段被剔除。 案例:三款模型生成的Dockerfile,只有GLM-5.1默认启用了--no-cache-dir和--exclude=dev-dependencies,镜像体积比其他两款小42%。 -
TypeScript类型守卫 :对
any或unknown类型的入参,是否添加了if (typeof data === 'object')等运行时校验? 案例:Claude Opus 4.6生成的API响应处理函数,直接对response.data做.map(),未校验data是否为数组,导致空数据时崩溃。 -
SQL注入防护 :所有数据库查询是否使用参数化查询(
session.execute(text("SELECT * FROM docs WHERE id = :id"), {"id": doc_id})),而非f-string拼接? 案例:Qwen3.6-Plus在生成搜索功能时,曾出现f"WHERE title LIKE '%{keyword}%'",被我立刻否决。 -
前端资源路径的公共基础路径(public base path) :Vite项目中,
import.meta.env.BASE_URL是否被正确用于静态资源引用? 案例:GLM-5.1生成的index.html里,<script type="module" src="/src/main.ts"></script>应为<script type="module" src="${import.meta.env.BASE_URL}src/main.ts"></script>,否则部署到子路径时JS 404。 -
错误边界的完整性 :React组件是否包裹了
<ErrorBoundary>?FastAPI路由是否设置了全局异常处理器? 案例:三款模型均未主动添加ErrorBoundary,这是我手动补上的,因为真实用户点击一个坏链接,不该让整个应用白屏。 -
日志级别与敏感信息过滤 :
logger.info()是否记录了用户密码、token等?错误日志是否包含完整的traceback(生产环境应关闭)? 案例:Claude Opus 4.6在生成logging_config.py时,level=logging.DEBUG,且未过滤password字段,存在安全风险。 -
Git忽略规则的完备性 :
.gitignore是否包含了__pycache__/,.env,node_modules/,dist/,.DS_Store? 案例:Qwen3.6-Plus生成的.gitignore漏掉了dist/,导致Vite构建产物被提交。 -
Docker健康检查(HEALTHCHECK) :
Dockerfile中是否定义了HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/health || exit 1? 案例:只有GLM-5.1在生成Dockerfile时,主动加入了这一行,体现了对容器编排场景的深度理解。 -
单元测试的覆盖率与边界 :
test_api.py是否覆盖了200、404、422等HTTP状态码?是否测试了空数据、超长字符串、特殊字符等边界输入? 案例:GLM-5.1生成的测试用例,对create_doc接口测试了title=""和content="<script>alert(1)</script>"两种情况,而其他两款仅测试了正常流程。
这份清单,是我把AI从“代码生成器”升级为“工程协作者”的核心契约。它不指望模型一次全对,但要求它知道“哪些地方容易错”,并在后续迭代中持续改进。
4. 实操过程与核心环节实现:从零到Docker镜像的完整流水线
4.1 需求澄清与技术方案:谁最先画出清晰的蓝图?
我把一份120字的PRD发给三款模型:“做一个内部技术文档平台,支持Markdown编辑、实时预览、历史版本对比、按部门设置编辑/只读权限。前端用React,后端用Python,数据库用SQLite,最终打包成Docker镜像,一键启动。”
-
GLM-5.1 的响应最“工程师”:它立刻反问了3个关键问题——“部门信息是静态配置还是动态管理?”、“历史版本是按天快照还是每次保存都存?”、“权限模型是RBAC还是ABAC?”。然后给出了一份包含4个模块(Auth, Docs, Versions, Permissions)的架构图文字描述,并明确指出“SQLite不支持行级锁,因此版本对比需在应用层实现,避免并发冲突”。它甚至估算出初始数据库schema:
docs(id, title, content, created_at, updated_at, author_id),versions(doc_id, content_hash, content, created_at)。整个方案没有一句废话,全是可执行的决策点。 -
Qwen3.6-Plus 的方案最“全面”:它列出了6种技术选型对比表(Django vs FastAPI, Vue vs React, PostgreSQL vs SQLite),并给出了推荐理由。但它把“权限按部门设置”直接等同于“Django Groups”,忽略了我们明确指定用FastAPI的约束。方案里还出现了“建议使用Redis缓存版本diff结果”,这在SQLite单机场景下属于过度设计,增加了不必要的复杂度。
-
Claude Opus 4.6 的方案最“学术”:它花了200字阐述“文档协同编辑的CRDT算法原理”,然后才提到“我们可以简化为乐观锁+版本号”。它对“离线编辑”需求的解读非常深刻,提出用
localStorage存储草稿,并设计了一个同步冲突解决策略(Last-Write-Win)。但它的技术栈建议是“Next.js App Router + Prisma ORM”,完全偏离了我指定的Vite+FastAPI要求。
结论 :在需求理解的第一关,GLM-5.1胜出。它没有炫技,而是紧扣约束,用提问厘清模糊点,用架构图建立共识,用schema设计锁定范围。这正是一个优秀技术负责人该有的样子——不是告诉你“能做什么”,而是帮你定义“该做什么”。
4.2 骨架搭建:谁的项目结构最经得起
ls -R
的审视?
我要求模型生成完整的项目骨架,包括所有必需的配置文件和空模块。这是检验模型对现代工程规范掌握程度的试金石。
-
GLM-5.1 生成的结构堪称教科书:
tech-docs/ ├── backend/ │ ├── main.py # Uvicorn入口,含health check │ ├── api/ │ │ ├── __init__.py │ │ └── routers/ │ │ ├── __init__.py │ └── docs.py # /docs/ CRUD端点 │ ├── core/ │ │ ├── config.py # 环境变量加载 │ │ └── security.py # JWT认证 │ ├── models/ # SQLModel定义 │ │ ├── __init__.py │ │ └── docs.py │ └── tests/ # 空test目录,含__init__.py ├── frontend/ │ ├── src/ │ │ ├── main.tsx # Vite入口 │ │ ├── App.tsx # 根组件 │ │ ├── lib/ │ │ │ ├── api.ts # API客户端 │ │ │ └── utils.ts # 工具函数 │ │ └── components/ │ │ ├── Editor.tsx # 编辑器 │ │ └── VersionList.tsx # 版本列表 │ └── vite.config.ts # 含base: '/docs/' ├── docker-compose.yml # nginx + backend + db └── README.md它连
vite.config.ts里base: '/docs/'这种细节都想到了,为后续Nginx反向代理预留了空间。 -
Qwen3.6-Plus 的结构稍显臃肿:
tech-docs/ ├── backend/ │ ├── app/ # 多了一层无意义的app/ │ │ ├── __init__.py │ │ └── main.py │ └── requirements.txt # 但里面写了`fastapi==0.110.0`,版本锁死,不推荐 ├── frontend/ │ ├── public/ # 但里面放了favicon.ico,没放index.html │ └── src/ │ └── components/ # 所有组件都在这,没分views/和utils/ └── Dockerfile # 但没生成docker-compose.yml它生成了
requirements.txt却没生成pyproject.toml(现代Python项目的标准),public/目录结构也不完整。 -
Claude Opus 4.6 的结构最“云原生”:
tech-docs/ ├── infra/ # 专门的infra目录 │ ├── terraform/ # 甚至生成了Terraform脚本! │ └── k8s/ # Kubernetes manifest ├── backend/ │ └── src/ # Go语言风格的src/,但我们的需求是Python └── frontend/ └── next.config.mjs # Next.js配置它彻底无视了“Python+React+SQLite”的约束,把一个单机小工具,硬生生设计成了云上SaaS架构。
结论
:GLM-5.1再次胜出。它的骨架不是“能跑就行”,而是“未来半年都无需大改”。每一个目录名、每一个文件名、每一个配置项,都透露出对Python/React生态的熟稔。当你拿到一个由它生成的项目,
cd
进去,
ls
一眼,就知道接下来该往哪个文件里写代码,这省下的时间,就是工程师最宝贵的财富。
4.3 功能实现:Markdown实时预览的“生死时速”
这是整个评测中最胶着、也最体现模型差异的环节。我要求模型实现“前端编辑Markdown,右侧实时渲染HTML”的功能,并确保渲染安全(防XSS)、样式美观(支持GitHub Flavored Markdown)、性能流畅(1000字以内延迟<100ms)。
-
GLM-5.1 的方案是:前端用
marked库(轻量、快、GFM支持好)+DOMPurify(专为XSS防护设计),后端不参与渲染,纯前端完成。它生成的Editor.tsx代码,useEffect里监听content变化,调用marked.parse(content),再用DOMPurify.sanitize()处理,最后setHtml()。整个过程无网络请求,延迟实测32ms。它甚至在marked的options里加了{ breaks: true, gfm: true },确保换行符和表格能正确渲染。 -
Qwen3.6-Plus 的方案是:前后端分离渲染。前端发送
POST /api/v1/render,后端用markdown-it库解析,再用sanitize-html过滤。这带来了额外的网络延迟(实测首屏180ms),且sanitize-html配置复杂,它生成的代码里漏掉了allowedTags: ['p', 'br', 'h1', 'h2'],导致<script>标签未被过滤,存在XSS漏洞。我让它补上,它又把allowedAttributes设成了空数组,结果连<a href="">的href属性都被干掉了,链接全部失效。 -
Claude Opus 4.6 的方案最“严谨”:它坚持用
rehype-sanitize+remark-gfm+unified这套组合,号称“AST级安全”。代码量是GLM-5.1的3倍,但渲染延迟高达210ms。更致命的是,它生成的unifiedpipeline里,remark-gfm插件版本写错了(^4.0.0),导致yarn install失败。我指出后,它花了两轮才修正为^3.0.0。
结论 :在“快、稳、准”这个铁三角上,GLM-5.1完胜。它没有被“最安全”“最标准”的教条绑架,而是选择了 在满足安全底线的前提下,追求极致的用户体验 。它知道,对于一个内部文档工具,32ms的延迟和210ms的延迟,对开发者专注力的损耗是数量级的差异。这背后,是对“开发者体验(DX)”的深刻理解。
4.4 集成与部署:谁让
docker-compose up -d
真正一键成功?
最后一关,是把所有模块缝合成一个可运行的整体。我要求模型生成
docker-compose.yml
,并指导我完成构建、启动、验证全流程。
-
GLM-5.1 生成的
docker-compose.yml是这样的:version: '3.8' services: nginx: image: nginx:alpine ports: ["80:80"] volumes: ["./frontend/dist:/usr/share/nginx/html"] depends_on: ["backend"] backend: build: context: ./backend dockerfile: Dockerfile environment: - DATABASE_URL=sqlite:///./app.db volumes: ["./backend/app.db:/app/app.db"] healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 3s retries: 3 db: image: sqlite3:latest # 它甚至虚构了一个sqlite3镜像?不,这是个错误!等等,
sqlite3:latest?SQLite是嵌入式数据库,没有独立的server镜像!这是一个低级错误。我立刻指出,它秒回:“抱歉,SQLite无需独立db服务,应移除db服务,将volumes挂载到backend服务即可。” 修正后的版本,简洁有力。docker-compose up -d后,curl http://localhost/health返回200,curl http://localhost/docs/返回401(未登录),一切符合预期。 -
Qwen3.6-Plus 生成的
docker-compose.yml里,backend服务的build.context写成了./backend/src,而实际代码在./backend/。volumes挂载路径也错了,./app.db映射到了/app/app.db,但DATABASE_URL里写的是sqlite:///./app.db,路径不一致,导致容器内找不到数据库文件。我让它修正,它改了两轮才对齐。 -
Claude Opus 4.6 直接放弃了Docker Compose,给我生成了一套Kubernetes的
deployment.yaml和service.yaml,并附上kubectl apply -f infra/k8s/的命令。当我提醒“我们只要Docker Compose”时,它花了整整45秒才生成一个勉强能用的版本,但nginx服务的volumes路径写成了Windows风格的C:\frontend\dist,在Mac上直接报错。
结论
:GLM-5.1虽有小瑕疵(虚构镜像),但
纠错速度快、理解修正意图准、最终交付物可靠
。它把Docker Compose当作一个“让事情简单发生”的工具,而不是炫技的舞台。当
docker-compose up -d
执行完毕,浏览器里弹出登录页的那一刻,我知道,这个工具,真的可以放进我的日常开发流里了。
5. 常见问题与排查技巧实录:那些藏在日志里的真相
5.1 “Connection refused”不是网络问题,是Uvicorn没起来
这是三款模型在生成
main.py
时,最常犯的错误:忘记在
if __name__ == "__main__":
块里加
uvicorn.run()
,或者参数写错。比如,GLM-5.1第一次生成的代码是:
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=8000)
看起来没问题,但
host="0.0.0.0"
在Docker容器内是必须的,否则外部无法访问。然而,当我在本地
python backend/main.py
运行时,却报
Connection refused
。为什么?
提示:
uvicorn.run()默认是loop="auto",在Mac M2上,auto会选asyncio,但某些情况下会卡住。解决方案是显式指定loop="asyncio",并加上reload=True(开发时):if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True, loop="asyncio")
我让三款模型都修正,Qwen3.6-Plus加了
reload=True
但漏了
loop
,Claude Opus 4.6则固执地认为
loop="auto"
足够,不肯改。只有GLM-5.1在第二轮就给出了完整、正确的启动命令。
5.2 “Module not found”不是包没装,是Vite的
resolve.alias
没配
前端
import { api } from '@/lib/api'
报错,
@/
路径找不到。Qwen3.6-Plus生成的
vite.config.ts
里,
resolve.alias
只配了
@: path.resolve(__dirname, 'src')
,但没配
@/lib
。它以为
@
配了,下面的路径自然就通了。实际上,Vite的
alias
是精确匹配的,
@/lib
必须单独配。
注意:正确的做法是配一个通配符:
resolve: { alias: { '@': path.resolve(__dirname, 'src'), '@/*': path.resolve(__dirname, 'src/*') } }这样
@/lib/api才能被正确解析为src/lib/api.ts。这个细节,90%的教程都不会提,但它是Vite项目里最常踩的坑之一。
5.3 “Docker build failed”不是代码错,是
.dockerignore
惹的祸
Qwen3.6-Plus生成的
.dockerignore
文件里,有一行
**/node_modules
。这看起来很合理,但问题在于,
node_modules
在
frontend/
目录下,而
Dockerfile
的
COPY . .
是从项目根目录执行的。
**/node_modules
会把
frontend/node_modules
忽略掉,但
backend/
目录下的
pyproject.toml
和
poetry.lock
文件,却因为
**/node_modules
这条规则,被
poetry install
命令误判为“应该忽略”,导致
poetry install
失败。
实操心得:
.dockerignore的规则顺序很重要。应该把node_modules放在最前面,后面再跟**/node_modules,或者直接写frontend/node_modules和backend/.venv,避免全局通配符的副作用。我最终的.dockerignore是:node_modules frontend/node_modules backend/.venv .git .DS_Store
5.4 “500 Internal Server Error”不是后端崩了,是前端
fetch
没带
credentials
当登录接口返回200,但后续所有API都返回401时,90%的人会去查后端JWT验证逻辑。其实,问题往往在前端。GLM-5.1生成的
api.ts
里,
fetch
调用是这样的:
export const login = (data: LoginData) =>
fetch('/api/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
它漏掉了
credentials: 'include'
。这意味着,后端
set-cookie
的
sessionid
,浏览器不会自动带上,导致后续请求没有凭证。
排查技巧:打开Chrome DevTools的Network面板,点开一个失败的API请求,看Headers里的
Request Headers,如果没有Cookie: sessionid=xxx,那就100%是前端没配credentials。这个坑,我踩过不下十次,现在看到401,第一反应就是查fetch配置。
5.5 “页面空白”不是React没渲染,是Vite的
base
路径错了
vite.config.ts
里
base: '/'
,但Nginx配置的location是
/docs/
。结果,
index.html
里引用的
/assets/index.xxxxx.js
,404了。浏览器控制台一片红。
解决方案:
vite.config.ts里必须配base: '/docs/',并且Nginx的location /docs/里,要加try_files $uri $uri/ /docs/index.html;,把所有前端路由都fallback到index.html。这个配置,GLM-5
471

被折叠的 条评论
为什么被折叠?



