本文记录了我为 Astro 静态博客集成 AI 助手的完整过程,从最初的想法萌生,到最终实现一个不影响阅读体验的侧边栏 AI 助手。
为什么想做这个功能
说实话,最开始想做这个功能,纯粹是因为觉得很酷。
看到各行各业都在拥抱 AI,从办公软件到代码编辑器,从搜索引擎到社交媒体,AI 正在渗透到我们生活的方方面面。作为一个技术博客,如果能有一个 AI 助手帮助读者更好地理解文章内容,岂不是很有意思?
除了「酷」之外,我认为这个功能确实能提升阅读体验:
- 一键摘要:长文章可以快速了解核心要点
- 选中解释:遇到不懂的术语,选中就能获得解释
- 自由提问:针对文章内容提出任何问题
这不就是每个读者都想要的「私人助教」吗?
最初的担忧:静态博客能做吗?
在动手之前,我最大的担忧是:这是一个纯前端静态博客,没有后端服务器,怎么调用 AI API?
众所周知,调用 AI API 需要 API Key,而 API Key 是不能暴露在前端代码中的。传统的做法是通过后端服务器中转请求,但我的博客是用 Astro 构建的静态站点,部署在 Vercel 上,根本没有传统意义上的「后端」。
带着这个疑问,我让 Claude 分析了我的项目结构。结果发现,Astro 支持 API Routes,配合 Vercel 的 Serverless Functions,完全可以实现服务端逻辑!
用户请求 → Vercel Edge/Serverless → DeepSeek API → 流式响应返回
API Key 安全地存储在 Vercel 的环境变量中,前端只需要调用我们自己的 API 端点,完美解决了安全问题。
技术方案设计
确定可行性后,我开始设计具体的技术方案。
核心功能
| 功能 | 说明 |
|---|---|
| 一键摘要 | 点击按钮,AI 生成文章核心要点 |
| 选中解释 | 选中文本后出现按钮,解释选中内容 |
| 自由提问 | 输入框,针对文章内容提问 |
技术选型
- AI 服务:DeepSeek API(性价比高,支持中文,32K 上下文)
- 状态管理:Nanostores(轻量级,适合 Astro)
- 动画效果:Motion(流畅的滑入滑出动画)
- Markdown 渲染:marked(AI 回答支持 Markdown 格式)
交互设计
这是我花了最多心思的部分。最初的设计是点击按钮后弹出一个全屏模态框,但实际使用后发现体验很差——你没法一边看文章一边问 AI,每次都要关闭面板才能看原文。
最终方案是:
- 桌面端:右侧固定侧边栏(384px 宽度),不遮挡文章内容
- 移动端:全屏模态框(屏幕太小,没办法)
这样读者可以一边阅读文章,一边和 AI 对话,体验流畅多了。
实现过程
第 1 步:创建 API 路由
首先在 src/pages/api/ai/chat.ts 创建 API 端点:
import type { APIRoute } from 'astro';
import { callDeepSeekStream } from '@/lib/ai/deepseek';
export const prerender = false;
export const POST: APIRoute = async ({ request }) => {
const apiKey = import.meta.env.DEEPSEEK_API_KEY;
if (!apiKey) {
return new Response(JSON.stringify({ error: 'API key not configured' }), {
status: 500,
});
}
const { action, content, title, query } = await request.json();
const response = await callDeepSeekStream(apiKey, action, content, title, query);
// 返回流式响应
return new Response(response.body, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
},
});
};
关键点:
export const prerender = false告诉 Astro 这是一个动态路由- 使用流式响应(SSE),实现打字机效果
- API Key 从环境变量读取,不会暴露给前端
第 2 步:封装 DeepSeek 客户端
在 src/lib/ai/deepseek.ts 中封装 API 调用逻辑:
export function buildSystemPrompt(action: AIActionType): string {
switch (action) {
case 'summary':
return `你是一个专业的文章摘要助手。请阅读文章内容,提取 3-5 个核心要点,用简洁的中文总结。
要求:
- 每个要点一句话
- 突出文章的核心观点和关键信息
- 使用 bullet points 格式`;
case 'explain':
return `你是一个知识解释助手。用户在阅读文章时选中了一段文字想要了解更多。
要求:
- 解释选中文字的含义
- 如果涉及专业术语,用通俗易懂的语言解释
- 结合文章上下文进行解释`;
case 'ask':
return `你是一个文章阅读助手。用户正在阅读一篇文章,并有一些问题。
要求:
- 基于文章内容回答用户的问题
- 如果问题超出文章范围,请说明并尽可能提供有用的信息`;
}
}
针对不同的操作类型,设计了不同的 System Prompt,让 AI 的回答更加精准。
第 3 步:状态管理
使用 Nanostores 管理 AI 助手的状态:
import { atom } from 'nanostores';
// 面板开关状态
export const $aiPanelOpen = atom<boolean>(false);
// 消息历史
export const $aiMessages = atom<AIMessage[]>([]);
// 加载状态
export const $aiLoading = atom<boolean>(false);
// 当前文章内容
export const $currentArticleContent = atom<string>('');
Nanostores 的好处是可以在 Astro 组件和 React 组件之间共享状态,非常适合这种混合架构。
第 4 步:实现选中文本弹窗
这是一个有趣的交互:用户选中文章中的文本后,在选中位置上方出现一个「AI 解释」按钮。
const handleMouseUp = useCallback(() => {
const selection = window.getSelection();
if (!selection || selection.isCollapsed) {
clearSelection();
return;
}
const text = selection.toString().trim();
if (!text || text.length < 2) {
clearSelection();
return;
}
// 检查选中是否在文章容器内
const container = document.querySelector('article');
if (!container?.contains(selection.anchorNode)) {
clearSelection();
return;
}
// 获取选中位置,显示弹窗
const rect = selection.getRangeAt(0).getBoundingClientRect();
setSelectionPosition({ x: rect.left + rect.width / 2, y: rect.top - 10 });
setSelectedText(text);
}, []);
第 5 步:Markdown 渲染
AI 的回答通常包含 Markdown 格式(列表、粗体、代码等),需要正确渲染:
import { marked } from 'marked';
function MarkdownContent({ content }: { content: string }) {
const html = useMemo(() => {
return marked.parse(content, { async: false }) as string;
}, [content]);
return (
<div
className="prose prose-sm dark:prose-invert"
dangerouslySetInnerHTML={{ __html: html }}
/>
);
}
遇到的问题和解决方案
问题 1:面板全屏遮挡文章
现象:最初实现的 AI 面板是全屏的,无法同时查看文章和 AI 回答。

