steemblog: Steem时光机 | Steem Time Machine

steemblog: Steem时光机 | Steem Time Machine

本文我们将介绍 steemblog 的最新的进展。‌

‌Image Source: Pixabay

#1 回顾

‌steemblog 是一项 Steem 文章镜像服务。‌

如果你还不了解这个服务,或许你可以通过访问:https://steemblog.github.io/@team-cn/ 作为一个案例体验一下。‌

在文章《用GitHub创建Steem文章镜像 | Mirroring Your Steem Blogs on GitHub | 免费博客备份服务: steemblog》中,我们提到过创建steemblog 服务的缘由,主要是为了更好的查看历史文章。‌

现在来看,它主要带来了以下几点助益:‌

  1. 首先,steemblog 作为一份档案和历史记录,将 steem 数据以便于阅读和查询的方式保存;虽然区块链技术被认为具有数据不可毁灭的潜力,但有一份额外的备份也存在价值;
  2. 其次,它提供了更好的用户体验访问速度,对于查询、搜索、整合个人或组织的文章,效率更高、更有愉悦感;
  3. 再次,它目前每日更新,虽然是存档,但基本可以保持是近实时的,多于过去几日的文章,也可以一并处理。

根据上一篇文章和微信群中的留言,我们已经帮以下12个账号部署了每日更新的博客镜像服务:‌

  1. @robertyan
  2. @andrewma
  3. @ericet
  4. @julian2013
  5. @annepink
  6. @bring
  7. @sunai
  8. @nostalgic1212
  9. @m18207319997
  10. @iguazi123
  11. @team-cn
  12. @lemooljiang

‌每日北京时间凌晨0点,steemblog 会自动同步镜像一次。‌

总体而言,对我来说 steemblog 是一个还比较好用的时光机(Time Machine),可以更好地学习和了解 steem 上各种有趣的事情。

下面举几个例子来看看吧:‌

#2 场景

‌以下是steemblog的几个有意思的应用场景。

(1)新手村访谈

初入CN区或者新手村的新人,想要了解一下社区内的朋友,其实没有看上去那么容易。去逐个翻看各个用户的历史文章,效率很低。‌

但新手村访谈部分解决了这个问题,我们只要看 #cn-interview 这个标签下的文章就行,但由于steemit / busy 查看标签历史或多或少都有点问题,所以也很难流畅地阅读。在以前,大概有2种方式:‌

  1. 看新手村的索引贴,例如2018版的:https://busy.org/@team-cn/2018-x61niax2i4/
  2. 阅读《Steem指南》的新手村计划部分:https://steem-guides.github.io/steemh/fl.html#section-17.3

但这两种方法更新可能不太及时,最近的访谈没有录入,而 steemblog 是及时更新的。 steemblog 的搜索功能也更快速和有针对性。‌

通过链接:https://steemblog.github.io/@team-cn/tags/cn-interview/,我们可以看到新手村历史上所有的访谈的记录:‌

最新的访谈:

最初的访谈:

按村名搜索:

screenshots from steemblog: https://steemblog.github.io/@team-cn/tags/cn-interview/

想要了解新手村 @team-cn 里的各位朋友,用 steemblog 看一看就一目了然了,确实很有趣。 这对新人也很有帮助,建议你们来看看。‌

(2)社区名人录

‌Steem是个社区,社区是由人组成的,其中有些人由于各种原因成为舆论的焦点或实际的掌权派,总是在历史上不断发生的事情。‌

—-‌

最近 #sct 标签称霸舆论,对于创始人之一的 Jack @jack8831 也接受到了大量的八卦和追捧。但要看家里有矿的男神的生辰八字,也不是这么容易的事啊。‌

steemblog 可以帮你解决这个问题。昨天服务器花了6分钟备份了 Jack @jack8831 的历史文章:https://steemblog.github.io/@jack8831/

然后,要阅读 Jack 的最新动态、历史观点、常用的标签,就容易多了。

不过韩文/朝鲜语我不懂,所以读起来还是需要借助机器翻译的力量。但不可思议的是,下面早期文章的图中的“基本英语”我居然看懂了!!

Jack 好像曾经喜欢给韩国朋友们分享英文,标题中也有“鲑鱼”(연어)的前缀:https://steemblog.github.io/@jack8831/tags/englishforkorean/

所以 steemcoinpan 上鲑鱼泛滥,似乎也在情理之中了。

‌screenshots from steemblog: https://steemblog.github.io/@jack8831/

Jack和团队做的 steemcoinpan 风头正劲,要向韩国朋友们学习,也不妨了解下他们的过往~‌

—-‌

回到CN区。‌

我之前读过《Steem指南》中的 《Steem 十讲》,觉得 刘美女 @deanliu 的观点和文章很独到,所以特地同步了刘美女的文章到 steemblog (服务器时间 18 分钟,刘美女的文章和标签都很多,同步较慢):https://steemblog.github.io/@deanliu/

刘美女已经写了 1400+ 文章,可谓笔耕不辍、涉猎广泛。

最常用的首标签(类别):除了CN区话题,摄影、旅游、美食、讨论 steem / blockchain、以及聊娃(TT)是日常。

最初的文章都是英文,拓荒时期CN区还没有形成规模吧:

老道茶馆的一些过往:https://steemblog.github.io/@deanliu/tags/laodr-teahouse/

‌screenshots from steemblog: https://steemblog.github.io/@deanliu/

不过其实之前 大鹏 @dapeng 也曾经帮助 刘美女 @deanliu 备份过文章( GitHub上的 文章 Markdown 源码 / Netlify 备份站点 ),只是并没有设置成自动更新、以及网站模板较简单罢了。从备份的角度,steemblog 也只是另一种工具和服务而已,并不新奇,但希望它也有所帮助。‌

—‌

我不是很喜欢八卦,所以八卦社区名人和豪杰们的任务,就交给大家自己去完成了。不过想要用 steemblog 来八卦哪些名人倒是可以给我提供一个名单,我可以帮助备份到 steemblog。‌

(3)活动的回顾

‌活动一般以标签的形式进行推广,所以与上面看到的以个人账户为主的文章展示方式并不相同。对于标签文章的备份,steemblog 也可以支持,但目前并没有部署单独对标签进行备份的案例,而且需要对界面做少量修改。‌

这里展示一下 我 @robertyan 主办的对联活动如何查看。‌

以 https://steemblog.github.io/@{username}/tags/{tag}/ 的格式,就能查看标签下的文章。由于对联活动的文章都是我发表的,所以可以这样查看:https://steemblog.github.io/@robertyan/tags/cn-couplet/

‌screenshot from steemblog: https://steemblog.github.io/@robertyan/tags/cn-couplet/

已经办到第四期了,欢迎对语言和文化有兴趣的朋友参与。这期的题目是给刚刚升级到50/60/70级的朋友们写寿联https://busy.org/@robertyan/qs55nyh8x9

(4)奇文共欣赏

‌第四种用途对我还是挺有帮助的:Steem上有一些极度认真的作者,对于喜欢看好文章用户,现在的steemit/busy/steempeak等界面都满足不了需求。‌

上面提到过我推崇 刘美女 @deanliu 的文章并在昨天同步到了 steemblog。其实更早之前,我还同步了 维参 @crowflew 的文章,是我之前在 steem 看到的最有意思的文章系列之一吧:https://steemblog.github.io/@crowflew/

对于阅读博客,steemblog 会显示目录方便作者通览全文和跳转到章节:

可以方便的跳转到前后文章、找到 steem的原文,以及回到顶部等:

‌screenshots from steemblog: https://steemblog.github.io/@crowflew/or-or-or-or/

当然,steem 上的奇文还有很多,如果你有觉得仔细希望阅读的作者,也不妨把他的账户分享给我,一起欣赏一下。‌

(5)揭秘黑历史

‌steemblog 不但能让有用的信息能够被快速查询到,还能让罪行无处遁形。是不是听着挺像一位侦探的?‌

好吧,下面我们来看看 机机 @julian2013 和 村长 @ericet 是如何互黑的。(其实你们是不是更愿意看 软哥 @softmetal 和 小P @pgr 互黑的桥段?)‌

到 steemblog 页面上,点击右上角的搜索进行“侦查”。‌

—-‌

机机谈论村长:

‌screenshots from https://steemblog.github.io/@julian2013/

—-‌

村长评价机机:

‌screenshots from https://steemblog.github.io/@ericet/

—-‌

看上去机机从来没说过村长“坏”话,而村长可不是这样。真的是这样吗?

那么到底究竟说了什么呢?还是要各位自己去看才行。真相永远只有一个,看了以后不妨告诉我。‌

(6)组织的起源

‌其实相比查看人的文章,我更感兴趣组织的发展历史和起源,以及它们的生长过程,比如 @team-cn、@steemstem、@utopian-io,这里也为他们备份了镜像以便于追根溯源、温故知新:‌

@team-cn

‌screenshot from https://steemblog.github.io/@team-cn/page/45/

—-‌

@steemstem

‌screenshot from https://steemblog.github.io/@steemstem/page/28/

—-‌

@utopian-io

‌screenshot from https://steemblog.github.io/@utopian-io/page/33/

希望这些团队不忘初心,破除艰险、勇往直前。‌

—-‌

以上这些例子,不知对你来说是不是也同样有趣或有帮助。‌

如果需要帮你镜像博客,可以在文章后面留言,本次提供5个免费服务名额,先到先得~‌

#3 进展

‌相比于《用GitHub创建Steem文章镜像 | Mirroring Your Steem Blogs on GitHub | 免费博客备份服务: steemblog》中的描述,steemblog 最近也有一些功能和性能更新,这里简要介绍:‌

  1. 编译性能优化:编译速度提高了大约有20~100倍。
  2. 多平台部署:支持备份镜像到Netlify等平台。
  3. 渲染优化:由于采用了组件,进行了优化以保证用户体验
  4. 问题修复:比如类别(category)显示顺序、编译内存溢出等

(1)编译性能优化

‌其实 steemblog 的功能实现是相对比较简单的(可以参考上一篇文章)。但真正有困难的是在 hexo 的编译速度。‌

hexo 虽然号称速度极快,但实际编译的时候简直是灾难。当然,原因不能完全归罪与 hexo 框架本身,更多是 hexo theme的问题。‌

例如,以下是几个账户一开始的编译所有文章花费的时间:

@andrewma: 400+ posts, 80+ tags, ~20 mins

@ericet: 630+ posts, 260+ tags, > 60 mins

很多人的文章数量都接近这个量级,如果要全都同步的话,时间耗费还是很大的,每天更新就要消耗很多资源。‌

除了优化编译方式,另一种是采用增量更新(即只编译新增加的文章), hexo 虽然自称是支持增量更新的,但实际情况是基本不支持、且有很多潜在的问题。‌

—-‌

(a) 优化编译过程

其实原先我只打算做一个简单的服务,不想在主题(theme)优化上花太多功夫,但真正用起来才发现开源世界并不是那么美好 :) ‌

为了完成给大家的每天更新的承诺,以下是优化的方案。‌

  1. 修改主题:由于我们采用的主题(https://github.com/ppoffice/hexo-theme-icarus)较多的使用了各种控件和大量使用了标签和分类等,使得功能较为强大,但带来的问题是编译成静态页面时的成本也很高,为了修复这个主题的问题,我们fork原来的项目,采用了修改后的主题:https://github.com/steemblog/hexo-theme-icarus
  2. 大量使用缓存(cache):hexo 自带有fragment_cache和partial方法,以方便缓存重复使用的组件(如head, footer, sidebar等),但 theme 的作者有时候不清楚可以这样使用,所以我们需要修改。
  3. 控件页面分离:除了使用cache,像很多组件是可以通过 iframe 或者 ajax 异步加载的形式来展示的,这样可以在实现静态页面的时候减少很多冗余的 控件的 html,减少文件大小、提高效率。
  4. 采用时间线布局:原来的 index、tag、archive等页面,都采用了直接展示文章内容的布局,其实不是很有必要,不方便阅读而且使得静态 html 页面变得很庞大。所以我们改成了 timeline 的 layout。
  5. 增加每页文章数量:默认的分页是 6 篇文章一页,对于时间线模式是太少了,所以改成了 20 篇文章一页。编译的速度受页面数量影响很大,而一页展示的文章多了,html 页面总数自然就少了、速度会有明显提高。

策略1:修改主题:

.gitmodules

1
[submodule "theme"] path = themes/icarus url = https://github.com/steemblog/hexo-theme-icarus.git

‌‌code from https://github.com/steemblog/blog | MIT License

策略2:大量使用缓存(cache)

themes/icarus/layout/layout.ejs

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
<% if (page.__widget) { %>
<%- body %>
<% } else { %>
<!DOCTYPE html>
<html <%- has_config('language') ? ' lang="' + get_config('language').substring(0, 2) + '"' : '' %>>
<head>
<%- partial('common/head', {}, {cache: false}) %>
</head>
<body class="is-<%= column_count() %>-column">
<%- partial('common/navbar', { page }) %>
<% function main_column_class() {
switch (column_count()) {
case 1:
return 'is-12';
case 2:
return 'is-8-tablet is-8-desktop is-8-widescreen';
case 3:
return 'is-8-tablet is-8-desktop is-6-widescreen'
}
return '';
} %>
<section class="section">
<div class="container">
<div class="columns">
<%- partial('common/widget', { position: 'left' }) %>
<%- partial('common/widget', { position: 'right' }) %>
<%- partial('component/pjax_widget_js', {}, {cache: true}) %>
<div class="column <%= main_column_class() %> has-order-2 column-main"><%- body %></div>
</div>
</div>
</section>
<%- partial('common/footer', {}, {cache: true}) %>
<%- partial('common/scripts', {}, {cache: true}) %>

<% if (has_config('search.type')) { %>
<%- partial('search/' + get_config('search.type'), {}, {cache: true}) %>
<% } %>
</body>
</html>
<% } %>

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

对于重复的模块,使用 partial(‘component’, {param}, {cache: true}) 来替换原来的实现‌

策略3:控件页面分离

将原来的 profile, recent_posts, category, tag, tagcloud, archive 等组件,生成独立的 html 页面,减少冗余的 html。此外,组件采用独立的页面也是实现增量编译所必须完成的,所以这一步必须要做。‌

themes/icarus/layout/layout.ejs 需要修改 generators,生成新的 widgets 页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Widget page generator
*/
module.exports = function (hexo) {
hexo.extend.generator.register('widget', function (locals) {
const widgets = hexo.extend.helper.get('get_config').bind(this)('widgets');
const component_widgets = widgets.filter((w) => (w.component))

return component_widgets.map(function(widget){
return {
path: `widgets/${widget.type}.html`,
layout: 'component/pjax_widget_src',
data: {
widget: widget,
__widget: true
}
};
});
});
}

‌code from https://github.com/steemblog/hexo-theme-icarus | MIT License

themes/icarus/layout/component/pjax_widget_ref.ejs 将原来的组件用对页面的引用替换。

1
2
3
4
5
<div class="card widget">
<div class="card-content">
<div id="widget-<%= widget.type %>" data-pjax="<%= `${get_config("root")}widgets/${widget.type}` %>.html" style="position: relative; width: 100%; display: block;"></div>
</div>
</div>

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

themes/icarus/layout/component/pjax_widget_src.ejs 对不同的组件,获取对应的内容

1
<%- partial(`widget/content/${page.widget.type}`, {widget: page.widget}) %>

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

themes/icarus/source/js/pjax_widget.js 在页面加载时通过 ajax 来通过url获取组件的 html,替换控件的内容为实际内容。

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
if (typeof(jQuery) !== 'undefined') {
(function($){
function load_pjax(element) {
// $.pjax({url: element.attr("data-pjax"), container: "#"+element.attr("id")})
$.get(element.attr("data-pjax"), function(data) {
element.html(data);
});
}
$("div[data-pjax]").each(function(){
load_pjax($(this));

})
})(jQuery);
} else {
(function(){
window.$ = document.querySelectorAll.bind(document);
function load_pjax(element) {
const url = element.getAttribute("data-pjax")
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
element.innerHTML = xhr.responseText;
};
xhr.send();
}
for (const e of $("div[data-pjax]")) {
load_pjax(e);
}
})();
}

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

