Open Interpreterã誰ã§ã䜿ããSlack botã«ããŸãã
TL;DR
- ä»è©±é¡ã®Open InterpreterãSlack botã«ããŸããã
- Codeã¯ãã¡ãã®ãªããžããªãã芧ãã ããã
ã¯ããã«
Open Interpreter楜ããã§ãŸããïŒ
ãã©ãããã€ãã€ãã€ãçèªãããããããšããèªèã§ééããªãããã§ãã
ã§ãããã®ã€ãããããã£ãŠãã®ã¯ãŸã äžéšã®äººã ãããã£ãããªãïŒ
ãšããããšã§ä»åã¯ãã®Open Interpreterã誰ã§ã䜿ããããã«Slack botã«ããæ¹æ³ã玹ä»ããŸãã
ãã¡ã€ã«ã®ã¢ããããŒããããŠã³ããŒããå¯èœã§ãã®ã§ãæäœéå¿ èŠãªæ©èœãå šãŠåãããã®ã宿ãããšæããŸãã





ãã®èšäºãèªãã°ãããããš
- Cloud Runã䜿ã£ãŠãµãŒããŒã¬ã¹ãªOpen Interpreterå®è¡ç°å¢ãæ§ç¯ããæ¹æ³
- Cloud Storageã䜿ã£ãŠã¹ããŒãã¬ã¹ãªCloud Runã§ãäŒè©±ãšãã¡ã€ã«ãç¶æããæ¹æ³
- Slack Botãäœæããé©åãªæš©éãèšå®ããæ¹æ³
- Slack ãããã¡ã€ã«ãã¢ããããŒãããŠOpen Interpreterã«æž¡ãæ¹æ³
- Open Interpreterã®äœæã»ç·šéãããã¡ã€ã«ãSlackçµç±ã§ããŠã³ããŒãããæ¹æ³
ãã®èšäºãèªãã§ãããããªãããš
- Open Interpreterã®æŽ»çšäºäŸ
- ä»åã®ã³ãŒãã®è©³çް(ãªããžããªãã¿ãŠã)
Bot ã®ä»æ§
ä»åäœæããBotã®ä»æ§ã¯ä»¥äžã®ããã«ãªã£ãŠããŸãã
- Botã¯ã¡ã³ã·ã§ã³ããããšã§èµ·åããã¹ã¬ããå ã«åºåçµæãéä¿¡ãã
- ã¹ã¬ããå ã§ããã«ã¡ã³ã·ã§ã³ããããšã§äŒè©±ãç¶ç¶ã§ãã
- ã¹ã¬ããããšã«äŒè©±å±¥æŽãšOpen Interpreterã®æäœãããã¡ã€ã«ãåé¢ããŠãã
- æ°èŠã¹ã¬ãããç«ãŠãã°ã¯ãªãŒã³ãªç¶æ ããåŠçãéå§ã§ãã
- è€æ°ã¹ã¬ããããå šãåãååã®ãã¡ã€ã«ãäœæãããŠããããã£ã³ã°ããªã
- ãã¡ã€ã«ãã¢ããããŒããããããèªã¿èŸŒãŸãããã
- ã¢ããããŒãããCSVãåæããããªã©ãå¯èœ
- Open Interpreterã®äœæãããã¡ã€ã«ãããŠã³ããŒãã§ãã
- äœæããã°ã©ããªã©ããå šãŠSlackå ã§é²èЧã§ãã
- å®çªã®ã©ã€ãã©ãªãäºåã«ã€ã³ã¹ããŒã«ããŠãã
- äœåãªã€ã³ã¹ããŒã«æéãçºçããã®ãé²ã
工倫ãã€ã³ã
ãã¡ã€ã«ãäŒè©±å±¥æŽãä¿æããã
åãã¹ã¬ããããã¯åããã¡ã€ã«ã«ã¢ã¯ã»ã¹ã§ããããã«ãããããåŠçã®åã«GCSããèªã¿ãšãããåŠçãçµãã£ããGCSã«ä¿åããè¡ããŸãã
å°ãã§ãããããããããã«GCSã®ãã±ããåãopen-interpreter-<UserId>ã®ããã«ããã¹ã¬ããããšã«GCSã®ãã¹(prefix)ãåããŸãã
def get_bucket_name(user_id: str) -> str:
return f"open-interpreter-{user_id}".lower()
Open Interpreterã®åŠçãå§ãŸãåã«GCSããCloud Runã«ãã¡ã€ã«ãèªã¿åãã
def download_files_from_bucket(bucket_name: str, destination_dir_path: str, blob_prefix: str) -> List[str]:
"""
Download files from GCS bucket to cloud run
:param bucket_name: bucket name to download
:param destination_dir_path: directory path to save files
:param blob_prefix: prefix of blob to download
"""
# Initialize the Cloud Storage client
storage_client = storage.Client()
if not os.path.exists(destination_dir_path):
os.makedirs(destination_dir_path)
# Check if the bucket exists
if not storage_client.lookup_bucket(bucket_name):
storage_client.create_bucket(bucket_name)
return []
# Get the bucket
bucket = storage_client.get_bucket(bucket_name)
file_paths = []
# Loop through the blobs (files) and download them
for blob in bucket.list_blobs(prefix=blob_prefix):
file_name = os.path.basename(blob.name)
destination_file_path = os.path.join(destination_dir_path, file_name)
blob.download_to_filename(destination_file_path)
file_paths.append(destination_file_path)
return file_paths
Open Interpreterã®åŠçãçµãã£ãåŸã«Cloud RunããGCSã«ãã¡ã€ã«ãä¿åããã
def upload_files_to_bucket(local_directory_path: str, bucket_name: str, blob_prefix: str):
"""
Upload files from cloud run to GCS bucket
:param local_directory_path: directory path to upload files in cloud run
:param bucket_name: bucket name to upload
:param blob_prefix: prefix of blob to upload
"""
# Initialize the Cloud Storage client
storage_client = storage.Client()
# Get the bucket
bucket = storage_client.get_bucket(bucket_name)
# Check if the directory exists
if not os.path.exists(local_directory_path):
return
ignore_patterns = get_ignore_patterns(".gitignore")
# Loop through each file in the temporary directory
for root, _, files in os.walk(local_directory_path):
for filename in files:
if any(fnmatch.fnmatch(filename, pattern) for pattern in ignore_patterns):
continue
source_file_path = os.path.join(root, filename)
relative_path = os.path.relpath(source_file_path, local_directory_path)
blob_name = os.path.join(blob_prefix, relative_path)
# Create a blob
blob = bucket.blob(blob_name)
# Upload the file
blob.upload_from_filename(source_file_path)
äœã§ãããã§ãä¿åããå¿
èŠã¯ãªãã®ã§.gitignoreã«èšèŒãããŠãããã¡ã€ã«ã¯ã¢ããããŒãããªãããã«ããŠããŸãã
ãã ããä»åã¯Cloud Runäžã«tmpãã©ã«ããäœã£ãŠäœæ¥ããã®ã§ãã(åŸè¿°)ãtmpãã©ã«ãå
ã«ã¯ç¡é§ãªãã®ã¯äœãããªããããããã®åŠçã¯äžèŠã£ãœãã§ãã
def get_ignore_patterns(ignore_file_path):
if os.path.exists(ignore_file_path):
with open(ignore_file_path, "r") as f:
return f.read().splitlines()
else:
return []
Open Interpreterã§ã¯ãããŸã§ã®äŒè©±å±¥æŽã®ååŸãèªã¿èŸŒã¿æ©èœãæèŒãããŠããŸãã
ããã§ãã®messagesãmessages.jsonãšããŠä¿åãããããGCSã«ã¢ããããããšã§äŒè©±å±¥æŽãä¿åããŸãã
messages = interpreter.chat("My name is Killian.", return_messages=True) # Save messages to 'messages'
interpreter.reset() # Reset interpreter ("Killian" will be forgotten)
interpreter.load(messages) # Resume chat from 'messages' ("Killian" will be remembered)
...
def read_messages_json(self) -> list:
"""
Read messages history from temp directory
:return: list of messages
"""
messages_file_path = os.path.join(self.temp_dir_path, "messages.json")
if os.path.exists(messages_file_path):
with open(messages_file_path, "r") as f:
messages = json.load(f)
else:
messages = []
logger.info({"message": "Read messages json.", "messages": messages})
return messages
def save_messages_json(self, messages: List[dict]):
"""
Save messages history to temp directory
:param messages: list of messages
"""
messages_json = json.dumps(messages, indent=4, ensure_ascii=False)
with open(os.path.join(self.temp_dir_path, "messages.json"), "w", encoding="utf-8") as f:
f.write(messages_json)
logger.info({"message": "Save messages json.", "messages": messages})
...
ãŠãŒã¶ãŒããã®ãã¡ã€ã«ã¢ããããŒããåãä»ããã
ãŠãŒã¶ãŒãSlackäžã§ãã¡ã€ã«ãã¢ããããŒããããševent["files"]ã«ãã®æ
å ±ãå
¥ãã®ã§ãfileã®idãå
ã«Slack APIã䜿ã£ãŠCloud Runã«ããŒãããŸãã
ãã®äžã§ããŠãŒã¶ãŒã¡ãã»ãŒãžã«ã<ãã¡ã€ã«å>ã<ã¢ããããŒãå
ã®ãã¹>ã«ã¢ããããŸãããããšè¿œå ããŠOpen Interpreterã«èªèãããŠãããŸãã
ãªãããã®åŠçãBotãžã®ã¡ã³ã·ã§ã³ãããªã¬ãŒãšããŠãããããã¡ã€ã«ãã¢ããããŒãããéãã¡ã³ã·ã§ã³ãå¿ é ã«ãªããŸãã
@slack_app.event("app_mention")
def mentioned(body, say: Say):
slack_token = os.environ["SLACK_BOT_TOKEN"]
client = WebClient(token=slack_token)
event = body["event"]
...
files = event.get("files", [])
file_paths = []
for file in files:
file_path = slack_api.load_file(client, file["id"], temp_dir)
file_paths.append(file_path)
message_by_user = f"I uploaded {file['name']} to {temp_dir}\n{message_by_user}"
...
def load_file(client: WebClient, file_id: str, save_dir_path: str) -> str:
try:
# Call the files.info method using the WebClient
result = client.files_info(file=file_id)
file_info = result["file"]
file_url = file_info["url_private"]
file_name = file_info["name"]
# Download the file
opener = urllib.request.build_opener()
opener.addheaders = [("Authorization", "Bearer " + os.environ.get("SLACK_BOT_TOKEN", ""))]
urllib.request.install_opener(opener)
if not os.path.exists(save_dir_path):
os.makedirs(save_dir_path)
file_path = os.path.join(save_dir_path, file_name)
urllib.request.urlretrieve(file_url, file_path)
return file_path
except SlackApiError as e:
raise e
次ã«ããŠãŒã¶ãŒãåŠççµæãããŠã³ããŒãã§ããããã«ããŸãã
ã§ããã°ChatGPTã«äžæãããšèªèããŠã»ããã§ãããããã§ã¯ããŒãã³ãŒãã£ã³ã°ã§å¯Ÿå¿ããŸãã
ãŠãŒã¶ãŒãã@Open Interpreter ããŠã³ããŒãããšã¡ãã»ãŒãžãéä¿¡ããæã ãåå²ããCloud Runäžã®ãã¡ã€ã«ãã¹ã¬ããã«éä¿¡ããŸãã
ãªãmessage.jsonã¯åºæ¬çã«äžèŠãªã®ã§é€ããŠããŸãã
...
loaded_file_paths = gcloud_storage.download_files_from_bucket(parent_message_user_id, thread_ts)
if message_by_user == "ããŠã³ããŒã":
for file_path in loaded_file_paths:
if file_path.endswith("/messages.json"):
continue
slack_api.upload_file_to_thread(client, channel_id, thread_ts, file_path)
return
...
def upload_file_to_thread(client: WebClient, channel_id: str, thread_ts: str, file_path: str):
try:
response = client.files_upload(channels=channel_id, thread_ts=thread_ts, file=file_path)
assert response["file"] # the uploaded file
except Exception as e:
logger.error({"message": "SlackApiError", **e.__dict__})
raise e
åæãªã¯ãšã¹ãåé¡
Cloud Runã§ã¯ã1ã³ã³ãããåæã«æããªã¯ãšã¹ãã®æ°ãæå®ããããšãã§ããŸãã
ããã§1ãæå®ããã°ããããããã®åé¡ã¯ç¡èŠã§ããŸãããä»åã¯ããã©ã«ãèšå®ã®ãŸãŸãå¥ã®æ¹æ³ã§å¯Ÿå¿ããŸãã
ããã¹ãããš2ã€ãããŸãã
- ã¹ã¬ããããšã«Cloud Runå ã§ã®äœæ¥ãã©ã«ããåå²ãã
- Open Interpreterã®åŒã³åºãæ¹ãå€ãã
ã¹ã¬ããããšã«Cloud Runå ã§ã®äœæ¥ãã©ã«ããåå²ãã
ã¹ã¬ããããšã«ãŠããŒã¯Cloud Runå
ã®äœæ¥ãã©ã«ããäœæããGCSããã®ããŠã³ããŒãã»ã¢ããããŒãããã®ãã©ã«ãã®ã¿ã§è¡ããŸãã
user_idãå«ããŠãŠããŒã¯ã«ããŠããŸãããå®éã¯thread_tsã®ã¿ã§è¯ãã¯ãã§ãã
def get_temp_dir(user_id: str, thread_ts: str) -> str:
return f"/tmp/{user_id}/{thread_ts}"
Open Interpreterã®åŒã³åºãæ¹ãå€ãã
Slack botçµç±ã§Open Interpreterã«åæã«è€æ°ã®ãªã¯ãšã¹ããéä¿¡ãããšãã¡ãã»ãŒãžãæ··ãã£ãŠããŸããšããçŸè±¡ãçºçããŸããã
ãŸ1ã.ãã ããŒãŒã¿ã¯ããèªãŠã¿ã³åœ¢èŸŒåŒã¿ã®ããã®ãã¡æŠã€èŠã«ããäœç¢ºæããŸãèªã
ãã2ã.ãšãã ãå§ããŸã¡ã€ãã«ã«ããããã¹ããããã®ãæžããã«èŸŒã¯ã¿ä»¥äžãŸãã
ã®ãŸã¹ãããããããŒã¯ãããŠã³å®åœ¢è¡åŒããŸãã®ã
ãã¡1ã€.ã« pandasãã©äœã€æããã©ãŸãªããã€ã³ã¹ãããŒã«ããããŸãïŒãã¡ã€æ¢ã«ã«åã€ã³ã¯ã¹ãäœã«ããŸããïŒãŒã«ãŸãããããã¡ã€ãŠã«ãã«ãæžå Žåãã¯èŸŒã¹ããããããã§ã¹ããã¯ãŸãïŒã
2äœ.ã§ããïŒ CSVãã¡ã€ã«ãèªã¿èŸŒã¿ãŸãã
詳现ãªåå ãä»çµã¿ã¯ããã£ãŠããªãã®ã§ãããåŒã³åºãæ¹ã®å€æŽã«ãã£ãŠãšããããæ²»ã£ãã®ã§ããã®ãŸãŸã«ããŠããŸãã
pythonã³ãŒãå ã§Open Interpreterã䜿ãæãéåžžã¯ãã®ããã«ããŠåŒã³åºããŸãã
import interpreter
interpreter.chat("Plot AAPL and META's normalized stock prices") # Executes a single command
interpreter.chat() # Starts an interactive chat
ãã®æinterpreter/__init__.pyãèªã¿èŸŒãŸããã®ã§ããããã®äžèº«ã¯ãããªã£ãŠããŸãã
from .interpreter import Interpreter
import sys
# This is done so when users `import interpreter`,
# they get an instance of interpreter:
sys.modules["interpreter"] = Interpreter()
# **This is a controversial thing to do,**
# because perhaps modules ought to behave like modules.
# But I think it saves a step, removes friction, and looks good.
# ____ ____ __ __
# / __ \____ ___ ____ / _/___ / /____ _________ ________ / /____ _____
# / / / / __ \/ _ \/ __ \ / // __ \/ __/ _ \/ ___/ __ \/ ___/ _ \/ __/ _ \/ ___/
# / /_/ / /_/ / __/ / / / _/ // / / / /_/ __/ / / /_/ / / / __/ /_/ __/ /
# \____/ .___/\___/_/ /_/ /___/_/ /_/\__/\___/_/ / .___/_/ \___/\__/\___/_/
# /_/ /_/
sys.modules["interpreter"] = Interpreter()ã®ããã§åæã«çæãããmessageãæ··ãã£ãã®ã ãšæãããŸãã
ããã§Interpreterã¯ã©ã¹ããã®ããã«åŒã³åºããŠäœ¿ãããšã§å¯Ÿå¿ããŸãã
from interpreter.interpreter import Interpreter
interpreter = Interpreter()
interpreter.chat("His name is Killian.")
䜿ããããªã©ã€ãã©ãªãäºåã«ã€ã³ã¹ããŒã«ããã
ã³ã³ããå
ã«äºåã€ã³ã¹ããŒã«ããã€ã³ã¹ããŒã«æžã¿ã§ããããšããã®ãããªã·ã¹ãã ã¡ãã»ãŒãžã«è¿œèšããŸãã
ããã«ããã³ã³ããã®ãããã€æéããã®ãããé·ããªããŸããããããŒãªããšããŸãã
You can use the following libraries without installing:
- pandas
- numpy
- matplotlib
- seaborn
- scikit-learn
- pandas-datareader
- mplfinance
- yfinance
- requests
- scrapy
- beautifulsoup4
- opencv-python
- ffmpeg-python
- PyMuPDF
- pytube
- pyocr
- easyocr
- pydub
- pdfkit
- weasyprint
Slack ãšã®ç¹ããã¿
FlaskãšSlack Boltã䜿ã£ãŠæ¥ç¶ããŸãã
Slack Botã¯3ç§ä»¥å
ã«ãªã¯ãšã¹ããè¿ããªããšãªã¯ãšã¹ãã®ãªãã©ã€ãè¡ãããŠããŸããŸãã
ããã§ããããç¡çãã察åŠããããã«middlewareãèšå®ããŠããŸãã
from flask import Flask, request
from slack_bolt import App, BoltResponse, Say
from slack_bolt.adapter.flask import SlackRequestHandler
from slack_sdk import WebClient
app = Flask(__name__)
slack_app = App(token=os.environ["SLACK_BOT_TOKEN"], signing_secret=os.environ["SLACK_SIGNING_SECRET"])
handler = SlackRequestHandler(slack_app)
@app.route("/slack/events", methods=["POST"])
def slack_events():
return handler.handle(request)
@slack_app.middleware
def handle_retry(req, next):
if "x-slack-retry-num" in req.headers and req.headers["x-slack-retry-reason"][0] == "http_timeout":
return BoltResponse(status=200, body="success")
next()
@slack_app.event("app_mention")
def mentioned(body, say: Say):
...
ããã«å ããã³ã³ããã®CPUåžžæèµ·åèšå®ãè¡ãå¿ èŠããããŸããèšå®æ¹æ³ã¯ãããã€ãšåãããŠèª¬æããŸãã
詳现ã¯ãã¡ãã®èšäºãã芧ãã ããã
ãããã€ãã
Slack Botã®èšå®ãšCloud Runã®èšå®ãè¡ãæ¥ããå¿ èŠããããŸããããããªã«é£ããã¯ç¡ãã§ãã
Slack Botãäœæããã
ãã¡ãã®ããŒãžããã¢ããªãäœæããŸãã
OAuthã®èšå®, Tokenã®ååŸ
OAuth & PermissionsããŒãžã®ScopesããBotã«ä»¥äžã®æš©éãäžããŸãã