解决方案:修改为桌面端侧边栏模式,固定宽度 384px,从导航栏下方开始。
className={cn(
'fixed right-0 z-[70] flex flex-col bg-background shadow-xl',
'top-16 h-[calc(100vh-4rem)] w-96 border-l border-border',
'md:top-0 md:h-full md:w-full md:border-l-0', // 移动端全屏
)}
这里有个坑:这个项目的 Tailwind 断点配置是反向的,md: 表示小于等于 768px,而不是标准的大于等于。所以默认样式是桌面端的,md: 前缀用于移动端覆盖。
问题 2:AI 回答显示原始 Markdown 符号
现象:AI 返回的内容包含 •、**粗体** 等 Markdown 语法,但直接显示为纯文本。
解决方案:使用 marked 库将 Markdown 转换为 HTML,配合 Tailwind 的 prose 类进行样式美化。
问题 3:组件没有渲染
现象:点击 AI 按钮没有任何反应。
原因:把组件放在了一个不存在的 slot 里(slot="right-sider"),导致组件根本没有被渲染。
解决方案:移除 slot 属性,让组件作为固定定位元素独立渲染。
最终效果
经过多次优化,最终实现了一个体验良好的 AI 助手:

功能特点:
| 功能 | 说明 |
|---|---|
| 一键摘要 | 快速生成文章核心要点 |
| 选中解释 | 选中文本即可获得解释 |
| 自由提问 | 针对文章内容自由提问 |
| 流式输出 | 打字机效果,体验流畅 |
| Markdown 渲染 | 支持列表、粗体、代码等格式 |
| 侧边栏模式 | 不遮挡文章,可同时阅读 |
部署配置
环境变量
在 Vercel 项目设置中添加:
| Name | Value |
|---|---|
DEEPSEEK_API_KEY | 你的 DeepSeek API Key |
获取 API Key
- 访问 DeepSeek 开放平台
- 注册账号并登录
- 在 API Keys 页面创建新的 Key
- 复制 Key 到 Vercel 环境变量
DeepSeek 的定价非常友好,对于个人博客来说,成本几乎可以忽略不计。
成本分析:个人博客用得起吗?
在决定集成 AI 功能之前,很多人可能会担心:调用 AI API 会不会很贵?
这是一个非常实际的问题。毕竟个人博客不是商业项目,没有收入来源,如果每个月要花几十上百块在 API 调用上,那确实不太划算。
实际花费数据
我在开发和测试过程中,进行了大量的 API 调用来验证功能。来看看实际的花费:

