• 请不要在回答技术问题时复制粘贴 AI 生成的内容
symphonyiceattac
V2EX  ›  程序员

一个 AI 写作工具从 Next.js 迁移到 TanStack Start,并改用 Gemini Batch API 的记录

  •  
  •   symphonyiceattac ·
    SymphonyIceAttack · 10h 33m ago · 498 views

    下面这版可以直接发到 V2EX 的「分享创造」或「程序员」节点。

    标题

    一个 AI 写作工具从 Next.js 迁移到 TanStack Start ,并改用 Gemini Batch API 的记录

    正文

    最近做了一个 AI 内容生成工具 WriteGeniuses:

    https://writegeniuses.com

    它目前支持两种内容:

    • 长篇 Blog Article
    • X Thread

    和常见的“输入一句话,直接返回整篇文章”不太一样。WriteGeniuses 会先生成结构化 Plan ,再拆成多个 Unit ,分别生成正文。用户可以单独编辑或重新生成某个段落,不需要每次都重跑整篇内容。

    项目最初是 Next.js + Convex ,后来前端迁移到了 TanStack Start 。迁移过程中顺便把生成架构也重做了一遍。

    目前主要技术栈是:

    • TanStack Start / TanStack Router
    • React 19
    • Convex
    • Google Gemini Batch API
    • Cloudflare
    • Tailwind CSS + shadcn/ui
    • Streamdown
    • Creem

    这次改动比较大的地方主要有这些。

    1. 从 Next.js 迁移到 TanStack Start

    迁移后,文件路由、beforeLoad 路由保护和路由参数都由 TanStack Router 管理。

    一开始虽然页面看起来能正常打开,但禁用 JavaScript 后只能看到骨架屏。原因是列表数据仍然完全依赖客户端的 Convex useQuery,服务端并没有真正拿到内容。

    后来把需要首屏展示的数据放进 route loader:

    • 服务端读取登录状态
    • 服务端查询 Convex
    • 通过 loaderData 输出到 HTML
    • 客户端启动后再由 useQuery 接管实时更新

    这样即使 JavaScript 还没加载,Content 和 Templates 页面也已经有基本内容,而不是一整页骨架。

    2. 把实时生成改成 Gemini Batch API

    最早的生成流程是在 Convex Action 里直接等待 Gemini 返回。

    短请求还好,但一篇文章会经历:

    1. 生成 Plan
    2. 生成多个 Paragraph 和 FAQ
    3. 生成图片建议
    4. 保存并组合 Markdown

    只要某个模型响应较慢,Action 就会长时间占用 worker 。并发任务一多,还碰到过 There are no available workers to process the request 和 600 秒超时。

    后来改成了 Batch API:

    • 创建文章后提交 Plan batch
    • Convex 立即释放 worker
    • Gemini 完成后调用静态 Webhook
    • Webhook 根据 batch name 找到本地 job
    • Plan 完成后继续提交 Unit batch
    • Unit 完成后保存正文并更新文章状态

    Convex 里单独保存了 batch job 和 batch request 。每条 Unit 请求都有自己的 requestKey,所以 Webhook 返回后不需要让模型再输出 unitId,直接使用请求记录完成映射。

    某个 Unit 失败时,也只重试对应 Unit ,不会重新生成整篇文章。

    3. Blog 和 X Thread 完全拆开

    最初 X Thread 复用了 Blog 的部分 Plan 和 Unit prompt ,结果很不理想。

    生成出来的帖子经常像被截断的文章段落:

    • 开头缺失
    • 结尾停在半句话
    • 混入字符数检查和模型自检文本
    • 每条内容单独看还行,连起来却没有 Thread 节奏

    后来加入 planKind,目前支持:

    • blog_article
    • x_thread

    两种类型现在分别拥有自己的:

    • 创建参数
    • Template
    • Plan schema
    • Plan prompt
    • Unit prompt
    • 内容校验
    • 详情页展示
    • 导出格式

    这样后面继续增加 Reddit Post 或其他内容类型时,不需要继续往 Blog schema 里塞字段。

    4. 结构化输出踩了不少坑

    Gemini 的 JSON 输出并不是设置了 application/json 就一定稳定。

    实际碰到过:

    • JSON 没有闭合
    • Batch 显示成功,但单条 request 失败
    • Schema 过于复杂,触发 Constraint is too tall
    • responseJsonSchemaresponseSchema 在 Batch 端表现不同
    • 模型返回了 JSON ,但字段不满足最终 Zod schema

    现在请求端使用 Gemini 原生 responseSchema,返回后仍然使用 Zod 做最终校验。Plan 、Blog Unit 、X Unit 和图片建议分别使用独立 schema ,没有为了省代码强行复用。

    5. Markdown 和 Mermaid

    文章详情页使用 Streamdown 渲染 Markdown ,目前支持代码、数学公式和 Mermaid 。

    Mermaid 这里也踩了一个小坑:

    B -- Requires --> C{Authentication (API Keys)}
    

    括号和旧式连线文字有时会触发解析错误。现在提示词要求:

    • 所有节点文字使用双引号
    • 带文字的连线使用 -->|Requires|
    • Mermaid code fence 必须完整闭合

    对应写法变成:

    graph TD
        A["Developer Application"] --> B{"Creem API Gateway"}
        B -->|Requires| C{"Authentication (API Keys)"}
    

    6. 目前已经能做什么

    现在的版本支持:

    • 从结构化 Brief 创建 Blog 或 X Thread
    • 公共模板和用户自定义模板
    • Serper 搜索结果作为研究参考
    • 分段生成和单 Unit 重试
    • Markdown 编辑与导出
    • Mermaid 图表渲染
    • 图片建议和生成提示词
    • 文章组管理
    • 积分消耗和 Creem 支付
    • SSR 首屏数据
    • 明暗主题

    新注册账号会有一些免费积分,可以直接跑一次 Blog 或 X Thread 。

    线上地址:

    https://writegeniuses.com

    目前还是早期版本,我自己最想继续改进的是生成内容的“真人感”,尤其是怎样让用户把真实经验、案例和数据更自然地带进 Brief ,而不是只生成结构正确但比较泛的内容。

    如果有 V 友愿意试一下,欢迎反馈创建流程、生成质量,或者你觉得下一个应该支持什么内容类型。

    9 replies    2026-07-21 22:09:50 +08:00
    kkth
        1
    kkth  
       10h 12m ago
    现在都在拥抱 TanStack 了吗?我也在搞这个
    symphonyiceattac
        2
    symphonyiceattac  
    OP
       9h 56m ago
    @kkth 因为 nextjs 绑定 vercel 平台并且太过死板不够灵活,它的打包器还不如 vite 。综合来看 tanstack 基本就是上位替代了
    takanashisakura
        3
    takanashisakura  
       9h 35m ago via iPhone
    下面这版可以直接发到 V2EX 的「分享创造」或「程序员」节点。

    不是哥们,你用 ai 优化发帖我能理解。你让 ai 直接给你整理全文我也忍了。ai 给你回复完你不自自己看一遍直接复制粘贴吗?😅
    itechify
        4
    itechify  
    PRO
       9h 16m ago
    ai 原文粘贴 小心楼下 ai 警察
    symphonyiceattac
        5
    symphonyiceattac  
    OP
       8h 26m ago
    @itechify
    @takanashisakura
    那太好了,这流量不就来了,黑红也是红,我是真饿了😋了
    yinglian
        6
    yinglian  
       8h 26m ago
    @itechify 绷不住了
    bbbblue
        7
    bbbblue  
       8h 19m ago
    nextjs 实在太复杂 资源浪费严重+和 vercel 基础设施绑的死死的

    写过几个都不想用了 有几个服务( docker 自托管部署 为了优化特意开了 PPR 和 cache 结果发现开这俩内存直接爆表。。。)比我的 java 服务内存还要高真是服了。。。
    symphonyiceattac
        8
    symphonyiceattac  
    OP
       8h 15m ago
    @bbbblue 太对了,我就是因为这个才直接放弃 nextjs 的
    反正 react 生态的东西都是通用的
    itechify
        9
    itechify  
    PRO
       6h 46m ago
    @symphonyiceattac 还笑是吧,等会账号没了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   955 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 59ms · UTC 20:56 · PVG 04:56 · LAX 13:56 · JFK 16:56
    ♥ Do have faith in what you're doing.