themes/icarus/layout/common/widget.ejs 对控件,也要添加 cache 模式,减少冗余。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<% function render_widget(widget) {
const component = widget.component || false;
const cache = widget.cache || false;
if (component) {
const cahced_prefix = cache ? 'cached_' : '';
const template = `component/${cahced_prefix}pjax_widget_ref`;
return partial(template, { widget })
} else {
return partial('widget/' + widget.type, { widget, post: page }, {cache: cache});
}
} %>
<div class="column <%= side_column_class() %> <%= visibility_class() %> <%= order_class() %> column-<%= position %> <%= sticky_class(position) %>">
<% get_widgets(position).forEach(widget => {%>
<%- render_widget(widget) %>
<% }) %>
<% if (position === 'left') { %>
<div class="column-right-shadow is-hidden-widescreen <%= sticky_class('right') %>">
<% get_widgets('right').forEach(widget => {%>
<%- render_widget(widget) %>
<% }) %>
</div>
<% } %>
</div>
<% } %>

‌code from https://github.com/steemblog/hexo-theme-icarus | MIT License

此外,每个组件都要重构将内容独立出来,这里不详细解释了。‌

策略4:采用时间线布局

将categories和tags页面的布局都显示为时间线模式,更容易浏览,且页面所占空间更小。‌

以 category 为例,展示为时间线 + 分页:‌

themes/icarus/layout/category.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="card">
<div class="card-content">
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li><a href="<%- url_for('/categories') %>"><%= _p('common.category', Infinity) %></a></li>
<% page.parents.forEach(category => { %>
<li><a href="<%- url_for(category.path) %>"><%= category.name %></a></li>
<% }) %>
<li class="is-active"><a href="#" aria-current="page"><%= page.category %></a></li>
</ul>
</nav>
</div>
</div>
<%- partial("component/timeline", {posts: page.posts}) %>
<% if (page.total > 1) { %>
<%- partial('common/paginator') %>
<% } %>

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

采用时间线以后,同时也会每个展示的 item 添加 cache:‌

themes/icarus/layout/component/timeline.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="card widget">
<div class="card-content">
<% if (typeof(head) !== 'undefined' &amp;&amp; head) { %>
<h3 class="tag is-link">
<%= head %>
</h3>
<% } %>
<div class="timeline">
<% posts.each(post => { %>
<%- partial("component/cached_item", {post}) %>
<% }) %>
</div>
</div>
</div>

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

策略5:增加每页的文章数量

可以有效减少页面总数,减少编译的负担。‌

blog/message.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Pagination
## Set per_page to 0 to disable pagination
per_page: 6
pagination_dir: page

index_generator:
per_page: 6

archive_generator:
per_page: 20
yearly: true
monthly: true

category_generator:
per_page: 20

tag_generator:
per_page: 20

code from https://github.com/steemblog/blog | MIT License

经过以上各类方案,编译所有文章的速度已经得到的极大的提高,大约已经提到了10~50倍了。

@andrewma: 400+ posts, 80+ tags, 1~2 mins

@ericet: 630+ posts, 260+ tags, 1~2 mins

(b) 实现增量编译

但我们的目标是增量更新,为了实现这个目的,我们需要做两方面修改:‌

  1. 增量生成markdown:无须每次都从 steem 获取 posts 并生成 markdown;
  2. 只编译受影响的 html 文件:修改 hexo 的 tag, category, archive, 和 post的generators,只在 html 受影响时生成。

步骤1:增量生成 markdown

为了不重复生成过去的posts的 markdown 文件,我们用 git repo 来管理过去已经生成的 markdown 文件,并只更新最近1天左右的文章。‌

同时,我们需要和之前的 source 对比,从而知道哪些文章需要更新。‌

blog/builder.py

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
def setup_source_repo(self):
git_clone_cmd = "git clone --depth 1 --branch {} --single-branch https://{}github.com/{}.git {}".format(SOURCE_BRANCH, self._get_github_pat(), self._get_repo(prefix=False), SOURCE_REPO_FOLDER)
os.system(git_clone_cmd)
# on `source` branch after clone
logger.info("Cloned source repo into workspace: {}".format(SOURCE_REPO_FOLDER))

def _commit_source(self):
os.chdir(SOURCE_REPO_FOLDER)
# commit the files into source repo
os.system("git add --all *")
res = os.system('git commit -m "Source updated: {}"'.format(get_uct_time_str()))
os.chdir("..")

if res == 0:
logger.info("Commited source into [{}] folder".format(SOURCE_REPO_FOLDER))
return True
else:
logger.info("Failed to add new source into [{}] folder".format(SOURCE_REPO_FOLDER))
return False

def _diff_files(self):
os.chdir(SOURCE_REPO_FOLDER)
res = subprocess.run(['git', 'diff', 'HEAD', 'HEAD~1', '--name-only'], stdout=subprocess.PIPE).stdout.decode('utf-8')
os.chdir("..")
files = [f for f in res.split("\n") if len(f) > 0]
logger.info("{} different files:\n{}".format(len(files), res))
return files

code from https://github.com/steemblog/blog | MIT License

为了保证每个用户运行时,文章的内容不会混乱,需要多source的workspace也做一些管理,这里不详细叙述了。‌

步骤2:只编译受影响的 html 文件

在用主题进行编译时,也需要知道哪些 markdown 文件需要更新,以及由此判断更新哪些 html。‌

themes/icarus/includes/helpers/diff.js

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
hexo.extend.helper.register('list_updated_posts', function (link) {
if (_posts.length == 0) {
process.chdir("source")
const res = execSync("git diff --name-only --cached").toString('utf8');
process.chdir("..")

const paths = res.split("\n").filter((p)=>(p.length > 0))
_posts = paths
}
return _posts
});

hexo.extend.helper.register('list_updated_categories', function (link) {
if (_categoreis.length == 0) {
const _list_updated_posts = hexo.extend.helper.get('list_updated_posts').bind(this)
const updated_posts = _list_updated_posts();

if (updated_posts.length > 0) {
for (const path of updated_posts) {
let post = Post.findOne({source: path})
if (post) {
_merge_categories(post.categories);
}
}
}
}
return _categoreis
});

hexo.extend.helper.register('list_updated_tags', function (link) {
if (_tags.length == 0) {
const _list_updated_posts = hexo.extend.helper.get('list_updated_posts').bind(this)
const updated_posts = _list_updated_posts();

if (updated_posts.length > 0) {
for (const path of updated_posts) {
let post = Post.findOne({source: path})
if (post) {
_merge_tags(post.tags);
}
}
}
}
return _tags
});

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

在 generators 中,只考虑那些受影响的文件。以category 为例:‌

themes/icarus/includes/generators/category.js

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
function needs_update(category) {
if (config.incremental) {
// in incremental mode, update the affected category pages only
const updated_categories = list_updated_categories();
if (updated_categories &amp;&amp; updated_categories.length > 0 &amp;&amp;
updated_categories.indexOf(category['name']) != -1) {
return true;
}
return false;
}
return true;
}

return locals.categories.reduce(function(result, category){
if (! needs_update(category)) {
return result;
}

const posts = category.posts.sort('-date');
const data = pagination(category.path, posts, {
perPage: perPage,
layout: ['category', 'archive', 'index'],
format: paginationDir + '/%d/',
data: {
category: category.name,
parents: findParent(category)
}
});

return result.concat(data);
}, []);

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

需要注意的是,这里需要找到 hexo 对应的几个 generator 的源码,并将其进行替换,例如 hexo-generator-tag、hexo-generator-category、hexo-generator-archive ‌

—‌

实现增量编译以后,经过测试,大概每天更新时,每个账户消耗的时间大约在几秒钟 到1分钟之间。12个账户更新时,总的耗时大约在2分钟至10分钟左右,包括环境部署的时间。

@andrewma: 400+ posts, 80+ tags, ~17s

@ericet: 630+ posts, 260+ tags, < 1min

—-‌

至此,编译性能优化的工作告一段落,可以支持更多账户的镜像同步了。‌

(2)多平台部署

‌除了 GitHub pages,现在常用的静态网站部署方式还有Netlify等。只需要按照Netlify的界面上步骤操作即可,或者参考 hexo deployment 一节的描述。‌

作为结果,我们可以将页面部署到不同环境:‌

‌注意:Netlify上目前只部署了 @robertyan 的文章,但也可以支持其他账户。‌

(3)渲染优化

‌由于采用了组件化的 html 页面内,在上文提到过需要动态加载 widget 的页面,由于页面加载资源的阻塞等问题,我们需要对页面做一些优化才能保证 widget 的渲染不被图片等资源影响太大。‌

这里主要是在 widget 的占位符组件被渲染以后,立即调用 ajax 加载组件的代码,以尽早显示这些 widgets;否则,首页打开时的卡顿还是挺明显的。‌

themes/icarus/layout/layout.ejs

1
2
3
4
5
6
7
8
9
10
<section class="section">
<div class="container">
<div class="columns">
<%- partial('common/widget', { position: 'left' }) %>
<%- partial('common/widget', { position: 'right' }) %>
<%- partial('component/pjax_widget_js', {}, {cache: true}) %>
<div class="column <%= main_column_class() %> has-order-2 column-main"><%- body %></div>
</div>
</div>
</section>

code from https://github.com/steemblog/hexo-theme-icarus | MIT License

主要需要优化的渲染过程就是组件的渲染,其余目前并未做更多测试。‌

(4)问题修复

‌除了以上提到的问题,由于在编译或展示时还需要一些别的问题,也一同修复了。‌

  1. @andrewma 想可以将 category 和 tag 等按使用频率从大到小排序;
  2. 编译 @lemooljiang 的博客时,产生 node / v8 的内存溢出问题,编译失败。

这些问题较容易修改:前者只需渲染前进行排序;后者需要对node运行的参数进行配置,改上限为8GB。后者的修改在:‌

blog/command.py

1
2
3
def configure():
settings.set_env_var("NODE_OPTIONS", "--max-old-space-size=8192")
os.system("cp -f _config.theme.yml themes/icarus/_config.yml")

#4 未来

‌steemblog 实现时 只是为了满足更好的阅读、搜索文章的需要。简言之,它是一个更好的博客“阅读器”(当然也是一个有趣的“时光机”),并不具有写的功能。‌

这是它想要解决的问题决定的,但它的未来可以有着有趣的发展方向,我们从小和大两个方面来讲。‌

—-‌

:从steemblog的角度来讲,我们可以做各类改进,包括功能、性能、运营等。‌

  • 功能:支持语言切换、主题切换、留言(原来就有,为了性能隐藏了)、图片本地保存等
  • 性能:hexo 模板的渲染速度可进一步优化
  • 运营:可以扩展现有系统以支持更多用户、可以考虑付费模式以满足更多需求(费用可能是一次性 10 STEEM 之类的)
  • 扩展:用户可以借助此框架和其他建站服务,快速构建基于 steem 数据的各种网站,包括博客、简历、图库等,以及各种基于 steem 数据的展示组件、基于 steem-js 的写操作 等等;

‌—-

:steemblog 展示了一种基于blockchain 的 app 的更多的可能性。为什么这么说呢?主要原因如下:‌

  • 一份数据、多种展示:与传统数据库相比,blockchain 数据库的公开性和可访问性使得它可以被方便用于基于同一份数据构建多种应用。这也是dApp有趣的方面。steemblog 通过 将 steem blockchain 的数据,进行二次加工,形成了不同的展示方式,这体现了一份数据、多种应用场景的特点。 steemblog 的意图和 steemit 等客户端不完全相同。这里强调的展示有更多 “数据应用”(data apps)的意味,并不全然是 web app 或者 mobile app的视角。
  • 分久必合、强调综合:steem 等区块链平台,现在流行的 dApp 以web app的思维,不断产生新的应用和平台,使得数据不断分化、分裂,到了一定复杂度后,需要一个可以集中管理各类数据与事务的调度中心,steemblog 对于历史的总览体现了这一种思想的一个方面。
  • 数据基础的改进方向:steem 类应用的用户体验的局限性一部分在于其数据基础设施的不灵活与低效,steemblog 通过分离数据读和写,改进了用户体验,但这只是一个方向。blockchain 数据基础设施有着更多的发展的可能性。
  • 社会:生产还是消费?:steemblog 把 steem 数据展现出的一个用户的能力、经验、历史等方面,更有效的展现出来,这是一个更具有生产、企业或产业角度的意义的展示。 steemblog 可以让我们更清楚地看到一个用户的时间是如何被应用与转化的。这种方式对于大规模协作的价值大于娱乐消费型 app 的需要。

这只是一个大致的方向,或许随着思考的深入我们可以把它演变成新的应用。‌

但有三点应该是对的:(1)你在 steem 的历史不会消失;(2)更好的时光机将会不断被发明出来;(3)未来必定会来到。‌

基于这些前提,我们或许能不断看到更多有趣的应用吧。‌

—-‌

