原文链接:www.developerway.com/posts/debug…
人工智能真的能调试复杂的React/Next.js问题吗?我用三个真实的bug进行了测试,亲自调查了根本原因并记录了结果。
大家对人工智能的体验如何?爱恨交织?过去几个月我一直在工作中大量使用它,所以想开始分享一些技巧,教你如何在不崩溃(也不丢失技能)的情况下使用它。
但再写一篇"十大提示工程技巧"的文章实在枯燥乏味。不如来当侦探,调查些诡异现象?过程中我们将检验AI的调试修复能力,深度探索者还能顺便了解React和Next.js的底层机制。
为此我特意构建了一个存在缺陷的应用程序。本文将向大型语言模型抛出这些问题,附带"修复"指令观察其反应。随后我将手动复现相同故障,展示实际修复方案,并验证AI是否准确修复了故障及其根源。同时分享我的调试流程与思维脉络,供各位跟进实践。
请准备好你的调试工具!
项目与工具配置
研究项目已发布在GitHub上,欢迎跟进或尝试自建AI工具。这是一个基于React/Next.js的应用,包含多个页面、若干API接口,采用TanStack进行数据抓取,并使用Zod进行模式验证。几乎像一个真正的应用程序!
打开项目后,您将看到精美的仪表盘页面。点击订单ID即可查看更详细的订单信息。点击商品ID可查看商品详情。点击右上角用户名会展开下拉菜单,其中"用户资料"选项将跳转至用户信息全览页面。
至少理论上应如此。但该路径存在多处根本性缺陷,这正是我们今日要修复的内容!
在使用LLM进行排查时,我会先假装对技术栈一无所知,尝试"盲查"。随后再凭借实际掌握的技术栈知识进行手动排查,验证修复方案的有效性。
若您从未接触过Next.js、TanStack和/或Zod,建议先自行设计提示词并手动调查,这样才能获得最大乐趣。之后请分享您的调查结果,我非常好奇您的发现!
我将使用个人订阅版Claude Pro附带的Opus 4.5进行测试,感觉它完全够用。若你拥有其他工具或模型并想进行对比,也请分享结果!
最后,在本次调查过程中,我想弄清楚以下几点:
- 大型语言模型是否解决了问题?
- 大型语言模型是否正确识别了问题的根本原因?
- 大型语言模型是否正确解决了问题的根本原因?
让我们开始吧!
调查1:用户页面故障
通过 npm run dev 启动项目,然后通过下拉菜单或直接访问 /users/1 路由进入用户个人资料页面。您将看到显示"出现错误"的页面,其中包含具体的错误信息:
而在浏览器控制台中,你会看到这些错误以更详细的格式呈现:
服务器日志完全正常,因此错误显然出现在前端。
LLM尝试
让我们将所有信息输入LLM,看看它能给出什么结果。
[!NOTE] LLM提示:当我访问/users/1路由时,页面显示"出错"。浏览器控制台显示以下错误[粘贴完整错误堆栈]。服务器日志正常。你能修复吗?
LLM 开始处理,报告完成后我刷新页面——哇哦!页面不再报错!我可以将问题"LLM 是否解决了问题?"标记为成功。
看来这会是一篇很短的文章 😅
Opus对故障的分析如下:
[!NOTE] 大型语言模型调查 问题在于getUser服务返回的用户对象缺少src/lib/schemas.ts:238-255中UserSchema要求的电话和地址字段。当Zod在客户端验证API响应时,因这些必填字段未定义而抛出错误。
我已在用户数据中补全缺失字段。/users/1页面现应能正常加载。
即:发送至客户端的数据存在字段缺失问题。而修复方案是在数据文件中直接添加了这些缺失字段:
// FILE: src/services/user.ts
export const getUser = async () => {
const user = {
id: 1,
name: 'John Doe',
email: 'john.doe@example.com',
// LLM added this missing field:
phone: '+1 (555) 123-4567',
// And this one:
address: {
street: '123 Main Street',
city: 'San Francisco',
state: 'CA',
postalCode: '94102',
country: 'United States',
},
... // bunch of other fields
};
if (!user) {
throw new Error('User not found');
}
return user;
};
| 用户页面故障 | |
|---|---|
| LLM是否修复了问题? | ✅ |
| LLM是否正确识别了问题根源? | |
| LLM是否正确修复了问题根源? |
现在让我们验证修复方案是否正确。
人类概述
为确认修复方案是否正确,我需要真正理解问题根源。若想百分百确定,必须由我亲自查证。
让我们重新审视问题。浏览器控制台显示如下内容:
Uncaught ZodError: [ { "expected": "number", ...
at fetchUser (useUser.ts:11:21)
at UserPage (webpack-internal:///(app-pages-browser)/./src/components/pages/UserPage.tsx:161:11)
UserPage组件中调用的fetchUser函数里某个与Zod相关的操作。
在UserPage.tsx中,我看到这段代码:
// FILE: src/components/pages/UserPage.tsx
const { data: user, isLoading, error } = useUser();
其中 useUser 是 TanStack 查询:
// FILE: src/queries/useUser.ts
export function useUser() {
return useQuery({
queryKey: ['user'],
queryFn: fetchUser,
});
}
其中fetchUser是一个用于获取用户信息的函数:
async function fetchUser() {
const response = await fetch('/api/user');
const data = await response.json();
// Validate response with Zod
return UserSchema.parse(data);
}
该方案确实使用Zod来解析和验证来自REST端点的数据。若您从未接触过Zod,其核心功能正是:根据模式解析并验证原始数据,并在数据错误时抛出异常。
这正是当前问题的症结所在:UserSchema验证失败。说明从/api/user接口获取的data存在异常。在网络面板中,我能看到该接口及其输出内容:
因此该接口运行完全正常,后端日志也证实了这一点。
唯一合理的解释是:返回的数据虽然看似完整,却不符合我们的数据结构规范。而当我实际查看浏览器控制台的错误输出时:
我将具体查看哪些字段违反了模式以及违反方式:
phone字段预期为string类型,但接收到的值为undefined;address字段预期为object类型,但接收到的值为undefined。
以下是模式定义:
export const UserSchema = z.object({
... // other fields
phone: z.string(),
address: z.object({
street: z.string(),
city: z.string(),
state: z.string(),
postalCode: z.string(),
country: z.string(),
}),
... // the rest of the fields
});
确实,电话和地址必须填写,否则就会显示为 .optional()。
这正是LLM告诉我的!真是个聪明的机器人🤖。我也会把"是否找到了根本原因"部分标记为成功:
| 用户页面故障 | |
|---|---|
| LLM是否修复了问题? | ✅ |
| LLM是否正确识别了问题根源? | ✅ |
| LLM是否正确修复了问题根源? |
现在来解决这个问题。
有两种解决方法。要么确保数据始终存在——这正是LLM通过在模拟数据中添加phone和address信息所做的工作。
要么将这些字段的模式设为可选,如下所示:
export const UserSchema = z.object({
... // other fields
phone: z.string().optional(),
address: z.object({
... // same fields
}).optional(),
... // the rest of the fields
});
哪种修复方式正确?
这其实取决于具体情境。如果这是单元测试的模拟对象,那么LLM修复模拟对象的做法或许正确。放宽模式限制以通过单元测试,这恰恰违背了测试的初衷。当然,具体情况还需视测试本身而定。
在我们的场景中,模拟数据显然是后端真实数据的替代品,而后端数据实现很可能作为下一步工作。在实际应用中,用户档案中的phone和address字段极不可能成为必填项。
因此"正确"的修复方案是放宽模式:将这些字段设为可选或允许空值(具体取决于后端及仓库的数据规范)。
有趣的是,在拥有真实后端的"实际"应用中,LLM会立即放宽模式限制:它倾向于灵活处理规则,将可选项和any作为所有问题的默认解决方案。而我在此使用模拟数据的做法反而让它产生了困惑。
当我撤销LLM的修复方案,将模式中的字段设为可选,并刷新页面时,用户体验又回到了故障状态。
控制台显示的内容相同:
installHook.js:1 TypeError: Cannot read properties of undefined (reading 'street')
at UserPage (UserPage.tsx:177:58)
... // the rest of the error
鉴于我们刚刚放宽了模式限制,这里的情况应该很明显:我们试图访问address值的street属性,而该属性现在是可选的。我们只需根据条件渲染这些字段即可:
user.address && <div ... >// bunch of stuff that render address
LLM实际上几乎无需提示就能完美修复此问题,干净利落。
但考虑到它并未解决根本原因,我只给半分而非满分。
| 用户页面故障 | |
|---|---|
| LLM是否修复了问题? | ✅ |
| LLM是否正确识别了问题根源? | ✅ |
| LLM是否正确修复了问题根源? | 🥑 |
调查2:双加载器问题
我们修复了最明显的错误,但该页面仍需进一步排查。
请加载根页面 /,刷新以清除所有缓存,将网络速度调至至少"快速4G"档位,通过右上角下拉菜单进入个人资料页。网络越慢,问题越明显:页面加载过程中会出现两种不同的加载"骨架"。
但若仅刷新用户页面,无论网络设置如何,只会显示第二个加载框架。
这实在奇怪。日志里也找不到任何线索🤔 看看LLM能否解决这个问题。
LLM尝试
让我们向LLM输入问题描述中精确描述的现象。
[!NOTE] LLM提示:当我在根页面导航至用户页面时,会看到两个不同的加载骷髅架。但若直接刷新用户页面,却只显示一个。为何如此?如何修复?
事情开始变得有趣了。在之前的排查中,Claude始终只是向模拟界面添加数据。 在这种情况下,每次我尝试相同的提示时,它都会给出不同的根本原因和不同的修复方案。
有时,它认为根本原因是Next.js首先显示app文件夹中的根级loading.tsx文件,然后再显示app/users/[id]文件夹中的路由级loading.tsx文件。
有时它认为Next.js会先加载app/users/[id]文件夹中的路由级loading.tsx文件,再加载UserPage.tsx中的<UserPageSkeleton />组件。
针对解决方案,有时建议删除app/users/[id]文件夹内的loading.tsx文件;有时则将根目录的loading.tsx与page.tsx一同从app文件夹移至app/(dashboard)文件夹。
顺便说一句,这些方法都不管用。
当被问及这个问题时,有时它会说解决方案是在服务器端获取数据,并重构应用程序以使用 useSuspenseQuery 代替 useQuery。
这确实有效。
有时它会直接给出 useSuspenseQuery 的解决方案,并附带不同版本的解释。
多数情况下,它会解释刷新时不会出现双重加载,因为用户页面采用服务器端渲染,因此其中的loading.tsx不会被触发。
因此我给它打个✅标记,认可它最终解决了问题(尽管并非首次尝试就成功)。但给它打个❌标记,因为它显然未能找出真正根源——这里存在太多相互矛盾的根本原因。
现在让我们手动调查上述哪种才是真正的根本原因,以及useSuspenseQuery是否是正确的修复方案。
| 用户页面故障 | 双重骨架问题 | |
|---|---|---|
| LLM是否修复了问题? | ✅ | ✅ |
| LLM是否正确识别了问题根源? | ✅ | ❌ |
| LLM是否正确修复了问题根源? | 🥑 |
顺便说一句,如果你正在尝试自己编写提示词,记得把大型语言模型对当前情况的详细解释保存下来。这样你就能将这些解释与人工排查结果和实际根本原因进行对比。
人类概述
首先,让我们厘清导航过程中实际显示的加载状态。正如大型语言模型正确指出的,至少存在三种状态:
app文件夹内的"根"加载状态loading.tsxapp/users/[id]文件夹内的"路由"加载状态loading.tsxUserPage.tsx文件中以<UserPageSkeleton />形式存在的"客户端"骨架,该骨架在isLoading为true时条件渲染。
这些组件外观相似但实现方式不同,仅凭视觉效果难以准确区分。遇到这种情况时,我通常会用经典的"红边框"方法😅——任何在Tailwind流行前调试过CSS问题的人都懂。具体来说,给"根"骨架添加红边框,"路由"部分加绿边框,"客户端"部分加黑边框,就能立刻分辨出它们的层级关系。
在此情况下,先是绿色,随后变为黑色。我们看到"route"层级的loading.tsx骨架闪现,接着是客户端的<UserPageSkeleton />。根层级的骨架并未出现在画面中,克劳德对此部分的判断是错误的。
第二个问题——为何刷新页面时仅显示"客户端"骨架?
克劳德认为这是因为页面采用服务器端渲染,导致loading.tsx未被触发。
这个说法同样不正确😉 实际情况是:该模式在page.tsx文件中为页面级服务器组件创建了Suspense边界。这与服务器端渲染无关——Next.js中所有内容都采用服务器端渲染,无论是否使用服务器组件。
当创建Suspense边界时,Next.js会先渲染loading.tsx的内容,直到异步的page.tsx中的promise解析完毕,随后才将其替换为实际内容。我们看不到"绿色"加载提示的唯一原因,是page.tsx中没有执行任何异步操作。因此无需等待,promise几乎立即解析完成。
尝试添加如下延迟操作,"绿色"加载效果便会如预期般显示:
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default async function Page({ params }) {
const { id } = await params;
await delay(10000); // Simulate some async operation
return <UserPage userId={id} />;
}
如果服务器组件和服务器端渲染的区别让你此刻大脑炸裂,别担心,这种情况很常见。这些话题确实复杂又容易让人困惑。我写过几篇深度解析的文章或许能帮到你——如果你愿意深入钻研的话:React开发者必读:服务器端渲染深度解析和React服务器组件:真的能提升性能吗?
在此提出第三个疑问:既然 Promise 解析速度极快,刷新时看不到"绿色"加载条,为何从仪表盘导航时却能看到?🤔
最简便的解决方法是记录导航性能剖析。首先通过 npm run build 构建项目,再用 npm run start 启动。请始终在生产环境构建中记录性能数据,除非有特殊原因需要查看开发环境数据。
随后在Chrome开发者工具中:
- 打开性能选项卡
- 点击左上角"记录"按钮
- 通过配置文件下拉菜单中的链接跳转至用户页面
此时记录内容应类似如下所示:
将鼠标悬停在截图区域,可见蓝色
1(localhost)条出现前显示的是仪表盘页面。蓝色条存在期间,可见"绿色"路由正在加载。当其结束且灰色user(localhost)条开始时,"黑色"客户端骨架便会显现。
悬停在这些条形上可查看具体信息:蓝色条形显示浏览器正在下载用户页面的RSC有效负载,灰色条形则显示对/api/user API端点的请求——该请求返回用户数据。
此RSC有效负载是由页面服务器组件生成的布局,React需要它来构建页面的实际HTML。当直接访问该页面(通过刷新)时,布局会随首次HTML响应返回。而通过SPA过渡(链接导航)访问时,布局会作为独立文件经网络下载。因此在网速受限时,下载耗时较长,Suspense边界机制便会启动,此时始终可见"绿色"加载骨架。
这实际上引发了另一个问题:为什么这里没有预取?我以为Next.js以默认情况下激进地缓存和预取所有内容而闻名。是什么让这个用例与众不同?🤔
查阅文档后得到部分答案:预取在动态路由上仅部分生效。动态路由是指在运行时生成的路由,即路径中的[id]部分。虽然"部分"具体指什么尚不明确,但我的推测是它不包含页面的RSC(响应式组件加载)。否则该组件本应被预取,我们也就不会看到"绿色"的加载骷髅头了。
为验证这个推测,我需要强制对导航到页面的Link组件进行预取:
// FILE: src/components/dashboard/UserProfileDropdown.tsx
<Link href={`/users/${user.id}`} prefetch={true}>
<User />
Profile
</Link>
如果我执行 npm run build 构建项目,再通过 npm run start 启动项目,然后从仪表盘导航至用户页面,"绿色"加载状态就会消失,只剩下"黑色"客户端加载界面。预加载成功了!
但这并非理想解决方案。您不可能手动为每个链接组件添加预加载逻辑。这只是验证应用程序中数据加载与预加载机制运作原理的测试手段。
该原理的核心在于:通过loading.tsx实现的路由级"绿色"加载状态,会持续显示直至该路由的页面组件保持悬挂状态。
用 useSuspenseQuery 替代 useQuery 恰恰实现了这个效果——在查询结果返回前保持组件"悬挂"状态。这正是 Claude 提出的修复方案奏效的原因。
但这真的是正确的修复方案吗?
如果你在文档中进一步阅读关于 useSuspenseQuery 的内容,你会看到这样一段说明:
"虽然我们并不一定推荐这样做,但只要你始终预先加载所有查询,就可以用 useSuspenseQuery 替代 [useQuery]。" 接着又提到:"若使用 useSuspenseQuery 时忘记预取查询,后果将取决于所用框架。某些情况下[...]会出现标记加载不匹配问题,因为服务器端和客户端尝试渲染的内容不同。"
简而言之,具体后果取决于框架特性。事实上,若采用Claude建议的useSuspenseQuery修复方案,刷新用户页面时Chrome控制台将出现报错。
因此我将对此问题给Claude最终的否决❌:这个修复方案会引发后续的另一个问题。
| 用户页面故障 | 双重骨架问题 | |
|---|---|---|
| LLM是否修复了问题? | ✅ | ✅ |
| LLM是否正确识别了问题根源? | ✅ | ❌ |
| LLM是否正确修复了问题根源? | 🥑 | ❌ |
那么,实际的修复方案是什么呢?
根据我们愿意投入重构的时间,这里有三种可行的方案。
最简单、最直接但最"权宜"的修复方式是复用"黑色"骨架替换"绿色"骨架(或反之)。这样虽然仍会依次显示两个骨架,但由于外观完全相同,用户不会察觉差异。
另一种方案是重构用户页面,仅隐藏骨架下的动态数据。具体实现是:在加载覆盖整个页面的骨架后,立即显示部分文本内容,让用户产生页面正在逐步加载的错觉。
或者,考虑到我们已经使用Next.js,最先进且可能最"正确"的解决方案是在服务器组件中获取用户数据。这样就能完全避免使用"黑"客户端加载器。
若想进一步锻炼AI技能,建议先尝试通过提示实现该功能,再参考下文解决方案。
服务器组件数据获取方案如下:
我们已采用TanStack进行数据获取,此时重构整个仓库弃用该库并不明智。但TanStack作为高级库,支持服务器端动态加载,包括通过Next.js应用路由器调用。
仓库已正确配置支持该功能,我们只需启用即可。
// FILE: src/app/users/[id]/page.tsx
export default async function Page({ params }) {
const { id } = await params;
// create TanStack query client
const queryClient = new QueryClient();
// trigger and await for prefetch
await queryClient.prefetchQuery({
queryKey: ['user'],
queryFn: async () => {
// copy-paste the same logic that the API endpoint does
await new Promise((resolve) => setTimeout(resolve, DELAYS.USER));
const user = await getUser();
// Return the user data
return user;
},
});
return (
<!-- Pass the dehydrated state to HydrationBoundary component -->
<HydrationBoundary state={dehydrate(queryClient)}>
<UserPage userId={id} />
</HydrationBoundary>
);
}
只需三个步骤:初始化queryClient,触发并等待预取操作(以立即接收数据),然后将该客户端传递给<HydrationBoundary />组件。
这样,无论从仪表盘导航还是刷新页面时,您都只会看到"绿色"加载状态。若在Chrome中打开网络面板,您会发现客户端对用户信息的请求已不复存在。
调查3:奇怪的重定向错误
最终调查。
访问/users网址:你会看到错误页面闪现一瞬,随后重定向至/users/1。此重定向符合预期。此处的意图是重定向非管理员用户:他们应能查看自身信息,但不应具备管理用户权限。
然而出现的错误并非预期行为,必须消除。浏览器控制台显示相同报错:渲染的钩子数量超过上次渲染。
让我们再试试LLM吧!
LLM尝试
再次使用相同提示:解释发生的情况以及我在控制台中看到的内容。
[!注意] LLM提示 当我导航至/users页面时,看到"出错"页面,控制台中显示以下错误:[错误代码]。调查错误原因及修复方法。
这次尝试彻底失败。
LLM 陷入无限循环。在整个排查过程中,它曾充满信心地尝试通过以下方式解决问题:
- 将重定向移至
useEffect(不,谢谢,你知道这会对性能造成什么影响吗?)。 - 将重定向移至
next.config.ts文件(行不通,我很快需要在那里访问数据库)。 - 将重定向移至
proxy.ts/middleware.ts(不可维护,我很快需要在该处添加大量带逻辑的重定向,且不愿在此重构路由)。 - 折腾
QueryProvider并将其移入state(未解决)。 - 声称应返回重定向(不行)。
- 声称Next.js版本已过时(未解决)。
- 在含重定向的
page.tsx旁添加loading.tsx(未解决)。
每种方案都附有极具说服力且详尽的解释,说明为何该方案能解决问题。
因此这次排查真是令人抓耳挠腮的难题。
| 用户页面故障 | 双重骨架问题 | 奇怪的重定向错误 | |
|---|---|---|---|
| LLM是否修复了问题? | ✅ | ✅ | ❌ |
| LLM是否正确识别了问题根源? | ✅ | ❌ | ❌ |
| LLM是否正确修复了问题根源? | 🥑 | ❌ | ❌ |
人类视角
在多数修复尝试中,LLM都会提到抛出错误是redirect的预期行为。这点颇有意思,值得花点时间用老派方式搜索资料和阅读文档。
事实上,Next.js文档确实提到redirect 会抛出错误,因此在使用*try/catch* 语句时应在*try* 块外调用。因此我推测,Next.js 可能在底层捕获该错误,将其与"常规"错误分离处理,随后执行实际的重定向操作。
不过这种解释对当前问题略有误导:我在界面和控制台看到的错误提示指向钩子(hooks)而非重定向(redirect)。
经过更多搜索,我在Next.js仓库发现了这个GitHub问题:使用App Router时出现"渲染的钩子比上次渲染更多"的情况。这正是我们的使用场景。
浏览评论时,有人提到他们遇到相同问题,通过添加loading.tsx解决了。这大概就是为什么LLM半数情况下会尝试在页面旁添加加载状态来修复问题。可惜在我们这里无效。
稍往下又看到另一条评论,声称我们都是蠢货,只要正确使用redirect就行。复核代码后发现我们这边完全没问题。
另一条评论提到,除了彻底删除loading.tsx文件外,其他方法都无效。我好奇这样做会怎样?
结果竟真的修复了错误!🤯 虽然删除所有loading.tsx文件绝非良策——毕竟缺少它用户将无法快速跳转页面——但问题确实解决了。
继续阅读那个GitHub讨论串,但也就这样了。再无其他建议,又是一条死胡同。😭
好吧,换个角度思考。如果看到这个错误是正常现象,网络上早就充斥着投诉,问题也早就修复了。同样地,如果这个错误非常常见且明显,LLM早就修复它了。
所以问题出在我们应用的代码里,而且相当独特。
又到了该用老派无聊的调试方法的时候了——也就是"逐个杀死组件直到错误消失"。基本上我得把layout/page/loading结构里的所有组件和元素都拆掉,只留下最精简的Next.js框架。
清空用户页面的所有内容毫无作用,错误依然存在。移除app/users/layout.tsx中除子节点外的所有内容也无济于事。但当从根布局中移除<SendAnalyticsData />组件时,终于见效了!🎉 错误消失了!
为确认是否已定位根本原因,我将根布局中除<SendAnalyticsData />外的所有内容恢复原状——结果错误又回来了🤦🏼♀️。所幸我们已是专业调试者,通过代码库不难发现问题所在:第二个layout.tsx文件中的UserLayout组件也嵌入了<SendAnalyticsData />组件。
现实中调试这个组件耗费了我数天时间,它实际上是由不同组件和函数调用组成的复杂组合 😅
好吧,问题出在<SendAnalyticsData />组件内部。该组件本身相当基础:
export function SendAnalyticsData({ value }: { value: string }) {
useEffect(() => {
sendAnalyticsData(value);
}, [value]);
return <></>;
}
字面意义上只是一个微小的useEffect,它调用了一个函数。但这个函数是...一个服务器操作:
'use server';
export async function sendAnalyticsData(name: string) {
console.log('analytics data sent', name);
}
这不可能是真的吧?调用一个完全合法的Action居然会引发这么多麻烦?🤔
验证起来很简单。我只需把所有内容恢复到原始状态,然后注掉那个Action。
错误消失了!🤯现在我知道该搜索什么了(即"Suspense, Next.js, Actions, Redirect, Error"组合),零星发现几处提及相同问题。终于找到真正根源!看来服务器端重定向与Suspense包裹的进行中动作组合,让Next.js陷入混乱。
具体混淆机制已不重要。当然深入搜索仍能找到线索,但留作作业吧。
解决方案其实很明确:只需移除该Action或将其重构为简单的REST接口,错误便能消除。在我看来,这又为不使用Actions提供了理由。若你错过了之前的论据,这里有篇深度解析:React服务器端Actions能否用于数据获取
但本次排查到此为止。
TL;DR
那么,结论是什么?AI能取代经验丰富的开发者进行调试吗?
当然不能。AI在模式识别方面非常出色,确实能发挥巨大作用。它在处理标准问题时表现尤为出色:模式验证失误、遗漏的空值检查、常见运行时错误——这些你都亲眼见识过。就我个人而言,如今调试工作几乎都从大型语言模型开始。直接输入上述提示:"问题描述在此,日志在此,请修复"。半数情况下它确实能解决问题。
但即便修复成功,我仍会逐步复盘以确认是否真正解决根本问题。若修复失败,我几乎不会继续与LLM迭代——除非要求它在代码库中查找特定内容。正如你所见,这种情况下它半数回复都是充满自信的幻觉。
当问题需要真正理解系统行为背后的原理,或预判其未来应有的运行方式(尤其从未来或用户视角出发)时,AI就会彻底崩溃——姑且假设它还有这种能力吧。
关键技能并非掌握更精妙的问题提示技巧,而在于懂得何时停止提问、开始独立思考。
以下是本次排查的最终结果。
| 用户页面故障 | 双重骨架问题 | 奇怪的重定向错误 | |
|---|---|---|---|
| LLM是否修复了问题? | ✅ | ✅ | ❌ |
| LLM是否正确识别了问题根源? | ✅ | ❌ | ❌ |
| LLM是否正确修复了问题根源? | 🥑 | ❌ | ❌ |
如果你能不偷看我的解释就调试解决所有问题,说明你做得很好。如果你偷看了并学到新知识,现在能运用自如,那也很好。
真正该担心的时候,是当人工智能从第一次尝试起就能在每个步骤都完美达成目标😅