èšå®ããããBotãã¯ãŒã¯ã¹ããŒã¹ã«ã€ã³ã¹ããŒã«ããŸãã
Install to Workspaceãšããç·ã®ãã¿ã³ãã¯ãªãã¯ããŸãã
ãã®åŸxoxb-ããå§ãŸãBot User OAuth Tokenãäœæãããã®ã§ããããã©ããã«ã¡ã¢ããŠãããŸãã
Secretã®ååŸ
Basic InformationããŒãžããSigning SecretãååŸãããããã©ããã«ã¡ã¢ããŠãããŸãã
ã³ã³ãããCloud Runã«ãããã€ãã
Bot User OAuth TokenãšSigning SecretãååŸã§ãããšããã§ãCloud Runã®ãã«ããèšå®ãè¡ããŸãã
ç°å¢å€æ°ã¯GCPã®ã³ã³ãœãŒã«ã§èšå®ãããã.envãã¡ã€ã«çã¯ãããŸããã
$PROJECT $REGIONãªã©ã¯é©å®æžãæããŠãã ããã
ã©ã€ãã©ãªãã€ã³ã¹ããŒã«ããŸãã£ãŠãããããããªãæéãããããŸãã
gcloud run deploy open-interpreter-slack-bot \
--source . \
--region $REGION \
--memory 2048Mi \
--project $PROJECT \
--allow-unauthenticated
ãããã€ãå®äºãããšãã®ããã«Cloud Runã®ãšã³ããã€ã³ãã衚瀺ãããŸãã®ã§ãã³ããŒããŠãããŸãã
https://open-interpreter-slack-bot-XXXXXXXXXXX.a.run.app

