Jinba Toolbox
Guides

ツール開発ガイド

Python/TypeScriptでツールを開発する方法

基本構造

Toolは InputOutputrun関数 の3つで構成されます。

Python

PydanticのBaseModelでスキーマを定義します。

from pydantic import BaseModel, Field

class Input(BaseModel):
    url: str = Field(description="取得するURL")

class Output(BaseModel):
    title: str
    content: str

def run(input: Input) -> Output:
    # 処理を実装
    return Output(title="...", content="...")

TypeScript

Zodでスキーマを定義します。

import { z } from "zod";

export const input = z.object({
  url: z.string().describe("取得するURL"),
});

export const output = z.object({
  title: z.string(),
  content: z.string(),
});

export async function run(data: z.infer<typeof input>) {
  // 処理を実装
  return { title: "...", content: "..." };
}

外部ライブラリ

ToolSetの設定画面でパッケージを追加すると、コード内でimportできます。

import requests  # パッケージ設定で "requests" を追加
from bs4 import BeautifulSoup  # "beautifulsoup4" を追加

def run(input: Input) -> Output:
    html = requests.get(input.url).text
    soup = BeautifulSoup(html, "html.parser")
    return Output(title=soup.title.string)

バージョン指定も可能です(例: requests==2.31.0)。

環境変数

APIキーなどの機密情報は環境変数で渡します。ToolSetの設定画面で登録できます。

import os

def run(input: Input) -> Output:
    api_key = os.environ["SLACK_API_KEY"]
    # api_key を使用...
export async function run(data: z.infer<typeof input>) {
  const apiKey = process.env.SLACK_API_KEY;
  // apiKey を使用...
}

環境変数

エラーハンドリング

例外を送出すると、エラー情報が記録されます。

def run(input: Input) -> Output:
    if not input.url.startswith("https://"):
        raise ValueError("URLはhttps://で始まる必要があります")
    # ...

実践例

Slack通知

import os
import requests
from pydantic import BaseModel, Field

class Input(BaseModel):
    channel: str = Field(description="通知先チャンネル")
    message: str = Field(description="メッセージ本文")

class Output(BaseModel):
    success: bool
    ts: str = Field(description="メッセージのタイムスタンプ")

def run(input: Input) -> Output:
    response = requests.post(
        "https://slack.com/api/chat.postMessage",
        headers={"Authorization": f"Bearer {os.environ['SLACK_BOT_TOKEN']}"},
        json={"channel": input.channel, "text": input.message},
    )
    data = response.json()
    if not data["ok"]:
        raise Exception(data["error"])
    return Output(success=True, ts=data["ts"])

Webページ取得

import requests
from bs4 import BeautifulSoup
from pydantic import BaseModel

class Input(BaseModel):
    url: str

class Output(BaseModel):
    title: str
    description: str
    text: str

def run(input: Input) -> Output:
    html = requests.get(input.url).text
    soup = BeautifulSoup(html, "html.parser")

    title = soup.title.string if soup.title else ""
    desc_tag = soup.find("meta", {"name": "description"})
    description = desc_tag["content"] if desc_tag else ""
    text = soup.get_text()[:2000]

    return Output(title=title, description=description, text=text)

外部API呼び出し

import os
import requests
from pydantic import BaseModel

class Input(BaseModel):
    query: str

class Output(BaseModel):
    results: list[dict]

def run(input: Input) -> Output:
    response = requests.get(
        "https://api.example.com/search",
        headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
        params={"q": input.query},
    )
    response.raise_for_status()
    return Output(results=response.json()["results"])

スキーマのベストプラクティス

descriptionを付与する

AIがToolを選択する際の判断材料になります。

class Input(BaseModel):
    query: str = Field(description="検索キーワード")
    limit: int = Field(default=10, description="最大取得件数(1-100)")
export const input = z.object({
  query: z.string().describe("検索キーワード"),
  limit: z.number().default(10).describe("最大取得件数(1-100)"),
});

Optional型

from typing import Optional

class Input(BaseModel):
    query: str
    filter: Optional[str] = None  # 省略可能

ネストした型

class Address(BaseModel):
    city: str
    country: str = "JP"

class Input(BaseModel):
    name: str
    address: Address

テスト

画面下部のテストパネルでInputを入力し、Cmd+Enterで実行できます。

Publishせずにドラフト状態のコードをテストできるため、開発中の動作確認に便利です。

On this page