昨天 @holger80 在 discord 和我聊到了关于 hexo theme 的问题,同时创建了 steemsites 这个项目(https://github.com/holgern/steemsites),可能也有通过 steem 构建站点,或者其他方面的考虑。‌

对于 steemblog 和相关的项目,我们在之后会进一步思考和探索。如有任何建议请随时留言讨论~‌

参考文献

  1. Hexo博客框架:https://hexo.io
  2. Hexo icaru 主题:
    1. 修改后:https://github.com/steemblog/hexo-theme-icarus
    2. 原作者:https://github.com/ppoffice/hexo-theme-icarus
  3. steemblog 博客镜像服务:https://steemblog.github.io/ (源码:https://github.com/steemblog/blog | MIT License)


Posted from my blog with SteemPress : https://robertyan.000webhostapp.com/2019/05/steemblog-steem%e6%97%b6%e5%85%89%e6%9c%ba-steem-time-machine


This page is synchronized from the post: steemblog: Steem时光机 | Steem Time Machine

用GitHub创建Steem文章镜像 | Mirroring Your Steem Blogs on GitHub | 免费博客备份服务: steemblog

用GitHub创建Steem文章镜像 | Mirroring Your Steem Blogs on GitHub | 免费博客备份服务: steemblog

本文介绍将Steem文章同步到GitHub pages的开源工具和免费服务:‌

  1. 开源工具 “blog”:https://think-in-universe.github.io/blog/
  2. 免费服务 “steemblog”:https://steemblog.github.io/

Image Source: Pixabay

缘由

最近在用steem的时候,觉得不管是用busy, steempeak还是steemit等,它们的界面设计对于看自己以前的文章并不太方便(当然想看其他人的旧文章也比较麻烦),并且由于国内的网络访问这些站点的速度都不太快,所以以博客服务来看,用户体验是挺差的;至于要方便地搜索、归类自己的文章就更麻烦了。‌

之前曾经有过一些浏览历史文章较方便的服务(如chinabb和steemitfriends),似乎也或者关闭或者收费了,因此暂时没有找到好用的服务。‌

于是想到可以把Steem上的文章备份成镜像,每日自动同步,便于自己梳理和分析;虽然我文章写的不多,但如同在《标签的本质 | The Nature of Tags(一)》里提到的,组织信息是人类的本能,于是便实现了这里的工具和服务。‌

GitHub镜像博客

关于为什么需要自己写文章同步工具,其实是值得询问的:‌

  1. 首先,博客镜像的工作相信之前有人也已经做过了,这并非什么新想法(但我简单搜了一下也没有找到可以立即复用的工具)。但是搭建博客镜像是一项需要适应自身需求的工作,所以自己动手的话可以有更高的灵活性和掌控度。
  2. 其次,即便之前有类似工作的话,可能也并不是同步到GitHub,或者也未必会做到近实时的更新,所以这项工作也可以作为一种服务,也并非完全没有价值。

为了完成这项工作,我们基于steem API、GitHub pagesHexo[1]框架创建了博客镜像搭建工具,效果如下(示例:https://think-in-universe.github.io/blog/):‌

1. 博客首页

左侧是用户的profile,右侧是近期的文章,中间为最近文章。

‌screenshot from https://think-in-universe.github.io/blog/

2. 侧边栏:类别和标签

Steem上的标签和类别,会同步到hexo框架下面,并能够正常显示。遗憾的地方在于由于steem上没有类别层级的概念,所以要分类文章,相对来说没有那么灵活。

‌screenshots from https://think-in-universe.github.io/blog/

3. 侧边栏:近期文章和归档

‌显示最近5篇文章,以及每个月的文章数量。可以看出,作为写作者而言,我是比较懒惰的 :) 和大家比还有很大差距。

‌screenshots from https://think-in-universe.github.io/blog/

4. 文章展示:右侧目录、代码高亮和原文链接

‌在右侧添加了一个目录控件,对于阅读长文是有帮助的。‌

代码高亮对类似本文的有代码的文章有一定帮助,steemit对代码高亮的处理是比较初级的。‌

原文链接其实是为了方便我自己有时候引用文章需要,在steem上找文章比较低效。

‌screenshots from https://think-in-universe.github.io/blog/

5. 搜索

‌搜索功能对于想要快速查阅或引用自己的文章,较有帮助。点击右上角的搜索按钮,可以进行快速搜索。

‌screenshot from https://think-in-universe.github.io/blog/

6. 独立的归档、类别、标签页等

‌如果要单独查看这些信息,可以到分别的独立页面下查看,如有需要,也可以建立其他的标签页。例如,归档页面的时间线,比steem看起来简洁一点:https://think-in-universe.github.io/blog/archives/

‌screenshots from https://think-in-universe.github.io/blog/

页面展示大体如此,主要的价值在于从文章的角度,信息的组织更为清晰。如果想要获得一个类似的博客镜像,大抵有两种方法:‌

  1. 如果你了解GitHub和GitHub pages如何使用,可以使用本文发布的开源代码(https://github.com/think-in-universe/blog),根据其中的README,搭建一个类似的镜像就行了。
  2. 如果你希望可以使用一个免费的博客镜像服务,可以参考文章最后一章提到的 steemblog 博客镜像服务,或者直接联系我。

如何实现博客镜像工具?

‌为了实现以上功能,我们可以基于博客框架Hexo[1],搭建从Steem同步数据、并发布到GitHub pages的博客镜像工具,可以支持基于用户名、标签、日期等查询方式的数据同步。下面简要介绍如何实现这一博客镜像工具。‌

工具的代码在GitHub开源:https://github.com/think-in-universe/blog

关于具体如何使用此工具,可以参考上面项目中的README的介绍:可以在本地安装后使用,也可以通过travis-ci部署。‌

本项目的代码里重用了 @cn-hello 小门童实现时的一些基本框架,所以需要增加的功能较少。工具的工作流程如下,也比较简单:‌

  1. 下载你的Steem文章;
  2. 用Hexo编译成静态文件;
  3. 用GitHub pages部署博客;

(1)下载你的Steem文章

‌由于重用了之前的SteemReader的方法,我们可以指定通过账户或者标签以及时间(天数)来获取文章。‌

blog/builder.py

1
2
3
4
5
6
7
8
9
10
11
class BlogBuilder(SteemReader):

def __init__(self, account=None, tag=None, days=None):
SteemReader.__init__(self, account=account, tag=tag, days=days)

def download(self):
if len(self.posts) == 0:
self.get_latest_posts()
if len(self.posts) > 0:
for post in self.posts:
self._write_content(post)

‌code from https://github.com/think-in-universe/blog | MIT License

为了将文章下载为hexo可识别的markdown格式,需要在markdown中加入相关ymal或json的元数据。以下为markdown模板,包含了标题、类别、日期、标签等信息,并指定显示文章的目录。‌

blog/message.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MESSAGES["blog"] = """
---
title: "{title}"
catalog: true
toc_nav_num: true
toc: true
date: {date}
categories:
- {category}
tags:
{tags}
thumbnail: {thumbnail}
---


{body}
"""

code from https://github.com/think-in-universe/blog | MIT License

使用Steem API,获取steem文章的元数据和markdown文本。‌

blog/builder.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def _write_content(self, post):
folder = self._get_content_folder()
c = SteemComment(comment=post)

# retrieve necessary data from steem
title = post.title.replace('"', '')
body = post.body
date_str = post.json()["created"]
date = date_str.replace('T', ' ')
tags = "\n".join(["- {}".format(tag) for tag in c.get_tags()])
category = c.get_tags()[0]
thumbnail = c.get_pic_url() or ''
url = c.get_url()

# build content with template
template = get_message("blog")
content = template.format(title=title, date=date, tags=tags, category=category, thumbnail=thumbnail, body=body, url=url)

# write into MD files
filename = os.path.join(folder, "{}_{}.md".format(date_str.split('T')[0], post["permlink"]))
with open(filename, "w", encoding="utf-8") as f:
f.write(content)

logger.info("Download post [{}] into file {}".format(title, filename))

code from https://github.com/think-in-universe/blog | MIT License

(2)用Hexo编译成静态文件

‌我们需要为建立的博客设置一个美观的主题。‌

我们这里使用了 https://github.com/ppoffice/hexo-theme-icarus 主题,需要将其作为一个git submodule加入到git repository中。‌

.gitmodules

1
2
3
[submodule "theme"]
path = themes/icarus
url = https://github.com/ppoffice/hexo-theme-icarus.git

‌code from https://github.com/think-in-universe/blog | MIT License

随后使用hexo命令来将markdown转换成生成静态的文档。‌

blog/command.py

1
2
3
4
5
6
7
@task(help={
})
def build(ctx):
""" build the static pages from steem posts """

os.system("cp -f _config.theme.yml themes/icarus/_config.yml")
os.system("hexo generate")

‌code from https://github.com/think-in-universe/blog | MIT License

(3)用GitHub pages部署博客

‌正式部署时,我们有两种方式,一是在本地使用hexo命令部署,或者在travis-ci 中定期每日进行同步。‌

hexo命令部署:blog/command.py

1
2
3
4
5
6
7
@task(help={
})
def deploy(ctx):
""" deploy the static blog to the GitHub pages """

build(ctx)
os.system("hexo deploy")

‌code from https://github.com/think-in-universe/blog | MIT License

travis-ci部署:.travis/deploy.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[ -z "${GITHUB_PAT}" ] &amp;&amp; exit 0
[ "${TRAVIS_BRANCH}" != "master" ] &amp;&amp; exit 0

git config --global user.email "${GIT_EMAIL}"
git config --global user.name "${GIT_USERNAME}"

git clone --depth 1 --branch gh-pages --single-branch https://${GITHUB_PAT}@github.com/${TRAVIS_REPO_SLUG}.git site
cd site
cp -r ../public/* ./

NOW=$(date +"%Y-%m-%d %H:%M:%S %z")
git add --all *
git commit -m "Site updated: ${NOW}" || true
git push -q origin gh-pages

‌code from https://github.com/think-in-universe/blog | MIT License

另外,需要注意,由于用travis-ci 部署时需要用户提供Git用户信息(用于commit到gh-pages)以及GitHub的token,所以需要以环境变量的方式进行配置。‌

博客镜像服务 http://steemblog.github.io/

‌今天在微信群中 岩哥 @andrewma 提到想要一个分析文章标签的服务,我想起搭建的这个博客镜像也有标签云和标签统计,所以帮助建一个类似的镜像服务就能解决该问题。‌

但由于岩哥对GitHub并不熟悉,使用上面提到的博客镜像工具可能较为困难,所以在此基于https://github.com/think-in-universe/blog 项目,建一个organization account用户管理博客,帮助有需要的人建博客镜像的子目录,这就是steemblog。‌

1. 如何使用博客镜像服务

‌目前,这个博客镜像服务可以在 https://steemblog.github.io 找到。‌

如果要添加一个新的用户到镜像同步中,只需添加账户到用户列表中即可(目前是手动添加的)。例如,我们添加了 @robertyan 和 @andrewma 到列表中。我们可以在以下链接访问他们的博客镜像:‌

  1. https://steemblog.github.io/@robertyan/
  2. https://steemblog.github.io/@andrewma/

与之前的工具需要手动配置用户信息不同,这里自动从steem同步了用户的profile。(不过跟我手动配置的差不多)

‌screenshot from https://steemblog.github.io/@robertyan/

@andrewma的博客镜像也创建成功了,不过头像和缩略图的处理可能需要做一些改进。

‌screenshot from https://steemblog.github.io/@andrewma/

比如岩哥关心的标签信息,可以在https://steemblog.github.io/@andrewma/ 中找到:

‌screenshots from https://steemblog.github.io/@andrewma/

同样的,我们可以继续添加新的用户,他们的镜像可以在 https://steemblog.github.io/@{账户名} 中找到。‌

对于这样的博客镜像,如有需要或建议,可以在文章后面留言讨论。‌

2. 如何建立博客镜像服务

https://github.com/steemblog/blog 是在 https://github.com/think-in-universe/blog 的基础上构建的,为了适应多用户的子目录的需要,需要对原来的项目的目录结构和部署方式做一些调整。‌

(1)从steem下载文章的同时,自动同步用户信息

通过steem获取的账户信息,自动更新_config.yml_config.theme.yml

相对应的模板在 blog/message.py 中,由于内容太长,这里不贴出。‌

blog/builder.py

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
def update_config(self):
if not self.account:
return

organization = BLOG_ORGANIZATION
logo = BLOG_AVATAR
favicon = BLOG_FAVICON

language = settings.get_env_var("LANGUAGE") or "en"

a = SteemAccount(self.account)
author = self.account
name = a.get_profile("name") or ""
# about = a.get_profile("about") or ""
location = a.get_profile("location") or ""
avatar = a.get_profile("profile_image") or ""
website = a.get_profile("website") or ""

# build config file with template
template = get_message("config")
config = template.format(organization=organization, language=language,
name=name, author=author)
filename = CONFIG_FILE
with open(filename, "w", encoding="utf-8") as f:
f.write(config)
logger.info("{} file has been updated for the account @{}".format(filename, author))

# build config theme file with template
template = get_message("config.theme")
config = template.format(organization=organization,
favicon=favicon, logo=logo,
author=author, name=name, location=location,
avatar=avatar, website=website)
filename = CONFIG_THEME_FILE
with open(filename, "w", encoding="utf-8") as f:
f.write(config)
logger.info("{} file has been updated for the account @{}".format(filename, author))

‌code from https://github.com/steemblog/blog | MIT License

(2)在生成静态网页时,相互隔离不同用户的路径

将不同账户的页面放置到@account子目录下。‌

blog/message.py

1
2
3
4
5
6
7
8
9
10
# URL
## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
url: http://{organization}.github.io
root: /@{author}/
permalink: :category/:post_title/
permalink_defaults:

# Directory
source_dir: source
public_dir: public/@{author}

‌code from https://github.com/steemblog/blog | MIT License

编译指定的steem用户的文章为静态页面,隔离放置在发布目录下。‌

blog/command.py

1
2
3
4
5
6
7
8
9
10
11
@task(help={
})
def build_all(ctx):
""" download the posts of all the accounts, and generate pages """

accounts = settings.get_env_var("STEEM_ACCOUNTS") or []
if accounts and len(accounts) > 0:
for account in accounts.split(","):
clean(ctx)
download(ctx, account)
build(ctx)

‌code from https://github.com/steemblog/blog | MIT License

(3)部署时,将页面推送到 steemblog.github.io‌

只需修改 .travis/deploy.sh 中的目标代码仓库的参数即可。‌

最后

‌究其本质,本文是对steem上的数据进行处理的一种尝试,在开放的区块链数据的基础上,我们可以根据场景,采取多种灵活的数据展现方式,这里的镜像博客无疑又是其中的一种。‌

希望本文提供的工具或服务对你有帮助,如果需要帮助你开启博客镜像服务,可以在本文留言,我会尽量提供支持。由于travis的使用也有一些限制,优先帮助前5位留言的朋友提供服务 :)‌

参考文献

  1. https://hexo.io
  2. Hexo icarus主题:https://github.com/ppoffice/hexo-theme-icarus
  3. 博客镜像工具:https://think-in-universe.github.io/blog/
  4. 博客镜像服务:https://steemblog.github.io/


Posted from my blog with SteemPress : https://robertyan.000webhostapp.com/2019/05/%e7%94%a8github%e5%88%9b%e5%bb%basteem%e6%96%87%e7%ab%a0%e9%95%9c%e5%83%8f-mirroring-your-steem-blogs-on-github-%e5%85%8d%e8%b4%b9%e5%8d%9a%e5%ae%a2%e5%a4%87%e4%bb%bd%e6%9c%8d%e5%8a%a1%ef%bc%9a-st


This page is synchronized from the post: 用GitHub创建Steem文章镜像 | Mirroring Your Steem Blogs on GitHub | 免费博客备份服务: steemblog

借问师友何处有,门童遥指新手村 | 关于 @cn-hello 小友的来历

借问师友何处有,门童遥指新手村 | 关于 @cn-hello 小友的来历

最近,CN区多了一位叫 @cn-hello 的小朋友,自称是CN区的小门童,每天在CN区路口守望,看到新人便上去提供一些建议,比如:“少侠,我看你能量很少了啊,休息会来村里喝杯茶坐坐吧” ,“那个谁,请你不要抄别人文章啊,会被举报的啊”,……,顺便给新人点个小小的赞。‌

随后又开始忽悠新人:“少年,我看你骨骼清奇,这本《Steem指南》似乎很适合你,10块钱卖你了吧?什么,没钱?那白送你了。” / “小伙,我们村里藏龙卧虎、灵气充沛,只要你肯按时做作业,可保你练成盖世神功,你来还是来还是来?”‌

他看到新人通常都很热情,但新人有时却一脸懵逼,不明白这货是干嘛来了。。。但cn-hello却很自我,每天都要炫耀一下自己勾搭了几位新人。。。‌

其实,@cn-hello 原来是一为牛角挂书的小牧童,本是勤于读书问道解惑的,但有了一些学问之后,却有些好为人师,看到迷途之人,免不了希望上前寒暄两句、指教一二。结果就从牧童变成了门童,负责村中的接引、指路一事了。‌

本文以下简单介绍一下他的来历,相关的一些想法,以及之后的计划。欢迎大家给这位小朋友提供建议。

image source: Wikipedia – 颐和园长廊彩绘 | CC BY-SA 2.0

小门童的介绍

来历

设计 @cn-hello 这个机器人(bot)的意义是颇为直接的,相信很多人也遇到过这个场景:刚来Steem发帖的时候,兴冲冲发了一个帖子,却无人问津,想要找前辈们请教一下,却不知道应该找谁,到其他作者或大佬帖子下留言,也不见得会被回复。所以cn-hello最初的功能便是为了主动发现身处迷雾之中的新人,告诉他们第一步该怎么走、接下来该怎么做、加入什么组织。‌

然而,本质上说 @cn-hello 也只是另一个欢迎机器人罢了(yet another welcome bot),有些欢迎机器人也确实并不令人喜欢,但或许也有一些差异:‌

  1. 他只关注用中文发帖的新人,只给中文用户留言;提供的建议也只用中文表达;
  2. 他提供的帮助信息,如新手村 @team-cn,如《Steem指南》,都是专为中文社区的新人们所创建的,更有针对性与亲切感
  3. 与其他某些欢迎机器人(welcome bot)相比,他并不以私利为目的而留言或点赞,他不发广告、不求赞,只是希望更好的发现和帮助新人。

虽然只运行了几天,但在帮助新人获取信息、找到团体方面也确实有一些帮助,也帮助我们更好地了解和认识了新人们的生存状态。在后文我们会进一步介绍这些内容。‌

—-‌

除了上面提到的这个显而易见的痛点,开始要写这个机器人的动机也有两方面:‌

  1. 一方面是在讨论《Steem指南》相关问题时,发现虽然有《指南》但很多人还是没有阅读的习惯,所以考虑是否可以实现一个发布攻略的小助手(advisor),根据新人发帖的情况,给他提供相应的修改建议;
  2. 另一方面,源于和村长 @ericet 的交流,关于平时是如何找新人进村的,村长说一般是自己去看的,没用什么工具。所以我想可以写个工具和机器人自动化这部分工作,村长也提到已经注册了 @cn-hello 这个账号,想来做类似的事情。

于是大概简单实现了目前的这位 @cn-hello 小门童,或许以后会有更多的功能,也欢迎大家提供建议,目的仍然是切实帮助到新手们。‌

—-‌

实际上,#cn-hello 过去是一个CN区新人常用的自我介绍的标签,最近似乎用的人少了很多。这里用 @cn-hello 作账户,用意也在于帮助新人,所以在意义上较为接近的。‌

功能

目前 @cn-hello 主要有两方面的功能:‌

(1)欢迎新人并提供建议

搜索1天内在 #cn 标签下用中文发表帖子的新人,并在他们的文章下回复新人可能遇到的困难,以及相关的建议,如下图所示:

image source: screenshot from the account @cn-hello ‘s comment‌

特别地,由于很多新人不了解RC的消耗,回复中添加了新人目前的能量可以发表的文章或回帖的总数,以及每天恢复的能量能发帖的数量。‌

关于中文区新人的定义,目前采用的是如下规则:‌

  1. 声望在 [25, 35] 的区间内;
  2. SP < 100;
  3. 帖子使用中文发表

(2)统计1日和7日的新人动态

现在 @cn-hello 定时于北京时间每日晚10点运行,首先搜索新人并回复,然后发布新人相关的统计信息,如2019年05月10日的帖子。统计信息包括:‌

  1. 文章推荐:今日发布帖子的新人以及推荐其中一篇帖子(通常是最新一篇);
  2. 7日新人统计:罗列过去7日发现的新人,以及他们的声望、能量(RC)、帖子数、粉丝数等信息。

这部分统计设计时主要是为了方便老用户为新人提供建议或支持,也可以帮助关注自己在初期的一些关键指标。如下图所示:

image source: screenshot from the account @cn-hello ‘s post

image source: screenshot from the account @cn-hello ‘s post‌

—-‌

目前功能是较为简单的,但对于发现新人,确实也有一些帮助;村长也会根据统计和@的通知,找到新人的帖子并帮助一些新手加入村内。‌

同时,也存在一些问题,似乎并不太容易解决,例如:‌

  1. 获取反馈困难:一般很难知道新手是不是读了 @cn-hello 的建议,以及对建议的反馈如何;确实也有一些新人看到了相关的回复,然后主动联系,但总体来说相当少;
  2. 新手可能不理解规则或建议:推荐的新手的文章有一部分是有抄袭或搬砖的问题的,虽然回复新人时指出了这一点,但可能新手并不理解实际的情况或不愿意相信,依然我行我素,结果还是被踩了。

源码

总体来说,这是一个较为简单的工程,但基本包括了一个论坛机器人常见的功能:监控、过滤、文本分析、回复、点赞、发表文章、通知,等等。‌

下面是本项目的源代码,开源在 @steem-guides 的GitHub账户下。‌

该机器人提供了如下命令,可供执行:

1
2
3
4
5
6
7
8
Available tasks:

cn-hello.reply reply to a post by cn-hello
cn-hello.search search the latest posts by newbies
cn-hello.summarize publish summary post for daily and weekly update
cn-hello.vote vote a post by cn-hello
cn-hello.welcome send welcome messages to newbies
steem.list-posts list the post by account, tag, keyword, etc.


代码较为简单,这里不赘述。但或许有一些基础类可以被复用,这里简单提一下:‌

  1. steem 模块
    1. collector.py:封装了以账户、标签、天数、数量等查询文章和回复的基本方法,如有需要做此类查询的,或许可以复用其中的 get_posts() / get_comments();
    2. comment.py 和 account.py:封装了一些常用操作,如各类常用信息的查询、是否被某人赞或者踩、纯文本的提取等;
  2. data 模块
    1. page_language.py:对文本使用的语言检测的封装;
    2. reader.py:可帮助实现监听某tag最新文章;
  3. cn_hello 模块
    1. bot.py, command.py, message.py, newbies.py: 稍作修改可创建一个新的机器人。
1
2
3
4
5
6
├── cn_hello
│ ├── __init__.py
│ ├── bot.py # the behaviors of the bot
│ ├── command.py # the commands that trigger the bot to act
│ ├── message.py # the messages for the bot to speak
│ └── newbies.py # the data operations of newbies

对于如何安装、执行命令等,可以参考GitHub repo的readme中的介绍。‌

所以,如果希望自己快速创建一个机器人实现点赞、回复、监控、通知等功能,或许可以参考这里的开源代码。‌

—-‌

关于识别中文,这里直接使用了 langdetect 这个 package,是对https://code.google.com/p/language-detection/ 项目的python移植。‌

具体可参见Language Detection Library for Java 中对算法的介绍。其核心是采用了Naive Bayes with character n-gram,计算不同字符拼写特征在哪种语言中出现的概率最高。‌

核心代码在项目的detector.py文件中,如下:

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
def get_probabilities(self):
if self.langprob is None:
self._detect_block()
return self._sort_probability(self.langprob)

def _detect_block(self):
self.cleaning_text()
ngrams = self._extract_ngrams()
if not ngrams:
raise LangDetectException(ErrorCode.CantDetectError, 'No features in text.')

self.langprob = [0.0] * len(self.langlist)

self.random.seed(self.seed)
for t in xrange(self.n_trial):
prob = self._init_probability()
alpha = self.alpha + self.random.gauss(0.0, 1.0) * self.ALPHA_WIDTH

i = 0
while True:
self._update_lang_prob(prob, self.random.choice(ngrams), alpha)
if i % 5 == 0:
if self._normalize_prob(prob) > self.CONV_THRESHOLD or i >= self.ITERATION_LIMIT:
break
if self.verbose:
six.print_('>', self._sort_probability(prob))
i += 1
for j in xrange(len(self.langprob)):
self.langprob[j] += prob[j] / self.n_trial
if self.verbose:
six.print_('==>', self._sort_probability(prob))

def _extract_ngrams(self):
'''Extract n-grams from target text.'''
RANGE = list(xrange(1, NGram.N_GRAM + 1))

result = []
ngram = NGram()
for ch in self.text:
ngram.add_char(ch)
if ngram.capitalword:
continue
for n in RANGE:
# optimized w = ngram.get(n)
if len(ngram.grams) < n:
break
w = ngram.grams[-n:]
if w and w != ' ' and w in self.word_lang_prob_map:
result.append(w)
return result

code from langdetect project | license: Apache License 2.0‌

未来

目前 @cn-hello 小门童的工作主要以接待和引导新人为主。之后的拓展依然以尽力帮助新人为目的,主要的改进可能在以下几个方面,但总体而言目前的优先级并不是非常高:‌

  1. 提供改进建议:根据作者对标签的使用、收益的情况、文章的主题等,提供在标签使用、活动参与等方面的建议,将《Steem指南》中的特定章节也会一并推送;
  2. 优化新人统计信息:给新人推荐其他steem数据分析平台的链接,方便新人了解掌握自己主要的状态和潜在的改进方向,可能只看统计帖子并不是很方便或有效;
  3. 优化文章推荐:推荐新人高质量的文章,以及包括提高新人在snax等注意力奖励平台上的收益等。这点似乎很多机器人都在做了,所以或许并不是 @cn-hello 的重点。

对小门童有任何建议,或者希望他还能帮新人们做些什么,请在文章下面留言哦~

小门童的思考

对新人的观察

透过 @cn-hello 每日对新人的访问和记录,我们也能获得一些关于新人的观察:‌

  1. 搬砖的倾向:在过去7人接待的28位新人中,至少有5位由于搬砖(抄袭)的问题声望被踩到25以下。一方面,我们的提醒没有达成特别好的效果;另一方面,新人不了解规则并希望轻松撸币容易有搬砖的倾向。
  2. “老”新人:大部分 @cn-hello 找到的新人都是1个月以内的创建的(18 / 28),但也有部分账户(6 / 20)已经创建了 1年左右或以上的。或许当时新人来访时,没有很快找到融入社区的方式,所以一直呈现若离若即的状态,希望 cn-hello能帮助缓解这一问题,目前看来似乎也并不容易。
  3. 积极的新人:目前积极回应 @cn-hello 的新人只有 (6 / 28)个,并不算很多,但能得到一些有效回应,也算部分完成任务了。

对新人不够友好(同时也是为了防止小号和机器人的泛滥),是Steem的一个严重的问题,也是比较奇怪的地方,但恐怕也是无奈之举。从实际上确实阻碍了新生力量的入场,给新玩家设置了较高的门槛。或许本质上这是西方文明“人性恶”的假设在作怪吧。‌

为了对抗新人面对的挑战,小门童表示不打算放弃新人“拯救”计划。‌

机器人的印象

小门童 @cn-hello 是机器人,但令他感到不解的是,steem上很多机器人并不打算遵循阿基莫夫的机器人三大定律或者四大定律,甚至恶行昭彰。

第零法则:机器人不得伤害整体人类,或坐视整体人类受到伤害;

第一法则:除非违背第零法则,否则机器人不得伤害人类,或坐视人类受到伤害;

第二法则:除非违背第零或第一法则,否则机器人必须服从人类命令;

第三法则:除非违背第零、第一或第二法则,否则机器人必须保护自己。

‌总体来说,steem上机器人给人的整体印象是不够正面的,既不智能、也不正义(不仁、不智)。‌

事实上,像“三大定律”这样义务化的伦理确实不适合steem上的机器人,他们不过是人的延伸,并不具有被编码的“道德”。或许在可预见的未来,人工智能的潜在危害也不过是“恶”的延伸,问题不在与机器本身有多智能,而首先在于设计和使用它的人是否能约束自身的贪婪和享乐的倾向。‌

—-‌

如果想要进一步了解steem上的机器人,《Steem指南》的机器人篇对于机器人有一些不完全的介绍,可作参考。‌

在帮助新人方面,@cn-hello 也只不过是其中之一。CN区还有很多可爱的机器人,有些依然活跃,有些则已经进入休假状态,比如 @cn-cutie.pie 和 @cn-naughty.boy。‌

之前的一些过去关于机器人问题的讨论,或许也可以作为参考:‌

  1. 聊聊机器人🤖 / Robots on STEEMIT
  2. steemit 还是机器人的天下
  3. 机器人是否会破坏steemit上的生态平衡?

‌@maiyude 的机器人教程,也可供初学者阅读,我们稍后也会整合到《Steem指南》中。‌

  1. steem-python 介绍:(1)(2)
  2. 点赞机器人开发:(1)(2)(3)

—-‌

虽然对于steem上的机器人存在许多争议,但所谓“自反而缩,虽千万人,吾往矣”,小门童希望能尽自己的一点绵薄之力,便已经足够了 :)‌

参考文献

  1. Wikipedia,机器人三定律
  2. Shuyo NakataniLanguage Detection Library for Java,2010年


Posted from my blog with SteemPress : https://robertyan.000webhostapp.com/2019/05/%e5%80%9f%e9%97%ae%e5%b8%88%e5%8f%8b%e4%bd%95%e5%a4%84%e6%9c%89%ef%bc%8c%e9%97%a8%e7%ab%a5%e9%81%a5%e6%8c%87%e6%96%b0%e6%89%8b%e6%9d%91-%e5%85%b3%e4%ba%8e-cn-hello-%e5%b0%8f%e5%8f%8b%e7%9a%84


This page is synchronized from the post: 借问师友何处有,门童遥指新手村 | 关于 @cn-hello 小友的来历

标签的本质 | The Nature of Tags(一)

标签的本质 | The Nature of Tags(一)

引子:了解Steem,从标签开始

1. 标签与新人

对于初来steem的新人来说,标签(tags)是必须理解、但却不能完全理解的概念

我们一旦开始发第一篇文章,就必须选择合适的标签。新手大概有几种常见的情况:

  1. 有的用户会发布自己擅长的内容,根据自己以往的经验,输入文章内容相关联的标签;
  2. 或者看到steemit的热门标签,从奖励高或者特定的热门标签中选择自己了解的,来发布相关的内容,比如introduceyourself、life、photography、travel、film、food等;
  3. 或者中文用户看到cn标签,发现里面全是中文内容、很有意思,于是用来发布中文文章;kr、jp、fr等标签也类似。

第一类是内容导向,根据内容生成标签;第二类是目的导向,根据功利或兴趣选择标签;第三类是语言或地区导向,根据习惯语言或者居住地选择标签。这些都很符合直觉,第一类可以保证标签是与内容相匹配的,第二类可以将帖子与奖励和兴趣相匹配,第三类将帖子与非英语语言和国别相匹配。虽然其中存在一些重叠,但只有充分结合这三者,才能真正掌握标签的使用。

对于标签的使用,可谓“易学难精”,添加一个标签的操作看似容易,但要选择合适自己的帖子内容的标签,需要对标签的内涵、意义与价值有更深入的理解。很多人初来steem,都是被其功利性的外衣(加密货币)所吸引,所以将大量的学习精力放在了解其加密货币特征上,这并没有错,但不全面,甚至可能一无所获。究其原因,是由于新人往往不了解steem的社交内涵;而不了解标签的意义,是不了解steem的社交含义的外在表现。

2. 标签与势力

我以为steem的三大支柱(the three pillars of steem)是:

  1. 包含区块链技术在内的分布式web和数据基础设施;
  2. 以标签为代表特征的社交网络系统;
  3. steem独有的货币设计与经济系统;

以此为基础,可以创建客户端、工具、博客、视频、音乐、问答、游戏等等各类应用,或形成各类社区、组织、甚至盈利性公司。凡是达成一定规模的,其明显标志之一便在于其本身的标签、或其支持的标签被大量的使用。最主要的有:

  1. 客户端:比如#partiko、#esteem、#busy,都有意鼓励自身标签的使用
  2. dApp:比如#utopian-io、#dtube、#drugwars,一旦应用流行起来其标签便被大量使用
  3. 组织:比如#cn、#team-cn、#votovzla、#steemitbloggers,虽然不一定有软件可以使用,却借用其他工具实现了对群体的聚拢,形成合力。

有趣的是,这三类基于steem的构建,分别对应到steem的三大支柱:

  1. 客户端和工具等,主要是基于技术的构建,提供或强化了技术基础设施,网页、移动或桌面客户端。上面几个案例#partiko、#esteem、#busy,包括#steempress、#steemplus等工具在内,都是如此。
  2. dApp从表面看似乎是提供了新的内容发布方式,但其相比于其他同类产品的差异性和创新性常常在于经济激励。比如dtube对发布视频的激励、utopian对开源代码的激励。
  3. 社区组织看似什么都没做,但实际上是实现了对人群的构建,形成了强有力的社区。#cn本身并不天然具有群体的凝聚力,但基于#cn社区,利用微信、QQ等群体管理工具实现的半开放组织,却增加了社交的紧密度。

这三种构建方式,由于其得到社区的广泛支持,经常能够得到大量代理,并给代理者、支持者和使用者提供实在的回报,从而进一步巩固了其在社区中的价值。围绕其的多方利益相关者及其行为,渐渐的形成社区中的一股股势力。

需要特别提一下的是容易被忽视的组织:之前@theycallmedan的SP竞赛中,#team-cn、#votovzla、#steemitbloggers这三股力量在平时看来并不那么流行,但在需要依靠凝聚力和号召力的竞赛中却同时脱颖而出,原因在于他们的服务对象主要在内部(尤其是#team-cn和#votovzla),有时不具有显性的对外扩张动机,甚至也不在意推广本身标签的使用。比如,@team-cn的目的在于让CN社区更有生命力,所以是#cn标签的活跃度(而不是#team-cn)与@team-cn的努力正相关。

热门标签,要么来自于以上这三种势力,要么是主题或兴趣相关的频道(life、photography、travel、film、food、introduceyourself),要么是语言类频道(cn、kr、jp、spanish、deutch)。所以,通常来说,热门的标签,或者是背后的势力强大,或者是已经有了很多爱好者,这两者往往相互促进。

  • 主题或兴趣相关频道涉及较为开放和宽泛,背后的支持力量不明确,所以虽然人气很高,但在势力的力量上有时反而不如上面三种。
  • 语言类频道,本身既是语言,也可能是组织;比如CN,本身是文章内容使用的语言(中文),也可以是使用该语言的人的组织(华人社区)。语言频道的强弱与其背后的组织势力的兴衰也存在密切的联系,比如@oflyhigh在#cn 标签不见了中提到,曾经的CN区由于@abit和@ned的支持而强大,也由于支持的消退而势微。

更有意思的是,有些势力根本不使用标签,或者即使有标签也并不鼓励使用。比如steemit inc.就不需要自己的标签,因为它至少过去承担政府的角色,希望对整个平台的运行负责;它今天开始放权,组织议会,就更加不需要被人提及。又比如steemcleaners扮演的执法角色,反对作品抄袭,也主要不在于标签的使用,而在于暗中的支持。或许也有一些黑社会组织或地痞流氓,他们横行霸道,也未必需要标签。

对于新人来说,常常不了解以上这几类势力和他们相关的标签的存在。好比一个初入武侠世界,以为天大地大,不知天高地厚,没有打算依赖任何门派和师承的新手,或者误打误撞跑到别的门派的地盘上去发拜帖,却完全不懂规矩,于是横冲直撞、处处碰壁、鼻青脸肿、一无所成。当然,自带身家和绝学的人民币玩家,可能可以一出道就呼风唤雨,但独行侠也往往不是一帆风顺的。

3. 为什么谈标签?

其实看了上面的描述,大体也能理解我们在本文谈标签的原因所在。但这里还是具体给些解释。

(1)新人需要掌握标签的“道”和“术”

现在新人学习steem,常常从操作和技巧开始,这并没有什么问题。但要更进一步,融入社区,则要对steem的本质有更清晰的领悟。

关于标签的用途和使用方法《Steem指南》1.6.42.87.1等章节中都有所介绍。另外下面几篇文章(应该还有更多),也介绍了标签的概念和使用技巧,可以作为大家的参考和学习。

感谢以上文章的作者贡献的智慧。但我认为这些介绍一定程度上都停留于技巧的层面,对于背后的原理和社会本质缺乏透视性观察;这是本文不从“术”着手、而从“道”着眼的原因所在。

理解steem的三大支柱,就能通过演绎法,从底向上去了解steem的运行规则;如果仅仅是停留于技巧,那么仅仅是看到steem的表面,而忽视了其核心。介绍steem的技术和经济,想必已经有很多文章,但关于社交的问题,我觉得通过标签来“格物致知”是最能了解其本质的。

读懂标签,就是读懂steem内的江湖与人心、读懂社区的运行规则、读懂技术的变迁,对于steem社交原理与变化的内涵,至少能读懂一大半。对于steem上的各种现象和怪现象,也能够通晓一二。

这里顺带简要谈一下《Steem指南》的问题。我们需要《Steem指南》的本质原因在于Steem内在的复杂性与多样性,需要提供一种学习方式帮助新人有效、快速地理解与融入社区(实际上世界上其他的社交网络也都不简单,只是作为普通用户不解其中味而已)。《Steem指南》着实帮助了大量的新人深入了解Steem,但随着时间的发展其内容缺乏有效更新与互动反馈,生命力在逐渐衰弱。

另一方面,我们看到,@team-cn 新手村依旧能切实帮助到新人,并形成了很强的互动性。这原因在于新人既需要一本武林秘籍,同时也需要一个可以信赖的门派(学校)。而一旦武林秘籍和门派(学校)的关联度降低的时候,在路边捡到静态的、乃至过时的剑法和心法也未必能被新人真正掌握了。所以,从这个意义上来说,新人不仅仅需要一份攻略,更需要一个学校(事实上是学习、社交、娱乐和活动的场所)。

最近,在得到@dapeng大鹏的认可后,@elizacheng @ericet @julian2013 @m18207319997 @minloulou @olymim @robertyan 对《Steem指南》的现有版本做了审核,大致确定了对其进行修改的范围,并将在近期邀请大家参与修订和编辑工作。毫无疑问,此次对《Steem指南》的修订和维护工作是站在CN社区内创作者们,以及@dapeng、@maiyude等编辑的巨人之肩膀上完成的,其未来的生命力也取决于CN区本身的生命力。同时,这一使命(mission)将促进《Steem指南》#steem-guies和新手村@team-cn的工作的融合(即攻略和学校的融合,是书写文字与多元交流的结合)。对于更具体的内容,我们会在之后发布相应的计划与参与方式。欢迎关注并参与《Steem指南》的修订工作和长期发展!

此外,需要说明的是,本文对标签(tags)问题的讨论,并非是对《Steem指南》的投稿。但正如上文提到的,从标签出发,是深入了解在Steem中如何写作、社交,了解社区活动和dApp的一个切入点,也是重新审视《Steem指南》的一个视角,对于新人而言,是有所裨益的。

帮助新人从“道”上理解标签(tags)、掌握steem,这是本文的目的之一。

(2)CN区再次崛起的逻辑

@oflyhigh在#cn 标签不见了中提到#cn标签曾经火热的历史原因,即见证人@abit和创始人@ned的支持。他们对CN区的支持其部分原因是在于对华人在区块链世界中的贡献的认可,也是对华人广阔用户基数的期望,抑或包含对同文同种的同胞的支持。但这种支持在逻辑上并非完美的,代理可以撤销、见证人可以隐退,如果CN区仅仅依赖这些不确定的强力而食其利,毫无疑问随时具有倾颓的可能。

相比之下#kr社区(韩国、南朝鲜、南高丽)表现出了强大的动力,尤其是出自#kr社区的大量dApp,推动了steem社区(至少表面)的繁荣。但我尚不清楚KR社区的历史及其火热的内在逻辑,这有待于我们在后面的分析中进一步挖掘,但毫无疑问KR社区的历史经验值得CN社区的主导者们深入学习,见贤思齐、见不肖而内自省。

基于上面的一些事实,我猜想标签的起落有其内在的逻辑:初期客户端、应用类的标签较少,以文化、语言、地区为主的标签最容易形成,并具有同语言圈、文化圈和地区内的扩张性,而随着新的社会构建与势力的形成,旧的文化/语言/地区社区的影响力必然受到一定影响。除非文化/语言/地区社区能够成为支持新的势力构建的基石,并不断孵化新的自带造血功能的标签/势力,否则其衰退或将不可避免,KR区的崛起、CN区的衰退可能可以作为一组例证。当然,CN区依然出现了大量有价值的应用,如#partiko、#wherein等,但相比起来,对全社区的辐射作用似乎不足,内敛胜于扩张。

如果上面的逻辑不错,CN区希望再次崛起或许依赖于更多、更有价值的由CN区生长出的标签,这包括cn--cn类标签(通常为CN区活动、或全局标签的本地化),以及不带有cn的更通用的标签的出现。

为了实现这一点,我们或许需要一个植根于CN区的标签孵化器和投资机构(Tags Incubator),借助CN区和外部力量,整合社区内外的智力、财力和社会资源,孵化更多有助于Steem、甚至有利于全人类的创新应用、工具、组织和活动。这里可能涉及到资源的质量与数量、条件的成熟度,以及“蛋”和“鸡”的关系,等等。另外,由于网络政策的影响,CN区用户的增长遇到一定困难,但即便如此,海外华人5000万以上的体量,也未必不可有所作为;而且,又何须只盯着华人用户呢?

关于这一问题,我们或许无法在后面的论述中做太深入的分析,但希望根据标签热度的历史性变迁规律做一些仔细的考察,如KR与CN区的横向比较。但即使我们无法提供真正的解决方案,只是引起这个话题本身,也达到此文的目的了。

最后,我们或许也可以一问,如果CN区真的衰落了,我们是否还有必要让它重新崛起呢?为什么#cn必须成为热门呢?其实也未必。但感情上、信念上,我们依然希望CN标签(以及它所代表的含义)不会就此消退,甚至可以逐渐壮大。这就是这个话题会引起很多人兴趣的原因吧。:)

从标签(tags)的角度观察社区势力的起落、引发对CN区发展的可能性的思考,这是本文的目的之二。

(3)对“标签”的思想探险

上面这两个为新人、为社区的思考角度,都是功利性的(即便是利他的或者公益的,也是“利”)的思维。这第三方面的原因,即对于标签这一对象本身的考察和研究,才是本文的“求真”主旨。“求力”的智慧固然令人向往,“求真”的理想却更令人沉醉。

为了理解这一点,我们需要跳出steem来谈标签。

首先,“标签”在生活中无处不在。

“物以类聚,人以群分” ,近代以来,我们是习惯于将物与人做分类或者划分的,比如生物学的分类系统的“域、界、门、纲、目、科、属、种”,比如图书的分类系统(杜威十进制图书分类法中国图书馆图书分类法等),比如将人群分成幼年、少年、青年、中年、老年,比如人体血型的A型、B型、O型、AB型,比如将数字分类的奇数、偶数、质数、合数,比如将语言分为en、cn、kr、fr,比如将网络分为中心化、去中心化、分布式,等等。“类”和“群”是一种特殊的标签,比如steem中的首标签或者主标签,一般也被用作类别(category)。

社会中也经常使用大量的身份标签,比如制服、比如角色。制服对于显示权威性、合法性和专业性有显著帮助,比如警服、比如医生的白大褂。父亲的角色、老师的角色,在他人看来也有一种隐含的意味,比如父亲的坚强、老师的慈爱,这些也是隐性的标签。比如聪明、美女、土豪,也都是我们给身边的人打上的标签。比如 @julian2013 机机是满腹经纶,比如 @ericet 村长是百科全书,比如 @softmetal和@pgr是好兄弟。

标签无处不在,我们却没有真正理解,这不断激发着我们的好奇心。

其次,“标签”是一个复杂的研究对象。

标签通常是对一个“类别”或“群体”的对象进行特征描述。按照区块链世界的话术来说,一旦被群体性使用就形成了某种“共识”,比如科学家团体对科学的崇拜、比如军人对尚武的认同、比如商人对利润的追求。“共识”又将影响信任,比如我们原因信任医生们的医术,又比如我们不那么信任罪犯会继续行善,这种信任也确实并非总是正确,毕竟群体的“标签”有时作用于个人会有差别。所以标签有很深入的外延,对标签的研究常常需要对“信任”、“认知过程”、“元数据”等多种社会、信息和生理机制有所了解

另一方面,既然物、人、信息、行为等等都可以被打上标签,就不可避免得进化出了各自的复杂的标签系统。要了解标签的形成、问题和发展,又必须深入到各个领域的语境之中,所以分析其共性便存在各种挑战。分类学、图书馆学(情报学)、博物学、语言学、认知科学、社会心理学、人类学、数据科学等学科都各自存在对标签的研究,足见其在理论与现实中的影响深远与研究方式的复杂性。

将复杂对象提取出某种本性,也是驱使我们探索的重要动力。

最后,掌握“标签”的本质,是认识自然、理解社会、了解文化的重要环节。

我们的大脑中存在一个“内置的标签系统”,它既是生物进化的结果、也是社会关系的产物,通过它,我们可以更加深入和广发的理解自然、学习文化、理解世人。标签与语言也有着极为密切的关系。

所以通过向内和向外观察世界中的标签系统,能帮助我们对宇宙和自身有更切实的了解,并借助标签系统对人类的知识、文化、技术和社会进行更有效的创新。正如我们提到的可以对Steem标签进行“格物致知”一样,于宇宙也能如此。

使用标签系统来作为理解世界的一个视角和切入点,也有必要的意义和价值。

理解标签的本质(the nature of tags),以及它在自然、社会、知识、技术和文化中的意义,这是本文的目的之三,也是最根本的目的。

source: Wikipedia – Tag (metadata) | CC BY-SA 2.0

什么是标签?| What

为了引出本文的核心内容的讨论,我们在引言部分以steem为着眼点已经做了很多铺陈,下文我们从起源、自然、社会、信息、技术和文化等各个角度来观察标签的演变与本质。

1. 被忽视的分类学:标签从何而来?

标签的特点在于对关于周边世界的信息的组织,“组织”(organize)近乎于人的本能。由博物学发展来的分类学,是对人类这一“组织”能力的集中体现,我们由此谈起。

(1)博物学传统中的分类学

我们在【未来教育学】成年人需要什么样的STEM教育?[(一)(二)(三)(四)(五)(六)(七)] 这一系列中,谈到了起源于希腊的理性科学传统。亚里士多德认为自然哲学(natural philosophy,也就是知识、科学)是最重要的知识形态;但同时,亚里士多德也做了大量博物学(即natural history,现在也建议翻译为“自然志”或“自然史”,以下用博物学指代natural history)研究,例如《动物的器官》、《动物的运动》、《动物的生成》等。亚里士多德认为博物学/自然志是一种比自然哲学低一级的知识形态,把博物学/自然志认为是自然哲学的准备阶段。博物学/自然志是自然哲学-数理实验科学传统之外的另一种科学传统,指的是对自然的观察、记录、分类和分析等,但随着现代数理实验科学的发达、以及功利主义的盛行,博物学传统在全世界都遇到了极大挑战。

自然志(博物学):追求自然秩序

与自然哲学对永恒不变的追求相比,博物学采取首先收集和鉴别“事实”,然后对其进行描述和命名,最后进行分类编目;其目的是尽可能详尽地描述和了解现象本身,更注重现象和事实的个别性、独特性、不可还原性,而不追究事物背后的原因。博物学的重要特点是追求“自然秩序”,其内容包括动物、植物、天文、地理、气象、水文、矿物等。分类学则是在博物学的这一科学传统的发展中的必然结果。

18世纪的瑞典的伟大博物学家林奈被称为“分类学之父”,其贡献主要是建立了以植物的性器官为分类依据的植物分类法,将植物划分为24纲、16目、1000多属、10000多种;以及建立了拉丁语双命名法,结束了生物命名问题上的混乱局面,被称为“亚当第二”(《圣经》说上帝造物,亚当命名)。《自然系统》《植物种志》是林奈在分类学上的伟大成就。

我们打开常见的植物志或者动物志书籍,多半都能被其中生动的图片和描述所吸引。作为一种信息描述方法的“标签”在“自然志”和“博物学”的书籍文字中并不常见,但对于生物性状特征的提取(如体型、分布、毛色等),本身也是一种标签化、特征化的过程,这在对生物迁徙、进化、起源的推理过程中非常必要。而分类,是在各种特征标签的基础上,形成的一种层级化、结构化、占主要地位的标签,是对大量特征标签的抽象,具有唯一性。从这种意义上,我们可以认为“标签”是“分类”的基础,例如林奈根据植物雄蕊和雌蕊的数目和相对位置设计的纲(class)、目(order)的分类法。

分类学与标签:信息组织技术

分类学(taxonomy)是一种从顶向下、系统的、中心化的对世界进行划分的方法,是具有明确的上下层级的树状结构,其本身是对自然秩序的有序观念的一种表现。由博物学发展出的进化论的核心论点(1)所有生物拥有共同的起源(2)现存生物多样性是自然选择的结果,也反应了同样的思想内容。这与西方的“唯一”的“真”的科学传统相一致。分类学是为博物学建立体系的有效方式,其传承者大多是百科全书式的通才。我们或许可以这么认为,哲学家的使命是发问,而博物学家(naturalist)的使命是分类

与生物分类类似,近代的图书馆系统也采取了类似的分类学方法(图书馆学),如杜威十进制图书分类法四部分类法(经史子集)中国图书馆图书分类法等,这既有计算机出现之前人类检索信息方式的影响,也有物理空间放置书籍和文献的原因。今天的图书馆仍然采用严格的分类方法摆放图书,但索引书籍的方法却有了极大改变,信息检索意义上的“标签”的价值得到了真正体现。我们在书籍查询时,有效利用标签和搜索,能快速过滤、发现和定位书籍,并发现相关联的书籍。

从其本质上来说,分类学是一种理解和组织万物信息的“技术”:一方面,它本身是一种求知的行为,有人类内在的寻求秩序、形成组织的动机;另一方面,它的产生也在于解决当时的某领域内的“信息过载”问题,以分类学为骨架,构建生物界和书籍文献的谱系,也为人类在自然探索和知识发现方面的协作提供了基础。作为一种技术,标签既是分类学的基础,也是分类学的补充。

source:Wikipedia – Taxonomy_(biology) | CC BY-SA 2.0

(2)“远古”与“现代”的大众分类法

分类法(taxonomy)是对“自然的秩序”的探寻的伟大成果与集大成者。事实上,近代博物学/自然志(natural history)的进展很大程度上取决于印刷术的发明和大翻译运动、地理大发现与商业的繁荣、以及医学教育对药用植物志的重视,其中印刷术的普及(即文字传播成本的降低)或许是主因。

但在柏拉图和亚里士多德的时代或之前,书写文字的普及程度并不高,体现为主流文化主要依赖口头交流和记忆,此一时代的信息组织与分类方式会有什么不同呢?

口语文化与大众分类法

沃尔特·翁在《口语文化与书面文化——语词的技术化》里,探讨了口语文化到书面文化的变迁,以及书面文化向现代次生口语文化转变的过程。其中提到口语文化与书面文化相比,具有几个重要的特点(以下列举其中5个,共有9个。《口语文化与书面文化——语词的技术化》,3.3节):

  1. 附加的而不是附属的:口语文化倾向于线性追加条目,比如“然后……然后……”,而不是建立树状结构。相反,书面文化往往把上下位关系引进话语。
  2. 聚合的而不是分析的:善用套话和格言警句,把重要的信息聚合在一起。
  3. 冗余的或“丰裕”的:经常重复以理清思路并帮助人理解。
  4. 移情和参与式的,而不是与认识对象疏离的:听众的参与是集体参与,有吟唱、呐喊、欢呼等呼应
  5. 情景式而不是抽象的:交流很大程度上取决于情境,一般不会用抽象的词汇和概念,而是用具体的物体名称

可以看到,在书面文字被大量普及之前,人们的表达和思维方式是偏向于更灵活、松散、自由的模式,这与标签的个人化、描述化的特点接近。甚至我们可以认可语音与文字也是借由标签-分类的逻辑创建出来:

  1. 自底向上的自由的标记(加标签),如对某一对象核心特征用语音或象形表示;
  2. 有意识的分类:比如对同一类标签,建立类别,比如同一词源和词根的词语、相同部首的汉字等
  3. 将语音和文字归纳成体系,形成系统的音、形、义相协同的语言。

从这个角度来看,语音和文字构成的语言,是最早的成体系的分类法,而对事物进行识别,形成语音或文字这种标记/标签,是实现语言体系的起始步骤。这种内嵌于人类语言的分类法,和我们的文化缠绕在一起,几乎成为人类的第二天性。由于语言的这种自底向上形成的历史特征(在今天的网络语言中也能观察到),我们可以认为在系统文字生成之前的口语文化,依靠的是一种“大众分类法”(folk taxonomy)进行信息组织,由参与者不断通过添加新的元素、聚合特征、情景式互动、通过反复使用而传播,其特征与我们在上文提到的口语文化的特点几乎一致。

数字媒体与次生口语文化

当文字广泛流行后,世界进入了数千年书面文化的时代,直到数字媒体的出现,我们进入了沃尔特·翁口中的“次生口语文化”(secondary orality)时代

随着电视、广播、互联网媒体的兴起,人们可以不需要阅读书面文字便能获得大量信息、进行娱乐、交流、甚至接受教育,以至于引起了环境媒介学派对《娱乐至死》的担忧,数字媒体与成熟的资本主义相配合,鼓动消费主义与娱乐倾向,加之书面文字被口语文化的排挤,阅读人数急剧下降,让人感到赫胥黎的《美丽新世界》恍若现实。在数字媒体时代,“口语文化”通过数字媒体所建立的新的传播渠道再次降临,同时随着“地球村”的出现,影响比前文字时代大的多、产生的“口语文化”的聆听群体也多得多,这就是现代的“次生口语文化”

如果我们仔细观察身边的群体与媒体,我们上文提到的“口语文化”的思维与表达特征几乎已经泛滥于媒体之间。与之相适应的,网络标签在web 2.0的热潮之中获得了充分的发展与应用,社交媒体平台中的标签既继承了原始标签口语文化的自由化、个人化、社交化、非专业化的特点,又发展了书面文化的“记录”的特点,并随着信息系统的发展变得高度灵活、便于查询与分析。这也是一种现代的大众分类法(Folksonomy

其代表性的系统如 wordpress、del.icio.us、豆瓣、Amazon、LibraryThing等等。其中,steem也充分应用了标签技术并赋予了标签更强大的生命力和社会权力,比如作者常常会为了获得奖励去使用某个标签,这实际是强化了的标签的社会属性,我们在后一章谈社会性标签时会进一步讨论。

source:Wikipedia – Walter J. Ong | CC BY-SA 2.0

(3)多层次的分类系统

在上面两节,我们讨论了自顶向下设计的“分类法”和自底向上的“大众分类法”,标签在其中都扮演了识别特征以及完善体系的作用,由此我们可以构建一个多层次的分类系统(classification system)。本小节简要说明此种系统是如何工作的。

组织信息:树状 + 网状

一般而言,以信息管理或知识管理为目标的分类系统存在一个主干骨架,即分类学(taxonomy),基于类别(category)建立,同时也存在标签等辅助信息(或称为元数据)用于方面理解系统中的事物。我们可以认为标签是一个关键字类型的辅助信息(keyword metadata),用1到n个简短的词语来描述对象的特征,作为人或用户理解系统中对象的媒介(如下图所示)。

source: 由作者绘制 | CC BY-SA 2.0

与标签相比,类别通常是唯一的;而标签是多维度的、自由的。就steem而言,如果使用标签系统,我们就能同时向多个社区投稿,比如steempress和cn-stem(如本文所做的那样),而类型就只有一个(主标签或首标签)。

类别系统通常是一个树状的索引系统(但也可能一个对象有两个类别,比如玻璃杯既是玻璃制品也是杯子),标签体系为分类学系统提供了一个网状结构的支撑系统,便于信息更灵活的发现、关联与再生。对标签进行合并、关联、分析,有能产生新的内部结构,这是标签系统的灵活之处。

像现代的图书查询系统、Windows和Mac的文件系统、Evernote或Wiki的文档管理系统、博客系统等,都采用了类似的设计。既保持了结构的有序性,又增加了标签的灵活性。

协调众人:自由 v.s. 控制

从上面的图示中,我们可以看到标签是人和事物的媒介。如果只从事物这一侧来看,结构可以是树状的和网状的,这更接近信息管理或知识管理的需要。但如果同时考察人的这一侧,则必须考虑协作、共识、专业与业余、标准与特殊、个人与团体等张力。这也是社会化的标签系统的复杂性所在。

一般而言,这需要考虑到具体的场景进行差别性设计。比如社会系统,既存在一定的金字塔结构,也要提供自由交流和沟通的网络循环,保持社会层级之间的流通。比如电子商务平台,既要允许商家添加标签,也要提供导航栏的类别方便消费者查看,还要根据商品属性,提供可以用户筛选的特性(价位、大小等)。

我们在前面两节提到的生物分类法和图书馆分类法,更倾向于控制,由专业学者和工作人员进行类别划分,但同时也从其他资料同步各类标签和描述以丰富书籍信息(如图书馆系统会参考豆瓣、GoodReads、LibraryThing等信息)。

相对的,自然语言系统、社交媒体,由于几乎没有人能进行顶层设计,一般采取自由的大众分类法的方式,随标签形式自由生长,根据标签的流行逐渐建立类别、谱系、关联等等。

本文中,我们讨论了标签的历史根源标签是人的“组织”本能的思维体现。标签作为对物的整体或部分性状的描述,为分类学的产生和发展提供了思维基础,并共同组成了分类系统,进而丰富了语言、博物学、网络和信息管理系统。

(未完待续)关于标签本质的探讨,我们仅仅将门打开了一条缝,之后我们会从社会、知识、技术、文化、语言等层面进一步观察和分析。

参考文献

  1. Steem中文社区 创作,《Steem指南》,@dapeng / @maiyude等 编辑
  2. @woolfe19861008:《Steem 指南》 中级篇 | 善用标签上:标签的基本概念介绍
  3. @woolfe19861008: 《Steem 指南》 中级篇 | 善用标签下:新手初到Steemit常用的一些标签
  4. @cherryzz:寻宝活动–tag标签(第一周10个标签)
  5. @ericet:如果我是一条小鱼(上)新手指南
  6. @ericet:如果我是一条小鱼(下)新手指南
  7. @oflyhigh:#cn 标签不见了
  8. Gene Smith:《标签》(Tagging: People-Powered Metadata for the Social Web),机械工业出版社,2012年
  9. 吴国盛:《什么是科学》,广东人民出版社,2016年
  10. P. A. 查德伯恩 :《博物学四讲》,邬娜 译,上海交通大学出版社,2017年
  11. 保罗·劳伦斯·法伯:《探寻自然的秩序》,杨莎 译,商务印书馆,2017年
  12. 吉奥乔·阿甘本 :《万物的签名》,尉光吉 译,三辉图书/中央编译出版社,2017年
  13. 詹姆斯·格雷克:《信息简史》,高博 译,人民邮电出版社,2013年
  14. 麦克卢汉:《理解媒介》,何道宽 译,商务印书馆,2000年
  15. 沃尔特·翁:《口语文化与书面文化——语词的技术化》,何道宽 译,北京大学出版社,2008年
  16. Wikipedia:https://en.wikipedia.org/wiki/Taxonomy_(general)


Posted from my blog with SteemPress : https://robertyan.000webhostapp.com/2019/03/%e6%a0%87%e7%ad%be%e7%9a%84%e6%9c%ac%e8%b4%a8-the-nature-of-tags%ef%bc%88%e4%b8%80%ef%bc%89


This page is synchronized from the post: 标签的本质 | The Nature of Tags(一)

【未来教育学】成年人需要什么样的STEM教育?(七)

【未来教育学】成年人需要什么样的STEM教育?(七)

【未来教育学】成年人需要什么样的STEM教育?(一)【未来教育学】成年人需要什么样的STEM教育?(二)中我们引出了问题“成年人需要什么样的STEM教育”,并从(1)希腊科学与近代科学的根源、(2)中国文明缺少“求真”的传统、(3)缺少科学精神带来的现实挑战,这三个方面,解释了成年人,尤其是中国成年人,为什么需要STEM教育?的原因。

【未来教育学】成年人需要什么样的STEM教育?(三)【未来教育学】成年人需要什么样的STEM教育?(四)【未来教育学】成年人需要什么样的STEM教育?(五)【未来教育学】成年人需要什么样的STEM教育?(六)中,我们具体讨论成年人需要什么STEM教育的问题。在(三)中,我们对其中的核心问题“科学精神与科学思想如何落地”展开了分析;在(四)和(五)中,我们讨论了科学读物与科学实践的关联,并详细介绍了推荐的读物和可采取的实践;在(六)中我们讨论了“以人为本的科学教育”的基本理念。

在本文中,我们继续讨论成年人需要什么STEM教育的问题:以人为本的科学教育应该如何针对不同的人群,采用合适的策略开展,回答“为什么学”、“学什么”和“怎么学”的问题。这是全文的最后一部分。

成年人需要什么STEM教育?(What)

3. 以人为本的科学教育

上文谈了“以人为本”的科学教育的内涵,这里继续谈如何人以群分地因材施教。

#1 老人

(1)为什么学:终极关怀

为了走向科学的本质,我们从老人谈起。对“老人”的定义未必有严格的生理上的年龄界限,但我们可以认为老人都面对一个共同的问题:“终极关怀”。

“终极关怀”,意指“源于人之存在的有限性,源于人之企盼生命存在的无限性,它是人类超越有限、追求无限以达到永恒的一种精神渴望”。“终极关怀”大体可以归于“神”“真”“义”:“神”即“宗教”赋予人的意义,“真”即对世界的真理的探求,“义”即“以天下为己任”、“为万世开太平”。当代中国人归于“神”(佛、道、耶)的有之,但并非文化主流;归于“义”,由于“仁爱”传统的影响,追寻者不少、实现却颇有困难;归于“真”,同样由于传统的关系,追随者太少、实现也不容易。大部分人趋利享乐,其中能谈得上终极关怀的,不是很多。

老年人面向人生的终点,对于“终极关怀”的思考相对更多。“神”、“真”和“义”未必有高下之别,但无疑的,“求真”在面对人生的终点 的问题上,可以是其中一种合理的选择。在离开世界前,真正认识了这个世界的永恒与丰富,是对人生意义的追寻方式之一。“自由-科学”为实现“真”提供了体系,因此“好奇心、理性思维与求真理想”可以在老人身上萌发,这至少可以回答老人的“为什么学”的问题

西塞罗在《论老年》中所言:老年依然可以参与政治生活及其他很多事物,老年虽然体力衰退、感官快乐减少,却更有理智;…… 这种快乐(写作、演讲等)完全是一种求知的乐趣,对于理智、受过良好教育的人来说,这种快乐是随着他们年龄的增长而增强的。(《论老年》中还有许多其他对老年的的深刻论述,值得思考。)

“家有一老,如有一宝”,老人的生活、事业和社会智慧,是有实际的启发作用和战略指导意义的;给年轻人的经验与鞭策,是有的放矢的。所以,除了“求真”的意义,老年人的智慧对于自己和影响范围内的实际生活,也有实用的(功利性的)帮助

然而,如何正确地传达这种对“求真”的“终极关怀”的理解,并非易事;本文此处只能指出大略的方向。对于更多具体性的商业化、产品化的媒体宣传与市场推广的实现过程,值得我们在更多文章或社会实践(老年大学、社会企业、公益组织等)中进一步具体讨论。

(2)学什么和怎么学:“快乐之学”

前文我们批判了“娱乐化教育”,但我们不反对“快乐之学”。我们反对的是人为设计的、以取悦学习者、通过游戏来“贿赂”和“利诱”学习者的“娱乐化”教育,但不反对自发自主的快乐之学,于老人便更是如此。

“快乐之学”应当以自发的、内在的兴趣(好奇心)的出发点,同时辅之以科学精神的“理性思维”方法,以真理为依归、以传播智慧为乐。

今天的老年大学,开设的课程,如养生、健身、语言、书画、摄影、音乐、戏曲等,与兴趣班接近,加之社交的功能,受到许多老人的喜爱。但这其中“理性”、“真理”、“智慧”的生存空间并不大,“娱乐”多于“真理”。

以兴趣驱动的求真之学,未必需要以老年大学为依托,独自便可以开展;但也可以存在较松散的俱乐部、社团或大学,交流与互相学习。如金庸之求学于剑桥与北大,可为一例。“快乐之学”未必需要有严肃的学术规范,但凭热烈的求知之心,可以行到什么程度便是程度,已是甚好;当然能维持高水准、与当世的前沿学术有所交流甚至引领潮流,也未必不可。

“兴趣”是较个人的事情,文、理、工、农、医、商、教育、艺术、法政等等,皆可入手,但方法上总难以离开读书或文献、观察和解释自然和社会、思考推理、分析实验、社会实践、与他人交流等等,在这些方面,很多仍然是老人的智慧与体力可以实行的。(我们在上一节提到的科学教育的四个方面、与前一章推荐的科学读物,也可以作为参考。)

在老年,能以“真”为乐,“活到老、学到老”,又何尝不是一种幸事呢?

#2 职场人士

(1)为什么学:“科学决策”

我们在“科学实践——实践2:建立科学决策系统”这一节中解释过科学决策系统的重要性。我们在这里用“学会决策”作为一种“方便法”来吸引“职场人士”的关注,也是基于他们对于决策的强烈需要与决策能力不足这一矛盾的观察。

决策的主题可以包括投资、教育、房产、战略等多个维度,越是重大的决策,越是容易摸不着头脑。在“知识付费”的热潮里,各种传播科普知识、技能、方法论、致富之道、教育宝典的课程很多,但无论哪一种都无法取代个人在面对决策时的思考、提供的可转化为行动的指导极少。究其原因,一方面是决策和技能类似,需要在实践中学习(learning by doing),另一方面重要的复杂的决策都受到多方面因素的影响,缺乏对整体世界的观察和理解,是无法独立思考、完成高质量决策的

决策依赖于对数理逻辑、“理论体系”、和复杂世界观测的综合把握,如果缺乏对“无用之学”的兴趣,加之现代世界分工的严密与专业化,往往令人陷入自我限定的狭小世界中,于是难以真正做出有效决策。当然,决策未必与商业有关,也可能关乎健康、关乎公益、关乎情感、关乎人类命运与前途。

从另一个方面来讲,我们引用很多职场人士崇拜的乔布斯笃信的一句话:“如果你把每一天都当成生命里的最后一天,你将在这一天发现,原来一切皆在掌握之中。” (来自史蒂夫·乔布斯在斯坦福大学毕业典礼的演讲)如果职场人士愿意认同“真”是一种“终极关怀”的话,并且他真的理解了乔布斯的话的内涵,那么“真”至少也可以是他每天生活的一部分。这也是为什么我们先从“老人”开始谈起的原因,从终点向前看,我们或许能看得更清晰。

(2)学什么和怎么学:“无用之学”

曾经有人向欧几里得学习几何学,学习几日后问老师所学的知识有什么用,欧几里得闻言大怒,“竟想跟我说有用之学,我从来都只教无用之学”,遂将学生逐出。希腊理性科学的传统,以“无用之学”为理想,“知识”的目的就是它本身。以“利”(金钱、权力)和“力”(技术、方法)为追求的职场人士恐怕很难理解这一点。

但要真正的学会给难题做决策,就不能仅仅学习以满足工作需要为目的的“专业技能”、或者赚钱的“方法论”,必须跳出“井底”,对世界的本质和本源进行观察,了解世界和历史的运行规律,跨越时空与先贤对话。要构建对于客观世界的观察和自己的“理论体系”(参见“科学实践——实践2:建立科学决策系统”一节),需要掌握历史、文化、自然科学等无用之学(而非仅仅是更流行的编程、金融、心理等),并结合职场人士在专业领域的专业知识与技能,在更高维度做出正确的决策,比如新能源企业的战略与产业结构变化的匹配、比如苹果公司对个人电脑的开创性设计。

所以,即便为了在现实中“求力”,职场人士也应该了解“自由-科学”的含义,多了解一些“无用之学”。

#3 父母

(1)为什么学:生命教育和灵魂教育

中年人白天是“职场人士”,到了晚上或者周末就要扮演“父母”的角色。作为父母的角色,也容易被“实用主义”的现实裹挟。比如,让孩子参加大量兴趣班、培训班、补习班,以“抢跑”为荣,一切唯“分数”、“升学”论,但同时将教育的责任推给学校和培训班,自身的思考和陪伴却远远不够。最值得警惕的是,缺乏对“生命教育”和“灵魂教育”的思考。

这里对“生命教育”和“灵魂教育”做如下考察:

生命教育:指帮助孩子实现生命的价值,表现为对生命的尊重、有意义地度过一生“自由-科学”传统的本意是对内在的“本性”的尊重,使人可以获得真正的“自由”;即我们在“老人”这一部分已经叙述过的“终极关怀”,从这个角度,父母有必要了解科学的真正的含义。

灵魂教育:柏拉图在《理想国》中将灵魂分为理智、意志和欲望(也称知、情、意);理智统御“意志”和“欲望”,转化为智慧和勇气。灵魂教育的意义在于,令人获得真正的智慧、勇敢的气魄、充沛的情感,同时懂得节制欲望,既是德育、也是智育和美育。孩子智慧的获取,离不开好奇心、理性思维与求真理想,而脱离智慧的勇敢与情感,则是盲目和可怕的。

因此,为了让孩子在生命与灵魂上有更深入的理解,父母也应当关心科学精神(当然还需理解情感、仁爱等),而不仅仅是表面的数学和科学的应试功能。

(2)学什么和怎么学:“身教”大于言传

为了让孩子掌握真正的“自由-科学”的精神,最根本的做法在于家长的“身教”。让家长自身成为一个有问题意识、会主动学习、能独立思考、关心世界、善于思辨与推理分析之人,掌握健全的知识体系,养成理性科学的决策能力,并向更多人传播科学文化,若能如此,孩子必能习以为常、切身了解到“科学”和“教育”对人性的意味之所在。

如果父母仍然无从着手,可以回退到上一节的“无用之学”以及老人的“快乐之学”去寻找答案,先从改变自己开始,改善孩子生长的家庭环境土壤。这实际上也是对现代学校教育制度的一种补充和改善。

#4 青年学生

(1)为什么学:“自由”与“仁爱”

接下来我们谈“青年”,尤其是大学生。大学教育的意义不在于掌握谋生的“专业技能”,而在于为成为未来的世界领袖奠定基石。这基石在于对生命的思考、对未来的选择、对品格的锤炼、对能力的锻造

青年不妨回到我们在前文提到的老人的立场,或者乔布斯的“把每天当做生命的最后一天”的视角,去了解自己的终极关怀,回归人性的理想,并养成宽厚、仁人、超然的品格,获得自由的“学习”与“思考”能力,鞭策自己找到真正的志向,这是成为“一流”人物的前提。“自由”是这样的一种志向,它通往“真”;“仁爱”也是这样的一种志向,它通往“善”(也就是“仁”和“义”)。

但无论“志向”还是“品格”、或“学习”与“思考”能力,都不是通过被动灌输知识可以获得的,无一不是通过在现实中的实践与磨练,通过日复一日的提问与思考,逐渐养成。如果青年不满足仅做一个没有独立人格的“螺丝钉”,就应该去尝试回到“自由”与“仁爱”的源头,建立宽广的知识视野,养成宽广的胸怀,获得真正的自由,成为“仁爱”之人

(2)学什么和怎么学:“自由教育”

这种方案其实在大学已经存在了,只是有待改进,这就是“自由教育”(liberal education,也叫博雅教育或通识教育)。自由教育起源于柏拉图和亚里士多德时期,有“自由七艺”的传统,强调的是传授所谓的“无用的知识”,如古典文学、古代历史、哲学传统等等。与之相对的是“专业教育”(professional education),以传授实用的知识为主。

“自由教育”倡导与伟大心灵的交谈,可谓是西方的“国学”。今天中国大陆的许多所谓“国学”班却走向了培养“礼”和“顺从”的误区,却没有促成学生与包括“仁爱”在内的诸子的传统的对话与思辨。中国的“自由教育”,应当一方面,通过真正教授“国学”(中国的和西方的),帮助学生作为一个中国人反思自己“从何而来”,找到自身在历史和世界中的位置;另一方面,使得学生以此为基础,保持开放的心态,广泛地阅读、思考和交流。

作为结果,经历过“自由教育”的学生,应该学会如何读书、如何思考和如何写作,尤其是非功利的、以思想的成长为目的的读书、思考与写作。郑也夫教授在“我们怎样读书、怎样思考”中对此给出了更多的建议,可以作为参考。

#5 学者 / 智识分子

(1)为什么学:“为天地立心,为生民立命,为往圣继绝学,为万世开太平”

青年可以成长为职场人士和父母,可以渐渐老去成为老人;或许她/他也会成为政治家、军事家、科学家、思想家,或者成为真正的英雄。

文化是人类的生物优势。从近代来看,历史的真正推动者大多是文化、科学、技术和制度领域精英。对于“历史是由人民大众决定”,这是在民主浪潮下的粉饰之言。我们无需否认大众的价值,但真正的历史是由少数人萌芽,而最终慢慢演变成大众的,科学革命是如此、儒学兴起也是如此。人类的真正的智慧在开始时总是少数,在不断变化的环境的自然选择之中,才导致变异的文化逐渐占据主流;“时势造英雄,英雄也造时势”。所以,我们应当重视“精英史观”与“英雄史观”,这与现代的平等、民主观念并不矛盾。

作为真正有志于成为英雄的“学术研究者”(学者)或者自认为智慧和见识在人类中属于卓越者(智识分子),更应对此有高度的自觉。张横渠的“为天地立心,为生民立命,为往圣继绝学,为万世开太平”,是对这精神的最好阐释。从现代的意义上来看,“为天地立心”既包含了“人”/“仁”的内涵,也可以容纳“求真”的精神,这也表明了“自由”与“仁爱”统一的一面;同时,为了实现“横渠四句”的理想,非有大无畏的“求知”精神与仁人之心不可,此乃“智仁勇”的相统一。文明之脊梁者,莫不如是。

(2)学什么和怎么学:解决真正的问题

今天的学者与智识分子,在功利的浪潮之中,有许多困于功利、损坏风气,或者面临生活艰辛的滋扰。即便几年来学者的经济条件已有所改善,但要养足胸中“求真”的浩然之气,却仍然不易。学者与智识分子应当努力成为时代的智者

人类的历史是不断由挑战与进步推动的。智者的意义,在于识别出当前时代中的重要问题,并有勇气、智慧与耐心去克服他们。“地心说”与“日心说”是这样的挑战,哥白尼、伽利略、笛卡尔、牛顿等构建了数理实验科学的基础,完成了近代科学革命;儒学的衰弱是这样的挑战,周敦颐、张横渠、程颢、程颐等“为天地立心,为生民立命,为往圣继绝学,为万世开太平”,推动了儒学再兴。

成长中的学者与智识分子,应当保有此种使命感。至于诺贝尔奖,也不过是外在的荣耀;真正的解决问题者,历史自能评判之

以上几者的关联

最后,补充一下以上几者的关系,令读者可知其内在逻辑:

  1. “老年”是“成人”的终点,我们以“向死而生”的视角,思考“求真”的意义。
  2. “青年”是“成人”的起点,所以我们更多是谈论如何在早期打好“求真”的基础,使一生受益。
  3. “职场”“父母”是脱离青年与进入老年之间的生活(也叫“中年”)的两面,是“求真”有现实意义、但也面临危机的时刻。
  4. “学者 / 智者”是一种精英史观的视角,这世界需要有人担当的“智慧”的责任,令文化的基因(习俗、制度、技术、科学等)在时间与空间中继承、变异、演化、发展。

联合国 科学-政策-商业 论坛:from Wikipedia – Science | CC BY-SA 2.0

总结

在本文《成年人需要什么样的STEM教育?》中,我们着重分析了两个问题:

  1. 成年人为什么需要STEM教育:从历史根源、文化差异、现实挑战三个方面,分析了科学教育的必要性。
  2. 成年人需要什么STEM教育:用植树做比方,“科学精神与科学思想”在于“播种”求真的人性理想,“科学读物与科学实践”分析了在显示的“知与行”的层面“根、茎、叶”的构造、“以人为本的科学教育”则讨论了如何“栽培、浇水、施肥”的问题。

写在最后

对于“成年人需要什么样的STEM教育?”的问题,我们暂时告一段落。对这个问题本身的回答也远远没有完成,只是刚刚起步;在这个领域我并非有深入思考的专才,所谈也必多有疏漏。

但我们认为,在这个时代追问“自由-科学”的传统,却命中了这个世界的核心,“一石激起千层浪”。在“五四运动”100年之际,再次回顾“科学”的问题,似乎也有着特殊的意义。以此为出发点,我们可以延伸出更多的问题,值得当代中国文明深刻反思,并指引我们“鞠躬尽瘁”,向着下一个“轴心时代”前行。

同时,必须承认,追寻苏格拉底的“求真”理想,在当今天下,是一条艰难的道路。在灵魂的“理智、意志与欲望”之间,我们会选择走向哪一个方面,我们仍难以笃定地预料。但我愿意相信,“求真”的理想自有其存在的本质的意义,正如陈寅恪先生在《清华大学王观堂先生纪念碑铭》中所说:“来世不可知也,先生之著述,或有时而不彰。先生之学说,或有时而可商。惟此独立之精神,自由之思想,历千万祀,与天壤而同久,共三光而永光。”

最后,试枚举与此文相关的话题。我们在未来可以进一步论述:

  1. “逾越政治的价值秩序”是否可能建立:在今天政治获得了自主性并尝试指挥一切的社会中,是否有可能建立“求真”的价值理想(或与“求真”同等地位的价值秩序),同时使得政治回归到手段而不是目的?
  2. 社会科学的教育问题:我们常把“科学”默认为自然科学,对于近代发展起来的社会科学,未来应怎样融入不同人群的教育?除了STEM,我们还需要什么?
  3. 技术和商业精神的反思:今天的技术与商业文化,已经蓬勃发展起来,但如何避免其有害的一面、补足其不足的方面,是使“求力”的社会不至于崩溃、或者陷入赫胥黎的“美丽新世界”所需要思考的。
  4. 日译汉语的文化与社会影响:“科学”、“自由”、“社会”等词都是晚清的日本学者用汉语翻译的,有些翻译今天看来不见得很好,这些词语的理解和运用对我们思维的潜意识和社会面貌存在哪些影响?
  5. “自由”与“仁爱”的传统如何共处与交融,是我们面对的时代命题,结合当下的文化、经济、技术的发展,在未来的几十年到两百年间,人类是否会迎来一个新的轴心时代?思想混乱、信仰缺失的社会文化局面,如何突围?
  6. 科学基础问题:今天的科学大厦的基础是牢固的吗?归纳法的自身缺陷如何克服?夸大一点说,能否在未来建立一种真正广泛连通自然科学与社会科学的紧密而完善的科学基础?是所谓的数理逻辑+大数据吗??
  7. 除了“科学”,还有哪些时代的核心词汇是现代中国人含混不清的?“自由”、“平等”、“民主”、“法治”、“封建”、“哲学”、“形而上学”?似乎都是,而且还有很多。澄清概念、反思根源,这也是今天哲学的意义所在。
  8. 面对人类文明成果的急剧增长,教育学如何应对这一问题?知识的快速增长发生在所有的科学领域,而互联网时代的信息爆炸与娱乐倾向又进一步压缩了人们的注意力,加剧了这一问题。如何帮助未来的人类在具有好奇心的同时,不被好奇心吞噬,是个巨大的挑战。

参考文献



Posted from my blog with SteemPress : https://robertyan.000webhostapp.com/2019/03/%e3%80%90%e6%9c%aa%e6%9d%a5%e6%95%99%e8%82%b2%e5%ad%a6%e3%80%91%e6%88%90%e5%b9%b4%e4%ba%ba%e9%9c%80%e8%a6%81%e4%bb%80%e4%b9%88%e6%a0%b7%e7%9a%84stem%e6%95%99%e8%82%b2%ef%bc%9f%ef%bc%88%e4%b8%83


This page is synchronized from the post: 【未来教育学】成年人需要什么样的STEM教育?(七)

【未来教育学】成年人需要什么样的STEM教育?(六)

【未来教育学】成年人需要什么样的STEM教育?(六)

【未来教育学】成年人需要什么样的STEM教育?(一)【未来教育学】成年人需要什么样的STEM教育?(二)中我们引出了问题“成年人需要什么样的STEM教育”,并从(1)希腊科学与近代科学的根源、(2)中国文明缺少“求真”的传统、(3)缺少科学精神带来的现实挑战,这三个方面,解释了成年人,尤其是中国成年人,为什么需要STEM教育?的原因。

【未来教育学】成年人需要什么样的STEM教育?(三)【未来教育学】成年人需要什么样的STEM教育?(四)【未来教育学】成年人需要什么样的STEM教育?(五)中,我们具体讨论成年人需要什么STEM教育的问题。在(三)中,我们对其中的核心问题“科学精神与科学思想如何落地”展开了分析;在(四)和(五)中,我们讨论了科学读物与科学实践的关联,并详细介绍了推荐的读物和可采取的实践。

在本文中,我们继续讨论成年人需要什么STEM教育的问题:以人为本的科学教育是如何针对不同人群展开。

成年人需要什么STEM教育?(What)

对于成年人需要什么STEM教育,我们在前面打过一个比方。“科学精神和科学思想”在于“播种”人性理想,科学读物和科学实践的三个层面分别对应科学之树的“根”、“茎”、“叶”,同时需要结合“亲身实践”与“以人为本”的教育,进行“浇水”、“施肥”,从自然与社会中汲取“阳光”、“雨露”,这棵科学之树,才能茁壮成长,造福世人。

接下来,我们继续谈如何“以人为本”地“浇水”、“施肥”。

3. 以人为本的科学教育

这是“成人需要什么STEM教育”的最后一章。

严格来说,前面两章的所写内容有一些拘谨,有些死板、太过干货,说教的味道重;但任何一项学习都需要有系统的基础,由根基打起,促进心灵与认知,并根据个人的兴趣与特质具体化、形象化、个性化(所谓“因材施教”)。有了前面的基础,我们继续讨论“因材施教”,才不会失之偏颇。

“科学”和“教育”:人性的舒展

话说回来,我极不喜欢“教育”这个词,“教”是师者传道授业解惑,“育”是培育保护,这都有违教育的本真。教育的本质在“学”(即自学、主动学习),而不是“教”;这在《论语》里体现得很明确:“学”出现了56次,“教”出现了7次。根据百度百科–教育词条,对词源的介绍:西方“educate”一词是内发之意。强调教育是一种顺其自然的活动,旨在把自然人所固有的或潜在的素质,自内而外引发出来,以成为现实的发展状态。“教育”的目的,应该是提供“土壤”、“阳光”、“雨露”,并引导树木向这自由、强健、伟大的未来,自主发展与探索;“树”也确实都是自己“长”出来的。(相比起来,“学校”一词尚能突出“学”的内涵。)

“科学”的词源,我们在前文“什么是科学”一节中也曾重点解释。如果我们用严复对Science翻译的“格致”(即“格物致知”),就能更好地理解我们这里“科学教育”的内涵,即自主的、内在的对于“真”和”知“的探求

不幸的是,这两个词的现代汉语,中国在晚清都采纳了日本学者翻译西方著作时所用的“科学”、“教育”的译法,竟遗祸至今。(此中国有识之士,当警醒慎思之问题;每思至此,莫不痛心于日本民族之狭隘、“康梁”之不察、严复之不成。)

所以,无论是“科学”还是“教育”,本意都应一种自发的、由内而外的人性舒展,但现在的“科学”和“教育”却都带有权威的意味。大众对于“科学”和“教育”的理解的极大扭曲,通过语言在潜意识层面的影响,加剧了现实中“求真”精神的稀缺。

所以,我们这里谈“科学教育”,尤其是针对成年人的科学教育,关键在于“启发”,希望谈的是如何启发人们以理性之心寻求真理,并走向实践。这帮助我们,回归科学和教育的本来面目

STEM的一种观察:内在的人性理想

在进一步深入之前,我想再谈一下 @honoru 米高和夫人创办的iCare World的启示。米高 @honoru 在《STEM 教學與親子教育 (上篇 - 共兩篇)》中提到这一事业之核心在于: “要大家製作「親子遊戲」及「親子動畫」。再慢慢從中加插「積極正面的價值觀」”、“將「品格教育」結合「STEM」內容,成為我們活動核心 - 培訓「有品格的遊戲開發者 及 動畫師」

我没有亲身参与iCare World的活动,仅从米高的描述来看,他们通过工程活动(游戏制作(如Unity 3D)、动画制作、App制作等)或使用工具的教育(使用IT技术/资讯技术等),同时借助游戏化(Gamification)、社会化(如亲子活动)等方法,作为实际的活动内容,来启发或显现人性内在的美好(亲情、友情、协作、互助、责任、担当等等)。

工程和产品内在自然包含着一种人性的理想(“自由”、“平等”、“互利”、“协作”) 和为了人而设计的理念。借由非功利的工程和学习,将其内在的人性品质自然显现出来,这是iCare World可以成立的内在逻辑。这是一种充满温情和善意的公益教育模式,值得我们深入思考与学习。

从我的观察来看,iCare World主要针对的是STEM中的Engineering和Technology,较少涉及Science和Math。其原因在于:

  1. 一方面,工程和技术背后有着具体的技能、需要动手实践,这种技能本身足够有趣、有用,能满足参与者(如父母和孩子)的学习兴趣,而科学和数学在学校和课堂里已经被讲的足够多了,公益机构参与的意义没有那么显著;
  2. 另一方面,相比“科学”,工程和技术对人与人之间协同、交流、互助的依赖性更高,这与中国的“仁爱”传统有着紧密的联系,有利于“品格”的塑造。

在iCare World的设计中,“非功利”也是十分重要的角色。如果这其中的工程和学习的活动,被添加了“功利”的色彩,例如参加考试、参与被官方认可的竞赛,其效果也必然受到一定影响。现代资本主义背景下的工厂与学校,都在一定程度上具有外在定义人的行为、扼制人的内在动力的倾向,这都某种程度上会阻碍“兴趣”的萌发与生长

这里将 @honoru 米高的文章用来做佐证的原因,在于我在这一章谈到的“科学教育”的方案,事实上也建立在一种相同的逻辑之上:卸掉现代学校制度、商业教育机构、以及功利的社会心态加在“科学教育”之上的重重功利性负担,让它回归自己内在的对人性的期待。

学校教育的误区:批量生产 v.s. 娱乐至死

随着“大众教育”的普及,现代的成年人很多都经历过学校教育。学校教育被认为是教育的核心和主要模式,但这在历史上并非一直如此。“教育的普及”无疑是现代人类重要的成就,各国对教育的极度重视也是历史上不曾有过的,但同时也出现了两个极端的问题。

类型一是工业化教育,这与现代的工业化生产密切相关,将人明确分工,对人才提出明确的技能要求,随着这一压力推向学校,人也像产品一样被高效率地批量生产出来,以满足社会生产力发展的需要;另一方面,家长和孩子为了参与激烈的社会竞争,把满足应试要求、获得学历作为学习的核心目标,进一步压缩了“求知”的内在动力和好奇心。这是一种极端的填鸭式、功利式的教育倾向,“功利化”、“把人当成机器”是这种模式的主要问题。这是一种奥威尔式的《1984》的模式。

类型二是娱乐化教育,与电视、互联网等的普及有密切联系,随着媒体行业、娱乐业、游戏产业的发展,教育也被商品化,教育被要求必须是好玩、有趣、零门槛的。学生像看电视、听故事和玩游戏一般学习,教育者极尽所能地满足学生对趣味性的要求,却从不要求学习者深入思考。这也是波兹曼《娱乐至死》中所揭露的问题,从商业到教育,“消费主义”盛行,以“消费者”的心态对待所有人,包括学生,其结果是扼杀内在的“学习主动性”和“思考能力”学生看似课程轻松愉快,其实深度的思考能力已经在无形中破坏,很多商业的“兴趣班”也存在这类现象。这是赫胥黎式的《美丽新世界》的模式。

残酷的是,中国的教育过去曾经由于效法苏联模式,走了工业化教育的路线;当下,随着资本主义文化的侵蚀,又有娱乐化教育的危险。而无论这两种中的哪一类,都没有真正给学生以自由:前者抹杀了选择的自由、而后者腐蚀了思考的自由;前者的诡异很多人都能觉察到,而后者的伤害却更为隐蔽,“温水煮青蛙”。在这种学校教育环境下生长的青少年,扼杀了兴趣、消灭了志向,在成年之后就更难成为面对世界性挑战的英才。由学校标准化教育机制带来的全球化的教育危机,是当下值得重视的问题。

教育的目的在于“树木”的自主生长,“阳光”、“雨露”、“蓝天”、“白云”、“狂风”、“暴雨”是时代的自然与社会环境,粗暴的“剪枝”与“揠苗助长”固然不可取,谄媚、利诱的富营养化“浇水”、“施肥”同样令人窒息。

并非说学校教育已经没有出路了,但今天“成人”甚至“父母”在“科学精神”、“教育思维”等方面的种种问题,却是在其幼小时就种下了“因”的。要真正回答“成人需要什么STEM教育”的问题,必须同时从学校教育(K-12)去找解决之道;有鉴于“学校教育”问题铺陈开去较为庞大,这一章我们还是先从“成人教育”入手。

成人教育:成人需要什么教育?

成人离开了“学校”这个小“社会”,却进入了社会与自然这个大“学校”。

近期有被叫做“知识付费”的商业模式非常火热,其本质是针对成人的知识产品,是对书籍、博客、电视、音频、视频的延伸和重新包装。但我不认为这是我们需要的“成人教育”,更无须说这种“知识付费”很多是打着“知识”旗号的娱乐化、碎片化的“教育”,把读博客、看说书、听故事当成学习。因为“教育”的本质在于“学”,而不在于“教”;知识付费模式成立的部分原因,就在于很多成年人不懂得如何自主学习、如何在实践中增长智慧。

成人教育的关键不在与“谁来教”、“教什么”,而在于怎么启发对“为什么学”、“学什么”、“怎么学”的思考与行动

我们在前文论证了,成人需要真正了解科学的“本源”和“本质”(“自由-科学”(“自由-知识”)的传统)、了解科学精神和科学思想,掌握科学思维与科学方法、进行科学实践。具体来说,可以分为四个方面:

  1. 培养好奇心、理性思维与求真理想
  2. 体系化地追求(无用的)知识、构建理论体系
  3. 运用于生活与工作实践、建立科学决策系统
  4. 传播科学文化与方法

第1部分,解决“为什么学”的问题,启发“学问思辨行”的启动;第2~4部分,解决“学什么”和“怎么学”的问题。难点首先在于第1部分。

关于“启发”,我们希望启发的是真正的“人性”与“智慧”,而不是通过娱乐化的糖衣或“贿赂”让“不愿者上钩”。现实落地时,却一定程度上需要使用佛家的“方便法”(譬如禅宗对佛学的简化,较少约束,令更多人可以参与)、“因材施教”,让对方更容易接受观念;但即便初衷美好、向现实做了妥协,又需要控制“实施”不走向理想的反面,如“自由、平等、博爱”是如此,如“民主、科学”是如此,如“共产主义”也是如此。

下文将人以群分,针对“老人、职场人士、父母、青年学生、学者 / 智识分子”分别讨论如何“因材施教”、启发内在的“自由-科学”精神。

柏拉图的学院(Academy)| Source: Wikipedia – Education | CC BY-SA 2.0

(未完待续)在全文的最后一部分,我们讨论不同人群的“为什么学”、“学什么”和“怎么学”的问题。


参考文献



Posted from my blog with SteemPress : https://robertyan.000webhostapp.com/2019/03/%e3%80%90%e6%9c%aa%e6%9d%a5%e6%95%99%e8%82%b2%e5%ad%a6%e3%80%91%e6%88%90%e5%b9%b4%e4%ba%ba%e9%9c%80%e8%a6%81%e4%bb%80%e4%b9%88%e6%a0%b7%e7%9a%84stem%e6%95%99%e8%82%b2%ef%bc%9f%ef%bc%88%e5%85%ad


This page is synchronized from the post: 【未来教育学】成年人需要什么样的STEM教育?(六)

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×