跳到主要內容

鑽石級贊助商 - KKBOX 帶你打造具備 NLP 功能的 Telegram Bot (上)

打造具備 NLP 功能的 Telegram Bot(上)

最近因為一些契機學了 Python 3,用它做了一個 Telegram Bot(GitHub 連結),裡面用到 NLP Service,用上下兩篇文章記錄一下實作過程還有眉角。上篇首先教大家如何做一個最基本的回聲 Chatbot,接下來我們可以透過 NLP 服務,讓 Chatbot 根據使用者不同的訊息做回答,這樣就變成更加人性化的聊天機器人囉!

使用的工具及服務:

  1. Python 3(for develop)
  2. pipenv(for dependency management)
  3. OLAMI(for NLP)
  4. ngrok(for testing)

Step 1. Creating new bot

Telegram 很有趣的地方在於,與其他通訊軟體(Line、Messenger)相比,開發者管理 Bot 的方式也是透過官方提供的一位 Bot 在處理的,它叫做 BotFather(眾 Bot 之父 XD)。如果已經有 Telegram 帳號,只要加 BotFather 為好友,就可以開始管理你的 Bot。

加入 BotFather 好友後,它會親切地問候,並告訴你他能為你提供什麼服務。
I can help you create and manage Telegram bots. If you're new to the Bot API, please see the manual ([https://core.telegram.org/bots](https://core.telegram.org/bots)).

You can control me by sending these commands:

