gateways/
1 つのフォルダに、2 つの無関係な仕事: エージェントを公開することと、チャットプラットフォームをそれに接続すること。
On this page
- 1. HTTP/MCP/Web UI 公開 ( exposes: "agent" | "mcp" | "webui" )
- 2. チャネルアダプタゲートウェイ ( type: "mock" | "cli" | "http" )
- 3. Push-style gateways ( type: "telegram" / "slack" / "discord" / "email" / "whatsapp-cloud" / "teams" / "twilio" / "github" / "signal" , and friends)
- Slack の永続的な接続
- Discord の永続的な接続 - クライアント駆動の必須ハートビート
- Email - サーバーレベルの依存関係、そして劣化したスレッディング/HITL
- WhatsApp Business Cloud API - Webhook 駆動、接続駆動ではない
- Microsoft Teams - Bot Framework Activity プロトコル
- Twilio SMS - 本質的に異なる署名方式、そしてデュアルパスのレスポンスモデル
- GitHub - @mention によってゲートされた issue/PR コメントスレッド
- Signal - 4 つ目の転送形式、外部の signal-cli デーモンに対して
- GatewaySession - wiring the agent to every push-style gateway
- push 型ゲートウェイはどうやって接続を維持するのか: 共有される ColdBox スケジューラ
- ロギング
gateways/
ãã® 1 ã€ã®ãã©ã«ãã®äžã«ãã gateways/*.bx/.json ãã¡ã€ã«ã¯ã2 ã€ã®å¥åã®ãç¡é¢ä¿ãªããšãã«ããŒããŠããŸã - ãããšã³ããªãã©ã¡ãã®çš®é¡ãã¯ããã® configure() æ§é äœã exposes ããŒãæã€ãã©ããã ãã§æ±ºãŸããŸãã
ããããæ··åããªãã§ãã ãã - HTTP å
¬éããããšãŒãžã§ã³ã (exposes: "agent") ã¯ããªãã®ãšãŒãžã§ã³ãçšã® REST API ã§ããããã£ãã«ã¢ããã¿ã²ãŒããŠã§ã€ (type: "http") ã¯ãã£ãããã©ãããã©ãŒã ã human-in-the-loop æ¿èªãããŒçšã® Webhook ãšã³ããã€ã³ãã§ããçæãããã«ãŒãã¯ãŸã£ããç°ãªããŸãã
flowchart TD
F["a file under gateways/"] --> Q{"does configure() return<br/>an 'exposes' key?"}
Q -->|"yes"| E["EXPOSURE<br/>a route into your agent"]
Q -->|"no - it has a 'type' key instead"| C["CHANNEL ADAPTER<br/>a connection to a chat platform"]
E --> E1["exposes: agent<br/>route().toAi()"]
E --> E2["exposes: mcp<br/>route().toMCP()"]
E --> E3["exposes: webui<br/>generated index.html + /api"]
C --> C1["mock / cli / http<br/>pull-driven: something calls US"]
C --> C2["telegram, slack, discord, email, whatsapp-cloud,<br/>teams, twilio, github, signal<br/>push-style: holds its own connection"]
C2 --> S["one GatewaySession<br/>bound to the root agent"]
style E fill:#d4edda,stroke:#155724
style C fill:#cce5ff,stroke:#004085
1. HTTP/MCP/Web UI å
Ž (exposes: "agent" | "mcp" | "webui")
ColdBox 8.1 ã®ãã€ãã£ã㪠AI Routing DSL ã䜿ã£ãŠããšãŒãžã§ã³ãããŸãã¯ããŒã«ã« MCP ãµãŒããŒã HTTP çµç±ã§å ¬éããŸã - ãããã¯ãWeb ãã£ãã UI ã§å¥é解説ããŠãããããããããã«ãããããã©ãŠã¶ãã£ãã UI ãšããŠå ¬éããŸãã
ãšãŒãžã§ã³ããå ¬éãã:
// gateways/expose.bx
class {
function configure() {
return {
exposes : "agent",
path : "/api/chat"
};
}
}
config/Router.bx ã«ã以äžãçæããŸãã
route( "/api/chat" ).toAi( "GeneratedAgent" )
ããã¯4 ã€ã®ãµãã«ãŒã: POST /api/chat/invokeãPOST /api/chat/stream (SSE)ãPOST /api/chat/batchãGET /api/chat/info ãèªåç»é²ããŸããçŽ ã® /api/chat ãã¹èªäœã¯ã«ãŒãã£ã³ã°å¯Ÿè±¡ã§ã¯ãããŸããã
ããŒã«ã« MCP ãµãŒããŒãå ¬éãã (mcp/ åç §):
class {
function configure() {
return {
exposes : "mcp",
path : "/mcp/tools",
target : "local-server" // must match an mcp/*.bx entry's declared name
};
}
}
route( "/mcp/tools" ).toMCP( "local-server" ) ãçæããŸãã
v1 ã® Web ãã£ãã UI ãå ¬éãã:
// gateways/chat.bx
class {
function configure() {
return {
exposes : "webui",
path : "/chat",
apiKeyEnvVar: "CHAT_UI_API_KEY" // optional - see below
};
}
}
å®éã®éç㪠<path>/index.html ãã¡ã€ã« (çŽæ¥é
ä¿¡ãããã«ãŒãã¯äžèŠã§ã) ã«å ããŠãåºå®ããã <path>/api ãã¬ãã£ãã¯ã¹é
äžã®å°çš API ãçæããŸããããã«ãããã·ã§ã«èªèº«ã®ãã¡ã€ã«ãšè¡çªããããšã¯æ±ºããŠãããŸããããã® API 㯠toAi() ã§ã¯ãªãçæããã handlers/ChatUi.bx ã§ããããã®ãšã³ããªã¯çæããã SQLite ã¹ãã¢ãäžç·ã«æã£ãŠããŸãã
Web UI ã¯åäžã®å
¬éã¹ã€ãããšãããã 1 ã€ã®ãµãã·ã¹ãã ã§ã - ã«ãŒãäžèЧãã¹ãã¢ãäŒè©±ãšèšå®ããã©ã³ãã£ã³ã°ãšããŒãããããŠãªã toAi() ã䜿ããªãã®ãã¯ããã¹ãŠç¬èªã®ããŒãžã«ãããŸã: Web ãã£ãã UIã
æ€èšŒ: exposes 㯠agentãmcpãwebui ã®ããããã§ããå¿
èŠããããŸããpath ã¯å¿
é ã§ããã¹ãŠã®å
¬éãšã³ããªã®éã§äžæã§ããå¿
èŠããããŸããmcp å
¬éã® target ã¯å¿
é ã§ãå®åšãã mcp/* ãšã³ããªã®å®£èšãããååãšäžèŽããå¿
èŠããããŸããwebui ã® apiKeyEnvVar ã¯å®å
šã«ä»»æã§ãå¿
é ãã£ãŒã«ããã§ãã¯ã¯ãããŸãã (äžèšåç
§)ã
2. ãã£ãã«ã¢ããã¿ã²ãŒããŠã§ã€ (type: "mock" | "cli" | "http")
bx-ai ã® IGateway (å€éšé
ä¿¡ / human-in-the-loop æ¿èªçšã®ãã£ãã«ã¢ããã¿) ãååã§ç»é²ããŸã - ãšãŒãžã§ã³ãèªèº«ã® REST API ãå
¬éããã®ãšã¯å¥ç©ã§ãã
// gateways/slack.bx
class {
function configure() {
return {
type : "http",
secretEnvVar : "SLACK_WEBHOOK_SECRET"
};
}
}
secretEnvVar ã¯çœ²åã·ãŒã¯ã¬ãããä¿æããç°å¢å€æ°ã®ååãæå®ããŸã - ã·ãŒã¯ã¬ããã®å€ãã®ãã®ã§ã¯ãããŸãããApplication.bx ã® onApplicationStart() ã«ã以äžãçæããŸãã
aiGatewayRegistry().register( aiGateway( "http", { secret : getSystemSetting( "SLACK_WEBHOOK_SECRET", "" ) } ) )
ã·ãŒã¯ã¬ããã¯ãµãŒããŒèµ·åæã«ã©ã€ãã«è§£æ±ºããããã®ãããžã§ã¯ãã®ä»ã®ç®æãã¹ãŠãšåããã·ãŒã¯ã¬ããã¯åžžã«å€éšã«çœ®ãããšããã«ãŒã«ã«åŸããŸã (ãããã€ãšã·ãŒã¯ã¬ãã åç
§) - çæããããœãŒã¹å
ã«ãªãã©ã«ãšããŠåã蟌ãŸããããšã¯æ±ºããŠãªããããããã±ãŒãžåããã .bxa ã«ãå«ãŸããããšã¯æ±ºããŠãããŸãããç°å¢å€æ°ãæªèšå®ã®å Žåãbx-ai èªèº«ã® HttpGateway ã¯ç©ºã®ã·ãŒã¯ã¬ãããã眲åãèšå®ãããŠããªãããšããŠæ±ããèµ·åæã«ã¯ã©ãã·ã¥ããã®ã§ã¯ãªããããã«å¿ããŠãªã¯ãšã¹ããæåŠããŸãã
æ€èšŒ: type 㯠mockãcliãhttp ã®ããããã§ããå¿
èŠããããŸããtype: "http" ãšã³ããªã«ã¯ secretEnvVar ãå¿
é ã§ãããšã³ããªèªèº«ã®ãã¡ã€ã«/ããŒã¹åã¯ããã¹ãŠã®ãã£ãã«ã¢ããã¿ãšã³ããªã®éã§äžæã§ããå¿
èŠããããŸããmock ã¯ãã¹ãå°çšã§ããcli 㯠bx-ai èªèº«ã«çµã¿èŸŒãŸãã human-in-the-loop æ¿èªãã£ãã«ã§ã (ããããã³ã°ãª stdin/stdout ã® A/R/Q ããã³ãã) - ããã¯ãã²ãŒããŠã§ã€ãæå®ãããŠããªãå Žåã« HumanInTheLoopMiddleware ãããã©ã«ãã§ã¢ã¿ãããããã®ã§ãã²ãŒããŠã§ã€ã¬ãžã¹ããªã«äžåè§Šããªã BX Agents èªèº«ã® chat åè©ãšã¯ç¡é¢ä¿ã§ãã
http ã¿ã€ãã®ãšã³ããªã¯ããã«ãå®éã® HTTP é
ç·ãåŸãŸã: bx-ai èªèº«ã® GatewayRequestProcessor::processHttp() ã«çŽæ¥ãããã·ãããçæããã handlers/Gateway.bx ã¢ã¯ã·ã§ã³ãšãconfig/Router.bx ã® 3 ã€ã®ã«ãŒãã§ãã
post( "/gateways/:gatewayName/events" ).toHandler( "Gateway.process" )
get( "/interactions/:requestID" ).toHandler( "Gateway.process" )
post( "/interactions/:requestID/decisions" ).toHandler( "Gateway.process" )
ColdBox ã«ã¯ããã®çšéã®ããã®çµã¿èŸŒã¿ã® toAiGateway() DSL çµç«¯åã¯ãããŸãã (ãã€ãã£ãã«ååšããã®ã¯ toAi() ãš toMCP() ã®ã¿ã§ã) - ãã®é
ç·ã¯ BX Agents èªèº«ãçæããã³ãŒãã§ãå°æ¥ã®ã³ã¢çµç«¯åãçæããã§ããããã®ãšåã圢ç¶ã«åŸã£ãŠããŸãã詳ãã㯠ColdBox ã³ã¢åã toAiGateway() ææ¡ãåç
§ããŠãã ããã
3. Push-style gateways (type: "telegram" / "slack" / "discord" / "email" / "whatsapp-cloud" / "teams" / "twilio" / "github" / "signal", and friends)
äžèšã® mock/cli/http ãšã¯ç°ãªãçš®é¡ã®ãã£ãã«ã¢ããã¿ã§ã - åä¿¡ HTTP ãªã¯ãšã¹ãã«ãã£ãŠé§åãããã®ã§ã¯ãªããpush åã²ãŒããŠã§ã€ã¯ãã©ãããã©ãŒã ãžã®ç¬èªã®æ¥ç¶ãä¿æããåä¿¡ã¡ãã»ãŒãžãå°çãããã³ã«ããªãã®ãšãŒãžã§ã³ãã« push ããŸãããããæ¬ç©ã®ãã£ããããããã«è¿ãäœéšã§ãã仿¥æç¹ã§ 4 ã€ã®è»¢é圢åŒãååšããŸãã
- ãã³ã°ããŒãªã³ã° (TelegramãEmail): ã¹ã±ãžã¥ãŒã«ãããã¿ã¹ã¯ã宿çã«ãã©ãããã©ãŒã ã«ãäœãæ°çã¯ãã?ããšå°ããŸã (Telegram ã®
getUpdatesãEmail ã® IMAP ããŒãªã³ã°)ã - æ°žç¶ç㪠WebSocket (Slack ã® Socket Mode çµç±ãDiscord ã® Gateway API çµç±): ã²ãŒããŠã§ã€ãããã©ãããã©ãŒã ããªã¢ã«ã¿ã€ã ã§ã€ãã³ãã push ããŠãããã©ã€ãã§é·æéæç¶ããæ¥ç¶ãä¿æããŸãã
- Webhookããã«é§å (WhatsApp Business Cloud APIãMicrosoft TeamsãTwilio SMSãGitHub): ãã®ã²ãŒããŠã§ã€ãç¬èªã®éä¿¡æ¥ç¶ãä¿æããã®ã§ã¯ãªãããã©ãããã©ãŒã ãå ¬éã® HTTP ãšã³ããã€ã³ãçµç±ã§ç§ãã¡ãåŒã³åºããŸã - 管çãã¹ãã¹ã±ãžã¥ãŒã©ã¿ã¹ã¯ããœã±ããã¯ãããŸãããäžèšã®åãµãã»ã¯ã·ã§ã³åç §ã
- ãµãŒããŒéä¿¡ã€ãã³ã (SSE) (SignalãããŒã«ã«ã§åã
signal-cliããŒã¢ã³ã«å¯ŸããŠ): ã²ãŒããŠã§ã€ãéãããŸãŸã«ãããé·æéæç¶ããäžæ¹åã®ã¹ããªãŒãã³ã° HTTP æ¥ç¶ã§ãåãã¬ã¹ãã³ã¹ããã£ãéã㊠push ãããã€ãã³ããèªã¿åããŸããäžèšã®ç¬èªãµãã»ã¯ã·ã§ã³åç §ã
// gateways/telegramChannel.bx
class {
function configure() {
return {
type : "telegram",
botTokenEnvVar: "TELEGRAM_BOT_TOKEN"
};
}
}
// gateways/slackChannel.bx
class {
function configure() {
return {
type : "slack",
botTokenEnvVar: "SLACK_BOT_TOKEN", // xoxb-... - chat.postMessage/chat.update
appTokenEnvVar: "SLACK_APP_TOKEN" // xapp-... - apps.connections.open (Socket Mode)
};
}
}
// gateways/discordChannel.bx
class {
function configure() {
return {
type : "discord",
botTokenEnvVar: "DISCORD_BOT_TOKEN" // Authorization: Bot <token> on every REST call and inside Identify
// intents: 37377 // optional override - defaults to GUILDS+GUILD_MESSAGES+DIRECT_MESSAGES+MESSAGE_CONTENT
};
}
}
// gateways/emailChannel.bx
class {
function configure() {
return {
type : "email",
imapHostEnvVar : "IMAP_HOST",
imapUsernameEnvVar: "IMAP_USERNAME",
imapPasswordEnvVar: "IMAP_PASSWORD",
fromAddressEnvVar : "EMAIL_FROM_ADDRESS"
// imapPort: 993 // optional override - defaults to 993 (IMAPS)
// pollIntervalSeconds: 60 // optional override - defaults to 60
};
}
}
// gateways/whatsappCloud.bx
class {
function configure() {
return {
type : "whatsapp-cloud",
accessTokenEnvVar : "WHATSAPP_ACCESS_TOKEN", // Graph API access token
phoneNumberIdEnvVar: "WHATSAPP_PHONE_NUMBER_ID", // the WhatsApp Business phone number ID sends go through
appSecretEnvVar : "WHATSAPP_APP_SECRET", // HMAC key verifying X-Hub-Signature-256 on inbound webhooks
verifyTokenEnvVar : "WHATSAPP_VERIFY_TOKEN" // shared secret Meta's GET verify handshake must echo back
// apiVersion: "v21.0" // optional override - defaults to "v21.0"
};
}
}
// gateways/teamsChannel.bx
class {
function configure() {
return {
type : "teams",
appIdEnvVar : "TEAMS_APP_ID", // the bot's own Microsoft App ID (also the inbound JWT's required aud claim)
appPasswordEnvVar : "TEAMS_APP_PASSWORD" // OAuth2 client-credentials secret
// tenantId: "..." // optional override for single-tenant apps - defaults to "botframework.com" (multi-tenant)
};
}
}
// gateways/twilioChannel.bx
class {
function configure() {
return {
type : "twilio",
accountSidEnvVar: "TWILIO_ACCOUNT_SID",
authTokenEnvVar : "TWILIO_AUTH_TOKEN", // also the X-Twilio-Signature HMAC key
fromEnvVar : "TWILIO_FROM_NUMBER" // the Twilio phone number outbound sends go through, E.164
// messagingServiceSid: "MG..." // optional - if set, used instead of `from` on outbound sends
// publicUrl: "https://your-real-public-host/webhooks/twilio" // optional override for reverse-proxy/tunnel deployments - see the Twilio subsection below
};
}
}
// gateways/githubChannel.bx
class {
function configure() {
return {
type : "github",
tokenEnvVar : "GITHUB_TOKEN", // a personal access token (repo/issues+PR read+write scope)
webhookSecretEnvVar: "GITHUB_WEBHOOK_SECRET", // HMAC key verifying X-Hub-Signature-256 on inbound webhooks
botNameEnvVar : "GITHUB_BOT_NAME" // the bot's own GitHub login - matched as "@botName" in comments
// apiBaseUrl: "https://api.github.com" // optional override - defaults to "https://api.github.com"
};
}
}
// gateways/signalChannel.bx
class {
function configure() {
return {
type : "signal",
accountEnvVar: "MY_SIGNAL_ACCOUNT" // the signal-cli-registered phone number this gateway sends/receives as, E.164
// httpUrl: "http://127.0.0.1:8080" // optional override - defaults to "http://127.0.0.1:8080", where signal-cli's own daemon HTTP API is expected to be listening
};
}
}
http ã® secretEnvVar ãšåããã·ãŒã¯ã¬ããã¯åžžã«å€éšã«çœ®ããã«ãŒã«ã§ã - ãã¹ãŠã® *EnvVar ããŒã¯ç°å¢å€æ°ã®ååãæå®ããèµ·åæã« getSystemSetting() çµç±ã§ã©ã€ãã«è§£æ±ºããããªãã©ã«ãšããŠåã蟌ãŸããããšã¯æ±ºããŠãããŸãããemail ã® imapHost/fromAddress ã¯æå·åŠçãªã·ãŒã¯ã¬ããã§ã¯ãããŸããããããã§ããã¹ãŠã®å€ããããã€ããšã«ç°ãªããããåãç°å¢å€æ°é§åã®ã³ã³ãã³ã·ã§ã³ããã®ãã¹ãŠã«äœ¿ãããŠããŸããã³ã¢ã®ã¿ã€ããšã¯ç°ãªããpush åã²ãŒããŠã§ã€ã®ã¯ã©ã¹ã¯ bx-ai ã§ã¯ãªã BX Agents èªèº«ã®å
éšã«ãããã (models/gateways/*.bx)ããã®ç»é²ã¯çãååã§ã¯ãªããçã®ã¯ã©ã¹ãã¹ãšããŠã¬ã³ããªã³ã°ãããŸãã
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.TelegramGateway", { "botToken" : getSystemSetting( "TELEGRAM_BOT_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.SlackGateway", { "appToken" : getSystemSetting( "SLACK_APP_TOKEN", "" ), "botToken" : getSystemSetting( "SLACK_BOT_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.DiscordGateway", { "botToken" : getSystemSetting( "DISCORD_BOT_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.EmailGateway", { "imapHost" : getSystemSetting( "IMAP_HOST", "" ), "imapUsername" : getSystemSetting( "IMAP_USERNAME", "" ), "imapPassword" : getSystemSetting( "IMAP_PASSWORD", "" ), "fromAddress" : getSystemSetting( "EMAIL_FROM_ADDRESS", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.whatsapp.WhatsAppCloudGateway", { "accessToken" : getSystemSetting( "WHATSAPP_ACCESS_TOKEN", "" ), "phoneNumberId" : getSystemSetting( "WHATSAPP_PHONE_NUMBER_ID", "" ), "appSecret" : getSystemSetting( "WHATSAPP_APP_SECRET", "" ), "verifyToken" : getSystemSetting( "WHATSAPP_VERIFY_TOKEN", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.TeamsGateway", { "appId" : getSystemSetting( "TEAMS_APP_ID", "" ), "appPassword" : getSystemSetting( "TEAMS_APP_PASSWORD", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.TwilioGateway", { "accountSid" : getSystemSetting( "TWILIO_ACCOUNT_SID", "" ), "authToken" : getSystemSetting( "TWILIO_AUTH_TOKEN", "" ), "from" : getSystemSetting( "TWILIO_FROM_NUMBER", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.GitHubGateway", { "token" : getSystemSetting( "GITHUB_TOKEN", "" ), "webhookSecret" : getSystemSetting( "GITHUB_WEBHOOK_SECRET", "" ), "botName" : getSystemSetting( "GITHUB_BOT_NAME", "" ) } ) )
aiGatewayRegistry().register( aiGateway( "bxModules.bxagents.models.gateways.SignalGateway", { "account" : getSystemSetting( "MY_SIGNAL_ACCOUNT", "" ) } ) )
æ€èšŒ: type: "telegram" ã«ã¯ botTokenEnvVar ãå¿
é ã§ããtype: "slack" ã«ã¯ botTokenEnvVar ãš appTokenEnvVar ã®äž¡æ¹ãå¿
é ã§ããtype: "discord" ã«ã¯ botTokenEnvVar ãå¿
é ã§ããtype: "email" ã«ã¯ imapHostEnvVarãimapUsernameEnvVarãimapPasswordEnvVarãfromAddressEnvVar ãå¿
é ã§ããtype: "whatsapp-cloud" ã«ã¯ accessTokenEnvVarãphoneNumberIdEnvVarãappSecretEnvVarãverifyTokenEnvVar ãå¿
é ã§ããtype: "teams" ã«ã¯ appIdEnvVar ãš appPasswordEnvVar ãå¿
é ã§ããtype: "twilio" ã«ã¯ accountSidEnvVarãauthTokenEnvVarãfromEnvVar ãå¿
é ã§ããtype: "github" ã«ã¯ tokenEnvVarãwebhookSecretEnvVarãbotNameEnvVar ãå¿
é ã§ããtype: "signal" ã«ã¯ accountEnvVar ãå¿
é ã§ã - ãã¹ãŠ http ã® secretEnvVar ãšåãæ¹æ³ã§ãã§ãã¯ãããŸãã
Slack v1 㯠Socket Mode ã®ã¿ã§ã - å
¬é Webhook ãšã³ããã€ã³ãã¯äžèŠã§ãçæããããŸãã (http ãšã¯ç°ãªãããã¡ãã¯å®éã®ã«ãŒããåŸãŸã - äžèš §2 åç
§)ãSlack ããµããŒãããããäžã€ã® Events-API/HTTP-webhook æ¹åŒã¯ããã§ã¯å®è£
ãããŠããŸãããåæ§ã« Discord v1 ããDiscord ã®ããäžã€ã® HTTP Interactions Endpoint URL Webhook ã¢ãŒãã§ã¯ãªããå®éã® Gateway API (æ°žç¶ç㪠WebSocket) ã§ã - ãã®çµæã察話 (interaction) ã¯å
¬éã® HTTP ãšã³ããã€ã³ãã§ã¯ãªãåãèªèšŒæžã¿æ¥ç¶ãéããŠå±ããããEd25519 çœ²åæ€èšŒã¯ããã§ã¯äžèŠã§ã (Discord èªèº«ã®ããã¥ã¡ã³ãã«ç
§ãããŠç¢ºèªæžã¿ã§ã)ã
Slack ã®æ°žç¶çãªæ¥ç¶
SlackGateway ã¯ãjava.net.http.HttpClient ã®éåæ WebSocket ã¯ã©ã€ã¢ã³ããéããŠãimplements="java:java.net.http.WebSocket$Listener" ãçŽæ¥å®è£
ãã BoxLang ã®ãªã¹ããŒã¯ã©ã¹ (models/gateways/support/SlackSocketListener.bx) ããããªããžããã圢ã§ãèªèº«ã® WebSocket ãä¿æããŸã - BoxLang ã¯ãããããã®ã€ã³ã¿ãŒãã§ãŒã¹ã®å®éã® JVM å®è£
ãšããŠã³ã³ãã€ã«ããŸããããã¯ãã€ã³ã¹ã¿ã³ã¹ããã®ãŸãŸ HttpClient.newWebSocketBuilder().buildAsync( uri, listener ) ã«æž¡ããŠããã£ã¹ããšã©ãŒãçºçããªãããš (å®éã®ãããã¯ãŒã¯å¢çã«å°éããéã«æåŸ
ããã java.net.ConnectException ã®ã¿ãçºçããããš) ã«ãã£ãŠçµéšçã«ç¢ºèªãããŠããŸããã¯ã©ã¹ãå®éã«å®£èšããŠããã¡ãœããã ãã JDK ã€ã³ã¿ãŒãã§ãŒã¹ã® default ã¡ãœããããªãŒããŒã©ã€ãããŸããå®è£
ãããŠããªããã®ã¯èªåçã« JDK èªèº«ã®ããã©ã«ãåäœã«ãã©ãŒã«ã¹ã«ãŒããŸããããã¯ãä»ã®ããããæ°žç¶æ¥ç¶ã²ãŒããŠã§ã€ (åŸè¿°ã® Discord) ãåŸãåç
§ãã¿ãŒã³ã§ããããŸãã
忥ç¶ã¯ãSlack èªèº«ã®ãããã³ã«ã·ã°ãã« - disconnect ãã¬ãŒã (warning/refresh_requested) ãäºæããªããœã±ããã¯ããŒãº - ã«ãã£ãŠèœåçã«é§åãããSlack ãææžåããŠæšå¥šããéããå€ãæ¥ç¶ãéããåã«æ°ããæ¥ç¶ãéããŸãã軜éãªã¹ã±ãžã¥ãŒã©ãŠã©ããããã° (slack-watchdog-<name>ã30 ç§ããš) ã¯ããããã©ã¡ãã®ã·ã°ãã«ãçºç«ããªãã£ãå Žåã®ããã®ã»ãŒããã£ãããã«éããŸããã
Discord ã®æ°žç¶çãªæ¥ç¶ - ã¯ã©ã€ã¢ã³ãé§åã®å¿ é ããŒãããŒã
DiscordGateway ãåãæ¹æ³ (models/gateways/support/DiscordSocketListener.bxãSlack ãšåã implements="java:java.net.http.WebSocket$Listener" ãã¿ãŒã³) ã§æ¥ç¶ããŸãããDiscord ã® Gateway ãããã³ã«ã«ã¯ Slack ã® Socket Mode ã«ã¯ãªãèŠä»¶ããããŸã - ãµãŒããŒèªèº«ã® Hello ãã¬ãŒã (opcode 10) ãã¯ã©ã€ã¢ã³ãã« heartbeat_interval ãäŒããã¯ã©ã€ã¢ã³ãã¯ãã®ããŒã¹ã§èªåãã Heartbeat ãã¬ãŒã (opcode 1) ãéãç¶ããå¿
èŠããããŸãããããªããã° Discord ã¯æ¥ç¶ãããŸã³ãåããããšã¿ãªããŠåæããŸãããã®éé㯠Hello ãå±ããŠåããŠåãã (æ¥ç¶åã«ã¯åãããªã) ãããããŒãããŒãã¯ããèªèº«ã®ã¹ã±ãžã¥ãŒã©ã¿ã¹ã¯ (discord-heartbeat-<name>) ãšããŠãã¬ãŒã ãã³ãã©ã®å
éšããåçã«ç»é²ãããæ°ãã Hello ãå±ããã³ã«åç»é²ãããŸã - ããã¯ä»ã®ãã¹ãŠã® push åã²ãŒããŠã§ã€ã® registerScheduledTasks() æã«åºå®ãããã¿ã¹ã¯ãšããDiscord èªèº«ã®ã»ãŒããã£ããããŠã©ããããã° (discord-watchdog-<name>ã30 ç§ããšãSlack ãšåã圹å²) ãšãç°ãªããŸãã
åããŒãããŒãã®ãã£ãã¯ã¯ãçŽåã®ããŒãããŒããå®éã«ç¢ºèªå¿çãããã (Heartbeat ACKãopcode 11) ããã§ãã¯ããŸã - ãããŠããªããã°ããã®æ¥ç¶ã¯ãŸã³ãåããŠãããšã¿ãªãããã¿ã€ã ã¢ãŠããåŸ
ã€ã®ã§ã¯ãªãèœåçã«åæ¥ç¶ãããŸãããã以å€ã®åæ¥ç¶ã¯ãDiscord èªèº«ãææžåããŠããã»ãã·ã§ã³ã¢ãã«ã«åŸããŸã - Reconnect ãã¬ãŒã (opcode 7) ã倧åã®ã¯ããŒãºã³ãŒãã¯ãæ¢åã®ã»ãã·ã§ã³ãããå Žåã¯æ°ããæ¥ç¶äžã§ã® Resume (opcode 6ãæåŸã®ã·ãŒã±ã³ã¹çªå·ãåç) ãããªã¬ãŒããŸããd: false ãæã€ Invalid Session ãã¬ãŒã (opcode 9)ããŸã㯠Discord ãã»ãã·ã§ã³ç¡å¹åãšææžåããŠããã¯ããŒãºã³ãŒã (4007ã4009) ã¯ã代ããã«æ°èŠã® Identify (opcode 2) ã匷å¶ããŸããå°ããªåºå®ã»ããã®ã¯ããŒãºã³ãŒã (4004 äžæ£ãªããŒã¯ã³ã4010 äžæ£ãªã·ã£ãŒãã4011 ã·ã£ãŒãã£ã³ã°å¿
é ã4012 äžæ£ãª API ããŒãžã§ã³ã4013/4014 äžæ£/æªèš±å¯ã®ã€ã³ãã³ã) 㯠Discord èªèº«ã®ããã¥ã¡ã³ãã«åŸãå埩äžèœã§ã - ãã®ã²ãŒããŠã§ã€ã¯ãã©ããåã³å€±æããã§ãããæ¥ç¶ãå詊è¡ããã®ã§ã¯ãªã忢ããŸãã
MESSAGE_CONTENT (ã®ã«ããã£ã³ãã«ãš DM ã®äž¡æ¹ã§ãã¡ãã»ãŒãžããã¹ããèªã¿åãããã«å¿
èŠ) 㯠Discord ã®ç¹æš© (privileged) Gateway ã€ã³ãã³ãã§ã - Discord Developer Portal ã§èªåã®ãããã«å¯ŸããŠæç€ºçã«æå¹åããå¿
èŠããããããªãã®ã¢ããªãèªèšŒæžã¿ (100 以äžã®ã®ã«ã) ã«ãªã£ãåŸã¯ Discord ã«ããæ¿èªãå¿
èŠã§ããããããªããšããã¹ãŠã®åä¿¡ã¡ãã»ãŒãžã¯ç©ºã® content ãã£ãŒã«ãã§å±ããŸãã
Email - ãµãŒããŒã¬ãã«ã®äŸåé¢ä¿ããããŠå£åããã¹ã¬ããã£ã³ã°/HITL
EmailGateway ã¯ãèªèº«ã®ãã©ãããã©ãŒã ã® API ãçŽæ¥è©±ããªãå¯äžã® push åã²ãŒããŠã§ã€ã§ããéä¿¡ã¡ãŒã«ã¯ãèªåã§çµãã HTTP/SMTP åŒã³åºãã§ã¯ãªããColdBox èªèº«ã® cbmailservices ã¢ãžã¥ãŒã« (MailService@cbmailservicesããã® BXMail ãããã³ã« - ããèªäœã¯ bx-mail ã¢ãžã¥ãŒã«ã® BoxLang èªèº«ã® bx:mail ã³ã³ããŒãã³ããåŒã³åºããŠããã ãã§ã) ãçµç±ããŸããã©ã¡ããå®éã®ããµãŒããŒã¬ãã«ã®ã¢ãžã¥ãŒã«ã€ã³ã¹ããŒã«ã§ã - ãã®ãããžã§ã¯ãèªèº«ã® box.json ã® dependencies ãšããŠå®£èšãããŠããŸã (ãã®ãã bx-agents ãã€ã³ã¹ããŒã«ãããšãµãŒããŒã«ãããããåŒã蟌ãŸããŸã) ããcbmailservices/bx-mail ã¯ã©ã¡ãããçæãããã¢ããªãå®éã«å®è¡ãããµãŒããŒäžã§æç€ºçãªã€ã³ã¹ããŒã«ãå¿
èŠã§ã (äž¡ã¢ãžã¥ãŒã«èªèº«ã®ããã¥ã¡ã³ã/ãœãŒã¹ã«ç
§ãããŠç¢ºèªæžã¿ã§ã - ã©ã¡ãã ColdBox ã BoxLang ã«ããªã€ã³ã¹ããŒã«ãããŠåºè·ãããããšã¯ãããŸãã) - email ã²ãŒããŠã§ã€ãæã€ãããžã§ã¯ãã bxAgents serve/ãããã€ããåã«ãå®éã® box install (ãŸãã¯åçã®ãã®) ãè¡ã£ãŠãã ãããEmailGateway 㯠application.cbController.getWireBox() ããæåã§ MailService@cbmailservices ã解決ããŸã (ScheduledGatewayBase.resolveScheduler() èªèº«ã® docblock ã§çç±ã確èªã§ããŸã - ãã®ã¯ã©ã¹ã¯ aiGateway() ã«ãã£ãŠ WireBox ã®å€åŽã§çŽæ¥æ§ç¯ããããããinject="" ã¯ããã§ã¯æ±ºããŠæ©èœããŸãã)ãã¹ã±ãžã¥ãŒã©èªäœã解決ãããæ¹æ³ãšåãã§ãã
bx-mail ã cbmailservices ãã¡ãŒã«ã®åä¿¡ã¯è¡ããªã (éä¿¡ã®ã¿) ãããå信㯠JDK æšæºã® jakarta.mail API ã«ããæçµã¿ã® IMAP ã§ã - ãã®ãããžã§ã¯ãèªèº«ã®ã¯ã©ã¹ãã¹äžã§æšç§»çã«å°éå¯èœã§ããããšãç¢ºèªæžã¿ã§ã (bx-mail 㯠commons-email2-jakarta ã«äŸåããããèªäœã jakarta.mail-api + Angus Mail ã®å®è£
ã«äŸåããŸã)ãããã¯æšæž¬ã§ã¯ãªããä»åã®ã»ãã·ã§ã³ã§å®éã® jar ã«å¯ŸããŠçµéšçã«æ€èšŒãããŠããŸããã¹ã±ãžã¥ãŒã«ãããã¿ã¹ã¯ (email-poll-<name>) ãæªèªã¡ãŒã«ãæ±ã㊠IMAP ãããŒãªã³ã°ããŸããTelegram ã®ãã³ã°ããŒãªã³ã°ãšåã圢ã§ãã
ã¹ã¬ããã£ã³ã°ãš human-in-the-loop ã¯ãã©ã¡ãããã£ãããã©ãããã©ãŒã ã®ã²ãŒããŠã§ã€ãšæ¯ã¹ãŠå£åããŠãããgetDeclaredCapabilities() ã¯æå³çã« "interactiveActions" ãçããŠæ£çŽã«ããã衚æããŠããŸãã
- ã¹ã¬ããã£ã³ã°ã¯ãéåžžã®è¿ä¿¡ã«ã€ããŠã¯å®éã®
Message-ID/In-Reply-To/ReferencesããããŒã䜿ããŸã (ã²ãŒããŠã§ã€ã¯èªåãè¿ä¿¡ããŠããåä¿¡Message-IDãåžžã«ææ¡ããŠããã®ã§ãéä¿¡ããè¿ä¿¡ã«In-Reply-Toãèšå®ããã®ã¯ç¢ºå®ã§ã) - v1 ã®ç°¡ç¥åãšããŠããã§ãŒã³å šäœã®å®å šãªèµ°æ»ã§ã¯ãªãReferencesã®æåã®ãšã³ã㪠(ãªããã°In-Reply-Toãããããªããã°ã¡ãã»ãŒãžèªèº«ã®Message-ID) ã§ã¹ã¬ããåããŸãã - Human-in-the-loop ã«ã¯ãã€ãã£ããªãã¿ã³/ã³ã³ããŒãã³ã衚é¢ããŸã£ãããããŸãã -
requestHumanInteraction()ã¯ãèš±å¯ãããæ±ºå®ããŒã¯ãŒããåæãããã¬ãŒã³ããã¹ãã®ã¡ãŒã«ãéãã人éã«ãã® 1 ã€ãæåã®è¡ãšããŠè¿ä¿¡ããããæ±ããŸãããã®è¿ä¿¡ãæ£ããä¿çäžãªã¯ãšã¹ãã«çŽä»ããåŠçã¯ãéåžžã®è¿ä¿¡ã®ããã«ã¯In-Reply-Toã«é ŒããŸãã (cbmailservices ã®send()ã¯éä¿¡ããæ¿èªã¡ãŒã«èªäœãã©ããªMessage-IDãå²ãåœãŠãããããå ¬éããªããã)ããã®ããã代ããã« Subject è¡ã«åã蟌ãŸãã[bxagents:<requestID>]ã¿ã°çµç±ã§è¡ãããŸã - å®éã®ã¡ãŒã«ããŒã¹ã®ãµããŒããã±ããã·ã¹ãã ãåãçç±ã§äœ¿ãã®ãšåãææ³ã§ããè¿ä¿¡ã®æåã®è¡ã¯ããã®ãªã¯ãšã¹ãèªèº«ã®èš±å¯ãããæ±ºå®ãš (å®å šäžèŽãŸãã¯ãã¬ãã£ãã¯ã¹äžèŽã倧尿åãåºå¥ãã) ç §åãããŸããèªèãããªãè¿ä¿¡ã¯åããã³ããããããã®ãŸãŸééããbx-ai èªèº«ã® HITL ã³ãŒãã£ããŒã¿ãŒã«æåŠãããŸãã
WhatsApp Business Cloud API - Webhook é§åãæ¥ç¶é§åã§ã¯ãªã
WhatsAppCloudGateway ã¯ãä»ã®ãã¹ãŠã® push åã²ãŒããŠã§ã€ãšã¯ç°ãªã圢ãããŠããŸãããã®ã²ãŒããŠã§ã€ãç¬èªã®éä¿¡æ¥ç¶ (ããŒãªã³ã°ã¿ã¹ã¯ã WebSocket) ãä¿æããã®ã§ã¯ãªããMeta ãå
¬é Webhook çµç±ã§ç§ãã¡ãåŒã³åºããŸãããã㯠ScheduledGatewayBase ã§ã¯ãªããbx-ai ã® BaseGateway ãçŽæ¥ extends ããŸã - 管çãã¹ãã¹ã±ãžã¥ãŒã©ã¿ã¹ã¯ããœã±ããã¯ãªããwhatsapp-cloud ã²ãŒããŠã§ã€ãšã³ããªãååšããå Žåã«ã®ã¿æžã蟌ãŸããã2 ã€ã®åºå®ã«ãŒãã«é
ç·ãããçææžã¿ã® handlers/WhatsAppCloud.bx ãããã ãã§ãã
get( "/webhooks/whatsapp-cloud" ).toHandler( "WhatsAppCloud.verify" )
post( "/webhooks/whatsapp-cloud" ).toHandler( "WhatsAppCloud.process" )
ã©ã¡ãã®ã¢ã¯ã·ã§ã³ããã²ãŒããŠã§ã€èªèº«ã® handleVerify()/handleWebhook() ãžã®èããã¹ã¹ã«ãŒã§ã - verify 㯠Meta ã®ãµãã¹ã¯ãªãã·ã§ã³ãã³ãã·ã§ã€ã¯ (GET ?hub.mode=subscribe&hub.verify_token=...&hub.challenge=...) ã«å¿çããã¢ãŒããšããŒã¯ã³ãäžèŽããå Žåã«ã®ã¿ (宿°æéã§æ¯èŒããããã§) challenge ããã¬ãŒã³ããã¹ããšããŠãã®ãŸãŸè¿ããŸããprocess ã¯ãäœããããŒã¹/ãã£ã¹ãããããåã«ãMeta èªèº«ã® X-Hub-Signature-256 ããã㌠(å³å¯ãªçã® POST ãã㣠- event.getHTTPContent() - ã«å¯Ÿãã HMAC-SHA256ãåããŒã¹/åã·ãªã¢ã©ã€ãºããã JSON ã§ã¯ããã€ãåãå€ãã眲åãå£ããŠããŸãããæ±ºããŠäœ¿ããŸãã) ãæ€èšŒããŸãããã㯠bx-ai èªèº«ã® HttpGateway/GatewaySecurity (ç°ãªãããããŒåãç°ãªã HMAC æ§æ) ãšã¯æ¬è³ªçã«ç°ãªãæ¹åŒã§ãããããããã§ã¯åå©çšãããŠããŸãã - ã¯ã©ã¹èªèº«ã® docblock ãåç
§ããŠãã ããã
Hermes Agent's èªèº«ã®å®éã®æ¬çªçš WhatsApp Cloud ã¢ããã¿ (gateway/platforms/whatsapp_cloud.pyãMIT ã©ã€ã»ã³ã¹) ããçŽæ¥ç§»æ€ãããŠããŸã - verify ãã³ãã·ã§ã€ã¯ãçœ²åæ¹åŒãWebhook ãã€ããŒãã®èµ°æ» (entry[].changes[].value.{messages,contacts})ãéä¿¡ã¡ãã»ãŒãž/ã€ã³ã¿ã©ã¯ãã£ããã¿ã³ã®åœ¢ (èš±å¯ãããæ±ºå®ã 3 ã€ä»¥äžãªããã€ãã£ããªãã¿ã³ãšããŠã4 ã€ä»¥äžãªããã¿ããããŠéãããªã¹ããšããŠæç»ãããWhatsApp èªèº«ãææžåããŠããå¶éã«äžèŽããŸã)ããããŠé·ãã®å¶é (4096 æåã®ã¡ãã»ãŒãžã20 æåã®ãã¿ã³ã©ãã«ã1024 æåã®ã€ã³ã¿ã©ã¯ãã£ãæ¬æããã¹ã) ã¯ãä»åã®ã»ãã·ã§ã³ã§ãã®ãœãŒã¹ããçŽæ¥èªã¿åããããã®ã§ããŒãããåå®è£
ããããã®ã§ã¯ãããŸãããåä¿¡ã¡ãã»ãŒãžã¯èªèº«ã® wamid ã«ãã£ãŠéè€æé€ãããŸã (Meta 㯠200 以å€ã®ããããå¿çã«å¯ŸããŠãæå€§ 7 æ¥é Webhook é
ä¿¡ãå詊è¡ããŸã)ãå¢çã®ãã FIFO ãã£ãã·ã¥çµç±ã§ãHermes èªèº«ã® _dedup_wamid ãæš¡å£ããŠããŸãã
v1 ã®ã¹ã³ãŒãã¯ãHermes èªèº«ãææžåããŠããå¶éãšäžèŽããŠããŸããCloud API ã® DM ã«ã¯å¥åã®ããã£ããããšã³ãã£ãã£ããªã - chat_id ã¯éä¿¡è
ã® wa_id ãã®ãã®ã§ã - ã°ã«ãŒãã¡ãã»ãŒãž (èªèº«ã® chat ãã£ãŒã«ãã§ã°ã«ãŒãã® JID ãèå¥ãããã®) ã¯ã¹ã³ãŒãå€ã§ããã¡ãã£ã¢ (ç»å/åç»/ææž/é³å£°) ã¯ããŠã³ããŒãããããååšããå Žåã®ãã£ãã·ã§ã³ã®ã¿ãæ±ãããŸããä»ã®ãã¹ãŠã® push åã²ãŒããŠã§ã€ãšåæ§ã«ãäžèšã§è§£èª¬ãããã¿ã€ãããšã« 1 ã€ã³ã¹ã¿ã³ã¹ããšããã¬ãžã¹ããªã®äžéãå
±æããŠãããwhatsapp-cloud ãäŸå€ã§ã¯ãããŸããã
çæããã handlers/WhatsAppCloud.bx èªèº«ã® ColdBox ãªã¯ãšã¹ãã³ã³ããã¹ãåŒã³åºã (event.getHTTPContent()/event.getHTTPHeader()/event.renderData()ãGET ãã³ãã·ã§ã€ã¯çšã® rc ã® URL ã¹ã³ãŒãã«ããŒãžãããã¯ãšãªãã©ã¡ãŒã¿) ã¯ãææžåãããæšæºç㪠ColdBox REST ãã³ãã©ã®ã€ãã£ãªã ã§ã - ãããã(ä»åã®ã»ãã·ã§ã³ã§å®éã® HMAC/JSON ã®æåã«å¯ŸããŠååã«ãŠããããã¹ãããçµéšçã«æ€èšŒãããŠãã) ã²ãŒããŠã§ã€èªèº«ã®çœ²å/ãã£ã¹ãããããžãã¯ãšã¯ç°ãªãããã®ç¹å®ã®çæãããã«ãŒãé
ç·ã¯ãå®éã® ColdBox ã®èµ·åã«å¯ŸããŠã¯æ€èšŒãããŠããŸãããknown-limitations.md ãåç
§ããŠãã ããã
Microsoft Teams - Bot Framework Activity ãããã³ã«
TeamsGateway 㯠WhatsAppCloudGateway ãšåãæ¹æ³ã§ Webhook é§åã§ã - BaseGateway ãçŽæ¥ extends ããMicrosoft èªèº«ã® Bot Connector ãµãŒãã¹ãåäžã®çææžã¿ã«ãŒãçµç±ã§ç§ãã¡ãåŒã³åºããŸãã
post( "/webhooks/teams" ).toHandler( "Teams.process" )
WhatsApp Cloud ãšã¯ç°ãªã GET verify ãã³ãã·ã§ã€ã¯ã¯ãããŸãã (Bot Framework ã«ã¯ Meta ã® hub.challenge ã«çžåœãããã®ããããŸãã) - ãã¹ãŠã®åä¿¡ã¢ã¯ãã£ããã£ã¯ãããã£ã«å¯Ÿãã HMAC 眲åã§ã¯ãªããAuthorization ããããŒå
ã®ãã¢ã©ãŒ JWT ã«ãã£ãŠæ€èšŒãããçœ²åæžã¿ POST ãšããŠå±ããŸãããã® JWT 㯠Bot Connector èªèº«ã® JWKS (https://login.botframework.com/v1/.well-known/openidconfiguration â ãã® jwks_uri) ã«å¯ŸããŠãã§ãã¯ãããŸã - RS256 眲åãaud ã¯ãããèªèº«ã®èšå®æžã¿ appId ãšäžèŽããå¿
èŠããããiss 㯠Bot Connector ã®åºå®ãããçºè¡è
æåå (https://api.botframework.com) ãšäžèŽããå¿
èŠããããŸããã©ã¡ãã 5 åéã®ã¯ããã¯ã¹ãã¥ãŒèš±å®¹ä»ãã§ãããã㯠BoxLang èªèº«ã® Java çžäºéçš (java.security.Signatureãjava.security.KeyFactoryãjava.math.BigInteger) ããæ§ç¯ããããæ¬ç©ã® RSA/JWT æ€èšŒã§ã - å€éšã® JWT ã©ã€ãã©ãªã¯ãããŸãããéä¿¡åŒã³åºãã¯å¥åã® OAuth2 ã¯ã©ã€ã¢ã³ãã¯ã¬ãã³ã·ã£ã«ããŒã¯ã³ã䜿ããŸã (login.microsoftonline.com/{tenantId}/oauth2/v2.0/token ããååŸãããã£ãã·ã¥ãããèšèŒãããæå¹æéã® 60 ç§åã«åååŸãããŸã)ã
Vercel Eve's èªèº«ã®å®éã® Teams ãã£ãã« (packages/eve/src/public/channels/teams/ãMIT ã©ã€ã»ã³ã¹) ããç§»æ€ãããŠããŸã - OAuth2 ãããŒãJWT æ€èšŒæ¹åŒãv3/conversations/{id}/activities[/{activityId}] REST ã®äžç¹ã»ããããã㊠Adaptive Card ã«ãã human-in-the-loop ã®åœ¢ (schema 1.5ãèš±å¯ãããæ±ºå®ããšã« 1 ã€ã® Action.Submit ãã¿ã³) ã¯ããã¹ãŠãã®å®è£
ãåæ ããŠããŸããHermes Agent èªèº«ã® msgraph_webhook.py ã¯ç¡é¢ä¿ã§ãããMicrosoft Webhookããšãã䌌ãååã«ããããããããã㯠Microsoft Graph ã®å€æŽéç¥Webhook (ã¡ãŒã«ããã¯ã¹/ãã©ã€ã/ãªã¹ãã®ãªãœãŒã¹å€æŽã€ãã³ããšãããéä¿¡ Teams ã¡ãã»ãŒãžã³ã°ããŸã£ããåäœããªãå¥ã® Microsoft 補å衚é¢) ãå®è£
ãããã®ã§ãããããã«ã¯äœãç§»æ€ãããŠããŸããã
v1 ã®ã¹ã³ãŒãã¯å人 (1:1 DM) ã®äŒè©±ã®ã¿ã§ã - ã°ã«ãŒããã£ãããšãã£ã³ãã«å šäœãžã®ã¡ãã»ãŒãžã«ã¯ããããã®ã¡ã³ã·ã§ã³ã²ãŒãã£ã³ã°ãšãEve èªèº«ã¯å®è£ ããŠããããã®ç§»æ€ã«ã¯å«ãŸããªããå¥ã®ãªãã©ã€ã¹ã¬ããã£ã³ã°ã¢ãã«ãå¿ èŠã§ããä»ã®ãã¹ãŠã® push åã²ãŒããŠã§ã€èªèº«ã® DM ãã¡ãŒã¹ã㪠v1 ã¹ã³ãŒããšäžèŽããŸããUI ã®å¯èªæ§ã®ãããBot Framework ãããã³ã«ã®æ¬æ¥ã® 80 KiB äžéã§ã¯ãªãã4000 æåã®ã¡ãã»ãŒãžãã£ã³ã¯ã®äžéã䜿ãããŠããŸã (Eve èªèº«ã® Adaptive Card ããã¹ãåãè©°ã宿°ã§ã)ã
Bot Connector ã® JWKS ã¯äžåºŠååŸãããã²ãŒããŠã§ã€ã€ã³ã¹ã¿ã³ã¹ã®çåæéäžãã£ãã·ã¥ãããŸã - Microsoft ãããã§ã«ãã£ãã·ã¥ãããŠãã kid ãšäžèŽããªãç¶æ
ã§çœ²åéµãããŒããŒã·ã§ã³ããå Žåããã®ã²ãŒããŠã§ã€ (ã²ããŠã¯ã¢ããªå
šäœ) ãåèµ·åããããŸã§æ€èšŒã倱æãç¶ããŸããv1 ã§ã¯å®æçãªãã£ãã·ã¥ç¡å¹åã¯å®è£
ãããŠããŸãããJWT æ€èšŒããžãã¯èªäœã¯ãä»åã®ã»ãã·ã§ã³ã§ãå®éã«ããŒã«ã«ã§çæãã RSA éµãã¢ãšæã§çœ²åãããã¹ã JWT ã«å¯ŸããŠçµéšçã«æ€èšŒãããŠããŸã (æå¹ãªçœ²åã¯åçãããæ¹ããããã眲å/誀ã£ã audience/æéåãã®ããŒã¯ã³ã¯ãããã 401 ã§æåŠãããŸã) - Eve ã®ãœãŒã¹ãèªãã ã ãã§ã¯ãããŸããã
Twilio SMS - æ¬è³ªçã«ç°ãªãçœ²åæ¹åŒããããŠãã¥ã¢ã«ãã¹ã®ã¬ã¹ãã³ã¹ã¢ãã«
TwilioGateway 㯠WhatsAppCloudGateway/TeamsGateway ãšåãæ¹æ³ã§ Webhook é§åã§ãã
post( "/webhooks/twilio" ).toHandler( "Twilio.process" )
Twilio èªèº«ã® Webhook å¥çŽãããã®ãããžã§ã¯ãã®ä»ã®ããããã²ãŒããŠã§ã€ãšæ¬è³ªçã«ç°ãªããã®ã«ããŠããç¹ã 2 ã€ãããã©ã¡ãã Vercel Eve ã®å®éã® Twilio ãã£ãã« (packages/eve/src/public/channels/twilio/ãMIT ã©ã€ã»ã³ã¹) ããå¿ å®ã«ç§»æ€ãããŠããŸãã
- åä¿¡ããã£ã¯ form-urlencoded ã§ã (
BodyãFromãToãMessageSidãAccountSid)ãJSON ã§ã¯ãããŸãã -TwilioGatewayã¯ãããèªåèªèº«ã§ããŒã¹ããŸã (java.net.URLDecoder)ãJSON ã®ãã·ãªã¢ã©ã€ãºã¯é¢äžããŸããã - çœ²åæ€èšŒã¯
X-Twilio-Signature: HMAC-SHA1ãbase64 ãšã³ã³ãŒãã§ã (ãã®ãããžã§ã¯ãã®ä»ã®ãããã Webhook ã²ãŒããŠã§ã€ã¯ HMAC-SHA256ã16 é²ãšã³ã³ãŒãã䜿ã£ãŠããŸã) - 眲å察象æååã¯ãå®éã®ãªã¯ãšã¹ã URL ã«ç¶ããŠãããŒã§ã¢ã«ãã¡ãããé ã«ãœãŒãããããã¹ãŠã® POST ãã©ã¡ãŒã¿èªèº«ã®key & valueã (åºåãæåãªãã§) ãã®ãŸãŸé£çµãããã®ã§ããURL èªäœã眲å察象ã®äžéšã§ããããããªããŒã¹ãããã·ããã³ãã«ã®èåŸã§åããããžã§ã¯ã (ColdBox ãevent.getUrl()ã§èŠã URL ããTwilio ãå®éã« POST ããå ãšäžèŽããªãå Žå) ã«ã¯ãä»»æã®publicUrlconfig ãªãŒããŒã©ã€ããå¿ èŠã§ããEve èªèº«ã®ããã¥ã¡ã³ãããã®webhookUrlãªãã·ã§ã³ã«ã€ããŠææããŠããã®ãšåãçš®é¡ã®èœãšã穎ã§ãã - åæ Webhook ã®ã¬ã¹ãã³ã¹ã¯åžžã«ç©ºã® TwiML
<Response></Response>ã§ã - Twilio èªèº«ã®ã¯ã©ã·ãã¯ãªãã¥ã¢ã«ãã¹ã¢ãã«ã§ããå®éã®ãšãŒãžã§ã³ãã®è¿ä¿¡ã¯ãGatewaySession ã®éåæã¿ãŒã³ãå®äºããåŸãMessages API ãžã®å¥åã®deliver()REST åŒã³åºããéããŠãã¢ãŠããªããã³ãã§åŸããéãããŸããEve èªèº«ã®emptyTwilioResponse()ãšæ£ç¢ºã«äžèŽããŸã (Eve ã¯åæç㪠TwiML<Message>ã§ã€ã³ã©ã€ã³ã«å¿çããããšã¯æ±ºããŠãããŸãã)ã
éä¿¡ã¯ãBasic èªèšŒã® REST åŒã³åºãã§ POST /2010-04-01/Accounts/{AccountSid}/Messages.json ã«å¯ŸããŠè¡ãããform ãšã³ã³ãŒãããããã㣠(ToãBodyããããŠèšå®ãããŠããã° From ãŸã㯠MessagingServiceSid) ã§ããv1 㯠SMS ããã¹ãã®ã¿ã§ã - Eve èªèº«ã® Twilio ãã£ãã«ã¯ SMS+é³å£°ã®è€åãã£ãã«ã§ã (/voice ã«ãŒãã<Gather>/<Say> TwiMLãé話æåèµ·ãã)ãé³å£°é¢é£ã®éšåã¯äžåç§»æ€ãããŠããŸããã
SMS ã«ã¯ãã€ãã£ããªãã¿ã³/ã«ãŒãã®ææ®µããŸã£ãããããŸãã (Eve èªèº«ã®ããã¥ã¡ã³ãã§ç¢ºèªæžã¿)ããã®ãã human-in-the-loop 㯠Email ãšåãæ¹æ³ã§å£åããŠããŸã - getDeclaredCapabilities() 㯠"interactiveActions" ãçããŠããŸã (Twilio ã®ã¯ã©ã·ãã¯ãª Messages API ã«ã¯ãã€ãã£ããªè¿ä¿¡/åŒçšã®æŠå¿µããªããã "threads" ãçããŠããŸã)ãrequestHumanInteraction() ã¯èš±å¯ãããæ±ºå®ãåæãããã¬ãŒã³ããã¹ãã® SMS ãéããŸããEmail (æçµçãªè¿ä¿¡ãçŽä»ããããã« Subject è¡ã« [bxagents:<requestID>] ã¿ã°ãåã蟌ã¿ãŸã) ãšã¯ç°ãªããSMS ã«ã¯ã¿ã°ãä»ãããã Subject è¡ããããŸãã - ãã®ããä¿çäžã®ãªã¯ãšã¹ãã¯ãéä¿¡è
èªèº«ã®é»è©±çªå· (conversationID) ãããŒã«ããŸããããã¯ãäžåºŠã«é»è©±çªå·ãããæå€§ 1 ä»¶ã®æªåŠç HITL ãªã¯ãšã¹ããããªãããšãåæãšãã v1 ã®ç°¡ç¥åã§ãã
(ãœãŒã¹ãã°ã¬ããããŠç¢ºèªããéã) é·ãå¶éããžãã¯ããŸã£ããæããããã£ã±ã Twilio èªèº«ã®ãµãŒããŒåŽã»ã°ã¡ã³ããŒã·ã§ã³ã«é Œã£ãŠãã Eve ãšã¯ç°ãªããTwilioGateway ã¯ä»ã®ãã¹ãŠã®ã²ãŒããŠã§ã€ã®ãã£ã³ãã³ã°æåãšã®äžè²«æ§ã®ããã1600 æå (Twilio èªèº«ãææžåããŠããåäžã¡ãã»ãŒãžã®é£çµäžé) ã§ MessageChunker ãé©çšããŸããHMAC-SHA1 çœ²åæ¹åŒã¯ãä»åã®ã»ãã·ã§ã³ã§ãBoxLang ã®å®è£
ãä¿¡é Œããåã«ãç¬ç«ããŠèšç®ãã Python ã® hmac/hashlib ã®åç
§å€ãšç
§ããåãããŠæ€èšŒãããŠããŸããWhatsApp Cloud èªèº«ã® HMAC-SHA256 æ¹åŒãšåãèŠåŸã§ãã
GitHub - @mention ã«ãã£ãŠã²ãŒãããã issue/PR ã³ã¡ã³ãã¹ã¬ãã
GitHubGateway ã¯ãå issueãPRããããã¯ã€ã³ã©ã€ã³ã¬ãã¥ãŒã³ã¡ã³ãã¹ã¬ãããã1 ã€ã®ãã£ããäŒè©±ãšããŠæ±ããŸã - ãšãŒãžã§ã³ãã¯ãã³ã¡ã³ãå
ã§æç€ºçã« @ã¡ã³ã·ã§ã³ ãããå Žåã«å¿çããåãã¹ã¬ããã«æ°ããã³ã¡ã³ããæçš¿ããŠè¿ä¿¡ããŸãããã®ã»ã¯ã·ã§ã³ã®ä»ã®ããããã²ãŒããŠã§ã€ãšåãæ¹æ³ã§ Webhook é§åã§ãã
post( "/webhooks/github" ).toHandler( "GitHub.process" )
Vercel Eve ã®å®éã® GitHub ãã£ãã« (packages/eve/src/public/channels/github/ãMIT ã©ã€ã»ã³ã¹) ããç§»æ€ãããŠããŸã - X-Hub-Signature-256 ã®æ€èšŒã¯ãWhatsApp Cloud èªèº«ã® Meta æ¹åŒãšåäžã®æ§æã§ããããšã確èªãããŠããŸã (çããã£ã«å¯Ÿãã HMAC-SHA256ã16 é²ãsha256= ãã¬ãã£ãã¯ã¹) - ãã®ãããžã§ã¯ãã§å¯äžãç¬èªã®çœ²åã¢ã«ãŽãªãºã ãäžèŠã§ä»ã®ã²ãŒããŠã§ã€ã®æ¹åŒããã®ãŸãŸåå©çšããŠããã²ãŒããŠã§ã€ã§ããaction: "created" ã䌎ã issue_comment ãš pull_request_review_comment ã€ãã³ãã®ã¿ããã£ã¹ããããããŸã (Eve èªèº«ã®ããã©ã«ãã§åŠçãããã€ãã³ãçš®å¥ãšäžèŽããŠããŸã - issues/pull_request/check_suite/check_run/workflow_run 㯠Eve ã«ãããã©ã«ãã®ãã£ã¹ãããããªããããã«ãé
ç·ãããŠããŸãã)ããã以å€ã®ãã¹ãŠã®ã€ãã³ãçš®å¥ã¯ (200 ã§) 確èªå¿çãããŸããç¡èŠãããŸãããã®ã²ãŒããŠã§ã€ã察å¿ããªãã€ãã³ãã«å¯Ÿãã GitHub ã®å詊è¡/ããã¯ç¡å¹åæåãé¿ããããã§ãã
ãã£ã¹ãããã®ã²ãŒãã¯ãæ¬ç©ã® @mention èŠä»¶ã§ããEve èªèº«ã® extractGitHubCommentTrigger() ããç§»æ€ãããŠããŸã - ã³ã¡ã³ãã¯ã@<botName> ã«ç¶ããŠæååã®çµãããéèå¥åæåãããå Žåã«ã®ã¿ãšãŒãžã§ã³ãã«å±ããŸã (ãã®ãã mybot ãšããååã®ãããã @mybot2 ã«èšåããã³ã¡ã³ãã§çºç«ããããšã¯æ±ºããŠãããŸãã) - ããã¯ããããä¿¡é Œããåã«ãä»åã®ã»ãã·ã§ã³ã§å®éã®æ£èŠè¡šçŸå
èªã¿ã®ã¹ã¢ãŒã¯ãã¹ãã«ãã£ãŠç¢ºèªãããŠããŸããããããã @mention ããŒã¯ã³ã¯ãããã¹ãããšãŒãžã§ã³ãã«å±ãåã«åãé€ãããŸãããããã«ãŒãã®é²æ¢ã¯ Eve èªèº«ã® 3 éšæ§æã®ã¬ãŒããåæ ããŠããŸã - ã³ã¡ã³ãã®èè
ã GitHub èªèº«ã® type: "Bot" ãæã€ãããã°ã€ã³ã {botName}[bot] ã«äžèŽãããããããã¯æ¬æã«ãã®ã²ãŒããŠã§ã€èªèº«ã® <!-- bxagents:posted --> ããŒã«ãŒ (æçš¿ãããã¹ãŠã®ã³ã¡ã³ãã«ä»å ãããŸã) ãå«ãŸããå Žåãããšãã¡ã³ã·ã§ã³ãå«ãã§ããŠãç¡èŠãããŸãã
ãäŒè©±ãã¯ãEve èªèº«ã®ã¢ãã«ã«äžèŽãã 2 ã€ã®åœ¢ã®ããããã§èå¥ãããŸã: éåžžã® issue/PR ã³ã¡ã³ãã¹ã¬ãã㯠repo:{owner}/{repo}:issue:{issueNumber}ãã€ã³ã©ã€ã³ PR ã¬ãã¥ãŒã³ã¡ã³ãã¹ã¬ãã㯠repo:{owner}/{repo}:review-comment:{reviewThreadRootCommentId} ã§ã - ã¬ãã¥ãŒã¹ã¬ãããžã®è¿ä¿¡ã¯åžžã«ãè¿ä¿¡ãããŠããç¹å®ã®ã³ã¡ã³ãã§ã¯ãªãã¹ã¬ããã®ã«ãŒãã³ã¡ã³ã (comment.in_reply_to_id ?? comment.id) å®ãŠã«ãªããè€æ°ã¡ãã»ãŒãžã®ããåãã 1 ã€ã®ã¹ã¬ããã«ãŸãšãŸããŸããéä¿¡ã®è¿ä¿¡ã¯ repos/{owner}/{repo}/issues/{issueNumber}/comments (éåžžã®ã¹ã¬ãã) ãŸã㯠repos/{owner}/{repo}/pulls/{pullRequestNumber}/comments/{reviewCommentId}/replies (ã¬ãã¥ãŒã¹ã¬ãã) ã« POST ãããŸãã
v1 ã®èªèšŒã¯ãEve èªèº«ã® GitHub App JWT + ã€ã³ã¹ããŒã«ããŒã¯ã³ãããŒã§ã¯ãªãããã¬ãŒã³ãªããŒãœãã«ã¢ã¯ã»ã¹ããŒã¯ã³ (tokenEnvVar) ã§ã - ååå®è£
ãšããŠããã·ã³ãã«ã§çŽæ¥çã«ç§»æ€å¯èœã§ã (Eve èªäœããŸãã«ãã®çç±ã§äºå解決æžã¿ããŒã¯ã³ã®ãã€ãã¹ããµããŒãããŠããããããããã«å¯Ÿå¿ããŸã)ãå°æ¥ç㪠GitHub App ã¢ãŒãã¯èªç¶ãªæ¡åŒµã§ãããããã§ã¯å®è£
ãããŠããŸããã(ãœãŒã¹ãèªãã§ç¢ºèªããéã) é
ä¿¡ ID ã®éè€æé€ããŸã£ããæããªã Eve ãšã¯ç°ãªããGitHubGateway 㯠WhatsApp Cloud èªèº«ã® wamid éè€æé€ã®èŠåŸã«äžèŽãã圢ã§ãX-GitHub-Delivery ã«ãã£ãŠå¢çã®ãã FIFO ãã£ãã·ã¥çµç±ã§éè€æé€ããŸãã
ãªããžããªã®ãã§ãã¯ã¢ãŠã/ã³ãŒãç·šé (Eve èªèº«ã® checkout.ts ã§ããªããžããªããµã³ãããã¯ã¹ã«ã¯ããŒã³ããŠãšãŒãžã§ã³ããã³ãŒããèªã¿æžãã§ããããã«ãããã®) ã¯ç§»æ€ãããŠããŸãã - ããã¯ã³ã¡ã³ãã€ã³/ã³ã¡ã³ãã¢ãŠãã®ãã£ãã衚é¢ã®ã¿ã§ããHuman-in-the-loop 㯠Twilio ãšåãæ¹æ³ã§å£åããŠããŸã (ãã€ãã£ããªãã¿ã³/ã«ãŒãã®ææ®µããããŸãã) - requestHumanInteraction() ã¯ãèš±å¯ãããæ±ºå®ã®ãããããæ·»ããŠãè¿ä¿¡ã®äžã§åã³ãããã« @ã¡ã³ã·ã§ã³ ãããã人éã«æ±ããã³ã¡ã³ããæçš¿ããŸãã(ãªã¯ãšã¹ãããšã®ã¿ã°ã§ã¯ãªã) conversationID ã§çŽä»ããããŸããTwilio èªèº«ã® HITL ãã©ãŒã«ããã¯ãšåã v1 ã®ç°¡ç¥åã§ãã
"whatsapp-personal" ãšããã¿ã€ãã¯ãããŸããã éå
¬åŒã®å人ã¢ã«ãŠã³ãããªããž (WhatsApp ã®ãã«ãããã€ã¹ Web ãããã³ã«ã§ãHermes Agent ã Node.js/Baileys ãµãããã»ã¹çµç±ã§å°éããçš®é¡ã®ãã®) ã¯èª¿æ»ãããŸããããæå³çã«å®è£
ãããŸããã§ãã - MIT ã©ã€ã»ã³ã¹ã®ãã€ãã£ã Java ã®éžæè¢ã ã£ã Cobalt (com.github.auties00:cobalt) ã¯ãå®éã« Maven Central ã«å
¬éãããŠããããŒãžã§ã³ã§ã¯åçš/ãããã©ã€ãšã¿ãªãªäŸåé¢ä¿ (com.aspose:aspose-words) ãåŒã蟌ãããšã倿ãããµãããã»ã¹ããªããžã«ããç§»æ€ããã€ãã£ã JVM ã¢ãããŒããåªå
ããŠèŠéãããŸãããgateways/* ãšã³ããªã§ type: "whatsapp-personal" ã宣èšãããšãä»ã®ããããæªãµããŒãã¿ã€ããšåæ§ã«ãunknown typeãæ€èšŒãšã©ãŒã§å€±æããŸãã詳ãã調æ»ã®å
šå®¹ã¯ docs/known-limitations.md ãåç
§ããŠãã ããã
Signal - 4 ã€ç®ã®è»¢é圢åŒãå€éšã® signal-cli ããŒã¢ã³ã«å¯ŸããŠ
SignalGateway ã¯ãäžèšã® WhatsApp Cloud/Teams/Twilio/GitHub ã®ããã« Webhook é§åã§ããªããã°ãSlack/Discord ã®ãã㪠WebSocket ã§ããããŸãã - Telegram/Slack/Discord/Email ãšåãããã« ScheduledGatewayBase ã extends ããŸãããèªèº«ã®æ¥ç¶ã¯ãµãŒããŒéä¿¡ã€ãã³ãã§ããjava.net.http.HttpClient ã®éåæ API (sendAsync() + BodyHandlers.ofLines()) çµç±ã§ä¿æããããåäžã®é·æéæç¶ãã GET {httpUrl}/api/v1/events?account=... ãªã¯ãšã¹ãã§ãsignal-cli èªèº«ã®ããŒã¢ã³ãåãã¬ã¹ãã³ã¹ããã£ãéã㊠push ããŠãã 1 è¡ 1 JSON ã€ãã³ããèªã¿åããŸããéä¿¡ã¯ãã¬ãŒã³ãª JSON-RPC 2.0 ã§ã (POST {httpUrl}/api/v1/rpcã{"jsonrpc":"2.0","method":"send","params":{...},"id":...})ãåãããŒã¢ã³ã«å¯ŸããŠè¡ãããŸãã
å
¬åŒã® Signal ããã API ã¯ååšããŸãã - SignalGateway ã¯ãsignal-cli ãèªèº«ã® daemon --http ã¢ãŒãã§åããŠãããã®ãšå®å
šã«éä¿¡ããŸããããã¯ãã®ã²ãŒããŠã§ã€ãäŸåããŠãããã®ã®ãèªèº«ã§ã¯ç®¡çããªãå€éšã®åææ¡ä»¶ã§ãããEmailGateway ãå€éšã® IMAP/SMTP ãµãŒããŒãšæã€é¢ä¿ãšåãã§ããHermes Agent's èªèº«ã®å®éã® Signal ãã£ãã«ããç§»æ€ãããŠããŸã - SSE/JSON-RPC ã®ã¯ã€ã€ãŒåœ¢åŒã忥ç¶ã®ããã¯ãªã宿° (2 ç§ãã 60 ç§ãžã®ææ°é¢æ°çå¢å ã+20% ã®ãžãã¿ãŒ)ããã㊠30 ç§/120 ç§ã®ã¢ã€ãã«ãŠã©ããããã°ã¯ããã¹ãŠãã®ãœãŒã¹ããçŽæ¥èªã¿åããããã®ã§ããŒãããåå®è£
ããããã®ã§ã¯ãããŸããã
åäœãã signal-cli ããŒã¢ã³ãçšæããããšã¯ããã®ãããžã§ã¯ãã®å€åŽã«ãããå®éã®ãæåã®ãäžåºŠããã®ã»ããã¢ããäœæ¥ã§ã: signal-cli ãã€ã³ã¹ããŒã«ããå®éã® Signal ã¢ã«ãŠã³ãã«ç»é²/ãªã³ã¯ã (signal-cli link ãŸã㯠registerãã©ã¡ããå®éã®é»è©±çªå·ãšããã€ã¹ãªã³ã¯ã® QR/æ€èšŒã¹ããããå¿
èŠã§ã)ãsignal-cli -a <account> daemon --http=127.0.0.1:8080 ãå®è¡ãããã®ããã»ã¹ã皌åããç¶ãã (systemd ãµãŒãã¹ãã³ã³ããã®ãµã€ãã«ãŒã§ãããbxAgents serve ãããªãã®ããã«èµ·åããŠããããã®ã§ã¯ãããŸãã) å¿
èŠããããŸããSignalGateway èªèº«ã® onConnect() 㯠account ãèšå®ãããŠããªããã° MissingConfig ã§å€§ãã倱æããŸãããããŒã¢ã³èªäœãæ€åºãããèµ·åãããããããšã¯ã§ããŸãã - æ¥ç¶æç¹ã§ httpUrl ã«å°éã§ããªãå ŽåãçŽ æ©ã倱æã§ã¯ãªãéåžžã®åæ¥ç¶ããã¯ãªããµã€ã¯ã«ãšããŠè¡šé¢åããŸãã
v1 ã¯DM ã®ã¿ã§ã - Hermes èªèº«ã® Signal ãã£ãã«ã¯ã°ã«ãŒãäŒè©±ãããã©ã«ãã§ãªããã€ã³/ãªããšããŠæ±ã£ãŠãããããã§ã¯ãã®ã¢ãŒãã®ã¿ãç§»æ€ãããŠããŸããHuman-in-the-loop 㯠Twilio/GitHub ã®ãã©ãŒã«ããã¯ãšåãæ¹æ³ã§å£åããŠããŸã (getDeclaredCapabilities() 㯠"interactiveActions" ãçããŠããŸã) - Signal ã®æ¢èª/ãªã¢ã¯ã·ã§ã³ã¯ signal-cli èªèº«ã® API ã§ã¯æžã蟌ã¿å°çšã®èŠãç®äžã®ã¹ããŒã¿ã¹ã§ãããæ¬ç©ã®åçãã£ãã«ã§ã¯ãªããããrequestHumanInteraction() 㯠Twilio èªèº«ã®é»è©±çªå·ããŒæ¹åŒã®ãã©ãŒã«ããã¯ã®ããã«ãconversationID ã§çŽä»ãããããã¬ãŒã³ããã¹ãã¡ãã»ãŒãžã«ãã©ãŒã«ããã¯ããŸããJSON-RPC/SSE ã®ããŒã¹ããžã㯠(handleSseEvent()ãåŒçšã¹ã¬ããã£ã³ã°ãã°ã«ãŒãã¡ãã»ãŒãžã®ãã£ã«ã¿ãªã³ã°ãHITL 決å®ã®ãããã³ã°) ã¯ãæãå€åŽã® rpcCaller/connector ã® I/O åŒã³åºãã ããã¹ã¿ãåããç¶æ
ã§ãå®éã®å
¬éã¡ãœãããéããŠé§åãããŸãããä»ã®ããããã²ãŒããŠã§ã€ãšåãã·ãŒã ãã¹ãã®èŠåŸã§ã - ããããã®ç°å¢ã§ã¯å®éã® signal-cli ããŒã¢ã³ãå©çšã§ããªãã£ããããå®éã®éåææ¥ç¶ã®ã©ã€ããµã€ã¯ã« (SSE ã¹ããªãŒã ãéãããšãæ¬åœã«äžå®å®ãªæ¥ç¶ã«å¯Ÿãã忥ç¶ããã¯ãªãã«ãŒããã©ã€ããªããŒã¢ã³ã«å¯Ÿãã JSON-RPC ã®ã©ãŠã³ãããªãã) ã¯ãšã³ãããŒãšã³ãã§ã¯äžåºŠãæŒç¿ãããŠããŸãããçžäºéçšã®ãã©ã¢ãŒãã³ã°ã¬ãã«ã§ã®ã¿ã¹ã¢ãŒã¯ãã¹ããããŠããŸããjava.net.http.HttpClient ã®çžäºéçšãã§ãŒã³èªäœã¯å¥å
šã§ããããšã確èªãããŠããŸã - ã¹ã¿ã³ãã¢ãã³ã®ã¹ã¢ãŒã¯ãã¹ãããå°éäžèœãªãã¹ãã¢ãã¬ã¹ã«å¯ŸããŠå®éã®ãããã¯ãŒã¯å¢çã§æ¬ç©ã® java.net.ConnectException ã«å°éããã©ã€ããªããŒã¢ã³ã«è§Šããããšã¯äžåºŠããªããã®ã®ãé
ç·ãæ©èœããããšã蚌æããŠããŸãã
GatewaySession - wiring the agent to every push-style gateway
å°ãªããšã 1 ã€ã® push åã²ãŒããŠã§ã€ãšã³ããªãæã€ãããžã§ã¯ãã¯ãçæããã interceptors/GatewaySessionBootstrap.bx ãåŸãŸããããã¯ãããžã§ã¯ãå
ã®ãã¹ãŠã® push åã²ãŒããŠã§ã€ããŸãšããåäžã® bx-ai GatewaySession ãæ§ç¯ãããããžã§ã¯ãã®ã«ãŒããšãŒãžã§ã³ãã«æçžããColdBox èªèº«ã®ããŒããå®äºããæç¹ã§äžåºŠã ãèµ·åããŸãã
// interceptors/GatewaySessionBootstrap.bx (GENERATED)
class {
function afterConfigurationLoad( event, interceptData ) {
var wirebox = getController().getWireBox()
var agent = wirebox.getInstance( "GeneratedAgent" )
var gatewaySession = aiGatewaySession(
agent : agent,
gateways : [ aiGatewayRegistry().get( "telegram" ) ],
policy : "queue",
maxQueueDepth: 50
)
gatewaySession.start()
application.bxaiGatewaySession = gatewaySession
}
}
çæããã倿°ã¯æå³çã« session ã§ã¯ãªã gatewaySession ãšããååã«ãªã£ãŠããŸã - session 㯠(request/server/url/form/cgi/thread ãšåæ§ã«) äºçŽããã BoxLang/ColdBox ã®ã¹ã³ãŒãåã§ããããããã®ããããã®ååãåå©çšããããŒã«ã«å€æ°ã¯ãéåžžã®ããŒã«ã«ãšããŠæ¯ãèã代ããã«ã©ã€ããªã¹ã³ãŒããšè¡çªããå¯èœæ§ããããŸãã
aiGatewayRegistry().get(...) ã®ããŒã¯åžžã«ã²ãŒããŠã§ã€ã® TYPE æåå ("telegram"ã"slack"ã"discord"ã"email" ãªã©) ã§ã - ãã㯠bx-ai ã®å®éã® GatewayRegistry.register() ãœãŒã¹ã«ç
§ãããŠç¢ºèªãããŠãããåžžã«ã²ãŒããŠã§ã€ã¯ã©ã¹èªèº«ã®åºå®ããã getName() ã§ããŒä»ããããåŒã³åºãå
ãæå®ãããã®ã¯äžå䜿ãããŸãããããããå®éã«å°ãããåž°çµ: åã push åã¿ã€ãã® gateways/* ãšã³ããªã 2 ã€ãããšããããžã§ã¯ãå
šäœã§åãã¬ãžã¹ããªã¹ãããã«è¡çªããŸã - 2 ã€ç®ã®ç»é²ãæåã®ç»é²ããµã€ã¬ã³ãã«äžæžãããŸãã仿¥æç¹ã§ã¯ãšã³ããªããšã®ãšã€ãªã¢ã¹ã¯ãããŸãã - ãã©ãããã©ãŒã ã¢ã«ãŠã³ãã远å ããããšã«ç°ãªãã¿ã€ãã䜿ãããè€æ°ã€ã³ã¹ã¿ã³ã¹ã®ãµããŒããåŸ
ã£ãŠãã ããã
GatewaySession ã®ããªã·ãŒã¯ãã«ãŒããããžã§ã¯ãã® Agent.bx äžã®ä»»æã® gatewaySession ãããã¯ã§å¶åŸ¡ã§ããŸãã
// Agent.bx
class extends="bxModules.bxai.models.runnables.AiAgent" {
function init() {
super.init( name: "...", model: aiModel( provider: "..." ) )
return this
}
function configure() {
return {
gatewaySession: { policy: "queue", maxQueueDepth: 50 } // both optional - these are the defaults
};
}
}
policy 㯠reject/queue/steer/interrupt ã®ããããã§ããå¿
èŠããããŸã (bx-ai èªèº«ã® GatewaySession ããªã·ãŒã®èªåœã§ã - äžã® GatewaySession ãåç
§) - ãã㯠build æã«ãã§ãã¯ããããããã¿ã€ããã¹ã¯ã¢ããªãèµ·åããéã®ã©ã³ã¿ã€ã ãšã©ãŒãšããŠè¡šé¢åããã®ã§ã¯ãªãã倧ãã倱æããŸãã
v1 ã®å¶é: GatewaySession ã¯åžžã«ã¡ããã© 1 ã€ã§ãåžžã«ãããžã§ã¯ãã®ã«ãŒããšãŒãžã§ã³ãã«æçžãããŸã - exposes: "agent" HTTP å
¬éãåžžã«ã«ãŒããšãŒãžã§ã³ãã®ã¿ã§ãããšããæ¢åã®åäŸãšäžèŽããŠããŸãããµããšãŒãžã§ã³ããæã€ãããžã§ã¯ãã¯ããŸã ç°ãªãã²ãŒããŠã§ã€ãç°ãªããµããšãŒãžã§ã³ãã«ã«ãŒãã£ã³ã°ããããšã¯ã§ããŸããã
åããªã·ãŒããã¿ãŒã³ããŸã å®è¡äžã«å±ããã¡ãã»ãŒãžã«å¯ŸããŠå®éã«äœãããã:
flowchart TD
M["a message arrives on thread T"] --> B{"is a run already<br/>in flight on T?"}
B -->|"no"| D["dispatch a new turn.<br/>The reply streams back through<br/>the gateway the message came from."]
B -->|"yes"| P{"policy"}
P -->|"reject"| R["Immediate 'busy' reply.<br/>Nothing is queued - the sender must resend."]
P -->|"queue<br/>(the default)"| Q["Enqueue, up to maxQueueDepth.<br/>Runs as its own turn once<br/>the current one finishes."]
P -->|"steer"| ST["agent.steerRun( T, text )<br/>Spliced into the SAME run at its next<br/>checkpoint - never a second turn."]
P -->|"interrupt"| I["agent.cancelRun( T ), AND enqueue.<br/>The current turn winds down at its next<br/>checkpoint, then this message runs."]
Q --> OVER{"queue already at<br/>maxQueueDepth?"}
I --> OVER
OVER -->|"yes"| R
style D fill:#d4edda,stroke:#155724
style R fill:#f8d7da,stroke:#721c24
ããã§ã®ãsteer (æèµ)ãã¯ãHermes Agent ã®éç Žå£çãªã¹ãã©ã€ã¹ãæå³ããŸã - å®è¡äžã®ã¿ãŒã³ã¯ãã®ãŸãŸé²ã¿ç¶ããæ°ããããã¹ãã¯ãã®äžã«æã蟌ãŸããŸãããã㯠Eve ã® turnPolicy: "steer" ãæå³ãããã® (ã¢ã¯ãã£ããªã¿ãŒã³ããã£ã³ã»ã«ããŠçœ®ãæããéå§ãã) ãšã¯ç°ãªããŸãããã®æåã¯ããã®èªåœã§ã¯ interrupt ã«çžåœããŸãã
cancelRun() ã steerRun() ãå³åº§ã«ã¯å¹ããŸãããã©ã¡ããã·ã°ãã«ãšããŠéããããã®ã©ã³ã®æ¬¡ã®ãã§ãã¯ãã€ã³ã (次㮠LLM åŒã³åºããããŒã«åŒã³åºãã®å) ã§å¹æãçºæ®ããŸãããããã£ãŠ interrupt ã¯ãçŸåšã®ã¿ãŒã³ãéããã«çµããããããäŸé Œãããããšã§ããããåæçã«çœ®ãæãããããšã§ã¯ãããŸããã
push åã²ãŒããŠã§ã€ã¯ã©ããã£ãŠæ¥ç¶ãç¶æããã®ã: å ±æããã ColdBox ã¹ã±ãžã¥ãŒã©
æ°ããããã¯ã°ã©ãŠã³ãã«ãŒãã®ããªããã£ããçšæããã®ã§ã¯ãªããpush åã²ãŒããŠã§ã€ã¯ã¢ããªèªèº«ã®ã©ã€ã㪠ColdBox ã¹ã±ãžã¥ãŒã©ã·ã³ã°ã«ãã³ (appScheduler@coldbox - ãããžã§ã¯ãã«ææžãã® schedules/Scheduler.bx ãããã°ããããåããŠããã®ãšåããã®) ã«å°éããããã«èªèº«ã®ååä»ãã¿ã¹ã¯ãåçã«ç»é²ããŸã - äŸãã° Telegram åãã®å®æçãªãã³ã°ããŒãªã³ã°ã¿ã¹ã¯ã§ãã1 ã€ã®å
±æã¹ã±ãžã¥ãŒã©ã«ããã¹ãŠã® push åã²ãŒããŠã§ã€ãããããèªèº«ã®ã¿ã¹ã¯ãç»é²ããŸã - ã²ãŒããŠã§ã€ããšã« 1 ã€ã®ã¹ã±ãžã¥ãŒã©ãæã€ããšã¯æ±ºããŠãªãããããžã§ã¯ãèªèº«ã® cron ãžã§ããšè¡çªããããšããããŸããã
ãã®ã³ã°
ãã¹ãŠã® push åã²ãŒããŠã§ã€ã¯ã1 ã€ã®å
±æ/ããã©ã«ãã®ã¢ããªãã°ã«ã§ã¯ãªããBoxLang ã® writeLog() ãä»ããŠèªèº«ã® gateway-<type> ãã°ãã¡ã€ã« (äŸãã° gateway-telegram) ã«æžã蟌ã¿ãŸã - ãã®ããããªãã¬ãŒã¿ãŒã¯èªåãæ°ã«ãããã©ãããã©ãŒã ã ãããã¢ããªãèšé²ããä»ã®ãã¹ãŠã®ãã€ãºãªãã« tail ã§ããŸãã