一句话说清核心收获
这篇文章会让你学会:用LLM从任意人生履历文本中抽取事件并自动渲染成互动时间线页面。看完就能跑出自己的Demo,不限于讣告,传记、履历、新闻时间线都可套用。
效果:输出一个能左右滑动的时间线
输入一段讣告原文(比如下面这位Edith的),系统自动解析出:出生、求学、成家、信仰转变、戒酒、临终等关键节点,并在地图上标注地点(如果有),点击每个事件可以展开详情。最终页面长这样:

交互式时间线实机截图,从左到右依次展开,每个节点可展开详情。
技术选型:三件套搞定
- 解析层:OpenAI GPT-4o-mini(便宜,解析精度够)
- 前端:Next.js 14 App Router + Tailwind CSS + react-vertical-timeline-component
- 部署:Vercel (Serverless + Edge)
为什么不直接手写正则?因为讣告/传记的写法千奇百怪,LLM泛化能力远超规则。一次API调用成本约0.001美元,性价比极高。
核心代码实现(关键片段)
1. 定义事件结构
typescript
1
2
3
4
5
6
7
8
// types.ts
export interface LifeEvent {
date: string; // 年份或具体日期,例如 "December 12, 1945"
title: string; // 事件标题,例如 "出生"
description: string; // 从原文提取的1-2句描述
location?: string; // 地点,可选,例如 "Augusta & Wrens"
type: 'birth' | 'achievement' | 'struggle' | 'faith' | 'death';
}
2. 用OpenAI API提取事件
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// lib/parseObituary.ts
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export async function parseLifeEvents(text: string): Promise<LifeEvent[]> {
const prompt = `
你是一个传记事件提取器。从以下讣告中提取所有关键人生事件,按时间顺序排列。
每个事件包含:date(尽量转换为年或年月),title(简短概括,如"戒酒"),description(原文1-2句),location(如果提到),type(枚举:birth|achievement|struggle|faith|death)。
只输出合法的JSON数组,不要其他文字。
原文:
"""
${text}
"""
`;
const response = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: prompt }],
response_format: { type: 'json_object' },
});
const raw = response.choices[0].message.content;
const parsed = JSON.parse(raw);
return parsed.events || parsed; // 兼容不同输出
}
3. 前端渲染(App Router下的API路由 + 页面组件)
typescript
1
2
3
4
5
6
7
8
9
10
11
12
// app/api/parse/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { parseLifeEvents } from '@/lib/parseObituary';
export async function POST(req: NextRequest) {
const { text } = await req.json();
if (!text || text.length < 20) {
return NextResponse.json({ error: '文本太短' }, { status: 400 });
}
const events = await parseLifeEvents(text);
return NextResponse.json(events);
}
tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// components/Timeline.tsx
'use client';
import { VerticalTimeline, VerticalTimelineElement } from 'react-vertical-timeline-component';
import 'react-vertical-timeline-component/style.min.css';
interface Props {
events: LifeEvent[];
}
export default function Timeline({ events }: Props) {
return (
<VerticalTimeline>
{events.map((ev, i) => (
<VerticalTimelineElement
key={i}
date={ev.date}
iconStyle={iconColorMap[ev.type]}
>
<h3>{ev.title}</h3>
<p>{ev.description}</p>
{ev.location && <span className="text-gray-400">📍 {ev.location}</span>}
</VerticalTimelineElement>
))}
</VerticalTimeline>
);
}
项目结构和配置
text
1
2
3
4
5
6
7
8
9
10
11
12
13
my-memorial-timeline/
├── app/
│ ├── api/parse/route.ts # 解析API
│ ├── page.tsx # 输入匡 + Timeline组件
│ └── layout.tsx
├── components/
│ └── Timeline.tsx
├── lib/
│ ├── parseObituary.ts # LLM解析函数
│ └── types.ts
├── .env.local # OPENAI_API_KEY=sk-xxxx
├── package.json
└── vercel.json # 可无
关键注意:在Vercel部署时,OpenAI API请求可能有冷启动延迟(约1-2秒)。建议将解析结果缓存到KV Store(Vercel KV),同一条文本避免反复调用。另外要设置Edge Runtime超时60秒(默认10秒可能不够)。
上线要避的坑
- API Key泄露:千万不要在客户端调用OpenAI,必须走自己的API路由(服务端)。
- 文本长度限制:GPT-4o-mini上下文128K,但一次解析超过5000字的讣告容易丢信息。建议截断到前2000字,或分段解析后合并。
- 事件顺序乱序:LLM有时会打乱顺序,返回结果后要在前端按date重新排序(用moment.js解析日期)。
- 地理位置标注:如果地点是城市名,可额外调用Mapbox Geocoding API添加地图标记,但免费额度有限,初期建议只展示文字地点。
- 样式适配移动端:react-vertical-timeline-component在手机上宽度溢出,要加
overflow-x: auto。
我的个人观点:为什么这个Demo值得做?
现在很多纪念网站(如Legacy.com)仍然靠人工编辑或简单模板。用AI自动解析并生成互动页面,可以将成本降到近乎零,还能一键生成个性化纪念页。未来任何有文本记述的人生(简历、回忆录、新闻人物)都可以通过这套流水线转化成可视化时间线。开发者现在应该关注的是结构化抽取 + 极简前端的组合,而不是重复造轮子去写规则解析。
你只需要一个OpenAI Key + 一个Vercel账号,2小时内就能跑通。