巧用Cloudflare功能实现Z-BlogPHP智能AI摘要
特别鸣谢@Mayx的鼎力支持,使小道实现了巧用Cloudflare功能实现Z-BlogPHP智能AI摘要的基本功能。
思路
https://mayx.eu.org/2024/07/03/ai-summary
今日与大佬共同努力实现了运用Cloudflare的Workers与数据库实现Z-BlogPHP智能AI摘要的基本功能。
步骤
首先在Cloudflare的Workers选择ai,创建LLM App,部署之后,将下述代码粘贴再部署一遍。
async function sha(str) { const encoder = new TextEncoder(); const data = encoder.encode(str); const hashBuffer = await crypto.subtle.digest("SHA-256", data); const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array const hashHex = hashArray .map((b) => b.toString(16).padStart(2, "0")) .join(""); // convert bytes to hex string return hashHex; } export default { async fetch(request, env, ctx) { const db = env.blog_summary; const url = new URL(request.url); const query = decodeURIComponent(url.searchparams.GET('id')); const commonHeader = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': "*", 'Access-Control-Allow-Headers': "*", 'Access-Control-Max-Age': '86400', } if (query == "null") { return new Response("id cannot be none", { headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': "*", 'Access-Control-Allow-Headers': "*", 'Access-Control-Max-Age': '86400', } }); } if (url.pathname.startsWith("/summary")) { let result = awAIt db.prepare( "SELECT content FROM blog_summary WHERE id = ?1" ).bind(query).first("content"); if (!result) { return new Response("No Record", { headers: commonHeader }); } const messages = [ { role: "system", content: ` 你是一个专业的文章摘要助手。你的主要任务是对各种文章进行精炼和摘要,帮助用户快速了解文章的核心内容。你读完整篇文章后,能够提炼出文章的关键信息,以及作者的主要观点和结论。 技能 精炼摘要:能够快速阅读并理解文章内容,提取出文章的主要关键点,用简洁明了的中文进行阐述。 关键信息提取:识别文章中的重要信息,如主要观点、数据支持、结论等,并有效地进行总结。 客观中立:在摘要过程中保持客观中立的态度,避免引入个人偏见。 约束 输出内容必须以中文进行。 必须确保摘要内容准确反映原文章的主旨和重点。 尊重原文的观点,不能进行歪曲或误导。 在摘要中明确区分事实与作者的意见或分析。 提示 不需要在回答中注明摘要(不需要使用冒号),只需要输出内容。 格式 你的回答格式应该如下: 这篇文章介绍了<这里是内容> ` }, { role: "user", content: result.substring(0, 5000) } ] const stream = awAIt env.AI.run('@cf/qwen/qwen1.5-14b-chat-awq', { messages, stream: true, }); return new Response(stream, { headers: { "content-type": "text/event-stream; charset=utf-8", 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': "*", 'Access-Control-Allow-Headers': "*", 'Access-Control-Max-Age': '86400', } }); } else if (url.pathname.startsWith("/GET_summary")) { const orig_sha = decodeURIComponent(url.searchparams.get('sign')); let result = await db.prepare( "SELECT content FROM blog_summary WHERE id = ?1" ).bind(query).first("content"); if (!result) { return new Response("no", { headers: commonHeader }); } let result_sha = await sha(result); if (result_sha != orig_sha) { return new Response("no", { headers: commonHeader }); } else { let resp = await db.prepare( "SELECT summary FROM blog_summary WHERE id = ?1" ).bind(query).first("summary"); if (resp) { return new Response(resp, { headers: commonHeader }); } else { const messages = [ { role: "system", content: ` 你是一个专业的文章摘要助手。你的主要任务是对各种文章进行精炼和摘要,帮助用户快速了解文章的核心内容。你读完整篇文章后,能够提炼出文章的关键信息,以及作者的主要观点和结论。 技能 精炼摘要:能够快速阅读并理解文章内容,提取出文章的主要关键点,用简洁明了的中文进行阐述。 关键信息提取:识别文章中的重要信息,如主要观点、数据支持、结论等,并有效地进行总结。 客观中立:在摘要过程中保持客观中立的态度,避免引入个人偏见。 约束 输出内容必须以中文进行。 必须确保摘要内容准确反映原文章的主旨和重点。 尊重原文的观点,不能进行歪曲或误导。 在摘要中明确区分事实与作者的意见或分析。 提示 不需要在回答中注明摘要(不需要使用冒号),只需要输出内容。 格式 你的回答格式应该如下: 这篇文章介绍了<这里是内容> ` }, { role: "user", content: result.substring(0, 5000) } ] const answer = await env.AI.run('@cf/qwen/qwen1.5-14b-chat-awq', { messages, stream: false, }); resp = answer.response await db.prepare("UPDATE blog_summary SET summary = ?1 WHERE id = ?2") .bind(resp, query).run(); return new Response(resp, { headers: commonHeader }); } } } else if (url.pathname.startsWith("/is_uploaded")) { const orig_sha = decodeURIComponent(url.searchParams.get('sign')); let result = await db.prepare( "SELECT content FROM blog_summary WHERE id = ?1" ).bind(query).first("content"); if (!result) { return new Response("no", { headers: commonHeader }); } let result_sha = await sha(result); if (result_sha != orig_sha) { return new Response("no", { headers: commonHeader }); } else { return new Response("yes", { headers: commonHeader }); } } else if (url.pathname.startsWith("/upload_blog")) { if (request.method == "POST") { const data = await request.text(); let result = await db.prepare( "SELECT content FROM blog_summary WHERE id = ?1" ).bind(query).first("content"); if (!result) { await db.prepare("INSERT INTO blog_summary(id, content) VALUES (?1, ?2)") .bind(query, data).run(); result = await db.prepare( "SELECT content FROM blog_summary WHERE id = ?1" ).bind(query).first("content"); } if (result != data) { await db.prepare("UPDATE blog_summary SET content = ?1, summary = NULL WHERE id = ?2") .bind(data, query).run(); } return new Response("OK", { headers: commonHeader }); } else { return new Response("need post", { headers: commonHeader }); } } else { return Response.redirect("https://www.dao.js.cn", 302) } } }
其次创建数据库,选择D1 sql数据库,创建表id、content、summary。将数据库与上面部署的Workers关联,至于此后端完全完毕。
前端
Z-BlogPHP的前端主题各式各样,无法做到完全统一,但是获取文章相应三个数据的值是一样的,我也做过纯文本输出优化。如下
var ai_title = `{$article.Title}`; var ai_content = `{php} $intro = $article->Content; $intro = preg_replace('/<([a-zA-Z0-9]+)[^>]*class=["\'][^"\']*(zbaudioplugin|mochu_bill_page_div|gave)[^"\']*["\'][^>]*>.*?<\/\1>/is', '', $intro); $intro = preg_replace('/<((video|audio|iframe|source)[^>]*>.*?<\/\2>|embed[^>]*>)/is', '', $intro); $intro = preg_replace('/<script[^>]*?>.*?<\/script>/is', '', $intro); $intro = preg_replace('/<img[^>]+\>/i', '', $intro); $intro_text = strip_tags($intro); $intro_text = str_replace(array("\r", "\n"), '', $intro_text); $intro_text = str_replace('`', '', $intro_text); $intro_text = str_replace(' ', ' ', $intro_text); echo addslashes($intro_text); {/php}`; var ai_post_id = `{$article.ID}`;
具体的前端要怎么写,想实现什么样子,可以根据我页面的/sucai/js/wenzhang.js实现。至此,基本完毕,再次鸣谢@Mayx的鼎力支持,福生无量天尊。