/newbot - create a new bot
/mybots - edit your bots [beta]
/mygames - edit your games ([https://core.telegram.org/bots/games](https://core.telegram.org/bots/games)) [beta]

Edit Bots
/setname - change a bot's name
/setdescription - change bot description
/setabouttext - change bot about info
/setuserpic - change bot profile photo
/setcommands - change the list of commands
/deletebot - delete a bot

Bot Settings
/token - generate authorization token
/revoke - revoke bot access token
/setinline - toggle inline mode ([https://core.telegram.org/bots/inline](https://core.telegram.org/bots/inline))
/setinlinegeo - toggle inline location requests ([https://core.telegram.org/bots/inline#location-based-results](https://core.telegram.org/bots/inline#location-based-results))
/setinlinefeedback - change inline feedback ([https://core.telegram.org/bots/inline#collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback)) settings
/setjoingroups - can your bot be added to groups?
/setprivacy - toggle privacy mode ([https://core.telegram.org/bots#privacy-mode](https://core.telegram.org/bots#privacy-mode)) in groups

Games
/newgame - create a new game ([https://core.telegram.org/bots/games](https://core.telegram.org/bots/games))
/listgames - get a list of your games
/editgame - edit a game
/deletegame - delete an existing game
使用 /new bot 指令,接著輸入 Chatbot name 及 username,最後就會得到
Done! Congratulations on your new bot. You will find it at t.me/chatbot_workshop_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.

Use this token to access the HTTP API:
606248605:AAGv_TOJdNNMc_v3toHK_X6M-dev_1tG-JA

For a description of the Bot API, see this page: [https://core.telegram.org/bots/api](https://core.telegram.org/bots/api)
第二行有一段網址 t.me/chatbotworkshopbot,它是之後 Chatbot 發佈後要提供給使用者加好友的連結,比如 [https://t.me/innovationchatbot](https://t.me/innovationchatbot)
606248605:AAGv_TOJdNNMc_v3toHK_X6M-dev_1tG-JA 則是 token,用來 access Telegram Bot API。請勿外洩,否則他人就可以擷取使用者傳給 Bot 的訊息,然後幫你回覆。

Step 2. Understanding chatbot’s architecture

/new bot 結束後,得到自己的 Bot 與 token。就可以開始和 Bot 對話(雖然它還不會回答)。講幾句話以後,透過瀏覽器進入以下連結
https://api.telegram.org/bot{$token}/getUpdates
$token 換成在 Step 1 拿到的 token,例如:
https://api.telegram.org/bot606248605:AAGvTOJdNNMcv3toHKX6M-dev1tG-JA/getUpdates
就會在瀏覽器上看到自己和 Chatbot 的對話紀錄
{
  ok: **true**,
  result: [
    {
      update_id: 140863081,
      message: {
        message_id: 2,
        from: {
          id: 338083968,
          is_bot: **false**,
          first_name: "zaoldyeck",
          username: "zaoldyeck9970",
          language_code: "zh-Hant-TW"
        },
        chat: {
          id: 338083968,
          first_name: "zaoldyeck",
          username: "zaoldyeck9970",
          type: "private"
        },
        date: 1527754411,
        text: "Hello"
      }
    }
  ]
}
https://api.telegram.org/bot{$token}/getUpdates 是 Telegram 提供的 API,讓我們可以用 token 取得使用者傳給 Bot 的訊息,Telegram Bot API 使用方式紀錄在官方文件。

取得使用者訊息的兩種方法-Long Polling vs Webhook

  1. Long Polling 是指程式間隔一定時間透過 getUpdates(上面所使用的方法)取得訊息,缺點是浪費資源、不夠即時,所以適合在程式還沒有 deploy,在 develop 和 test 階段時使用。
  2. Webhook 是指向 Telegram 設定一組 callback url,只要當使用者傳送訊息給 Chatbot,Telegram 就會把用息連同 metada 透過 url 傳給 web server。適合在程式已經 deploy,有固定 url 的 production 環境使用。
完整 Chatbot 運作流程如下,NLP 與 Third party API integration 的環節會在後面兩篇文章實作。

Step 3. Prepare development environment

先建立一個專案目錄
$ mkdir $project_name
進入專案目錄
$ cd $project_name
install 開發 Chatbot 需要的 Module
$ pipenv install --three python-telegram-bot flask gunicorn requests
參數及 module 說明
--three 用 Python 3 建立虛擬環境
python-telegram-bot Telegram Bot API wrapper.
flask Web framework. Using for building webhook API.
gunicorn Python WSGI HTTP server for UNIX. Using for deploying web server.
requests HTTP client library.
安裝完後,專案目錄中會增加兩個檔案
Project Directory
├── Pipfile.lock
└── Pipfile
Take a look at Pipfile
` [[source]] url = "https://pypi.python.org/simple" verifyssl = true name = "projectname"
[packages] python-telegram-bot = "" flask = "" gunicorn = "" requests = ""
[dev-packages]
[requires] python_version = "3.6" `
接著,在專案目錄中新增一個 config file,把 Chatbot 所用到 Service 的 key 及 secret 都寫在 config file 中。這麼做的優點如下:
  1. service key 及 secret 不應該寫在程式碼裡,避免因為有做版本控制,在 git commit & push 後將機密資訊暴露在 public repository
  2. 集中管理,日後若需要更改 key,直接編輯 config file 即可,不用更動程式碼,也能確保程式中有用到 key 及 secret 的模組都會同步更新
  3. 部署專案時,比起在 server 設定環境變數,loading config file 的方式會更加方便容易
Python 有預設的 Configuration file parser module
在專案目錄中新增 config.ini 檔案
$ touch config.ini
編輯成以下格式
ini [TELEGRAM] ACCESS_TOKEN = WEBHOOK_URL =
使用時,把各 service 申請到的 token / key / secret 放在 = 後面即可,例如:
[TELEGRAM]
ACCESS_TOKEN = 606248605:AAGv_TOJdNNMc_v3toHK_X6M-dev_1tG-JA
至此開發環境準備完成,專案目錄中共有三個檔案
Project Directory
├── config.ini
├── Pipfile.lock
└── Pipfile

Step 4. Develop first echo chatbot

這一步會先完成一個鸚鵡回話 Chatbot,講解 python-telegram-bot 提供哪些模組幫助我們做 Telegram Bot 開發。
在專案目錄中,新增 main.py 檔案
$ touch main.py
程式碼如下
`python import configparser import logging
import telegram from flask import Flask, request from telegram.ext import Dispatcher, MessageHandler, Filters

Load data from config.ini file

config = configparser.ConfigParser() config.read('config.ini')

Enable logging

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) logger = logging.getLogger(name)

Initial Flask app

app = Flask(name)

Initial bot by Telegram access token

bot = telegram.Bot(token=(config['TELEGRAM']['ACCESS_TOKEN']))
@app.route('/hook', methods=['POST']) def webhookhandler(): """Set route /hook with POST method will trigger this method.""" if request.method == "POST": update = telegram.Update.dejson(request.get_json(force=True), bot)
    # Update dispatcher process that handler to process this message
    dispatcher.process_update(update)
return 'ok'
def replyhandler(bot, update): """Reply message.""" text = update.message.text update.message.replytext(text)

New a dispatcher for bot

dispatcher = Dispatcher(bot, None)

Add handler for handling message, there are many kinds of message. For this handler, it particular handle text

message.

dispatcher.addhandler(MessageHandler(Filters.text, replyhandler))
if name == "main": # Running server app.run(debug=True) `
完成後,就可以用 pipenv running。在專案目錄中執行以下命令
$ pipenv run python3 main.py
Flask server 就會開始運行
* Serving Flask app "main" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
2018-06-15 16:38:07,843 - werkzeug - INFO -  * Running on [http://127.0.0.1:5000/](http://127.0.0.1:5000/) (Press CTRL+C to quit)
2018-06-15 16:38:07,845 - werkzeug - INFO -  * Restarting with stat
2018-06-15 16:38:08,279 - werkzeug - WARNING -  * Debugger is active!
2018-06-15 16:38:08,324 - werkzeug - INFO -  * Debugger PIN: 244-087-163
但是程式是在本機運行,要怎麼讓使用者傳送給 Telegram Bot 的訊息可以透過 webhook 傳送給本機 web server 呢?
此時就可以用 ngrok 服務,它會幫我們在 cloud 建立 public domain 對 local 端的 poxy。執行以下命令
$ ngrok http 5000
5000 剛好對應 Flask App listening 的 port
Session Status                online
Session Expires               7 hours, 58 minutes
Version                       2.2.8
Region                        United States (us)
Web Interface                 [http://127.0.0.1:4040](http://127.0.0.1:4040)
Forwarding                    [http://76f2a29d.ngrok.io](http://76f2a29d.ngrok.io) -> localhost:5000
Forwarding                    [https://76f2a29d.ngrok.io](https://76f2a29d.ngrok.io) -> localhost:5000

Connections                   ttl     opn     rt1     rt5     p50     p90
                              2       0       0.03    0.01    1.21    1.89
接下來就可以把 https://76f2a29d.ngrok.io/hook 設定為 Telegram Bot 的 webhook。
請注意,ngrok 只有在 development 使用,production 環境還是會將程式部署到正式的 service
設定 Telegram Bot webhook 的方法可參考文件,透過瀏覽器進入以下連結
https://api.telegram.org/bot{$token}/setWebhook?url={$webhook_url}
$token 和 $webhook_url 請換成在 Step 1 申請到的,例如:
https://api.telegram.org/bot606248605:AAGv_TOJdNNMc_v3toHK_X6M-dev_1tG-JA/setWebhook?url=https://bcf4cd97.ngrok.io/hook
看到瀏覽器出現以下訊息,就表示 webhook 設定成功了
{
  ok: true,
  result: true,
  description: "Webhook was set"
}
開始和 Chatbot 對話,它就會向鸚鵡一樣回覆你傳給它的訊息。

到這邊為止的完整程式碼

Step 5. Add NLP feature for chatbot

嘗試過 Microsoft LUIS 及 Google Dialog Flow,它們都沒有預設的中文語音對話模組,都要自己提供語料自己 train。最後選擇用 OLAMI Open API,它是威盛電子旗下的智能語音互動平台,裡面的 IDS 模組只要 Chatbot 串接完就可以得到許多技能,包含聊天、問天氣、股市、音樂、百科、食譜、算數學。雖然 NLP 的作法是 Rule-based,但現階段是很成熟的 Solution。接下來教大家怎麼把 OLAMI Open API 整合進 Telegram Bot 中。
先註冊 OLAMI 帳號,在 OLAMI 我的應用介面建立新應用,得到 App Key 及 App Secret,把它們填入專案目錄中的 config.ini 檔案
[OLAMI]
APP_KEY = your_app_key
APP_SECRET = your_app_secret
對應用點選變更設定 => 對話系統模組,勾選所有模組 => 儲存設定

回到專案目錄,新增資料夾,名字叫做 nlp
$ mkdir nlp
進入 nlp 資料夾,新增兩個檔案, __init__.py 及 olami.py
$ cd nlp
$ touch __init__.py
$ touch olami.py
做完上述動作後的專案目錄結構
Project Directory
├── nlp
|   ├── __init__.py
|   └── olami.py
├── config.ini
├── main.py
├── Pipfile
└── Pipfile.lock
新增 __init__.py 是為了讓 main.py import nlp 的時候認定 nlp 是一個 Module。
編輯 nlp/__init__.py
python from . import olami
編輯 olami.py
`python import configparser import json import logging import time from hashlib import md5
import requests

Load data from config.ini file

config = configparser.ConfigParser() config.read('config.ini')
logger = logging.getLogger(name)
class NliStatusError(Exception): """The NLI result status is not 'ok'"""
class Olami: URL = 'https://tw.olami.ai/cloudservice/api'
def __init__(self, app_key=config['OLAMI']['APP_KEY'], app_secret=config['OLAMI']['APP_SECRET'], input_type=1):
    self.app_key = app_key
    self.app_secret = app_secret
    self.input_type = input_type

def nli(self, text, cusid=None):
    response = requests.post(self.URL, params=self._gen_parameters('nli', text, cusid))
    response.raise_for_status()
    response_json = response.json()
    if response_json['status'] != 'ok':
        raise NliStatusError(
            "NLI responded status != 'ok': {}".format(response_json['status']))
    else:
        return response_json['data']['nli'][0]['desc_obj']['result']

def _gen_parameters(self, api, text, cusid):
    timestamp_ms = (int(time.time() * 1000))
    params = {'appkey': self.app_key,
              'api': api,
              'timestamp': timestamp_ms,
              'sign': self._gen_sign(api, timestamp_ms),
              'rq': self._gen_rq(text)}
    if cusid is not None:
        params.update(cusid=cusid)
    return params

def _gen_sign(self, api, timestamp_ms):
    data = self.app_secret + 'api=' + api + 'appkey=' + self.app_key + \
           'timestamp=' + str(timestamp_ms) + self.app_secret
    return md5(data.encode('ascii')).hexdigest()

def _gen_rq(self, text):
    obj = {'data_type': 'stt', 'data': {'input_type': self.input_type, 'text': text}}
    return json.dumps(obj)
`
olami.py 主要在實作 request OLAMI NLI API 的 method。request OLAMI NLI API 時需要利用 App Key 及 App Secret 生成一組 sign,放入 url parameter 才能通過 OLAMI API Server 的驗證。
sign 生成方法 Document
OLAMI NLI API Document
完成後,回到專案目錄,編輯 main.py,import nlp module,並將原來 reply_handler method 中直接回傳使用者訊息的邏輯,改為先將使用者訊息傳給 OLAMI NLI API,再把結果回傳給使用者。
`python +from nlp.olami import Olami
def replyhandler(bot, update): """Reply message.""" text = update.message.text - text = update.message.text - update.message.replytext(text) + reply = Olami().nli(text) + update.message.reply_text(reply) `
修改後的完整 main.py
`python import configparser import logging
import telegram from flask import Flask, request from telegram.ext import Dispatcher, MessageHandler, Filters
from nlp.olami import Olami

Load data from config.ini file

config = configparser.ConfigParser() config.read('config.ini')

Enable logging

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO) logger = logging.getLogger(name)

Initial Flask app

app = Flask(name)

Initial bot by Telegram access token

bot = telegram.Bot(token=(config['TELEGRAM']['ACCESS_TOKEN']))
@app.route('/hook', methods=['POST']) def webhookhandler(): """Set route /hook with POST method will trigger this method.""" if request.method == "POST": update = telegram.Update.dejson(request.get_json(force=True), bot)
    # Update dispatcher process that handler to process this message
    dispatcher.process_update(update)
return 'ok'
def replyhandler(bot, update): """Reply message.""" text = update.message.text reply = Olami().nli(text) update.message.replytext(reply)

New a dispatcher for bot

dispatcher = Dispatcher(bot, None)

Add handler for handling message, there are many kinds of message. For this handler, it particular handle text

message.

dispatcher.addhandler(MessageHandler(Filters.text, replyhandler))
if name == "main": # Running server app.run(debug=True) `
對 Telegram Bot 進行測試,它已經可以回應詢問天氣、算數、股價等問題。

但有些問題的回答有點奇怪,似乎只回覆片段訊息,為什麼會這樣呢?

原來 OLAMI 不同的 IDS 對話模組回傳的 Data Structure 不一樣(參考說明文件)我們需要根據不同 Intent 的訊息處理回傳內容。

Step 6. Intent detection

Intent detection 是 Chatbot 的重要核心,從使用者的訊息判斷他的意圖與期望獲得的資訊。若意圖不夠明確,Chatbot 可以藉由更進一步的問題抽絲剝繭出使用者明確的需求。例如:
使用者:「今天天氣如何?」
Chatbot:「請問您詢問的是哪裡的天氣?」
使用者:「台北」
Chatbot:「台北今天陰,多雲,最高溫度 30.2℃,最低溫度 26.9℃,南西風輕風。」
使用者:「告訴我今日新聞」
Chatbot:「以下幾則新聞,請問您想知道哪一則的詳細內容?
  1. 27 歲被酸「一把年紀還沒人愛」!Kiwebaby 聽到卻笑了
  2. 鍾文榮/拼經濟靠女性 口紅是最後買的起的奢侈品?
  3. 李沃牆/美中貿易戰劍拔弩張 新興市場股匯失血崩跌
  4. 損失 350 億!南韓最大虛擬貨幣平台 Bithumb 遭駭 全面中斷交易
  5. 侯友宜文大宿舍爭議 段宜康:該由政風啟動調查的是新北市府」
使用者:「4」
Chatbot:「南韓虛擬貨幣交易所 Bithumb 於 20 日爆出駭客入侵消息,市值350 億韓元(約新台幣 95.3 億元)的虛擬貨幣遭竊。這是南韓 2018 年以來發生的第 2 起虛擬貨幣平台駭客事件,韓國網路振興院(KISA)稍早已經獲報並著手調查。」
幸好這些 OLAMI 的 IDS 對話模組都有 handle!程式只要處理不同模組回傳的 Data Structure 即可。
編輯 nlp/olami.py,新增 intent_detection method,原本程式是將 OLAMI NLI API response 的結果取 result 的值回傳(第 10 行);改成先判斷 Intent 的 type(第 28 及 32~40 行),再根據 type 取不同的值做回傳。
python class Olami: def nli(self, text, cusid=None): response = requests.post(self.URL, params=self._gen_parameters('nli', text, cusid)) response.raise_for_status() response_json = response.json() if response_json['status'] != 'ok': raise NliStatusError( "NLI responded status != 'ok': {}".format(response_json['status'])) else: - return response_json['data']['nli'][0]['desc_obj']['result'] + nli_obj = response_json['data']['nli'][0] + return self.intent_detection(nli_obj) + + def intent_detection(self, nli_obj): + def handle_selection_type(type): + reply = { + 'news': lambda: desc['result'] + '\n\n' + '\n'.join( + str(index + 1) + '. ' + el['title'] for index, el in enumerate(data)), + 'poem': lambda: desc['result'] + '\n\n' + '\n'.join( + str(index + 1) + '. ' + el['poem_name'] + ',作者:' + el['author'] for index, el in + enumerate(data)), + 'cooking': lambda: desc['result'] + '\n\n' + '\n'.join( + str(index + 1) + '. ' + el['name'] for index, el in + enumerate(data)) + }.get(type, lambda: '對不起,你說的我還不懂,能換個說法嗎?')() + return reply + + type = nli_obj['type'] + desc = nli_obj['desc_obj'] + data = nli_obj.get('data_obj', []) + + reply = { + 'kkbox': lambda: data[0]['url'] if len(data) > 0 else desc['result'], + 'baike': lambda: data[0]['description'], + 'news': lambda: data[0]['detail'], + 'joke': lambda: data[0]['content'], + 'cooking': lambda: data[0]['content'], + 'selection': lambda: handle_selection_type(desc['type']), + 'ds': lambda: desc['result'] + '\n請用 /help 指令看看我能怎麼幫助您' + }.get(type, lambda: desc['result'])() + + return reply
修改後的完整 olami.py
`python import configparser import json import logging import time from hashlib import md5
import requests
config = configparser.ConfigParser() config.read('config.ini')
logger = logging.getLogger(name)
class NliStatusError(Exception): """The NLI result status is not 'ok'"""
class Olami: URL = 'https://tw.olami.ai/cloudservice/api'
def __init__(self, app_key=config['OLAMI']['APP_KEY'], app_secret=config['OLAMI']['APP_SECRET'], input_type=1):
    self.app_key = app_key
    self.app_secret = app_secret
    self.input_type = input_type

def nli(self, text, cusid=None):
    response = requests.post(self.URL, params=self._gen_parameters('nli', text, cusid))
    response.raise_for_status()
    response_json = response.json()
    if response_json['status'] != 'ok':
        raise NliStatusError(
            "NLI responded status != 'ok': {}".format(response_json['status']))
    else:
        nli_obj = response_json['data']['nli'][0]
        return self.intent_detection(nli_obj)

def _gen_parameters(self, api, text, cusid):
    timestamp_ms = (int(time.time() * 1000))
    params = {'appkey': self.app_key,
              'api': api,
              'timestamp': timestamp_ms,
              'sign': self._gen_sign(api, timestamp_ms),
              'rq': self._gen_rq(text)}
    if cusid is not None:
        params.update(cusid=cusid)
    return params

def _gen_sign(self, api, timestamp_ms):
    data = self.app_secret + 'api=' + api + 'appkey=' + self.app_key + \
           'timestamp=' + str(timestamp_ms) + self.app_secret
    return md5(data.encode('ascii')).hexdigest()

def _gen_rq(self, text):
    obj = {'data_type': 'stt', 'data': {'input_type': self.input_type, 'text': text}}
    return json.dumps(obj)

def intent_detection(self, nli_obj):
    def handle_selection_type(type):
        reply = {
            'news': lambda: desc['result'] + '\n\n' + '\n'.join(
                str(index + 1) + '. ' + el['title'] for index, el in enumerate(data)),
            'poem': lambda: desc['result'] + '\n\n' + '\n'.join(
                str(index + 1) + '. ' + el['poem_name'] + ',作者:' + el['author'] for index, el in
                enumerate(data)),
            'cooking': lambda: desc['result'] + '\n\n' + '\n'.join(
                str(index + 1) + '. ' + el['name'] for index, el in
                enumerate(data))
        }.get(type, lambda: '對不起,你說的我還不懂,能換個說法嗎?')()
        return reply

    type = nli_obj['type']
    desc = nli_obj['desc_obj']
    data = nli_obj.get('data_obj', [])

    reply = {
        'kkbox': lambda: data[0]['url'] if len(data) > 0 else desc['result'],
        'baike': lambda: data[0]['description'],
        'news': lambda: data[0]['detail'],
        'joke': lambda: data[0]['content'],
        'cooking': lambda: data[0]['content'],
        'selection': lambda: handle_selection_type(desc['type']),
        'ds': lambda: desc['result'] + '\n請用 /help 指令看看我能怎麼幫助您'
    }.get(type, lambda: desc['result'])()

    return reply
`
再一次測試 Telegram Bot

現在,Telegram Bot 可以查人物、說笑話、甚至連多段式對話(選擇有興趣的新聞)都可以做出回應了。
範例是將所有 OLAMI IDS 對話模組全部實作,你也可以只實作需要的模組。
但如果想要的對話功能 OLAMI IDS 對話模組沒有怎麼辦?例如我希望 Chatbot 幫我找動漫歌曲

下一篇文章再跟大家介紹如何為 Chatbot 添加新技能。
範例完整程式碼
如果想要把現階段的 Chatbot 部署上 production 環境,步驟可以參考下一篇文章的 Step. 9 Deployment

留言

這個網誌中的熱門文章

會眾新服務「療癒市集」結合紅酒瑜伽、冥想正念、按摩小站、氮氣咖啡 | Introducing the Healing Market with Yoga Wine, Meditations, Massage Station, Nitro Coffee

新 [English version below] 今年的 COSCUP x KCD 2022 Taiwan 嘗試推出新的會眾服務,希望在繁忙的平日還抽空在假日來參與活動時、能夠療癒一下心靈與身體的負擔,「 療癒市集 」希望能夠為你帶來不一樣的體驗! 由於部分課程需要 預先報名 ,如果你有意參與課程,請直接 寄信報名 ,並等候志工收件處理,感謝! 以下是相關的課程簡介。 紅酒瑜伽 照片來源:台南安平雅樂軒酒店 都市生活步調快,上班壓力大,周末總想找些紓壓的活動幫自己充飽電,用更好的狀態去迎接下個挑戰。而說到現在最新穎,時尚的選擇那就不能不提風靡歐美的「紅酒瑜珈」。現在不用出國,在 COSCUP 也可以體驗這種身心靈保養的運動。 課程中,老師也會指引學員在不同階段品嘗手中的葡萄酒,感受這支紅酒在不同醒酒階段的各種風味,細細品嚐它的層次與韻味。酒精也同時能夠增加血液循環,讓身體發熱,達到肌肉暖身,類似熱瑜珈的運動效果! 紅酒瑜珈是什麼? 於 2017 年誕生於紐約,紅酒瑜珈是把紅酒帶進瑜珈練習的一種課程。在瑜珈練習的過程中,學員們手上各有一杯紅酒。老師帶領著學員練習瑜珈姿勢,並加入酒杯動作來增加難度與運動量。比如說,手握紅酒杯進行戰士三式(Warrior III),為了不讓液體撒出來,其實比起沒有道具輔助的瑜珈需要多一點肌耐力,所以可以達到更大的脂肪燃燒跟運動效果。 課程須知 每一課程時長為一個小時,費用 $470元/人,每一堂最多 12 人。 講座內容包含:活動包含約 45 分鐘的紅酒瑜珈活動,及約 15 分鐘的講解,課程會提供酒杯,若損壞葡萄酒杯,則每只費用 $250。 需自備:瑜伽墊、水壺、毛巾等個人用品。 ★ 由於課程需要預先報名,如果你有意參與此課程,請參閱 課程時間表 並直接 寄信報名 、等候志工收件處理,感謝! 冥想正念 我們都渴望獲得內心的平靜,尤其在現在這個快速、忙碌更迭的時代,在這個無時無刻都在面對比較、落後焦慮的世代。透過冥想與正念,你會更加理解你自己,你也會更加理解你的周遭一切,而點滴的時光之間,再次放下自我,又擁抱自我。 課程須知 每一課程時長為一個小時,費用 $350元/人,每一堂最多 10 人(未滿 5 人不開課)。 講座內容包含:冥想正念概念介紹、正念心理...

Cortex主鏈上線、Genesis Block生成! 開啟AI智能合約新時代

毫無疑問,繼比特幣之後,以太坊的智能合約——一個分布式、公開透明、不可篡改、可審查的計算程序,是區塊鏈技術應用的一個重大突破。區塊鏈技術天生可以為智能合約提供可信的執行環境。然而,當前以太坊的智能合約,受限於基礎設計,目前還無法真正執行更覆雜的AI計算程序。 基於很多因素,例如虛擬機的設計、AI模型在異構環境下推斷結果的不確定性、以及鏈上AI推斷比較慢的確認速度等等,傳統認知一般認為在區塊鏈上運行AI計算程序是不現實的。想要實現Cortex白皮書設計的既定目標,需要解決上述一系列工程挑戰,這是一個全新且艱巨的探索方向,需要對AI和區塊鏈兩個領域均有極深的了解。 經過一年多的努力研發和探索,Cortex於 2019年6月26日18:00(UTC+8)正式發布Cortex主鏈:這是人類歷史上第一條真正可以進行鏈上AI推斷的公鏈。 Cortex成功實現在區塊鏈上真正的運行AI計算程序,通過全網共識來驗證AI推斷的每一步。鏈上所有全節點,保證了推斷結果的去中心化、確定性和公平透明的特性。這對於我們即將進入的去中心化世界有著巨大影響,因為有了AI計算程序的加持,現在的智能合約已經變得更加智能,智能合約(Smart Contract)已經正式升級為AI智能合約(AI Smart Contract)。 在本文中,我們會從幾個方面如核心 技術創新、主鏈特性、AI智能合約應用場景、去中心化人工智能自治系統的願景、下一階段的發展計劃 等全面闡述和介紹Cortex AI公鏈。 一. 核心技術創新 1. CVM(CortexVirtual Machine,Cortex虛擬機) 像以太坊這樣的傳統區塊鏈無法有效整合AI的最大原因之一是因為它們的虛擬機是在CPU上運行的,不適合執行覆雜的AI程序。而我們的CVM能利用GPU,非常適合有效地執行AI程序。 Cortex在以太坊虛擬機(EVM)的基礎上添加了對 Cortex 鏈上 AI 推斷功能的支持。該功能主要分為兩個模塊,一方面是在 CVM 中支持了 Inference 指令,包含Infer(code: 0xc0),InferArray(code: 0xc1),前端如remix編譯合約時同步增加一條infer函數來封裝 CVM 接口,實現鏈上 AI 推斷功能;另一方面,Synapse 作為定點化 AI 推斷引擎,又名 CVM Executor...

COSCUP Lightning Talks - 2026 ⚡️

COSCUP 2026 閃電秀 / Lightning talks Photo by COSCUP 2025 紀錄組 閃電秀是一個由多場超短時的議程發表構成的一個表演性質居多的活動,通常會被放在獨佔時段,所有會眾都會聚集到這個會議廳觀賞這齣表演,稱之為閃電秀 (Lightning Talks)。 今年的閃電秀將於 2026 Aug 09 週日的下午 16:15 - 17:00 在 RB105 議程軌開講。 本次閃電秀的參加規則如下: 每個講題 3 分鐘,時間一到就會立刻切掉您的畫面,並邀請觀眾拍手掌聲鼓勵。 歡迎將您想曝光的 Projects、Idea 或小議題在這裡跟大家分享! 應遵守 COSCUP 的 CoC 規則 的原則之下進行發表演說,主持人有權基於本規則的判斷將不適合的發表暫停,並向大家說明理由。 需要再時限內完成報名 敬請自備筆電(和 HDMI 轉接器)上台 需要提早一場議程 (在結束前) 到 Main Hall (RB105) 報到 沒有限制發表語言,但建議可以使用英文或中文,大部分的現場觀眾能夠識別這兩種語言 *所有時區皆為 UTC+8 Lightning Talks is an event featuring multiple short speeches or presentations, typically held within an exclusive time slot. All attendees gather in the main hall to watch the show. This year, the Lightning talks is on Aug 09th from 16:15 - 17:00 (UCT+8) at Room RB105! Each talk is limited to 3 minutes. Once the time is up, your screen will be cut off immediately, and the audience will be invited to applaud and show encouragement. You're welcome to share any projects, ideas, or small topics you...