AI:基于 Eino + DeepSeek 配合 RSS 实现的每日简报功能

AI:基于 Eino + DeepSeek 配合 RSS 实现的每日简报功能

功能说明

  • 支持日期、星期
  • 支持天气、温度、湿度
  • 支持新闻摘要

直接看代码

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package daily

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"testing"
"time"

"github.com/cloudwego/eino-ext/components/model/ark"
"github.com/cloudwego/eino/components/model"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
"github.com/mmcdole/gofeed"
"github.com/stretchr/testify/require"
)

var fp = gofeed.NewParser()

func createModel(t *testing.T) model.ChatModel {
deepseek, err := ark.NewChatModel(t.Context(), &ark.ChatModelConfig{
BaseURL: "https://ark.cn-beijing.volces.com/api/v3",
APIKey: "[key]", // 请填写你的 API Key
Model: "deepseek-v3-241226",
})
require.NoError(t, err)
return deepseek
}

func TestDailyReport(t *testing.T) {
chain := compose.NewChain[string, *schema.Message]().
AppendParallel(
compose.NewParallel().
AddLambda("weather", compose.InvokableLambda(func(ctx context.Context, input string) (string, error) {
return fetchWeather(t), nil
})).
AddGraph("news",
compose.NewChain[string, string]().
AppendLambda(compose.InvokableLambda(func(ctx context.Context, input string) (*gofeed.Feed, error) {
return fp.ParseURL(input)
})).
AppendLambda(compose.InvokableLambda(func(ctx context.Context, input *gofeed.Feed) (string, error) {
var buffer bytes.Buffer
for i, item := range input.Items {
buffer.WriteString(fmt.Sprintf("%d. %s\n", i+1, item.Title))
}
return buffer.String(), nil
})),
),
).
AppendLambda(compose.InvokableLambda(func(ctx context.Context, input map[string]any) (string, error) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("今日日期: %s, %s\n\n", time.Now().Format(time.DateOnly), time.Now().Weekday()))
buffer.WriteString(fmt.Sprintf("今日天气: %s\n", input["weather"].(string)))
buffer.WriteString(fmt.Sprintf("今日新闻:\n%s\n", input["news"].(string)))

return buffer.String(), nil
})).
AppendGraph(
compose.NewChain[string, *schema.Message]().
AppendLambda(compose.InvokableLambda(func(ctx context.Context, input string) ([]*schema.Message, error) {
return []*schema.Message{
schema.SystemMessage(`You are a highly professional news summarization expert, capable of categorizing and organizing the article materials I provide into a summary format, presented in a point-by-point manner. Ultimately, deliver it to me in plain text, without Markdown syntax, in Chinese, as today's news summary.
**Format as follows:**

今天是 2021 年 3 月 19 日,星期五。天气晴朗,气温 20 度。以下是今日新闻摘要:

1. 企业动态:

- 华为将于3月20日至21日举行中国合作伙伴大会2025。
- 模速空间、无问芯穹和上海仪电推出国内首个“算力生态超市”。

2. 科技行业:

- 最低调“六小虎”阶跃星辰开年首秀,Agent落地智能终端,印奇也参与。
- “AI服务商”阿里巴巴值得重估。

...
`),
schema.UserMessage("Here are the articles:\n\n" + input),
}, nil
})).AppendChatModel(createModel(t)),
)

r, err := chain.Compile(t.Context())
require.NoError(t, err)

message, err := r.Stream(t.Context(), "https://36kr.com/feed")
require.NoError(t, err)

for {
msg, err := message.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
}
require.NoError(t, err)
fmt.Print(msg.Content)
}
}

type weatherResult struct {
Status string `json:"status"`
Count string `json:"count"`
Info string `json:"info"`
InfoCode string `json:"infocode"`
Lives []struct {
Province string `json:"province"`
City string `json:"city"`
AdCode string `json:"adcode"`
Weather string `json:"weather"`
Temperature string `json:"temperature"`
WindDirection string `json:"winddirection"`
WindPower string `json:"windpower"`
Humidity string `json:"humidity"`
ReportTime string `json:"reporttime"`
TemperatureFloat string `json:"temperature_float"`
HumidityFloat string `json:"humidity_float"`
} `json:"lives"`
}

