给博客添加 AI 摘要

本文介绍一种基本通用的方法,为博客添加一个酷炫的 AI 摘要功能.

更好的阅读体验: 给博客添加一个 AI 摘要 - Sun Blog (csun.site)

感谢 @enjoy 大佬开源的后端代码和 @qxchuckle 开源的前端代码,本文在两位大佬的代码基础上修改完成.

AI 摘要后端搭建

使用 Cloudflare Workers 搭建 AI 摘要的后端,进入 cloudflare 的 Workers 和 Pages,创建 worker,输入下面的代码,然后保存并部署

function addHeaders(response) {
	response.headers.set('Access-Control-Allow-Origin', '*')
	response.headers.set('Access-Control-Allow-Credentials', 'true')
	response.headers.set(
		'Access-Control-Allow-Methods',
		'GET,HEAD,OPTIONS,POST,PUT',
	)
	response.headers.set(
		'Access-Control-Allow-Headers',
		'Origin, X-Requested-With, Content-Type, Accept, Authorization',
	)
}
async function sha256(message) {
	// encode as UTF-8
	const msgBuffer = await new TextEncoder().encode(message);
	// hash the message
	const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
	// convert bytes to hex string
	return [...new Uint8Array(hashBuffer)]
		.map((b) => b.toString(16).padStart(2, "0"))
		.join("");
}
export default {
	async fetch(request, env, ctx) {
		const url = new URL(request.url);
		if (url.pathname.startsWith('/api/summary')) {
			let response
			if (request.method == 'OPTIONS') {
				response = new Response('')
				addHeaders(response)
				return response
			}
			if (request.method !== 'POST') {
				return new Response('error method', { status: 403 });
			}
			if (url.searchParams.get('token') !== env.TOKEN) {
				return new Response('error token', { status: 403 });
			}
			let body = await request.json()
			const hash = await sha256(body.content)
			const cache = caches.default
			let cache_summary = await cache.match(`http://objects/${hash}`)
			if (cache_summary) {
				response = new Response(
					JSON.stringify({
						summary: (await cache_summary.json()).choices[0].message.content
					}),
					{ headers: { 'Content-Type': 'application/json' } },
				)
				addHeaders(response)
				return response
			}
			const cache_db = await env.DB.prepare('Select summary from posts where hash = ?').bind(hash).first("summary")
			if (cache_db) {
				response = new Response(
					JSON.stringify({
						summary: cache_db
					}),
					{ headers: { 'Content-Type': 'application/json' } },
				)
				addHeaders(response)
				ctx.waitUntil(cache.put(hash, new Response(
					JSON.stringify({
						choices: [
							{
								message: {
									content: cache_db,
								}
							}
						]
					}),
					{ headers: { 'Content-Type': 'application/json' } },
				)))
				return response
			}
			const init = {
				body: JSON.stringify({
					"model": env.MODEL,
					"messages": [
						{
							"role": "system",
							"content": "你是一个摘要生成工具,你需要解释我发送给你的内容,不要换行,不要超过200字,不要包含链接,只需要简单介绍文章的内容,不需要提出建议和缺少的东西,不要提及用户.请用中文回答,这篇文章讲述了什么?"
						},
						{
							"role": "user",
							"content": body.content
						}
					],
					"safe_mode": false
				}),
				method: "POST",
				headers: {
					"content-type": "application/json;charset=UTF-8",
					"Authorization": env.AUTH
				},
			};
			const response_target = await fetch(env.API, init);
			const resp = await response_target.json()
			response = new Response(
				JSON.stringify({
					summary: resp.choices[0].message.content
				}),
				{ headers: { 'Content-Type': 'application/json' } },
			)
			ctx.waitUntil(cache.put(`http://objects/${hash}`, response_target))
			await env.DB.prepare('INSERT INTO posts (hash, summary) VALUES (?1, ?2)').bind(hash, resp.choices[0].message.content).run()
			addHeaders(response)
			return response
		}
		return new Response('Hello World!');
	},
};

添加环境变量

进入 worker 的设置->变量添加几个环境变量

  • API: openai 或其他镜像站地址, https://xxxx/v1/chat/completions
  • AUTH: api key, Bearer sk-*******
  • MODEL: 使用的模型
  • Token: 自定义,用于请求生成摘要时鉴权

绑定 D1 数据库

为了不重复生成摘要,使用了 Cloudflare caches.defaultD1 作为缓存,只有同一篇文章第一次才会消耗

新建 D1 数据库,创建一个 posts 表,字段分别为 hashsummary ,表结构如下:

在 Workers 中绑定 D1 数据库

前端搭建

新建一个 js 文件,粘贴下列代码:

summary/summary.js at main · sun-i/summary (github.com)

并将 381 行的链接修改为上述搭建的后端 woker 链接,token 修改为自定义的 token

然后在 blog 页面内引入下列代码

<!-- 可以在网页结构的任何位置插入,只要你能够 -->
<script src="你新建的js文件"></script>

<!-- 但要确保的是,下列代码一定要在上述 js 之后插入 -->
<script data-pjax defer>
  new ChucklePostAI({
    // 文章内容所在的元素属性的选择器,也是AI挂载的容器,AI将会挂载到该容器的最前面
    el: '#post>#article-container',
    summary_directly: true,
    rec_method: 'web',
    // 若网站开启了 PJAX, 则开启
    pjax: true,
  })
</script>

el 参数不同 blog 会有不同,可以参考 通用教程 | TianliGPT (zhheo.com)获取

参考

博客 AI 摘要生成 - 软件开发 - LINUX DO

qxchuckle/Post-Summary-AI: 一个较通用的,生成网站内文章摘要,并推荐相关文章的 AI (github.com)

通用教程 | TianliGPT (zhheo.com)

21 Likes

好帖,可以回去试试

2 Likes

GOOD!

1 Like

建议后端对跨域进行限制,因为如果不使用后端来生成的话,token就没有什么意义了,f12就可以看见。

2 Likes

嘶确实,应该可以用cf直接限制,后面再改改

typecho的那个el:应该是啥

Sakurairo: 花里胡哨(雾
但是如果不改主题文件只能用官方 Key

看那个通用教程里面找下吧

linuxdo上传不了文件,发布了消息?

吐掉了,一直弹403error,私信,聊天都不行

学习

感谢

麻了,解决了跨域、解决了广告屏蔽、但是解决不了域名问题,使用默认分配的域名就超时,绑定域名就403,为啥啊

有没有更新

感谢佬分享

感谢分享

notion next怎么设置

2 Likes

已安装部署, 直接返回403 跨域

跨域在CF规则中设置就可以,但是这个解决了还有其他问题