代码改变世界

[置顶]无疆_炎戎的博文导航

2011-05-26 10:44 by 姜 萌@cnblogs, 阅读(511) 阅读, 推荐(0) 推荐, 收藏,
摘要:姜萌,网名:无疆_炎戎(Contract Me:MSN:dokhell@live.cn;QQ:871644901)分布式平台研发,智能家电PCFocus onDistributed Connected SystemBased On ……==============不一般的分隔符===================MY OPEN SOURCEhttp://www.codeplex.com/site/users/view/dokhell曾经的创业时期(2010)高性能服务器架构之路-MongoDB(一):MongoDB快速入门(java版)团队Android小作品Chinchilla Guita 阅读全文

深入剖析开源AI阅读器项目Saga Reader基于大模型的文本转换与富文本渲染优化方案

2025-06-16 08:57 by 姜 萌@cnblogs, 阅读(172) 阅读, 推荐(0) 推荐, 收藏,

引言

AI阅读器作为一种新型的内容消费工具,正在改变人们获取和处理信息的方式。本文将介绍Saga Reader项目中如何利用大型语言模型(LLM)进行网页内容抓取、智能优化和富文本渲染,特别是如何通过精心设计的提示词(prompt)引导LLM生成样式丰富的HTML内容,提升用户阅读体验。

关于Saga Reader

基于Tauri开发的著名开源AI驱动的智库式阅读器(前端部分使用Web框架),能根据用户指定的主题和偏好关键词自动从互联网上检索信息。它使用云端或本地大型模型进行总结和提供指导,并包括一个AI驱动的互动阅读伴读功能,你可以与AI讨论和交换阅读内容的想法。

Github - Saga Reader,完全开源,可外部服务0依赖,可纯本地电脑运行的AI项目。欢迎大家关注分享。🧑‍💻码农🧑‍💻开源不易,各位好人路过请给个小星星💗Star💗。

核心技术栈:Rust + Tauri(跨平台)+ Svelte(前端)+ LLM(大语言模型集成),支持本地 / 云端双模式

关键词:端智能,边缘大模型;Tauri 2.0;桌面端安装包 < 5MB,内存占用 < 20MB。

运行截图

系统架构概述

Saga Reader的内容处理流程主要包含以下几个关键步骤:

  1. 内容抓取:通过爬虫或RSS获取原始网页内容
  2. 内容净化(Purge):清理原始HTML中的无关元素
  3. 内容优化(Optimize):将净化后的内容转换为富文本格式
  4. 内容摘要(Melt):生成文章摘要
  5. 内容渲染:在前端展示优化后的内容

这些步骤形成了一个完整的内容处理管道,每个环节都由专门的处理器负责。

基于LLM的文章处理器

在Saga Reader中,文章处理的核心是ArticleLLMProcessor类,它实现了IArticleProcessor接口,负责调用LLM进行内容转换:

/// 基于LLM的文章处理器。
pub struct ArticleLLMProcessor {
    /// Agent化的生成式服务实例。
    agent: CompletionAgent,
    /// 用于与Agent交互的user prompt。
    user_prompt_command: String,
}

impl IArticleProcessor for ArticleLLMProcessor {
    async fn process(&self, input: &Article) -> anyhow::Result<Article> {
        let mut output = input.clone();
        let content = output.content.as_ref().unwrap();
        let mut chat = format!(r#"## 原内容\n"{}"\n"#, content);
        chat.push_str(self.user_prompt_command.as_str());
        let content = self.agent.completion(chat).await?;
        output.content.replace(content);
        Ok(output)
    }
}

这个处理器的工作方式很直观:它接收一篇文章,将文章内容与预设的提示词组合,发送给LLM,然后用LLM的输出替换原始内容。

优化器(Optimizer)的实现

在内容处理管道中,优化器(Optimizer)是将净化后的内容转换为富文本格式的关键组件:

pub struct Optimizer {}

impl IPresetArticleLLMProcessor for Optimizer {
    fn new_processor(llm_section: LLMSection) -> anyhow::Result<ArticleLLMProcessor> {
        let options = AITargetOption {
            temperature: Some(0.1),
            ..Default::default()
        };
        ArticleLLMProcessor::new(llm_section, SYSTEM_PROMPT.into(), USER_PROMPT_COMMAND_OPTIMIZE.into(), options)
    }
}

优化器使用较低的temperature值(0.1),这有助于生成更加确定性的输出,确保HTML结构的一致性和稳定性。

从Markdown到富HTML的转变

最近的一项重要升级是将LLM的输出从简单的Markdown转变为样式丰富的HTML。这一转变的核心在于系统提示词(System Prompt)的设计。

系统提示词设计

新的系统提示词将LLM定位为"专业内容设计师",要求它生成"视觉现代化的HTML电子邮件片段":

You are to act as a professional Content Designer. Your task is to convert the provided article into **visually modern HTML email snippets** that render well in modern email clients like Hotmail.

Few-shot示例约束

提示词中包含了多种HTML组件的模板,作为few-shot示例,引导LLM按照特定的样式生成内容:

  1. 标准段落:用于介绍、结论和过渡文本
  2. 要点列表:用于组织多个核心观点
  3. 强调文本:用于突出关键词或短语
  4. 引用块:用于突出重要观点或原文引用
  5. 图片块:用于嵌入文章中的图片

例如,要点列表的模板如下:

<ul style="margin:20px 0; padding-left:0; list-style-type:none;">
<li style="position:relative; margin-bottom:12px; padding-left:28px; font-family:'Google Sans',Roboto,Arial,sans-serif; font-size:15px; line-height:1.6" class="text-surface-700-300">
    <span class="preset-filled-primary-500" style="position:absolute; left:0; top:0; width:18px; height:18px; border-radius:50%; color:white; text-align:center; line-height:18px; font-size:12px;">1</span>
    Description of the first key point
</li>
</ul>

输出要求

提示词还详细规定了输出的要求,包括:

  • 美观优雅的设计,和谐的配色方案
  • 一致的视觉风格
  • 将Markdown图片链接转换为HTML img标签
  • 使用多种视觉元素增强可读性
  • 移除与正文无关的操作性信息
  • 翻译为中文
  • 使用过渡文本连接各个组件
  • 适当引用重要的原文片段
  • 使用高亮样式标记关键点

前端渲染实现

在前端,ArticleRenderWidget.svelte组件负责渲染优化后的内容:

<script lang="ts">
	/** eslint-disable svelte/no-at-html-tags */
	import type { ArticleRenderProps, ArticleRenderType } from './types';
	import Markdown from './Markdown.svelte';
	import { removeCodeBlockWrapper } from '$lib/utils/text';
	import { featuresApi } from '$lib/hybrid-apis/feed/impl';
	import { onMount } from 'svelte';

	const { value }: ArticleRenderProps = $props();
	const purgedHtml = $derived(removeCodeBlockWrapper(value));
	const renderType: ArticleRenderType = $derived(purgedHtml[0] === '<' ? 'html' : 'markdown');
	let htmlContainer: HTMLDivElement | null = $state(null);