func fetchWeather(t *testing.T) string {
resp, err := http.Get("https://restapi.amap.com/v3/weather/weatherInfo?key=[key]&city=440300&extensions=base") // 此处使用的是高德 API(有免费额度),此处使用的是深圳天气,实际应用,可自定义城市编码,或者做个映射关系。附 API 地址:https://lbs.amap.com/api/webservice/guide/api/weatherinfo/#t1
require.NoError(t, err)
defer resp.Body.Close()

var res weatherResult
require.NoError(t, json.NewDecoder(resp.Body).Decode(&res))
require.Equal(t, "1", res.Status)

return fmt.Sprintf("天气: %s,气温: %s,湿度: %s", res.Lives[0].Weather, res.Lives[0].Temperature, res.Lives[0].Humidity)
}

阅读更多
用 Llama3.1 解决大模型的经典问题:9.9 和 9.11 谁大

用 Llama3.1 解决大模型的经典问题:9.9 和 9.11 谁大

AI 的回复

首先是 GPT-4 的回复:

1
2
3
4
5
6
7
8
9
10
9.11 比 9.9 大。

在比较两个数字时,我们从左到右逐位进行比较:

1. 比较整数部分:9 和 9 相等。
2. 比较小数部分:.11 和 .9。

在小数部分比较时,.11 可以分解为 0.11,而 .9 可以分解为 0.90。显然,0.11 < 0.90。

因此,9.11 比 9.9 大。
阅读更多
Go:一种很“新”的类型断言

Go:一种很“新”的类型断言

分享组件:go-kratos-ecosystem/components

注:该功能计划在 v2.11.0 版本中发布。PR

引言

我们在早期使用 Go 做类型断言的时候,大多是这么用:

1
2
3
4
5
6
var v interface{}
v = 10

if _, ok := v.(int); ok {
fmt.Println("v is int")
}

那现在,我们只需要这么用:

1
2
3
if IsType[int](v) {
fmt.Println("v is int")
}
阅读更多
关于 12306 售票的一些思考研究

关于 12306 售票的一些思考研究

声明:本内容不一定是官方实际应用情况,仅为个人思考研究的方案。

问题:

买过票的都知道,乘客可以选车次的某站到某站,那意味着这期间的站点,该座位是不可售的。

那么,12306 是如何计算这个座位的售票情况的呢?

阅读更多
没用的小知识:人民币复印

没用的小知识:人民币复印

声明:本文仅为科普实践,不鼓励任何人进行非法活动。(保命要紧🤐🤐🤐🤐)

黑白可复印;彩印不行,会变成黑纸。效果如图:

阅读更多
随想:关于「及时行乐」

随想:关于「及时行乐」

昨晚突然想到一个问题:

假如我知道我只能活 1 天,我会怎么活?

然后我放大点:1年、10年、50年,甚至100年。

想想,结果好像都差不多,都是「及时行乐」。

那我现在每天辛辛苦苦,是图个啥呢? 🤔🤔

【架构设计】Websocket 消息格式之系统事件

【架构设计】Websocket 消息格式之系统事件

封面来源网络,如有侵权,请联系删除。

命名规范

系统事件的命名以 socket: 为前缀,例如 socket:connect 事件。

事件列表

WSS 连接成功 socket:connected

  • 事件发送方:服务端

  • 事件信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "event": "socket:connected",
    "data": {
    "socket_id": "1ba1b6cb-0127-48f8-b5a5-9fd79a7c68a6", // socket 唯一ID
    "heart": {
    "interval": 30, // 心跳间隔,单位:秒(用户客户端发送心跳事件)
    "timeout": 25 // 心跳超时,单位:秒(用于客户端接收心跳事件)
    }
    }
    }
阅读更多
2023-丽江游

2023-丽江游

行程

  • D1:深圳飞丽江,入住酒店,夜游丽江古城
  • D2:玉龙雪山,印象丽江,蓝月谷
  • D3:茶马古道骑马,坐车前往泸沽湖,泸沽湖划船,篝火晚会
  • D4:坐车返丽江,自由行(夜游束河古镇)
  • D5:丽江飞深圳

行程日期:2023-05-24 ~ 2023-05-28

相册

D1

阅读更多
分享:CSP 模型

分享:CSP 模型

CSP 是 Communicating Sequential Process 的简称,中文直译为通信顺序进程,或者叫做交换信息的循序进程,是用来描述并发系统中进行交互的一种模式。

