110 lines
7.9 KiB
Markdown
110 lines
7.9 KiB
Markdown
> 适用版本:v0.2.2
|
||
|
||
`rewrite` 组件是 zenfeed 中负责对信息流内容进行动态处理和转换的核心模块。它允许用户通过声明式的规则配置,利用大型语言模型 (LLM) 等工具,对内容的元数据(标签)进行修改、丰富、过滤,甚至决定是否丢弃某条信息。
|
||
|
||
## 1. 设计理念与哲学
|
||
|
||
* **Prometheus 的 `relabel_config`**: 借鉴其强大的标签重写能力。在 Prometheus 中,`relabel_config` 允许用户在采集指标前后动态地修改标签集,实现服务发现、指标过滤和路由等高级功能。`rewrite` 组件将此思想应用于信息流处理,将每一条信息(如一篇文章、一个帖子)视为一个标签集,通过规则来操作这些标签。
|
||
* **管道 (Pipeline) 处理模式**: 信息的处理过程被设计成一个可配置的 ETL 管道。每个规则是管道中的一个处理阶段,信息流经这些规则,逐步被转换和打标。这种模式使得复杂的处理逻辑可以被分解为一系列简单、独立的步骤,易于理解和维护。
|
||
* **AI 能力的模块化与按需应用**: 大型语言模型 (LLM) 被视为一种强大的"转换函数"。用户可以根据需求,在规则中指定使用哪个 LLM、配合什么样的提示词 (Prompt) 来处理特定的文本内容(例如,从文章正文生成摘要、分类、评分等)。这种设计使得 AI 能力可以灵活地嵌入到信息处理的任意环节。
|
||
* **内容即标签 (Content as Labels)**: 这是 zenfeed 的一个核心抽象。原始信息(如标题、正文、链接、发布时间)和经过 AI 或规则处理后产生的衍生信息(如类别、标签、评分、摘要)都被统一表示为键值对形式的"标签"。这种统一表示简化了后续的查询、过滤、路由和展示逻辑。
|
||
* **声明式配置优于命令式代码**: 用户通过 YAML 配置文件定义重写规则,而不是编写代码来实现处理逻辑。这降低了使用门槛,使得非程序员也能方便地定制自己的信息处理流程,同时也使得配置更易于管理和版本控制。
|
||
|
||
> 简单说这是一条专门针对 Feed 处理的可配置工作流
|
||
|
||
## 2. 业务流程
|
||
|
||
内容重写组件的核心工作流程是接收一个代表信息单元的标签集 (`model.Labels`),然后按顺序应用预定义的重写规则 (`Rule`),最终输出一个经过修改的标签集,或者指示该信息单元应被丢弃。
|
||
|
||
其处理流程可以概括为:
|
||
|
||
1. **接收标签集**: 组件的入口是一个 `model.Labels` 对象,代表待处理的信息单元。
|
||
2. **顺序应用规则**: 系统会遍历用户配置的每一条 `Rule`。
|
||
3. **规则评估与执行**: 对于每一条规则,系统会:
|
||
* **定位源文本**: 根据规则指定的 `source_label` (默认为 `content`),找到相应的文本内容。
|
||
* **条件检查**: 检查源文本是否满足规则中声明的 `skip_too_short_threshold`(最小长度,默认为300字符)。若不满足,则跳过当前规则。
|
||
* **文本转换 (可选)**: 若规则声明了 `transform` (如通过 `to_text` 使用 LLM 和特定 `Prompt` 进行处理),则源文本会被转换为新文本。此转换结果将用于后续的匹配。
|
||
* **模式匹配**: 使用规则中声明的 `match` 正则表达式 (默认为 `.*`) 来匹配(可能已被转换过的)文本。若不匹配,则跳过当前规则。
|
||
* **执行动作**: 若文本匹配成功,则执行规则声明的 `Action`:
|
||
* `ActionDropFeed`: 指示应丢弃当前信息单元,处理流程终止。
|
||
* `ActionCreateOrUpdateLabel`: 使用(可能已被转换过的)匹配文本,为规则中指定的 `Label` 创建或更新标签值。
|
||
4. **输出结果**:
|
||
* 若所有规则处理完毕且未触发 `ActionDropFeed`,则返回最终修改并排序后的 `model.Labels`。
|
||
* 若任一规则触发 `ActionDropFeed`,则返回 `nil`,表示丢弃。
|
||
* 处理过程中若发生错误(如 LLM 调用失败),则会中止并返回错误。
|
||
|
||
|
||
## 3. 使用示例
|
||
|
||
以下是一些如何使用 `rewrite` 规则的场景示例:
|
||
|
||
### 示例 1: 内容分类打标
|
||
|
||
* **目标**: 根据文章内容,自动为其添加一个 `category` 标签,如 "Technology", "Finance" 等。
|
||
* **规则配置 (概念性)**:
|
||
```yaml
|
||
- source_label: "content" # 使用文章正文作为分析源
|
||
transform:
|
||
to_text:
|
||
llm: "qwen-default" # 使用名为 "qwen-default" 的 LLM 配置
|
||
prompt: "{{ .category }} 可以接着补充你额外的要求" # 使用预设的 "category" prompt 模板
|
||
match: ".+" # 匹配 LLM 返回的任何非空分类结果
|
||
action: "create_or_update_label"
|
||
label: "category" # 新标签的键为 "category"
|
||
```
|
||
* **效果**: 如果一篇文章内容是关于人工智能的,LLM 可能会返回 "Technology"。经过此规则处理后,文章的标签集会增加或更新一个标签,例如 `{"category", "Technology"}`。**后续可用于,“查询分类为 Technology 的文章”,“基于分类为 Technology 的文章发送每日科技日报”...**
|
||
|
||
### 示例 2: 基于 LLM 评分过滤低质量内容
|
||
|
||
* **目标**: 让 LLM 对文章内容进行评分 (0-10),如果评分低于 4,则丢弃该文章。
|
||
* **规则配置 (包含两条规则)**:
|
||
|
||
* **规则 2.1: 内容评分**
|
||
```yaml
|
||
- source_label: "content"
|
||
transform:
|
||
to_text:
|
||
llm: "qwen-default"
|
||
prompt: "{{ .score }} 可以接着补充你额外的要求" # 使用预设的 "score" prompt 模板
|
||
match: "^([0-9]|10)$" # 确保 LLM 返回的是 0-10 的数字
|
||
action: "create_or_update_label"
|
||
label: "ai_score" # 将评分结果存入 "ai_score" 标签
|
||
```
|
||
* **规则 2.2: 根据评分过滤**
|
||
```yaml
|
||
- source_label: "ai_score" # 使用上一条规则生成的评分作为判断依据
|
||
# 无需 Transform
|
||
match: "^[0-3]$" # 匹配 0, 1, 2, 3 分
|
||
action: "drop_feed" # 丢弃这些低分文章
|
||
```
|
||
* **效果**: 文章首先会被 LLM 评分并打上 `ai_score` 标签。如果该评分值在 0 到 3 之间,第二条规则会将其丢弃。
|
||
|
||
### 示例 3: 基于特定标签值添加新标签
|
||
|
||
* **目标**: 如果文章的 `source` 标签值是 "Hacker News",则添加一个新标签 `source_type: "community"`。
|
||
* **注意**: 当前 `ActionCreateOrUpdateLabel` 会将匹配成功的 `text` (即 `source_label` 的值或其转换结果)作为新标签的值。若要实现固定值标签,需要通过 LLM 转换。
|
||
* **规则配置 (通过 LLM 实现映射)**:
|
||
```yaml
|
||
- source_label: "source" # 源标签是 "source"
|
||
transform:
|
||
to_text:
|
||
llm: "qwen-mini"
|
||
# Prompt 需要精心设计,告诉 LLM 如何根据输入映射到输出
|
||
# 例如,Prompt 可以包含类似 "If input is 'Hacker News', output 'community'. If input is 'GitHub Trending', output 'code'." 的逻辑
|
||
prompt: |
|
||
Analyze the input, which is a news source name.
|
||
If the source is "Hacker News", output "community".
|
||
If the source is "GitHub Trending", output "code".
|
||
If the source is "V2EX", output "community".
|
||
Otherwise, output "unknown".
|
||
Return ONLY the type, no other text.
|
||
match: "^(community|code|unknown)$" # 确保 LLM 输出的是预期的类型
|
||
action: "create_or_update_label"
|
||
label: "source_type" # 新标签的键
|
||
```
|
||
* **效果**: 如果某文章的 `source` 标签值为 "Hacker News",经过 LLM 处理后(理想情况下)会输出 "community",然后 `source_type` 标签会被设置为 `{"source_type", "community"}`。
|
||
|
||
这些示例展示了 `rewrite` 组件的灵活性和强大功能,通过组合不同的源标签、转换、匹配条件和动作,可以实现复杂的内容处理和信息增强逻辑。
|
||
|
||
|