ã³ã³ãœãŒã«äžã§ã®èšå®
ç¶ããŠGCPã³ã³ãœãŒã«ã§Cloud Runãéããå
ã»ã©ãããã€ãããµãŒãã¹ãéããŸãã
äžèšãããã€ã³ãã³ãããã®ãŸãŸäœ¿ã£ãå Žåopen-interpreter-slack-botãšããååã«ãªã£ãŠããã¯ãã§ãã
æ°ãããªããžã§ã³ç·šéãšãããã€ãéžæãã以äžã®ç°å¢å€æ°ãèšå®ããŸãã
- OPENAI_API_KEY
- SLACK_BOT_TOKEN (å ã»ã©ååŸãããã®)
- SLACK_SIGNING_SECRET (å ã»ã©ååŸãããã®)
ããã«ãCPU ã®å²ãåœãŠãšæéããCPU ãåžžã«å²ãåœãŠãéžæããŸãã
èšå®ãçµãã£ãããã®ãŸãŸãããã€ãã¯ãªãã¯ããŸãã
Event Subscriptionã®èšå®
æåŸã«ãåã³Slackã®èšå®ã«æ»ããSlackã®EventãSubscribeããŸãã
Event SubscriptionsããŒãžã§Enable Eventsããå
ã»ã©Cloud Runã®ãããã€åŸã«ååŸããURLãããŒã¹ãããŸãã
ãã®æãURLã®æ«å°Ÿã«/slack/eventsã远å ããŸãã
ãããšSlackãããã®ãšã³ããã€ã³ãã«ãã«ã¹ãã§ãã¯ãè¡ãããŸãã
ãããéããªãå ŽåCloud Runã§äœããã®ãšã©ãŒãèµ·ããŠããŸãã
Subscribeããã€ãã³ãã«ã¯app_mentionãéžæããã°OKã§ãã
ããã«ãŠäœæå®äºã§ãïŒïŒ
ã©ããã®ãã£ã³ãã«ã«BotãæåŸ
ããã¡ã³ã·ã§ã³ããŠã¿ãŸãããã
çµããã«
è¯ãOpen Interpreterã©ã€ããïŒ
Discussion
ããã«ã¡ã¯ã䟿å©ãªããŒã«ã®ç޹ä»ã®ã¿ãªããCloud Runã®è§£èª¬ã«ããªã£ãŠããèšäºã®å ¬éãããããšãããããŸãã
ãã²èªåã®ç°å¢ã§ã䜿ã£ãŠã¿ãããšæã£ãã®ã§ãããã©ã€ãã©ãªã®ã€ã³ã¹ããŒã«ãCPUåžžæèµ·åãšããããšã§ãè²»çšããããããããããããªãããšå¿é ã§ãã
ããå·®ãæ¯ããªããã°ãæ¯æ¥ãªããæ¯æåäœã§ã©ããããã³ã¹ãããããããã(ãããã¯ããã£ãŠããªãã)æããŠããã ããŸãããïŒ