	onMount(() => {
		if (!htmlContainer) return;
		const anchorClickInterceptor = (event: MouseEvent) => {
			// 检查点击的元素是否是一个链接
			const target = event.target as HTMLElement;
			if (target?.tagName === 'A') {
				// 阻止默认的链接跳转行为
				event.preventDefault();
				// 获取链接的 href 属性
				const url = (target as HTMLAnchorElement).href;
				// 调用特定函数来处理链接
				featuresApi.open_article_external(url);
			}
		};
		(htmlContainer as HTMLDivElement).addEventListener('click', anchorClickInterceptor);
		return () => {
			(htmlContainer as HTMLDivElement).removeEventListener('click', anchorClickInterceptor);
		};
	});
</script>

{#if renderType === 'html'}
	<div bind:this={htmlContainer} class="p-6 preset-filled-surface-50-950">{@html purgedHtml}</div>
{:else}
	<Markdown {value} />
{/if}

该组件能够智能判断内容类型,对HTML和Markdown分别采用不同的渲染方式:

  • 对于HTML内容,直接使用Svelte的{@html}指令渲染
  • 对于Markdown内容,使用Markdown.svelte组件渲染

此外,组件还实现了链接点击拦截,确保外部链接在适当的环境中打开。

内容处理流水线

FeaturesAPIImpl中,我们可以看到完整的内容处理流水线:

async fn process_article_pipelines(
    &self,
    article: &mut Article,
    purge: &ArticleLLMProcessor,
    optimizer: &ArticleLLMProcessor,
    melt: &ArticleLLMProcessor,
) -> anyhow::Result<(Article, Article, Article)> {
    let out_purged_article = purge.process(article).await?;
    info!(
        "article purged, title = {}, source_link = {}, optimizing",
        article.title, article.source_link
    );

    let out_optimized_article = optimizer.process(&out_purged_article).await?;
    info!(
        "purged article optimized, title = {}, melting",
        out_purged_article.title
    );
    if let Some(optimized_content) = out_optimized_article.content.clone() {
        if optimized_content.contains("QINO-AGENTIC-EXECUTION-FAILURE") {
            return Err(anyhow::Error::msg("QINO-AGENTIC-EXECUTION-FAILURE"));
        }
    }

    let out_melted_article = melt.process(&out_optimized_article).await?;
    info!(
        "optimized article melted, title = {}, recording",
        out_melted_article.title
    );

    Ok((
        out_purged_article,
        out_optimized_article,
        out_melted_article,
    ))
}

这个方法依次调用三个处理器:

  1. purge:清理原始HTML
  2. optimizer:将净化后的内容转换为富文本
  3. melt:生成文章摘要

处理完成后,返回三个版本的文章,分别对应处理流程的三个阶段。

技术亮点与创新

1. 基于Few-shot的HTML样式约束

通过在系统提示词中提供HTML组件的模板,我们实现了对LLM输出的精确控制。这种few-shot示例约束的方法,使LLM能够生成符合预期样式的HTML内容,而不是简单的文本或基础Markdown。

2. 多模态内容处理管道

Saga Reader实现了一个完整的内容处理管道,从原始网页到富文本展示,每个环节都由专门的处理器负责。这种模块化设计使系统易于维护和扩展。

3. 智能渲染适配

前端组件能够智能判断内容类型,对HTML和Markdown分别采用不同的渲染方式,确保最佳的展示效果。

结论

Saga Reader项目通过精心设计的提示词和完整的内容处理管道,成功地将大型语言模型应用于文本转换与富文本渲染优化。这种方法不仅提升了用户阅读体验,也为AI辅助内容处理提供了一个可行的实践范例。

未来,我们可以进一步探索更多的视觉元素和交互方式,使AI生成的内容更加丰富多样,更好地满足用户的阅读需求。同时,随着大型语言模型能力的不断提升,我们也可以期待更加智能和个性化的内容处理方案。

📝 Saga Reader系列技术文章

【实战】基于 Tauri 和 Rust 实现基于无头浏览器的高可用网页抓取

2025-06-14 19:18 by 姜 萌@cnblogs, 阅读(318) 阅读, 推荐(1) 推荐, 收藏,
摘要:一、背景 在 Saga Reader 的早期版本中,存在对网页内容抓取成功率不高的问题。主要原因是先前采用的方案为后台进程通过 reqwest 直接发起 GET 请求获取网站 HTML 的方案,虽然仿真了Header内容,但仍然会被基于运行时的反爬机制(如 Browser指纹交叉验证、运行时行为识别 阅读全文

Svelte 5 状态管理全解析:从响应式核心到项目实战

2025-06-10 13:16 by 姜 萌@cnblogs, 阅读(198) 阅读, 推荐(0) 推荐, 收藏,
摘要:Svelte 5 的状态管理以 "编译时优化" 为核心,通过 响应式声明(Reactive Declarations) 和 状态容器(Stores) 的组合,实现了简洁高效的状态控制。本文将结合 Svelte 5 官方文档(Svelte 5 Stores 文档)与 Saga Reader 项目的实战 阅读全文

Svelte 5状态管理实战:基于Tauri框架的AI阅读器Saga Reader开发实践

2025-06-08 14:33 by 姜 萌@cnblogs, 阅读(164) 阅读, 推荐(1) 推荐, 收藏,
摘要:一、项目背景:当AI阅读遇到跨平台需求 Saga Reader(麒睿智库)是一款基于AI技术的轻量级跨平台阅读器,核心功能涵盖RSS订阅、内容智能抓取、AI内容处理(如翻译、摘要)及本地存储。项目采用Rust(后端)+Svelte(前端)+Tauri(跨平台框架)的技术组合,目标是在老旧设备上实现" 阅读全文

Svelte 5 在跨平台 AI 阅读助手中的实践:轻量化前端架构的极致性能优化

2025-06-07 09:14 by 姜 萌@cnblogs, 阅读(239) 阅读, 推荐(2) 推荐, 收藏,
摘要:📌 一、引言:为什么选择 Svelte 5 来构建 Saga Reader 的前端? 作为一个典型的前端开发者,去年在为公司调研Rust前端工具链、LLM应用开发技术体系的时候,对这类技术领域产生了浓厚的兴趣,也是出于早期曾经是一名Android移动应用个人开发者角色的经历,习惯性的给自己设定了一 阅读全文

揭秘 Saga Reader 智能核心:灵活的多 LLM Provider 集成实践 (Ollama, GLM, Mistral 等)

2025-05-26 11:53 by 姜 萌@cnblogs, 阅读(48) 阅读, 推荐(0) 推荐, 收藏,
摘要:前言 随着大型语言模型 (LLM) 的飞速发展,将其集成到各类应用中以提升智能化水平已成为一种趋势。Saga Reader 作为一款现代化的 RSS 阅读器,在 LLM 集成方面做出了前瞻性的设计,不仅支持通过 Ollama 实现本地化 LLM 功能,还具备了接入多种在线 LLM 服务(如智谱 GL 阅读全文

【实战】Rust与前端协同开发:基于Tauri的跨平台AI阅读器实践

2025-05-26 09:18 by 姜 萌@cnblogs, 阅读(411) 阅读, 推荐(2) 推荐, 收藏,
摘要:一、背景与目标:为什么做一个“非典型”的RSS阅读器? 在信息爆炸的时代,RSS依然是高效获取结构化内容的重要方式,但市面上主流阅读器要么功能冗余(如集成社交属性),要么技术栈陈旧(依赖Electron导致内存占用高、性能差)。我们希望打造一款简约轻量、高效率、高性能、隐私安全的RSS阅读器,核心需 阅读全文

【实战】深入浅出 Rust 并发:RwLock 与 Mutex 在 Tauri 项目中的实践

2025-05-19 10:43 by 姜 萌@cnblogs, 阅读(298) 阅读, 推荐(2) 推荐, 收藏,
摘要:引言 你是否遇到过 Rust 并发场景下的资源竞争、性能瓶颈? 当多个线程同时抓取网页导致 IP 被封、多线程读写本地数据引发一致性问题时,如何优雅地实现线程安全? 本文结合开源项目 Saga Reader 的真实开发场景,深度解析 Arc/Mutex/RwLock 的实战技巧,带你从 “踩坑” 到 阅读全文

开源我的一款自用AI阅读器,引流Web前端、Rust、Tauri、AI应用开发

2025-05-13 10:26 by 姜 萌@cnblogs, 阅读(652) 阅读, 推荐(3) 推荐, 收藏,
摘要:前沿 - 为什么要做这个开源软件 作为一个典型的前端开发者,去年在为公司调研Rust前端工具链、LLM应用开发技术体系的时候,对这类技术领域产生了浓厚的兴趣,也是出于早期曾经是一名Android移动应用个人开发者角色的经历,习惯性的给自己设定了一个工具主题的产品,用来练习使用Rust与AI的应用开发 阅读全文

未雨绸缪,Win8 Metro开发实例分享

2012-02-10 17:01 by 姜 萌@cnblogs, 阅读(3545) 阅读, 推荐(3) 推荐, 收藏,
摘要:着手win8metro的东西有一段时间了,在这里用一个实实在在的小游戏设计分享给大家。我使用的开发环境是官方9月份公开的Windows Developer Preview(含VS11 P版),不过某些厂商内部可以拿到Developer Preview Update和Win8 Beta。目前开发语言可以选择C++,C#,Js,vb和其他.NET语言。基础API和.NET BCL几乎差不多,做了很多删减(比如socket),去掉了所有阻塞性的同步api,IO上只有异步api供使用(不过由于c#4.5的await和async,异步代码写起来和同步代码差不多,开发体验灰常不错哦),一些资源路径和使.. 阅读全文
点击右上角即可分享
微信分享提示