前言
前几天看一篇博文《Hi,AI 摘要
》时,突然我也想弄一个 AI 摘要功能。 Xalaok 博主给了我一个折腾的思路,我突然想起我的 GitHub Actions 的免费额度一直没有用,所以就想到用 GitHub Actions 来实现我的愿望,说干就干!
挑选 AI
我从六家 AI 进行挑选(包括 ChatGPT , Claude , Gemini , DeepSeek , 豆包, Grok),原本打算用 Google Gemini API,因为 Gemini 是我最喜欢用的 AI ,而且 Google 很大方,它的 API 免费!但是由于地区限制 + 我的魔法不是很灵,这个迟迟不让我申请,然后我就转向了 DeepSeek ,虽然我最讨厌 DeepSeek,但我记得我有 DeepSeek 的免费10元额度,所以我就选了 DeepSeek ,后来才发现我之前用过。
申请 API
- 访问DeepSeek 开放平台
- 注册一个账号
- 获取 Key:点击左侧 “ API Keys ” ,创建并保存好
sk- 开头的密钥。
- 充值:10元钱应该就够,下面是 DeepSeek API 的计费规则
| 方式 |
价格 |
备注 |
| 百万tokens输入(缓存命中) |
0.2元 |
内容高度相似 |
| 百万tokens输入(缓存未命中) |
2元 |
内容崭新 |
| 百万tokens输出 |
3元 |
输出内容 |
扣减费用 = token 消耗量 × 模型单价
配置 GitHub Secrets
- 在你博客的 GitHub 仓库中 - > Settings -> Secrets and variables -> Actions
- 点击 New repository secret
- 名称:
DEEPSEEK_API_KEY
- 值:填入刚才的
sk-... 密钥
全自动摘要 Python 脚本
- 在仓库根目录下创建
.github/scripts/summarize.py,内容写下面内容(下面内容由 Gemini 替我编写,效果只能说一般,我感觉是 DeepSeek 的问题)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
import os, requests, frontmatter
from pathlib import Path
API_KEY = os.environ.get("DEEPSEEK_API_KEY")
API_URL = "https://api.deepseek.com/v1/chat/completions"
def get_summary(content):
headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
# 优化后的结构化 Prompt
prompt = (
"你是一个专业的博文摘要助手。请阅读以下文章内容,并遵循以下准则:\n"
"1. 撰写一段150字以内的中文摘要。\n"
"2. 摘要必须准确、客观地概括全文核心论点或主要内容,严禁臆想或加入文中未提及的事实。\n"
"3. 语言要精炼,直接输出摘要正文,不要有'这篇文章介绍了'、'摘要如下'等废话。\n"
"4. 尊重原意,保持中立的专业语气。\n\n"
f"文章内容如下:\n{content[:3500]}" # 稍微增加了截取长度以提供更多上下文
)
data = {
"model": "deepseek-chat",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3 # 降低随机性,让输出更严谨、更尊重原文
}
try:
res = requests.post(API_URL, json=data, headers=headers).json()
return res['choices'][0]['message']['content'].strip()
except Exception as e:
print(f"API 请求出错了: {e}")
return None
# --- 地毯式搜索 ---
base_dir = Path(__file__).resolve().parent.parent.parent
print(f"当前仓库根目录定位在: {base_dir}")
files_checked = 0
for path in base_dir.rglob("*.md"):
# 1. 过滤掉 .github, archetypes 以及所有 index.md (包括 _index.md)
if any(part in str(path) for part in [".github", "archetypes"]) or path.name.lower() in ["index.md", "_index.md"]:
continue
files_checked += 1
post = frontmatter.load(path)
print(f"检测到文件: {path.relative_to(base_dir)}")
# 2. 逻辑判断:没有 description,且不是草稿
if not post.get('description') and not post.get('draft'):
print(f" 🚀 正在生成摘要...")
summary = get_summary(post.content)
if summary:
# 移除摘要中可能出现的换行符,保证 Front Matter 格式整洁
post['description'] = summary.replace('\n', ' ')
with open(path, 'wb') as f:
frontmatter.dump(post, f)
print(f" ✅ 摘要已写入!")
elif post.get('description'):
print(f" ⏩ 跳过:已有摘要")
elif post.get('draft'):
print(f" ⏩ 跳过:是草稿(draft: true)")
print(f"扫描完毕,共检查了 {files_checked} 个有效的文章文件。")
|
- 然后点击右上角绿色的 Commit changes… 按钮
- 回到仓库主页,再次点击 Add file -> Create new file
- 文件名输入:
.github/workflows/summary.yml,它是告诉 GitHub 何时运行上面的脚本。我们设置的是“当你 Push(推送)新文章时”,它就自动叫醒 summarize.py 起来打工。输入下面内容(下面内容由 Gemini 替我编写)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
|
name: DeepSeek Auto Summary
on:
push:
branches: [ main ] # 如果你的主分支叫 master,记得改成 master
jobs:
summarize:
runs-on: ubuntu-latest
# 这一步极其重要:赋予 GitHub Action 把生成的摘要写回你仓库的权限
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整历史
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install requests python-frontmatter
- name: Run Summary Script
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
run: python .github/scripts/summarize.py
- name: Commit and Push changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .
# 如果没有新摘要生成,git commit 会报错,这里加个 echo 避免任务失败
git commit -m "chore: auto generate AI summaries by DeepSeek" || echo "No changes to commit"
git push
|
- 回到你本地的 VS Code ,在终端输入
git pull origin main --rebase来让你本地和 GitHub 拥有相同的文件。
如何使用
在本地 post 当中的 markdown 文件只要没有 description 且不是 draft 模式, AI 就会自动运行,填写 description 来写摘要。
做个美化
- 在 custom.scss 文件中加入下面代码(包含打字机效果、打字动画、光标闪烁):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/* 打字机动画效果 */
.article-details .article-description {
display: inline-block;
white-space: normal; /* 允许换行 */
overflow: hidden;
border-right: 2px solid; /* 光标效果 */
animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite;
}
/* 打字动画 */
@keyframes typing {
from { max-height: 0; opacity: 0; }
to { max-height: 500px; opacity: 1; }
}
/* 光标闪烁 */
@keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: var(--accent-color); }
}
|
- 在你的博客根目录下,创建文件:
layouts/partials/head/custom.html(如果文件夹不存在就新建,有内容就加到最后)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<script src="https://unpkg.com/typed.js@2.1.0/dist/typed.umd.js"></script>
<style>
/* 定制打字机光标样式 */
.typed-cursor {
color: var(--accent-color);
font-size: 1.1em;
margin-left: 2px;
}
/* 确保摘要容器高度不会因为没有文字而塌陷 */
.article-description.is-typing {
min-height: 1.5em;
}
</style>
|
- 打开
layouts/partials/article/components/details.html,然后找到显示 {{ with .Params.description }} 的位置,,将其替换为:
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
{{ with .Params.description }}
<div class="ai-summary-container">
<div class="ai-badge">
<span class="ai-icon">✨</span>
<span class="ai-text">DeepSeek AI 摘要</span>
</div>
<h3 class="article-subtitle is-typing" id="typed-summary-{{ $.File.UniqueID }}"></h3>
</div>
<script>
(function() {
const initTyped = () => {
if (window.Typed) {
new Typed('#typed-summary-{{ $.File.UniqueID }}', {
strings: [{{ . | safeHTML }}],
typeSpeed: 40,
startDelay: 800, // 稍微延迟,等毛玻璃动画出现
showCursor: true,
cursorChar: '_',
loop: false
});
} else {
setTimeout(initTyped, 100);
}
};
initTyped();
})();
</script>
<style>
/* AI 摘要卡片增强:毛玻璃效果更深邃 */
.ai-summary-card {
margin: 2rem 0;
padding: 2rem; /* 增加内边距,让大号文字有呼吸空间 */
border-radius: 16px;
background: rgba(var(--card-background-rgb), 0.5);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(var(--accent-color-rgb), 0.25);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
transition: transform 0.3s ease;
}
/* 悬停微动效果 */
.ai-summary-card:hover {
transform: translateY(-2px);
border: 1px solid rgba(var(--accent-color-rgb), 0.4);
}
/* AI 标识标签:更大更显眼 */
.ai-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: var(--accent-color);
color: #fff;
padding: 5px 15px; /* 增大内边距 */
border-radius: 30px;
font-size: 1.4rem; /* 标签文字加大 */
font-weight: 700;
margin-bottom: 1.5rem;
box-shadow: 0 4px 10px rgba(var(--accent-color-rgb), 0.3);
letter-spacing: 0.5px;
}
.ai-badge-icon {
font-size: 1.6rem; /* 图标同步加大 */
}
/* 摘要正文:大字号,高清晰度 */
.article-subtitle.is-typing {
margin: 0 !important;
font-size: 1.8rem !important; /* 核心文字再次加大 */
line-height: 1.8 !important; /* 增加行高,提升大字号的阅读舒适度 */
color: var(--card-text-color-main);
font-weight: 500; /* 略微加粗,让文字更稳重 */
letter-spacing: 0.2px;
}
/* 打字机光标:同步加粗 */
.typed-cursor {
color: var(--accent-color);
margin-left: 4px;
font-size: 2rem;
font-weight: bold;
}
</style>
{{ end }}
|
我遇到的可笑问题
前面说过我以为我有免费的额度,所以我就没充值,然后 Gemini 给我写的脚本让强制运行成功(就是避免那种 Description 都写完了,运行不了而报错),所以 GitHub Action 运行日志外围显示绿色的✅,我也没进去看日志,我一直以为是 Gemini 给我写的脚本问题,忙活半天,我才去看日志,一看吓一跳: “ API 请求出错了!”,当时我才意识到我充值问题,然后充值了10元钱,但其实要不是我以为我不用花钱,我绝对用豆包。
最好笑的事情:这篇文章明里暗里不喜欢 DeepSeek ,却还得用 DeepSeek 给我生成摘要……