在开发测试阶段,我一共调用了 11 次 API,包括:
- 多次测试一键摘要功能
- 测试选中解释功能
- 测试自由提问功能
- 调试流式响应
这 11 次调用的总花费是多少呢?

0.03 元。没错,三分钱。
为什么这么便宜?
DeepSeek 的定价策略非常亲民:
| 模型 | 输入价格 | 输出价格 |
|---|---|---|
| deepseek-chat | ¥1 / 百万 tokens | ¥2 / 百万 tokens |
一篇 3000 字的中文文章,大约是 2000-3000 tokens。加上 System Prompt 和 AI 的回答,一次完整的对话大约消耗 4000-6000 tokens。
按这个计算:
- 单次调用成本:约 0.003 元(不到一分钱)
- 每天 10 次调用:约 0.03 元
- 每月 300 次调用:约 1 元
对于一个个人博客来说,每月 300 次 AI 调用已经是非常高的使用量了。即使你的博客流量很大,每月的 API 成本也很难超过 10 块钱。
成本控制建议
虽然成本已经很低,但还是有一些优化空间:
- 限制文章长度:对于超长文章,可以只发送前 N 个字符
- 缓存常见问题:对于「一键摘要」,可以考虑缓存结果
- 设置调用频率限制:防止恶意刷接口
目前我没有做这些优化,因为成本实在太低了,优化的收益不大。
结论
经济上完全可行。 对于个人博客来说,DeepSeek API 的成本几乎可以忽略不计。与其担心 API 费用,不如把精力放在如何提升用户体验上。
写在最后
从最初的「这能做吗?」到最终实现一个完整的 AI 助手,整个过程让我深刻体会到了 AI 时代编程的不同。
1 小时完成整个功能
说出来你可能不信,从零开始到功能完全可用,我只花了 1 个多小时。
这在以前是不可想象的。如果让我自己从头写这个功能,光是研究 DeepSeek API 文档、设计组件结构、处理流式响应、调试各种边界情况,少说也要一两天。
但有了 Claude Code,一切都变得不一样了:
- 需求描述 → Claude 分析项目结构,确认技术可行性
- 方案设计 → Claude 给出完整的文件结构和实现思路
- 代码实现 → Claude 逐个文件编写代码
- 问题修复 → 遇到 bug 直接描述现象,Claude 定位并修复
整个过程我几乎没有手写代码,更多的是在「指挥」和「验收」。
提示词的重要性
在这个过程中,我深刻体会到:提示词的质量决定了 AI 的输出质量。
好的提示词应该:
- 描述清晰:不要说「帮我加个 AI 功能」,而是说「我想在文章页面添加一个 AI 助手,支持一键摘要、选中解释、自由提问三个功能」
- 提供上下文:告诉 AI 你的项目用了什么技术栈、有什么限制条件
- 分步骤推进:复杂功能拆成小步骤,一步一步来
- 及时反馈:遇到问题立刻描述现象,让 AI 快速定位
比如当 AI 面板全屏遮挡文章时,我没有说「这个不对」,而是说「现在点击 AI 按钮后面板是全屏的,我希望桌面端是右侧侧边栏,不遮挡文章内容」。清晰的描述让 Claude 一次就改对了。
AI 时代的编程方式
以前遇到不会的技术,可能需要花几天时间查文档、看教程、踩坑。现在有了 AI 助手,我可以直接描述需求,让 AI 帮我分析可行性、设计方案、编写代码。我的角色从「写代码的人」变成了「指挥 AI 写代码的人」。
这并不意味着不需要学习了。相反,你需要:
- 理解技术原理:才能判断 AI 的方案是否合理
- 具备审美能力:才能设计出好的交互体验
- 掌握调试技巧:AI 写的代码也会有 bug
- 学会提问:好的提示词是高效协作的关键
但毫无疑问,AI 大大降低了技术实现的门槛。以前觉得「太难了做不了」的功能,现在可以大胆尝试。
我喜欢 AI,我喜欢代码,我喜欢用 AI 提升我的项目。 即使不会,也要用 AI 做出来——这或许就是 AI 时代编程的新姿势吧。
如果你也想给自己的博客加上 AI 助手,希望这篇文章能给你一些参考。有问题欢迎在评论区交流!