前言
前几天迁移到了 Astro,当时明言将继续往 Next.js 迁移。在 gpt-5.5 (xhigh) 的帮助下,目前程序已日臻完善,就是 UI 审美——毫无疑问 GPT 连抄也抄不会。
迁移的部分
Astro 迁移到 Next.js 最大的好处就是 App Router 了,像 Astro 的 SSG 是不够的,增量更新页面能节省全量加载 HTML 的带宽,配合动画的话,UX 是能肉眼可见提升的。
Pagefind 部分实现了引入 @pagefind/component-ui 来更好地与程序和 UI 集成,顺便应用了其默认的 CJK 分词优化——其实应该引入 Jieba 的,但考虑到内容体量,实施后者实无必要。
布局使用了 Nested Layout 以增加可操控性——虽然 GPT 把 style 写得稀烂。
做好了和 Netlify 耦合的准备:做了针对某些路由运行 Edge 函数的工作,首期实验当然是引入 Cloudflare Turnstile 和 GeoIP 禁止。
const COOKIE_NAME = 'access_cookie';
const TTL = 60 * 60 * 12;
async function sign(payload: string, secret: string) {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign'],
);
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));
return btoa(String.fromCharCode(...new Uint8Array(sig)));
}
async function verifyCookie(cookie: string | undefined, secret: string, route: string) {
if (!cookie) return false;
const [payload, sig] = cookie.split('.');
if (!payload || !sig) return false;
const expected = await sign(payload, secret);
if (expected !== sig) return false;
const data = JSON.parse(atob(payload));
return data.route === route && data.exp > Date.now() / 1000;
}
async function verifyTurnstile(token: string, secret: string) {
const res = await fetch('https://api.example.com/challenges/turnstile/v0/siteverify', {
method: 'POST',
body: new URLSearchParams({ secret, response: token }),
});
const data = await res.json();
return data.success;
}
function createCookie(route: string, secret: string) {
const payload = {
route,
iat: Date.now() / 1000,
exp: Date.now() / 1000 + TTL,
};
const encoded = btoa(JSON.stringify(payload));
const sig = sign(encoded, secret);
return `${encoded}.${sig}`;
}
export default async function handler(req: Request) {
const url = new URL(req.url);
const route = url.pathname;
const SESSION_SECRET = process.env.SESSION_SECRET!;
const TURNSTILE_SECRET = process.env.TURNSTILE_SECRET!;
if (await verifyCookie(req.headers.get('cookie')?.match(/access_cookie=([^;]+)/)?.[1], SESSION_SECRET, route)) {
return new Response(null, { status: 200 });
}
if (req.method === 'GET') {
return new Response(`<form method="POST">Turnstile + Submit</form>`, {
headers: { 'content-type': 'text/html' },
});
}
const form = await req.formData();
const token = form.get('cf-turnstile-response')?.toString();
if (!token || !(await verifyTurnstile(token, TURNSTILE_SECRET))) {
return new Response('Forbidden', { status: 403 });
}
const cookie = await createCookie(route, SESSION_SECRET);
return new Response(null, {
status: 302,
headers: {
location: route,
'set-cookie': `access_cookie=${cookie}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${TTL}`,
},
});
}
评价
除了前面对 UI 感知能力的指指点点外,gpt-5.5 (xhigh) 还有过于严格边界的倾向,会写非常多的校验和检查脚本,且插入主工作流。这造成了后期一个任务有将近 75% 的时间在跑测试之类浪费时间和 Token 的情况,以至于连 ChatGPT 都看不下去了。
- 一句话总结
这次 Codex 花费约 $164,其中 57% 是 cached input 再次膨胀导致的“重复上下文税”,不是模型变贵,而是你“喂太多重复背景”。
虽然说了 9 句废话。
不过这个特性与场景有关,写 Python 就没这么多事儿,注意经常观察即可。
"scripts": {
"prepare:vendor": "node scripts/prepare-vendor-assets.mjs",
"access:manifest": "node --experimental-strip-types scripts/manage-access-policy.ts generate",
"dev": "pnpm prepare:vendor && next dev",
"build": "node scripts/prepare-vendor-assets.mjs && node --experimental-strip-types scripts/manage-access-policy.ts generate && next build && node scripts/normalize-static-output.mjs out && pagefind --site out",
"verify:out": "pnpm check:content && pnpm images:check && pnpm check:types && pnpm check:deploy && pnpm check:access-policy && pnpm test:edge-access && pnpm check:output && pnpm check:links && pnpm check:image-output && pnpm check:search-index && pnpm check:interactions && pnpm check:seo && pnpm check:final && pnpm report:build",
"preview": "node scripts/serve-static.mjs out",
"check:types": "next typegen && tsc --noEmit",
"check:content": "node --experimental-strip-types scripts/check-content.ts",
"check:access-policy": "node --experimental-strip-types scripts/manage-access-policy.ts check",
"test:edge-access": "node --experimental-strip-types scripts/test-edge-access.ts",
"images:check": "node --experimental-strip-types scripts/manage-image-metadata.ts check",
"images:refresh": "node --experimental-strip-types scripts/manage-image-metadata.ts refresh",
"check:output": "node --experimental-strip-types scripts/check-static-output.mjs",
"check:links": "node scripts/check-html-links.mjs out",
"check:image-output": "node --experimental-strip-types scripts/check-image-output.mjs out",
"check:search-index": "node --experimental-strip-types scripts/check-search-index.ts out",
"check:interactions": "node --experimental-strip-types scripts/check-client-interactions.mjs out",
"check:seo": "node --experimental-strip-types scripts/check-seo-output.mjs out",
"check:deploy": "node scripts/check-deployment-config.mjs",
"check:final": "node scripts/check-final-acceptance.mjs out",
"report:build": "node scripts/report-build-size.mjs out",
"report:build:json": "node scripts/report-build-size.mjs out --json"
}
这还是我强令废除诸如 playwright 之类的抽象检查和冗余脚本的结果。你能想象五小时内连一个计划都执行不完的智能体吗?真的是和豆包一样聪明了。
后记
init
終わり。