CSP 最早出现于计算机科学家 Tony Hoare 在 1978 年发表的论文中(你可能不熟悉 Tony Hoare 这个名字,但是你一定很熟悉排序算法中的 Quicksort 算法,他就是 Quicksort 算法的作者,图灵奖的获得者)。最初,论文中提出的 CSP 版本在本质上不是一种进程演算,而是一种并发编程语言,但之后又经过了一系列的改进,最终发展并精炼出 CSP 的理论。CSP 允许使用进程组件来描述系统,它们独立运行,并且只通过消息传递的方式通信。

就像 Go 的创始人之一 Rob Pike 所说的:“每一个计算机程序员都应该读一读 Tony Hoare 1978 年的关于 CSP 的论文。”他和 Ken Thompson 在设计 Go 语言的时候也深受此论文的影响,并将 CSP 理论真正应用于语言本身(Russ Cox 专门写了一篇文章记录这个历史),通过引入 Channel 这个新的类型,来实现 CSP 的思想。

—— Go 并发编程实战课-Channel

阅读更多
分享:克拉克三大定律(Clarkes 三法则)

分享:克拉克三大定律(Clarkes 三法则)

克拉克基本定律(英语:Clarke’s three laws)是英国著名科幻作家亚瑟·查理斯·克拉克积累有关科学文化方面的经验提出的。

  • 定律一:如果一个年高德劭的杰出科学家说,某件事情是可能的,那他几乎就是正确的;但如果他说,某件事情是不可能的,那他很可能是错误的;
  • 定律二:要发现某件事情是否可能的界限,唯一的途径是跨越这个界限,从可能跑到不可能中去;
  • 定律三:在任何一项足够先进的技术和魔法之间,我们无法做出区分。
阅读更多
有度 Golang 版 SDK
PHP 代码风格检测/修复工具:Laravel Pint

RocketMQ 安装

此处基于 Macbook Pro 的安装演示

环境依赖

安装 JDK

阅读更多

SPM 淘宝-导购效果跟踪

摘录为主

什么是 SPM

SPM是淘宝社区电商业务(xTao)为外部合作伙伴(外站)提供的一套跟踪引导成交效果数据的解决方案。

下面是一个跟踪点击到宝贝详情页的引导成交效果数据的SPM示例:

http://detail.tmall.com/item.htm?id=3716461318&&spm= 2014.123456789.1.2

其中spm=2014.123456789.1.2 便是下文所说的SPM编码

SPM编码:用来跟踪页面模块位置的编码,标准spm编码由4段组成,采用a.b.c.d的格式(建议 全部使用数字),其中,

阅读更多

Hyperf:JSON-RPC 服务

Server 端

定义服务(示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

declare(strict_types=1);

namespace App\JsonRpc;

use Hyperf\RpcServer\Annotation\RpcService;

/**
* 注意,如希望通过服务中心来管理服务,需在注解内增加 publishTo 属性.
* @RpcService(name="TestsService", protocol="jsonrpc-http", server="jsonrpc-http")
*/
class TestsService
{
// 实现一个加法方法,这里简单的认为参数都是 int 类型
public function add(int $a, int $b): int
{
// 这里是服务方法的具体实现
return $a + $b;
}
}
阅读更多

LeetCode:链表中倒数第 k 个节点

题目

输入一个链表,输出该链表中倒数第 k 个节点。为了符合大多数人的习惯,本题从 1 开始计数,即链表的尾节点是倒数第 1 个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

阅读更多

LeetCode: 移动零

题目

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

1
2
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
阅读更多

LeetCode:Excel 表列名称

题目

给你一个整数 columnNumber ,返回它在 Excel 表中相对应的列名称。

例如:

A -> 1
B -> 2
C -> 3

Z -> 26
AA -> 27
AB -> 28

阅读更多

LeetCode:全排列

题目

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

1
2
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
阅读更多

LeetCode:字符串压缩

题目

字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串 aabcccccaaa 会变为 a2b1c5a3。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。

示例1:

1
2
输入:"aabcccccaaa"
输出:"a2b1c5a3"
阅读更多

LeetCode:回文排列

题目

给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。

回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。

回文串不一定是字典当中的单词。

示例1:

1
2
输入:"tactcoa"
输出:true(排列有"tacocat"、"atcocta",等等)
阅读更多