来源:互联网 更新时间:2026-06-09 07:32
上个月我做了一个小工具:粘贴一篇长文,AI 帮你生成三句话摘要。
没有复杂的后台管理系统,没有用户体系,没有付费墙。就一个输入框,一个按钮,一段漂亮的输出。
三天开发,第四天上线 Product Hunt。当天拿了 Top 5。
这篇文章完整记录技术实现过程。代码不多,但每一行都是反复删减后留下的。
整个产品只有三个功能:
没了。就这些。
graph LRA["用户输入长文"] --> B["调用 AI API"]B --> C["流式输出摘要"]C --> D["一键复制"]style B fill:#8b5cf6,color:#fff
| 层 | 选择 | 理由 |
|---|---|---|
| 框架 | Next.js 14 (App Router) | 前后端一体,Route Handler 直接当 API |
| 样式 | CSS Modules | 不引入额外依赖,够用就好 |
| AI | OpenAI gpt-4o-mini | 便宜、快、摘要质量够 |
| 部署 | Vercel | 零配置,推完代码自动上线 |
独立开发的一个原则:能少引一个依赖就少引一个。每多一个 npm install,就多一份未来的维护债。
summarizer/├── app/│ ├── layout.tsx # 全局布局│ ├── page.tsx # 首页(唯一页面)│ ├── page.module.css # 首页样式│ └── api/│ └── summarize/│ └── route.ts # AI 摘要接口├── components/│ ├── TextInput.tsx # 输入框组件│ ├── SummaryOutput.tsx # 摘要输出组件│ └── CopyButton.tsx # 复制按钮├── lib/│ └── openai.ts # OpenAI 封装└── package.json
一共 8 个文件。每个文件都承担了明确的职责,不多不少。
// app/api/summarize/route.tsimport { NextRequest } from 'next/server';export async function POST(req: NextRequest) { const { text } = await req.json(); // 输入校验:不废话,直接拦 if (!text || text.length < 50) { return new Response(JSON.stringify({ error: '文本太短,至少 50 个字' }), { status: 400 }); } if (text.length > 10000) { return new Response(JSON.stringify({ error: '文本太长,最多 10000 个字' }), { status: 400 }); } // 调用 OpenAI 流式接口 const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`, }, body: JSON.stringify({ model: 'gpt-4o-mini', stream: true, temperature: 0.3, // 摘要场景用低温度,输出更稳定 messages: [ { role: 'system', content: '你是一个专业的文本摘要助手。请用三句话概括用户提供的文章核心内容。要求:第一句点明主题,第二句总结关键论点,第三句给出结论或启发。语言精炼,不要套话。', }, { role: 'user', content: `请摘要以下文章:${text}`, }, ], }), }); // 直接透传 SSE 流给前端 return new Response(response.body, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }, });}
这里直接把 OpenAI 的 SSE 流透传给前端。不做中间缓存,不做二次封装。数据流向越短,延迟越低。
// components/SummaryOutput.tsx'use client';import { useState } from 'react';import styles from './SummaryOutput.module.css';interface Props { text: string; onComplete: (summary: string) => void;}export default function SummaryOutput({ text, onComplete }: Props) { const [summary, setSummary] = useState(''); const [loading, setLoading] = useState(false); const handleSummarize = async () => { setLoading(true); setSummary(''); const response = await fetch('/api/summarize', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }), }); if (!response.ok || !response.body) { setSummary('摘要生成失败,请重试'); setLoading(false); return; } // 流式读取 SSE const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; let fullText = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; // 最后一行可能不完整,留到下次 for (const line of lines) { if (line === 'data: [DONE]') continue; if (!line.startsWith('data: ')) continue; try { const json = JSON.parse(line.slice(6)); const content = json.choices?.[0]?.delta?.content || ''; fullText += content; setSummary(fullText); // 逐字更新,打字机效果 } catch { // 跳过解析失败的行 } } } setLoading(false); onComplete(fullText); }; return ( {summary && ( {summary}
{!loading && } )} );}
流式读取 SSE 并逐字更新,打造打字机效果,让用户实时看到 AI 在“思考输出”。
// components/CopyButton.tsx'use client';import { useState } from 'react';import styles from './CopyButton.module.css';export default function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); const handleCopy = async () => { try { await na vigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { // 降级方案:用 textarea 选中复制 const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); setCopied(true); setTimeout(() => setCopied(false), 2000); } }; if (!text) return null; return ( 下饭影视APP下载安装指南
灵宝派对手游下载安装地址推荐
和平精英如何做到压枪稳-和平精英怎样才能压枪稳
下载浏览器app下载安装选择推荐
《Off Campus》第二季官宣:这对CP还在,但不再是主角
4D采矿者官网在哪下载 最新官方下载安装地址
免费影视剧APP推荐
儿子穿新中式现身大会堂 马斯克罕见用中文回应:他正在学习普通话
Elysium Above 履云录官网在哪下载 最新官方下载安装地址
名单曝光!库克、马斯克等将随团到访中国 黄仁勋不在其中
抖音最火沙雕男生网名(精选100个)
短剧《情绪超市》剧情介绍
短视频软件推荐
免费看电影的软件推荐
苹果macOS 27将优化界面设计并测试AI驱动的Safari标签页自动分组功能
网络热词聊污是什么意思
网石18禁MMO《RAVEN2:渡鸦》大型更新推出全新职业“军阀”
KuCoin基本面分析
洛克王国世界S2赛季狂欢怪谈介绍
SpaceX狂揽AI人才,马斯克亲自面试且不看简历背景
手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc