下面这版可以直接发到 V2EX 的「分享创造」或「程序员」节点。
标题
一个 AI 写作工具从 Next.js 迁移到 TanStack Start ,并改用 Gemini Batch API 的记录
正文
最近做了一个 AI 内容生成工具 WriteGeniuses:
它目前支持两种内容:
和常见的“输入一句话,直接返回整篇文章”不太一样。WriteGeniuses 会先生成结构化 Plan ,再拆成多个 Unit ,分别生成正文。用户可以单独编辑或重新生成某个段落,不需要每次都重跑整篇内容。
项目最初是 Next.js + Convex ,后来前端迁移到了 TanStack Start 。迁移过程中顺便把生成架构也重做了一遍。
目前主要技术栈是:
这次改动比较大的地方主要有这些。
迁移后,文件路由、beforeLoad 路由保护和路由参数都由 TanStack Router 管理。
一开始虽然页面看起来能正常打开,但禁用 JavaScript 后只能看到骨架屏。原因是列表数据仍然完全依赖客户端的 Convex useQuery,服务端并没有真正拿到内容。
后来把需要首屏展示的数据放进 route loader:
loaderData 输出到 HTMLuseQuery 接管实时更新这样即使 JavaScript 还没加载,Content 和 Templates 页面也已经有基本内容,而不是一整页骨架。
最早的生成流程是在 Convex Action 里直接等待 Gemini 返回。
短请求还好,但一篇文章会经历:
只要某个模型响应较慢,Action 就会长时间占用 worker 。并发任务一多,还碰到过 There are no available workers to process the request 和 600 秒超时。
后来改成了 Batch API:
Convex 里单独保存了 batch job 和 batch request 。每条 Unit 请求都有自己的 requestKey,所以 Webhook 返回后不需要让模型再输出 unitId,直接使用请求记录完成映射。
某个 Unit 失败时,也只重试对应 Unit ,不会重新生成整篇文章。
最初 X Thread 复用了 Blog 的部分 Plan 和 Unit prompt ,结果很不理想。
生成出来的帖子经常像被截断的文章段落:
后来加入 planKind,目前支持:
blog_articlex_thread两种类型现在分别拥有自己的:
这样后面继续增加 Reddit Post 或其他内容类型时,不需要继续往 Blog schema 里塞字段。
Gemini 的 JSON 输出并不是设置了 application/json 就一定稳定。
实际碰到过:
Constraint is too tallresponseJsonSchema 和 responseSchema 在 Batch 端表现不同现在请求端使用 Gemini 原生 responseSchema,返回后仍然使用 Zod 做最终校验。Plan 、Blog Unit 、X Unit 和图片建议分别使用独立 schema ,没有为了省代码强行复用。
文章详情页使用 Streamdown 渲染 Markdown ,目前支持代码、数学公式和 Mermaid 。
Mermaid 这里也踩了一个小坑:
B -- Requires --> C{Authentication (API Keys)}
括号和旧式连线文字有时会触发解析错误。现在提示词要求:
-->|Requires|对应写法变成:
graph TD
A["Developer Application"] --> B{"Creem API Gateway"}
B -->|Requires| C{"Authentication (API Keys)"}
现在的版本支持:
新注册账号会有一些免费积分,可以直接跑一次 Blog 或 X Thread 。
线上地址:
目前还是早期版本,我自己最想继续改进的是生成内容的“真人感”,尤其是怎样让用户把真实经验、案例和数据更自然地带进 Brief ,而不是只生成结构正确但比较泛的内容。
如果有 V 友愿意试一下,欢迎反馈创建流程、生成质量,或者你觉得下一个应该支持什么内容类型。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.