自社サービスにAIチャットボット機能を導入した話
はじめに
顧客管理/マーケティングを目的とする、LINE拡張ツールを開発しております、@mitsuyashiです。
2025年11月、LINEにAIチャットボット機能がリリースされました。
顧客応答をAIがチャットで行ってくれるというものです。
この発表より先んじて開発を行っていましたが、リリースの先を越される形になりました。
12月中にリリースの目処が立ったため(記事公開時にはリリース済みの予定)、技術的な工夫や苦労について語れたらと思います。
LINE拡張ツールとは
LINE公式アカウントを運用する方法は大きく3つあります。
- OAM(Official Account Manager):LINE社提供の無料管理画面
- Messaging API:開発者向けAPI(自社開発が必要)
- LINE拡張ツール:SaaSサービス
本記事のサービスは「LINE拡張ツール」に分類されます。OAMの標準機能では足りない企業が、開発コストをかけずに高度な顧客管理・マーケティングを実現するためのツールです。
OAMとの大きな違いとして、複数店舗管理、セグメント機能を強みとしています。
本題

実際の応答イメージ
AIサービス選定
実装にあたり使用するAIサービスとして、OpenAI APIを採用しました。
理由としては大きく2つです。
一つは社内で導入実績があったこと。
もう一つは、シンプルなチャットボットなので、他の候補と比べた際に代表的なOpenAIで申し分ないと判断したためです。
OpenAI APIの機能の中でも、実装にはAssistants APIを使用しました。
通常のチャットと違い、カスタムエージェントの作成に特化しており、
- ユーザーのコンテキストスレッドをOpenAIで保持してくれる
- 毎回プロンプトを投げる必要がない
などの特徴があります。
参考記事
設計
サービスのアーキテクチャとして、共通で使用する部分は単独のライブラリとして管理しています。
その共通領域の抽象化に力を入れて実装しました。
OpenAIの機能が、AssistantsからResponsesへの過渡期だったのと、これから先、他のサービスを使う可能性があると考えたためです。実際にAssistantsは非推奨となったため、今後、Responses APIへの移行を予定しています
コードサンプル
export class OpenAPIProvider implements AIProvider {
public readonly provider = 'openai' as const;
public readonly name = 'OpenAI';
private client: OpenAI;
private config: AIProviderConfig;
constructor(config: AIProviderConfig) {
this.config = config;
this.client = new OpenAI({
apiKey: config.apiKey,
baseURL: config.baseUrl,
timeout: config.timeout || 30000,
});
}
public async createAssistant(request: AssistantCreateParams): Promise<Assistant> {
try {
if (!this.isConfigured()) {
throw new AIError('OpenAI API key is not configured', 'MISSING_API_KEY', this.name);
}
const assistant = await this.client.beta.assistants.create(request);
return assistant;
} catch (error) {
if (error instanceof AIError) {
throw error;
}
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
throw new AIError(
`CreateAssistant API error: ${errorMessage}`,
'API_ERROR',
this.name,
error instanceof Error ? error : undefined
);
}
}
// 省略 ...
public async updateAssistant(assistantId: string, request: AssistantUpdateParams){}
public async deleteAssistant(assistantId: string): Promise<{ id: string; object: string; deleted: boolean }> {}
public async createThread(): Promise<Thread> {}
// ......
}
工夫した点
Assistantの取得をサービス内で行わないようにしました。
一つのAPIKeyを使っているため、万が一AssistantsのIDが流出した場合に、他のアカウントの情報が取得できる可能性があるためです。
具体的な実装としては、OpenAIの情報はAssistantsのIDのみを保存し、プロンプトはサービス側で保持し、上書きのみできるようにしています。
この設計により、万が一AssistantsのIDが流出しても、プロンプト情報自体は守られるため、被害を最小限に抑えられます。
また、弊サービスでプロンプトを保持するメリットとして
- API Keyが変更となった際や、AIサービスが変わった場合の移行がスムーズ
- OpenAIと弊サービスで同期が取れないことが起こらない
などがあります。
図解イメージ
苦労した点
インフラ面で既存システムの知識が足りておらず、キャッチアップに時間がかかりました。
特に時間がかかった点が、LINEへのメッセージ送信の処理部分です。
複数人でLINEにチャットを送り、何人かに応答されない事象が発生しました。
これは弊サービス特有のインフラの事情が関係していました。
既存の機能として、ユーザーからチャットが送られた際に返信をする「応答メッセージ」機能があります。
「営業時間」が含まれていたら -> 営業時間を返す
「駐車場」が含まれていたら -> 駐車場の案内を返す
この処理はバッチ処理とキューシステムの組み合わせで実行されていました。
バッチ処理は10人ずつ処理するシステムで、タイムアウトが30秒でした。
AI応答の場合、そのタイムアウトに引っ掛かり、タイムアウト以降の人は応答されない現象が起きました。
解決策としては別のLambdaにAI処理を切り出し、非同期で実行することにより、タイムアウトを機にする必要がなくなりました。
おわりに
AI機能は敷居が高いと思われがちですが、コンテキストの管理を含め、公式で用意されているものだけで色々できます。皆さんも試してみてください。
Discussion