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

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

  •  
  •   symphonyiceattac ·
    SymphonyIceAttack · Jul 21 · 1675 views
    This topic created in 34 days ago, the information mentioned may be changed or developed.

    下面这版可以直接发到 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 友愿意试一下,欢迎反馈创建流程、生成质量,或者你觉得下一个应该支持什么内容类型。

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

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

    写过几个都不想用了 有几个服务( docker 自托管部署 为了优化特意开了 PPR 和 cache 结果发现开这俩内存直接爆表。。。)比我的 java 服务内存还要高真是服了。。。
    symphonyiceattac
        8
    symphonyiceattac  
    OP
       Jul 21
    @bbbblue 太对了,我就是因为这个才直接放弃 nextjs 的
    反正 react 生态的东西都是通用的
    itechify
        9
    itechify  
       Jul 21
    @symphonyiceattac 还笑是吧,等会账号没了
    johanbo
        10
    johanbo  
       Jul 22
    为什么不用 astro ,速度更快,体验更好
    symphonyiceattac
        11
    symphonyiceattac  
    OP
       Jul 22
    @johanbo astro 更适合的纯文档站点
    johanbo
        12
    johanbo  
       Jul 22   ❤️ 1
    @symphonyiceattac #11 错了,astro7 已经可以和做中等程度的 saas 应用了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   4276 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 10:14 · PVG 18:14 · LAX 03:14 · JFK 06:14
    ♥ Do have faith in what you're doing.