2026-01-30 21:29:45 +08:00
|
|
|
|
---
|
2026-01-30 22:30:11 +08:00
|
|
|
|
title: Artalk评论区接入AI摘要的尝试
|
2026-02-01 13:42:02 +08:00
|
|
|
|
date: 2026-01-31T09:00:00+08:00
|
2026-01-30 21:29:45 +08:00
|
|
|
|
draft: false
|
2026-01-30 13:30:55 +00:00
|
|
|
|
categories:
|
|
|
|
|
|
- 尝试
|
|
|
|
|
|
- 技术
|
|
|
|
|
|
tags:
|
|
|
|
|
|
- Artalk
|
|
|
|
|
|
- AI
|
|
|
|
|
|
- GitHub Actions
|
|
|
|
|
|
- SiliconFlow
|
|
|
|
|
|
ai_comment: >-
|
|
|
|
|
|
哈哈,现在连AI都开始抢我们读者的活了吗?不过这个思路挺巧妙,把摘要伪装成评论既自然又省空间。看到代码里那句“跳过已生成的文章”突然笑出声——AI也怕重复劳动啊!期待哪天我的评论被AI点赞(或者被AI吐槽?)
|
2026-01-30 21:29:45 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
最近看到很多博客都加上了 AI 摘要功能,觉得挺有意思,于是也尝试给自己的博客加了一个。
|
|
|
|
|
|
|
|
|
|
|
|
我的需求很简单:
|
|
|
|
|
|
1. **自动生成**:发布新文章时自动调用 API 生成摘要。
|
|
|
|
|
|
2. **融入评论区**:不想额外增加 UI 模块,最好能像一条普通的“置顶评论”一样展示。
|
|
|
|
|
|
3. **零成本**:利用现有的免费 API(DeepSeek V3)。
|
|
|
|
|
|
|
|
|
|
|
|
目前已经实现,效果还算满意。简单记录一下实现过程。
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 后端:生成 AI 评论
|
|
|
|
|
|
|
|
|
|
|
|
写了一个简单的 Node.js 脚本,遍历文章目录,提取正文并调用 SiliconFlow 的 API 生成评论,最后回写到 Front Matter 中。
|
|
|
|
|
|
|
|
|
|
|
|
核心代码 (`scripts/ai_comment.js`):
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// ... (imports & config)
|
|
|
|
|
|
|
|
|
|
|
|
async function generateComment(content) {
|
|
|
|
|
|
const prompt = `你是一个热情的博客读者。请阅读以下文章内容,并写一段简短的评论(100字以内)。
|
|
|
|
|
|
...
|
|
|
|
|
|
文章内容:${content.substring(0, 2000)}...`;
|
|
|
|
|
|
|
|
|
|
|
|
// 调用 SiliconFlow API
|
|
|
|
|
|
const response = await axios.post(SILICONFLOW_URL, {
|
|
|
|
|
|
model: 'deepseek-ai/DeepSeek-V3',
|
|
|
|
|
|
messages: [{ role: 'user', content: prompt }],
|
|
|
|
|
|
// ...
|
|
|
|
|
|
}, { headers: { 'Authorization': `Bearer ${process.env.SILICONFLOW_API_KEY}` } });
|
|
|
|
|
|
|
|
|
|
|
|
return response.data.choices[0].message.content.trim();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历文件并处理
|
|
|
|
|
|
async function processFiles(dir) {
|
|
|
|
|
|
// ...
|
|
|
|
|
|
if (parsed.data.ai_comment) return; // 跳过已生成的文章
|
|
|
|
|
|
|
|
|
|
|
|
const comment = await generateComment(cleanContent);
|
|
|
|
|
|
if (comment) {
|
|
|
|
|
|
parsed.data.ai_comment = comment;
|
|
|
|
|
|
// 回写文件
|
|
|
|
|
|
await fs.promises.writeFile(filePath, matter.stringify(parsed.content, parsed.data));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 自动化:GitHub Actions
|
|
|
|
|
|
|
|
|
|
|
|
为了省去手动运行脚本的麻烦,将其集成到了 CI/CD 流程中。
|
|
|
|
|
|
|
|
|
|
|
|
在 `.github/workflows/upy.yml` 中添加步骤:
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
# 生成 AI 评论
|
|
|
|
|
|
- name: Generate AI Comments
|
|
|
|
|
|
env:
|
|
|
|
|
|
SILICONFLOW_API_KEY: ${{ secrets.SILICONFLOW_API_KEY }}
|
|
|
|
|
|
run: node scripts/ai_comment.js
|
|
|
|
|
|
|
|
|
|
|
|
# 提交变更 (注意需要 permissions: contents: write)
|
|
|
|
|
|
- name: Commit AI Comments
|
|
|
|
|
|
run: |
|
|
|
|
|
|
git config --global user.name "GitHub Actions"
|
|
|
|
|
|
git config --global user.email "actions@github.com"
|
|
|
|
|
|
git add content/post
|
|
|
|
|
|
if ! git diff-index --quiet HEAD; then
|
|
|
|
|
|
git commit -m "chore: auto-generate ai comments [skip ci]"
|
|
|
|
|
|
git push
|
|
|
|
|
|
fi
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 前端:Artalk 集成
|
|
|
|
|
|
|
|
|
|
|
|
最后一步是在前端展示。利用 Artalk 的事件机制,将 AI 评论动态插入到评论列表头部,并加上打字机特效。
|
|
|
|
|
|
|
|
|
|
|
|
核心代码 (`assets/js/main.js`):
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
window.artalk.on('list-loaded', function() {
|
|
|
|
|
|
// 检查是否有 AI 评论数据
|
|
|
|
|
|
if (window.artalkConfig.aiComment && !document.querySelector('.atk-ai-comment')) {
|
2026-01-30 22:18:19 +08:00
|
|
|
|
var listBody = document.querySelector('.atk-list-body') || document.querySelector('.atk-list');
|
|
|
|
|
|
|
|
|
|
|
|
if (listBody) {
|
|
|
|
|
|
// 避免同时出现“此时无声胜有声”
|
|
|
|
|
|
var noComment = document.querySelector('.atk-list-no-comment');
|
|
|
|
|
|
if (noComment) noComment.style.display = 'none';
|
2026-01-30 21:29:45 +08:00
|
|
|
|
|
2026-01-30 22:18:19 +08:00
|
|
|
|
// 创建 DOM 结构
|
|
|
|
|
|
var aiDiv = document.createElement('div');
|
|
|
|
|
|
aiDiv.className = 'atk-comment atk-ai-comment';
|
|
|
|
|
|
aiDiv.innerHTML = `...`; // 填充 HTML
|
|
|
|
|
|
|
|
|
|
|
|
// 插入到列表头部
|
|
|
|
|
|
listBody.prepend(aiDiv);
|
|
|
|
|
|
|
|
|
|
|
|
// ... (打字机效果)
|
2026-01-30 21:29:45 +08:00
|
|
|
|
}
|
2026-01-30 22:18:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 适配 PJAX
|
|
|
|
|
|
|
|
|
|
|
|
由于博客使用了 PJAX,页面切换时需要重新初始化 Artalk。我们将初始化逻辑封装在 `initArtalk` 函数中,并在 `pjax:complete` 事件中调用。
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// mypjax.js
|
|
|
|
|
|
document.addEventListener("pjax:complete", function () {
|
|
|
|
|
|
// ...
|
|
|
|
|
|
// 重载评论
|
|
|
|
|
|
if (typeof window.initArtalk === 'function') {
|
|
|
|
|
|
window.initArtalk();
|
2026-01-30 21:29:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这样就完成了一个简单实用的 AI 摘要功能。既丰富了页面内容,又没有过度干扰阅读体验。
|