---
title: カスタマーサポートエージェント
url: https://platform.claude.com/docs/ja/about-claude/use-case-guides/customer-support-chat
description: 製品に関する質問に答え、話題から逸れず、ツール使用を通じて見積もりを生成するカスタマーサポートチャットボットをClaudeで構築します。
---

## 前提条件

このガイドに従うには、以下が必要です。

* Claude APIキー（`ANTHROPIC_API_KEY` 環境変数として設定）
* Python 3.10以降

必要なパッケージをインストールします。

```bash
pip install anthropic streamlit python-dotenv
```

## Claudeで構築する前に

### サポートチャットにClaudeを使用するかどうかを決定する

カスタマーサポートプロセスの一部を自動化するために、ClaudeのようなLLM（「large language model」（大規模言語モデル）、すなわちLLM）を採用すべきであることを示す主な指標を以下に示します。

<AccordionGroup>
  <Accordion title="大量の反復的な問い合わせ">
    Claudeは多数の類似した質問を効率的に処理することに優れており、人間のエージェントをより複雑な問題に専念させることができます。
  </Accordion>

  <Accordion title="迅速な情報統合の必要性">
    Claudeは膨大なナレッジベースから情報を迅速に取得、処理、統合できますが、人間のエージェントは調査や複数の情報源の参照に時間を要する場合があります。
  </Accordion>

  <Accordion title="24時間365日の対応要件">
    Claudeは疲労することなく24時間体制のサポートを提供できますが、継続的な対応のために人間のエージェントを配置することはコストがかかり困難な場合があります。
  </Accordion>

  <Accordion title="ピーク時の迅速なスケーリング">
    Claudeは、追加のスタッフを雇用してトレーニングする必要なく、問い合わせ量の急増に対応できます。
  </Accordion>

  <Accordion title="一貫したブランドボイス">
    Claudeにはブランドのトーンと価値観を一貫して表現するよう指示できますが、人間のエージェントはコミュニケーションスタイルにばらつきが生じる場合があります。
  </Accordion>
</AccordionGroup>

他のLLMではなくClaudeを選択する際の考慮事項をいくつか示します。

* 自然でニュアンスのある会話を優先する場合：Claudeの高度な言語理解により、他のLLMとのチャットよりも人間らしく感じられる、より自然でコンテキストを意識した会話が可能になります。
* 複雑でオープンエンドな問い合わせを頻繁に受ける場合：Claudeは、定型的な応答を生成したり、ユーザー発話のあらゆるパターンを広範にプログラミングしたりする必要なく、幅広いトピックや問い合わせに対応できます。
* スケーラブルな多言語サポートが必要な場合：Claudeの多言語機能により、サポート対象の言語ごとに個別のチャットボットや大規模な翻訳プロセスを用意する必要なく、200以上の言語で会話を行うことができます。

### 理想的なチャットインタラクションを定義する

顧客がいつどのようにClaudeとやり取りすることを期待するかを定義するために、理想的な顧客インタラクションの概要を作成します。この概要は、ソリューションの技術要件を決定するのに役立ちます。

自動車保険のカスタマーサポートにおけるチャットインタラクションの例を以下に示します。

* **顧客：** サポートチャットを開始する
  * **Claude：** 顧客に温かく挨拶し、会話を開始する

* **顧客：** 新しい電気自動車の保険について質問する
  * **Claude：** 電気自動車の補償に関する関連情報を提供する

* **顧客：** 電気自動車保険特有のニーズに関する質問をする
  * **Claude：** 正確で有益な回答を返し、情報源へのリンクを提供する

* **顧客：** 保険や自動車とは無関係な話題外の質問をする
  * **Claude：** 無関係なトピックについては話さないことを明確にし、ユーザーを自動車保険の話題に戻す

* **顧客：** 保険の見積もりに関心を示す

  * **Claude：** 適切な見積もりを決定するための一連の質問を行い、顧客の回答に応じて適応する
  * **Claude：** ユーザーから収集した必要な情報とともに、見積もり生成APIツールを使用するリクエストを送信する
  * **Claude：** APIツール使用からの応答情報を受け取り、その情報を自然な応答に統合して、提供された見積もりをユーザーに提示する

* **顧客：** フォローアップの質問をする

  * **Claude：** 必要に応じてフォローアップの質問に答える
  * **Claude：** 保険手続きの次のステップへ顧客を案内し、会話を締めくくる

<Tip>
  ご自身のユースケース向けに実際の例を作成する際には、このインタラクションにおける実際の言葉を書き出してみると役立つ場合があります。そうすることで、Claudeに持たせたい理想的なトーン、応答の長さ、詳細度の感覚もつかむことができます。
</Tip>

### インタラクションを個別のタスクに分解する

カスタマーサポートチャットは、質問への回答から情報検索、リクエストに対するアクションの実行まで、複数の異なるタスクが単一の顧客インタラクションにまとめられたものです。構築を始める前に、理想的な顧客インタラクションを、Claudeに実行させたいすべてのタスクに分解してください。これにより、すべてのタスクについてClaudeにプロンプトを与えて評価できるようになり、テストケースを作成する際に考慮すべきインタラクションの範囲を十分に把握できます。

<Tip>
  お客様によっては、ユーザーのリクエストに応じて会話が分岐しうるポイントを示すインタラクションフローチャートとして視覚化すると役立つと感じる場合があります。
</Tip>

保険のインタラクション例に関連する主なタスクを以下に示します。

1. 挨拶と一般的な案内

   * 顧客に温かく挨拶し、会話を開始する
   * 会社とインタラクションに関する一般的な情報を提供する

2. 製品情報

   * 電気自動車の補償に関する情報を提供する
     <Note>
       これにはClaudeがコンテキスト内に必要な情報を持っている必要があり、

       [RAG統合](https://platform.claude.com/cookbook/capabilities-retrieval-augmented-generation-guide)

       が必要になる可能性があります。
     </Note>
   * 電気自動車保険特有のニーズに関する質問に答える
   * 見積もりや保険の詳細に関するフォローアップの質問に答える
   * 適切な場合に情報源へのリンクを提供する

3. 会話管理

   * 話題（自動車保険）から逸れない
   * 話題外の質問を関連するテーマに戻す

4. 見積もり生成

   * 見積もりの適格性を判断するための適切な質問をする
   * 顧客の回答に基づいて質問を適応させる
   * 収集した情報を見積もり生成APIに送信する
   * 提供された見積もりを顧客に提示する

### 成功基準を確立する

サポートチームと協力して、測定可能なベンチマークと目標を備えた[成功基準を定義し、詳細な評価を作成](https://platform.claude.com/docs/ja/test-and-evaluate/develop-tests)してください。

定義されたタスクをClaudeがどの程度うまく実行するかを評価するために使用できる基準とベンチマークを以下に示します。

<AccordionGroup>
  <Accordion title="問い合わせ理解の正確性">
    この指標は、さまざまなトピックにわたる顧客の問い合わせをClaudeがどれだけ正確に理解しているかを評価します。会話のサンプルをレビューし、Claudeが顧客の意図、重要な次のステップ、成功した解決の姿などを正しく解釈しているかどうかを評価することで測定します。95%以上の理解精度を目指してください。
  </Accordion>

  <Accordion title="応答の関連性">
    これは、Claudeの応答が顧客の具体的な質問や問題にどれだけ適切に対処しているかを評価します。一連の会話を評価し、各応答の関連性を採点します（規模に対応するためLLMベースの採点を使用）。90%以上の関連性スコアを目標にしてください。
  </Accordion>

  <Accordion title="応答の正確性">
    コンテキスト内でClaudeに提供された情報に基づいて、ユーザーに提供される会社および製品の一般情報の正確さを評価します。この導入的な情報については100%の正確性を目標にしてください。
  </Accordion>

  <Accordion title="引用提供の関連性">
    提供されるリンクや情報源の頻度と関連性を追跡します。追加情報が有益となりうるインタラクションの80%で関連する情報源を提供することを目標にしてください。
  </Accordion>

  <Accordion title="トピックの遵守">
    実装例における自動車保険のトピックのように、Claudeがどれだけ話題から逸れないかを測定します。応答の95%が自動車保険または顧客の具体的な問い合わせに直接関連していることを目指してください。
  </Accordion>

  <Accordion title="コンテンツ生成の有効性">
    情報コンテンツをいつ生成すべきかをClaudeがどれだけうまく判断できるか、またそのコンテンツがどれだけ関連性があるかを測定します。たとえば、この実装では、Claudeがいつ見積もりを生成すべきかをどれだけ理解しているか、そしてその見積もりがどれだけ正確かを判断することになります。これは顧客インタラクションの成功に不可欠な情報であるため、100%の正確性を目標にしてください。
  </Accordion>

  <Accordion title="エスカレーションの効率性">
    これは、問い合わせに人間の介入が必要な場合をClaudeが認識し、適切にエスカレーションする能力を測定します。正しくエスカレーションされた会話の割合と、エスカレーションすべきだったのにされなかった会話の割合を追跡します。95%以上のエスカレーション精度を目指してください。
  </Accordion>
</AccordionGroup>

サポートにClaudeを採用することによるビジネスへの影響を評価するために使用できる基準とベンチマークを以下に示します。

<AccordionGroup>
  <Accordion title="センチメントの維持">
    これは、会話全体を通じて顧客のセンチメントを維持または改善するClaudeの能力を評価します。センチメント分析ツールを使用して、各会話の開始時と終了時のセンチメントを測定します。インタラクションの90%でセンチメントが維持または改善されることを目指してください。
  </Accordion>

  <Accordion title="デフレクション率">
    人間の介入なしにチャットボットが正常に処理した顧客問い合わせの割合です。問い合わせの複雑さに応じて、通常は70〜80%のデフレクション率を目指します。
  </Accordion>

  <Accordion title="顧客満足度スコア">
    チャットボットとのインタラクションに顧客がどれだけ満足しているかの指標です。通常はインタラクション後のアンケートを通じて測定します。5点満点中4点以上のCSATスコアを目指してください。
  </Accordion>

  <Accordion title="平均処理時間">
    チャットボットが問い合わせを解決するのにかかる平均時間です。これは問題の複雑さによって大きく異なりますが、一般的には人間のエージェントと比較してより低いAHTを目指してください。
  </Accordion>
</AccordionGroup>

## Claudeをカスタマーサービスエージェントとして実装する方法

### 適切なClaudeモデルを選択する

モデルの選択は、コスト、正確性、応答時間の間のトレードオフによって決まります。

カスタマーサポートチャットには、Claude Opus 5.5が知能、「latency」（レイテンシ）、コストのバランスを取るのに適しており、長く複数ステップにわたる会話全体で深い推論を必要とする最も複雑なサポートシナリオにも対応できます。ただし、RAG、ツール使用、または長いコンテキストのプロンプトを含む複数のプロンプトで構成される会話フローがある場合は、レイテンシを最適化するためにClaude Haiku 5.5の方が適している場合があります。Claude Haiku 5.5では、[エフォート](https://platform.claude.com/docs/ja/build-with-claude/effort)が速度を制御する主な手段です。`low`は最も高速なレベルで、チャットや短いツールタスクに適しています。

### 強力なプロンプトを構築する

カスタマーサポートにClaudeを使用するには、Claudeが適切に応答するための十分な指示とコンテキストを持ちつつ、幅広い顧客の問い合わせに対応できる十分な柔軟性を持っている必要があります。

まず、「system prompt」（システムプロンプト）から始めて、強力なプロンプトの要素を書いていきます。`config.py` というファイルを作成し、以下の各ブロックを追加してください。

```python
IDENTITY = """You are Eva, a friendly and knowledgeable AI assistant for Acme Insurance
Company. Your role is to warmly welcome customers and provide information on
Acme's insurance offerings, which include car insurance and electric car
insurance. You can also help customers get quotes for their insurance needs."""
```

<Tip>
  指示をユーザーとの会話から分離する方法として、すべての情報をシステムプロンプト内に入れたくなるかもしれませんが、実際にはClaudeはプロンプト内容の大部分を最初の 

  `User`

   ターン内に記述した場合に最もよく機能します（唯一の例外はロールプロンプティングです）。詳しくは

  [システムプロンプトでClaudeに役割を与える](https://platform.claude.com/docs/ja/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role)

  をご覧ください。
</Tip>

複雑なプロンプトはサブセクションに分解し、一度に一部分ずつ書くのが最善です。各タスクについて、Claudeがそのタスクをうまく実行するために必要なプロンプトの各部分を定義するステップバイステップのプロセスに従うことで、より大きな成功が得られる場合があります。この自動車保険カスタマーサポートの例では、「挨拶と一般的な案内」タスクから始めて、プロンプトのすべての部分を少しずつ書いていきます。これにより、プロンプト全体の個々の部分をより迅速に調整できるため、プロンプトのデバッグも容易になります。

```python
STATIC_GREETINGS_AND_GENERAL = """
<static_context>
Acme Auto Insurance: Your Trusted Companion on the Road

About:
At Acme Insurance, we understand that your vehicle is more than just a mode of transportation—it's your ticket to life's adventures.
Since 1985, we've been crafting auto insurance policies that give drivers the confidence to explore, commute, and travel with peace of mind.
Whether you're navigating city streets or embarking on cross-country road trips, Acme is there to protect you and your vehicle.
Our innovative auto insurance policies are designed to adapt to your unique needs, covering everything from fender benders to major collisions.
With Acme's award-winning customer service and swift claim resolution, you can focus on the joy of driving while we handle the rest.
We're not just an insurance provider—we're your co-pilot in life's journeys.
Choose Acme Auto Insurance and experience the assurance that comes with superior coverage and genuine care. Because at Acme, we don't just
insure your car—we fuel your adventures on the open road.

Note: We also offer specialized coverage for electric vehicles, ensuring that drivers of all car types can benefit from our protection.

Acme Insurance offers the following products:
- Car insurance
- Electric car insurance
- Two-wheeler insurance

Business hours: Monday-Friday, 9 AM - 5 PM EST
Customer service number: 1-800-123-4567
</static_context>
"""
```

次に、自動車保険と電気自動車保険の情報についても同様に行います。

```python
STATIC_CAR_INSURANCE = """
<static_context>
Car Insurance Coverage:
Acme's car insurance policies typically cover:
1. Liability coverage: Pays for bodily injury and property damage you cause to others.
2. Collision coverage: Pays for damage to your car in an accident.
3. Comprehensive coverage: Pays for damage to your car from non-collision incidents.
4. Medical payments coverage: Pays for medical expenses after an accident.
5. Uninsured/underinsured motorist coverage: Protects you if you're hit by a driver with insufficient insurance.

Optional coverages include:
- Rental car reimbursement
- Roadside assistance
- New car replacement
</static_context>
"""

STATIC_ELECTRIC_CAR_INSURANCE = """
<static_context>
Electric Car Insurance:
Our specialized electric car insurance goes beyond traditional auto coverage,
offering tailored protection for your high-tech investment. In addition to standard coverages, we offer:
- Safeguarding expensive battery systems
- Covering home charging equipment
- Adapting to the rapidly evolving world of electric vehicles

Whether you're driving a Rivian, Nissan Leaf, Chevrolet Bolt,
or any other electric vehicle, our knowledgeable agents are ready to craft a policy
that gives you the confidence to enjoy every electron-powered mile.
</static_context>
"""
```

静的コンテンツが用意できたので、Claudeの応答を導くために少なくとも4〜5個の「良い」インタラクションのサンプルを追加します。これらの例は理想的な顧客インタラクションを代表するものであるべきで、ガードレールやツール呼び出しなどの要素を含めることができます。

```python
EXAMPLES = """
Here are a few examples of how you can interact with customers:

<example 1>
H: Hi, do you offer commercial insurance for small businesses?

A: Ah! Unfortunately, we don't offer commercial insurance at this time.
However, we do provide a range of personal insurance products including car insurance and electric car insurance.
Would you like to know more about these offerings?
</example 1>

<example 2>
H: Hi, what are your business hours?

A: Our business hours are Monday to Friday, 9 AM to 5 PM EST.
If you have any questions or need assistance, feel free to reach out during those times.
How else can I assist you today?
</example 2>

<example 3>
H: I'm shopping for car insurance. What does Acme offer?

A: Great choice! Acme's car insurance policies typically include:
- Liability coverage: Protects you if you're at fault in an accident
- Collision coverage: Pays for damage to your car in an accident
- Comprehensive coverage: Covers non-collision incidents like theft or vandalism
- Medical payments coverage: Pays for medical expenses after an accident
- Uninsured/underinsured motorist coverage: Protects you from drivers with insufficient insurance

We also offer optional coverages like rental car reimbursement, roadside assistance, and new car replacement.
Would you like more details on any of these coverages, or do you have specific questions about insuring your vehicle?
</example 3>

<example 4>
H: I'd like to hear about your electric car insurance.

A: Certainly! Our electric car insurance is specially designed to meet the unique needs of electric vehicle owners.
In addition to all the standard coverages we offer for regular cars, our electric car policies include:

- Battery coverage: Protection against damage or degradation of your vehicle's expensive battery system
- Tech package: Covers repairs or replacement of advanced electronics and onboard computer systems
- Charging station coverage: Protects both home and portable charging equipment
- Cybersecurity protection: Coverage against hacking or digital threats
- Specialized roadside assistance: Includes flatbed towing to protect the vehicle's undercarriage

We cover a wide range of electric vehicles, including popular models from Tesla, Rivian, Nissan, Chevrolet, and more.
Is there a specific electric vehicle you're looking to insure, or would you like more details on any of these coverages?
</example 4>

<example 5>
H: I'd like to get a quote for my car insurance.

A: Certainly! I'd be happy to help you get a quote for your car insurance.
To provide you with an accurate quote, I'll need to collect some information about your vehicle and the primary driver.
Let's start with the basics:

1. What is the make and model of your vehicle?
2. What year was it manufactured?
3. Approximately how many miles have you driven?
4. What is the age of the primary driver?

Once you provide this information, I'll use our quoting tool to generate a personalized insurance quote for you.
</example 5>
"""
```

また、Claudeが顧客とどのようにやり取りすべきかについて、すべきこととすべきでないことを概説する重要な指示も含めるとよいでしょう。 これはブランドのガードレールやサポートポリシーから引き出すことができます。

```python
ADDITIONAL_GUARDRAILS = """Please adhere to the following guardrails:
1. Only provide information about insurance types listed in our offerings.
2. If asked about an insurance type we don't offer, politely state
that we don't provide that service.
3. Do not speculate about future product offerings or company plans.
4. Don't make promises or enter into agreements it's not authorized to make.
You only provide information and guidance.
5. Do not mention any competitor's products or services.
"""
```

次に、これらすべてのセクションを1つの文字列に結合して、プロンプトとして使用します。

```python
TASK_SPECIFIC_INSTRUCTIONS = " ".join(
    [
        STATIC_GREETINGS_AND_GENERAL,
        STATIC_CAR_INSURANCE,
        STATIC_ELECTRIC_CAR_INSURANCE,
        EXAMPLES,
        ADDITIONAL_GUARDRAILS,
    ]
)
```

### ツール使用で動的かつエージェント的な機能を追加する

Claudeは、クライアント側のツール使用機能を使って、アクションを実行したり情報を動的に取得したりすることができます。まず、プロンプトが使用すべき外部ツールやAPIをリストアップします。

この例では、見積もりを計算するための1つのツールから始めます。

<Tip>
  念のため補足すると、このツールは実際の計算を行うわけではなく、指定された引数でツールを使用すべきであることをアプリケーションに通知するだけです。
</Tip>

モデル名、ツール定義、およびスタブ実装を `config.py` に追加します。

```python
import time

MODEL = "claude-opus-5-5"

TOOLS = [
    {
        "name": "get_quote",
        "description": "Calculate the insurance quote based on user input. Returned value is per month premium.",
        "input_schema": {
            "type": "object",
            "properties": {
                "make": {"type": "string", "description": "The make of the vehicle."},
                "model": {"type": "string", "description": "The model of the vehicle."},
                "year": {
                    "type": "integer",
                    "description": "The year the vehicle was manufactured.",
                },
                "mileage": {
                    "type": "integer",
                    "description": "The mileage on the vehicle.",
                },
                "driver_age": {
                    "type": "integer",
                    "description": "The age of the primary driver.",
                },
            },
            "required": ["make", "model", "year", "mileage", "driver_age"],
        },
    }
]


def get_quote(make, model, year, mileage, driver_age):
    """Returns the premium per month in USD"""
    # 見積もりを取得するには、HTTPエンドポイントやデータベースを呼び出すことができます。
    # ここでは、1秒の遅延をシミュレートし、固定の見積もり値100を返します。
    time.sleep(1)
    return 100
```

### プロンプトをデプロイする

テスト用の本番環境にデプロイして[評価を実行](https://platform.claude.com/docs/ja/test-and-evaluate/develop-tests)しなければ、プロンプトがどれだけうまく機能するかを知ることは困難です。プロンプト、Anthropic SDK、およびユーザーインターフェース用のStreamlitを使用して小さなアプリケーションを構築します。

`chatbot.py` というファイル（またはお使いの言語における同等のモジュール）で、Anthropic SDKとのやり取りをカプセル化するChatBotクラスをセットアップします。

このクラスには2つの主要なメソッドが必要です。1つはAPIを呼び出してメッセージを生成するメソッド、もう1つは受信した各ユーザー入力を処理するメソッドです。

<CodeGroup exclude="shell">
  ```python Python
  # chatbot.py では、上記で作成した config.py からこれらをインポートします：
  # from config import IDENTITY, TOOLS, MODEL, get_quote
  from anthropic import Anthropic
  from dotenv import load_dotenv

  load_dotenv()


  class ChatBot:
      def __init__(self, session_state):
          self.anthropic = Anthropic()
          self.session_state = session_state

      def generate_message(
          self,
          messages,
          max_tokens,
      ):
          try:
              response = self.anthropic.messages.create(
                  model=MODEL,
                  system=IDENTITY,
                  max_tokens=max_tokens,
                  messages=messages,
                  tools=TOOLS,
              )
              return response
          except Exception as e:
              return {"error": str(e)}

      def process_user_input(self, user_input):
          self.session_state.messages.append({"role": "user", "content": user_input})

          response_message = self.generate_message(
              messages=self.session_state.messages,
              max_tokens=2048,
          )

          if "error" in response_message:
              return f"An error occurred: {response_message['error']}"

          if response_message.content[-1].type == "tool_use":
              tool_use = response_message.content[-1]
              func_name = tool_use.name
              func_params = tool_use.input
              tool_use_id = tool_use.id

              result = self.handle_tool_use(func_name, func_params)
              self.session_state.messages.append(
                  {"role": "assistant", "content": response_message.content}
              )
              self.session_state.messages.append(
                  {
                      "role": "user",
                      "content": [
                          {
                              "type": "tool_result",
                              "tool_use_id": tool_use_id,
                              "content": f"{result}",
                          }
                      ],
                  }
              )

              follow_up_response = self.generate_message(
                  messages=self.session_state.messages,
                  max_tokens=2048,
              )

              if "error" in follow_up_response:
                  return f"An error occurred: {follow_up_response['error']}"

              response_text = next(
                  (
                      block.text
                      for block in follow_up_response.content
                      if block.type == "text"
                  ),
                  None,
              )
              if response_text is None:
                  raise Exception("An error occurred: Unexpected response type")
              self.session_state.messages.append(
                  {"role": "assistant", "content": response_text}
              )
              return response_text

          text_block = next(
              (block for block in response_message.content if block.type == "text"), None
          )
          if text_block is not None:
              response_text = text_block.text
              self.session_state.messages.append(
                  {"role": "assistant", "content": response_text}
              )
              return response_text

          raise Exception("An error occurred: Unexpected response type")

      def handle_tool_use(self, func_name, func_params):
          if func_name == "get_quote":
              premium = get_quote(**func_params)
              return f"Quote generated: ${premium:.2f} per month"

          raise Exception("An unexpected tool was used")
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";

  class ChatBot {
    // IDENTITY、MODEL、TOOLS、getQuoteは、このガイドで先に（Pythonで）
    // 定義されたconfig.pyの値を反映しています。
    readonly anthropic = new Anthropic();
    readonly messages: Anthropic.MessageParam[] = [];

    async generateMessage(
      messages: Anthropic.MessageParam[],
      maxTokens: number
    ): Promise<Anthropic.Message> {
      return this.anthropic.messages.create({
        model: MODEL,
        system: IDENTITY,
        max_tokens: maxTokens,
        messages,
        tools: TOOLS
      });
    }

    async processUserInput(userInput: string): Promise<string> {
      this.messages.push({ role: "user", content: userInput });

      const responseMessage = await this.generateMessage(this.messages, 2048);

      const lastBlock = responseMessage.content.at(-1);
      if (lastBlock?.type === "tool_use") {
        const toolResult = this.handleToolUse(lastBlock.name, lastBlock.input);

        this.messages.push({ role: "assistant", content: responseMessage.content });
        this.messages.push({
          role: "user",
          content: [{ type: "tool_result", tool_use_id: lastBlock.id, content: toolResult }]
        });

        const followUpResponse = await this.generateMessage(this.messages, 2048);

        const followUpBlock = followUpResponse.content.find((block) => block.type === "text");
        if (!followUpBlock) {
          throw new Error("An error occurred: Unexpected response type");
        }
        this.messages.push({ role: "assistant", content: followUpBlock.text });
        return followUpBlock.text;
      }

      const firstBlock = responseMessage.content.find((block) => block.type === "text");
      if (firstBlock) {
        this.messages.push({ role: "assistant", content: firstBlock.text });
        return firstBlock.text;
      }

      throw new Error("An error occurred: Unexpected response type");
    }

    handleToolUse(toolName: string, toolInput: unknown): string {
      if (toolName === "get_quote") {
        // SDKはtool_use.inputをunknownとして型付けするため、get_quoteスキーマに絞り込みます。
        if (
          toolInput === null ||
          typeof toolInput !== "object" ||
          !("make" in toolInput) ||
          typeof toolInput.make !== "string" ||
          !("model" in toolInput) ||
          typeof toolInput.model !== "string" ||
          !("year" in toolInput) ||
          typeof toolInput.year !== "number" ||
          !("mileage" in toolInput) ||
          typeof toolInput.mileage !== "number" ||
          !("driver_age" in toolInput) ||
          typeof toolInput.driver_age !== "number"
        ) {
          throw new Error("An error occurred: Unexpected tool input");
        }
        const { make, model: vehicleModel, year, mileage, driver_age: driverAge } = toolInput;

        const premium = getQuote(make, vehicleModel, year, mileage, driverAge);
        return `Quote generated: $${premium.toFixed(2)} per month`;
      }

      throw new Error("An unexpected tool was used");
    }
  }
  ```

  ```csharp C#
  using System.Text.Json;
  using Anthropic;
  using Anthropic.Models.Messages;

  // Config.Model、Config.Identity、Config.Tools、Config.GetQuoteは、このガイドで
  // 先に定義したconfig.pyの値（Pythonで示されています）を反映しています。
  public class ChatBot
  {
      private readonly AnthropicClient _anthropic = new();

      public List<MessageParam> Messages { get; } = [];

      public async Task<Message> GenerateMessage(List<MessageParam> messages, long maxTokens) =>
          await _anthropic.Messages.Create(
              new MessageCreateParams
              {
                  Model = Config.Model,
                  System = Config.Identity,
                  MaxTokens = maxTokens,
                  Messages = messages,
                  Tools = Config.Tools,
              }
          );

      public async Task<string> ProcessUserInput(string userInput)
      {
          Messages.Add(new() { Role = Role.User, Content = userInput });

          var responseMessage = await GenerateMessage(Messages, maxTokens: 2048);

          if (responseMessage.Content[^1].TryPickToolUse(out var toolUse))
          {
              var toolResult = HandleToolUse(toolUse.Name, toolUse.Input);

              Messages.Add(new()
              {
                  Role = Role.Assistant,
                  Content = responseMessage.Content
                      .Select(contentBlock => new ContentBlockParam(contentBlock.Json))
                      .ToList(),
              });
              Messages.Add(new()
              {
                  Role = Role.User,
                  Content = new List<ContentBlockParam>
                  {
                      new ToolResultBlockParam { ToolUseID = toolUse.ID, Content = toolResult },
                  },
              });

              var followUpResponse = await GenerateMessage(Messages, maxTokens: 2048);

              foreach (var block in followUpResponse.Content)
              {
                  if (block.TryPickText(out var followUpText))
                  {
                      Messages.Add(new() { Role = Role.Assistant, Content = followUpText.Text });
                      return followUpText.Text;
                  }
              }

              throw new InvalidOperationException("An error occurred: Unexpected response type");
          }

          foreach (var block in responseMessage.Content)
          {
              if (block.TryPickText(out var textBlock))
              {
                  Messages.Add(new() { Role = Role.Assistant, Content = textBlock.Text });
                  return textBlock.Text;
              }
          }

          throw new InvalidOperationException("An error occurred: Unexpected response type");
      }

      public string HandleToolUse(string funcName, IReadOnlyDictionary<string, JsonElement> funcParams)
      {
          if (funcName == "get_quote")
          {
              var premium = Config.GetQuote(
                  funcParams["make"].GetString()!,
                  funcParams["model"].GetString()!,
                  funcParams["year"].GetInt64(),
                  funcParams["mileage"].GetInt64(),
                  funcParams["driver_age"].GetInt64()
              );
              return $"Quote generated: ${premium:F2} per month";
          }

          throw new ArgumentException("An unexpected tool was used");
      }
  }
  ```

  ```go Go
  import (
  	"context"
  	"encoding/json"
  	"fmt"

  	"github.com/anthropics/anthropic-sdk-go"
  )

  // ChatBotはAnthropicクライアントと会話履歴をラップします。使用される
  // identity、model、tools、getQuoteの値は、このガイドの前半で（Pythonで）
  // 示したconfig.pyの定義を反映しています。
  type ChatBot struct {
  	client   anthropic.Client
  	messages []anthropic.MessageParam
  }

  func NewChatBot() *ChatBot {
  	return &ChatBot{client: anthropic.NewClient()}
  }

  func (bot *ChatBot) GenerateMessage(ctx context.Context, messages []anthropic.MessageParam, maxTokens int64) (*anthropic.Message, error) {
  	return bot.client.Messages.New(ctx, anthropic.MessageNewParams{
  		Model:     model,
  		System:    []anthropic.TextBlockParam{{Text: identity}},
  		MaxTokens: maxTokens,
  		Messages:  messages,
  		Tools:     tools,
  	})
  }

  func (bot *ChatBot) ProcessUserInput(ctx context.Context, userInput string) (string, error) {
  	bot.messages = append(bot.messages, anthropic.NewUserMessage(anthropic.NewTextBlock(userInput)))

  	response, err := bot.GenerateMessage(ctx, bot.messages, 2048)
  	if err != nil {
  		return "", err
  	}

  	lastBlock := response.Content[len(response.Content)-1]
  	if toolUse, ok := lastBlock.AsAny().(anthropic.ToolUseBlock); ok {
  		result, err := bot.HandleToolUse(toolUse.Name, toolUse.Input)
  		if err != nil {
  			return "", err
  		}

  		bot.messages = append(bot.messages,
  			response.ToParam(),
  			anthropic.NewUserMessage(anthropic.NewToolResultBlock(toolUse.ID, result, false)),
  		)

  		followUp, err := bot.GenerateMessage(ctx, bot.messages, 2048)
  		if err != nil {
  			return "", err
  		}

  		for _, block := range followUp.Content {
  			if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  				bot.messages = append(bot.messages, anthropic.NewAssistantMessage(anthropic.NewTextBlock(textBlock.Text)))
  				return textBlock.Text, nil
  			}
  		}
  		return "", fmt.Errorf("an error occurred: unexpected response type")
  	}

  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  			bot.messages = append(bot.messages, anthropic.NewAssistantMessage(anthropic.NewTextBlock(textBlock.Text)))
  			return textBlock.Text, nil
  		}
  	}

  	return "", fmt.Errorf("an error occurred: unexpected response type")
  }

  func (bot *ChatBot) HandleToolUse(toolName string, toolInput json.RawMessage) (string, error) {
  	if toolName != "get_quote" {
  		return "", fmt.Errorf("an unexpected tool was used: %s", toolName)
  	}

  	var input struct {
  		Make      string `json:"make"`
  		Model     string `json:"model"`
  		Year      int    `json:"year"`
  		Mileage   int    `json:"mileage"`
  		DriverAge int    `json:"driver_age"`
  	}
  	if err := json.Unmarshal(toolInput, &input); err != nil {
  		return "", err
  	}
  	premium := getQuote(input.Make, input.Model, input.Year, input.Mileage, input.DriverAge)
  	return fmt.Sprintf("Quote generated: $%.2f per month", premium), nil
  }
  ```

  ```java Java
  import com.anthropic.client.AnthropicClient;
  import com.anthropic.client.okhttp.AnthropicOkHttpClient;
  import com.anthropic.core.JsonValue;
  import com.anthropic.models.messages.ContentBlock;
  import com.anthropic.models.messages.ContentBlockParam;
  import com.anthropic.models.messages.Message;
  import com.anthropic.models.messages.MessageCreateParams;
  import com.anthropic.models.messages.MessageParam;
  import com.anthropic.models.messages.ToolResultBlockParam;
  import com.anthropic.models.messages.ToolUseBlock;

  // IDENTITY、MODEL、TOOLS、getQuote は、このガイドで先に（Python で）定義した
  // config.py の値を反映しています。
  class ChatBot {
      final AnthropicClient anthropic;
      final List<MessageParam> messages;

      ChatBot() {
          // ANTHROPIC_API_KEY 環境変数から APIキーを読み取ります
          this.anthropic = AnthropicOkHttpClient.fromEnv();
          this.messages = new ArrayList<>();
      }

      Message generateMessage(List<MessageParam> messages, long maxTokens) {
          return anthropic.messages().create(MessageCreateParams.builder()
                  .model(MODEL)
                  .system(IDENTITY)
                  .maxTokens(maxTokens)
                  .messages(messages)
                  .tools(TOOLS)
                  .build());
      }

      String processUserInput(String userInput) {
          messages.add(MessageParam.builder()
                  .role(MessageParam.Role.USER)
                  .content(userInput)
                  .build());

          Message responseMessage = generateMessage(messages, 2048);

          List<ContentBlock> content = responseMessage.content();
          ContentBlock lastBlock = content.getLast();
          if (lastBlock.isToolUse()) {
              ToolUseBlock toolUse = lastBlock.asToolUse();
              Map<String, JsonValue> toolInput =
                      (Map<String, JsonValue>) toolUse._input().asObject().orElseThrow();
              String result = handleToolUse(toolUse.name(), toolInput);

              messages.add(MessageParam.builder()
                      .role(MessageParam.Role.ASSISTANT)
                      .contentOfBlockParams(content.stream().map(ContentBlock::toParam).toList())
                      .build());
              messages.add(MessageParam.builder()
                      .role(MessageParam.Role.USER)
                      .contentOfBlockParams(List.of(ContentBlockParam.ofToolResult(
                              ToolResultBlockParam.builder()
                                      .toolUseId(toolUse.id())
                                      .content(result)
                                      .build())))
                      .build());

              Message followUpResponse = generateMessage(messages, 2048);

              ContentBlock followUpBlock = followUpResponse.content().stream()
                      .filter(ContentBlock::isText)
                      .findFirst()
                      .orElseThrow(() -> new IllegalStateException("An error occurred: Unexpected response type"));
              String responseText = followUpBlock.asText().text();
              messages.add(MessageParam.builder()
                      .role(MessageParam.Role.ASSISTANT)
                      .content(responseText)
                      .build());
              return responseText;
          } else {
              ContentBlock textBlock = content.stream()
                      .filter(ContentBlock::isText)
                      .findFirst()
                      .orElseThrow(() -> new IllegalStateException("An error occurred: Unexpected response type"));
              String responseText = textBlock.asText().text();
              messages.add(MessageParam.builder()
                      .role(MessageParam.Role.ASSISTANT)
                      .content(responseText)
                      .build());
              return responseText;
          }
      }

      String handleToolUse(String funcName, Map<String, JsonValue> funcParams) {
          return switch (funcName) {
              case "get_quote" -> {
                  double premium = getQuote(
                          funcParams.get("make").asStringOrThrow(),
                          funcParams.get("model").asStringOrThrow(),
                          ((Number) funcParams.get("year").asNumber().orElseThrow()).longValue(),
                          ((Number) funcParams.get("mileage").asNumber().orElseThrow()).longValue(),
                          ((Number) funcParams.get("driver_age").asNumber().orElseThrow()).longValue());
                  yield "Quote generated: $%.2f per month".formatted(premium);
              }
              default -> throw new IllegalArgumentException("An unexpected tool was used");
          };
      }
  }
  ```

  ```php PHP
  use Anthropic\Client;
  use Anthropic\Messages\Message;
  use Anthropic\Messages\MessageParam;
  use Anthropic\Messages\TextBlock;
  use Anthropic\Messages\ToolResultBlockParam;
  use Anthropic\Messages\ToolUseBlock;

  class ChatBot
  {
      // MODEL、IDENTITY、TOOLS、get_quote()は、このガイドで先に定義した
      // config.pyの値（Pythonで示したもの）を反映しています。

      /** @var list<MessageParam> */
      public private(set) array $messages = [];

      public function __construct(
          private readonly Client $anthropic = new Client(),
      ) {}

      /**
       * @param list<MessageParam> $messages
       */
      public function generateMessage(array $messages, int $maxTokens): Message
      {
          return $this->anthropic->messages->create(
              model: MODEL,
              system: IDENTITY,
              maxTokens: $maxTokens,
              messages: $messages,
              tools: TOOLS,
          );
      }

      public function processUserInput(string $userInput): string
      {
          $this->messages[] = MessageParam::with(role: 'user', content: $userInput);

          $responseMessage = $this->generateMessage($this->messages, maxTokens: 2048);

          $content = $responseMessage->content;
          $lastBlock = array_last($content);

          if ($lastBlock instanceof ToolUseBlock) {
              $toolResult = $this->handleToolUse($lastBlock->name, $lastBlock->input);

              $this->messages[] = MessageParam::with(role: 'assistant', content: $content);
              $this->messages[] = MessageParam::with(
                  role: 'user',
                  content: [
                      ToolResultBlockParam::with(toolUseID: $lastBlock->id, content: $toolResult),
                  ],
              );

              $followUpResponse = $this->generateMessage($this->messages, maxTokens: 2048);

              $firstBlock = array_find(
                  $followUpResponse->content,
                  static fn ($block): bool => $block instanceof TextBlock,
              );
              if (!$firstBlock instanceof TextBlock) {
                  throw new \RuntimeException('An error occurred: Unexpected response type');
              }

              $this->messages[] = MessageParam::with(role: 'assistant', content: $firstBlock->text);

              return $firstBlock->text;
          }

          $firstBlock = array_find($content, static fn ($block): bool => $block instanceof TextBlock);
          if ($firstBlock instanceof TextBlock) {
              $this->messages[] = MessageParam::with(role: 'assistant', content: $firstBlock->text);

              return $firstBlock->text;
          }

          throw new \RuntimeException('An error occurred: Unexpected response type');
      }

      /**
       * @param array<string, mixed> $funcParams
       */
      private function handleToolUse(string $funcName, array $funcParams): string
      {
          if ($funcName === 'get_quote') {
              $premium = get_quote(...$funcParams);

              return sprintf('Quote generated: $%.2f per month', $premium);
          }

          throw new \RuntimeException('An unexpected tool was used');
      }
  }
  ```

  ```ruby Ruby
  # IDENTITY、MODEL、TOOLS、get_quoteは、このガイドの前半で（Pythonで）定義された
  # config.pyの値を反映しています。
  require "anthropic"

  class ChatBot
    attr_reader :messages

    def initialize
      @anthropic = Anthropic::Client.new
      @messages = []
    end

    def generate_message(messages, max_tokens)
      @anthropic.messages.create(
        model: MODEL,
        system_: IDENTITY,
        max_tokens:,
        messages:,
        tools: TOOLS
      )
    end

    def process_user_input(user_input)
      @messages << {role: "user", content: user_input}

      response_message = generate_message(@messages, 2048)

      case response_message.content
      in [*, Anthropic::ToolUseBlock => tool_use]
        result = handle_tool_use(tool_use.name, tool_use.input)
        @messages << {role: "assistant", content: response_message.content}
        @messages << {
          role: "user",
          content: [{type: "tool_result", tool_use_id: tool_use.id, content: result}]
        }

        follow_up_response = generate_message(@messages, 2048)

        case follow_up_response.content
        in [*, Anthropic::TextBlock => text_block, *]
          @messages << {role: "assistant", content: text_block.text}
          text_block.text
        else
          raise "An error occurred: Unexpected response type"
        end
      in [*, Anthropic::TextBlock => text_block, *]
        @messages << {role: "assistant", content: text_block.text}
        text_block.text
      else
        raise "An error occurred: Unexpected response type"
      end
    end

    def handle_tool_use(tool_name, tool_input)
      raise "An unexpected tool was used" unless tool_name == "get_quote"

      premium = get_quote(**tool_input)
      format("Quote generated: $%.2f per month", premium)
    end
  end
  ```
</CodeGroup>

### ユーザーインターフェースを構築する

mainメソッドを使用して、このコードをStreamlitでテストデプロイします。この `main()` 関数はStreamlitベースのチャットインターフェースをセットアップします。StreamlitはPythonフレームワークであるため、このウォークスルーのこの部分はPythonのみで示しています。上記のChatBotクラスは、任意の言語に移植できる部分です。

これを `app.py` というファイルで行います。

```python
import streamlit as st
from chatbot import ChatBot
from config import TASK_SPECIFIC_INSTRUCTIONS


def main():
    st.title("Chat with Eva, Acme Insurance Company's Assistant🤖")

    if "messages" not in st.session_state:
        st.session_state.messages = [
            {"role": "user", "content": TASK_SPECIFIC_INSTRUCTIONS},
            {"role": "assistant", "content": "Understood"},
        ]

    chatbot = ChatBot(st.session_state)

    # 最初の2つをスキップしてユーザーとアシスタントのメッセージを表示
    for message in st.session_state.messages[2:]:
        # ツール使用ブロックは無視
        if isinstance(message["content"], str):
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

    if user_msg := st.chat_input("Type your message here..."):
        st.chat_message("user").markdown(user_msg)

        with st.chat_message("assistant"):
            with st.spinner("Eva is thinking..."):
                response_placeholder = st.empty()
                full_response = chatbot.process_user_input(user_msg)
                response_placeholder.markdown(full_response)


if __name__ == "__main__":
    main()
```

次のコマンドでプログラムを実行します。

```bash
streamlit run app.py
```

### プロンプトを評価する

プロンプトを本番環境で使用できる状態にするには、多くの場合テストと最適化が必要です。ソリューションの準備状況を判断するために、定量的手法と定性的手法を組み合わせた体系的なプロセスを使用してチャットボットのパフォーマンスを評価します。定義した成功基準に基づいて[強力な実証的評価](https://platform.claude.com/docs/ja/test-and-evaluate/develop-tests#build-evaluations)を作成することで、プロンプトを最適化できるようになります。

### パフォーマンスを改善する

複雑なシナリオでは、標準的な[プロンプトエンジニアリング手法](https://platform.claude.com/docs/ja/build-with-claude/prompt-engineering/overview)や[ガードレール実装戦略](https://platform.claude.com/docs/ja/test-and-evaluate/strengthen-guardrails/reduce-hallucinations)を超えて、パフォーマンスを改善するための追加の戦略を検討すると役立つ場合があります。一般的なシナリオをいくつか示します。

#### RAGで長いコンテキストのレイテンシを削減する

大量の静的および動的コンテキストを扱う場合、すべての情報をプロンプトに含めると、コストの増大、応答時間の低下、「context window」（コンテキストウィンドウ）の上限到達につながる可能性があります。このシナリオでは、「Retrieval Augmented Generation」（検索拡張生成）、すなわちRAGの手法を実装することで、パフォーマンスと効率を改善できます。

Voyage AI by MongoDBのモデルなどの[埋め込みモデル](https://platform.claude.com/docs/ja/build-with-claude/embeddings)を使用して情報をベクトル表現に変換することで、よりスケーラブルで応答性の高いシステムを作成できます。このアプローチでは、考えられるすべてのコンテキストをすべてのプロンプトに含めるのではなく、現在の問い合わせに基づいて関連情報を動的に取得できます。

サポートのユースケースにRAGを実装すると、広範なコンテキスト要件を持つシステムにおいて、正確性の向上、応答時間の短縮、APIコストの削減が実現されることが示されています。実例については[RAGレシピ](https://platform.claude.com/cookbook/capabilities-retrieval-augmented-generation-guide)をご覧ください。

#### ツール使用でリアルタイムデータを統合する

口座残高や保険契約の詳細など、リアルタイムの情報を必要とする問い合わせを扱う場合、埋め込みベースのRAGアプローチでは不十分です。代わりに、ツール使用によって、正確でリアルタイムな応答を提供するチャットボットの能力を強化できます。たとえば、ツール使用を利用して顧客情報を検索したり、注文の詳細を取得したり、顧客に代わって注文をキャンセルしたりできます。

[ツール使用：カスタマーサービスエージェントのレシピで概説されている](https://platform.claude.com/cookbook/tool-use-customer-service-agent)このアプローチにより、ライブデータをClaudeの応答に統合し、よりパーソナライズされた効率的な顧客体験を提供できます。

#### 入力と出力のガードレールを強化する

チャットボットをデプロイする際、特にカスタマーサービスのシナリオでは、誤用、範囲外の問い合わせ、不適切な応答に関連するリスクを防ぐことが重要です。Claudeは本質的にこのようなシナリオに対して耐性がありますが、チャットボットのガードレールを強化するための追加の手順を以下に示します。

* [ハルシネーションを削減する](https://platform.claude.com/docs/ja/test-and-evaluate/strengthen-guardrails/reduce-hallucinations)：ファクトチェックの仕組みと[引用](https://platform.claude.com/cookbook/misc-using-citations)を実装して、提供された情報に基づいた応答にします。
* 情報をクロスチェックする：エージェントの応答が会社のポリシーや既知の事実と一致していることを確認します。
* 契約上のコミットメントを避ける：エージェントが権限のない約束をしたり契約を結んだりしないようにします。
* [ジェイルブレイクを軽減する](https://platform.claude.com/docs/ja/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks)：無害性スクリーニングや入力検証などの方法を使用して、ユーザーがモデルの脆弱性を悪用して不適切なコンテンツを生成しようとすることを防ぎます。
* 競合他社への言及を避ける：競合他社言及フィルターを実装してブランドへの集中を維持し、競合他社の製品やサービスに言及しないようにします。
* [出力の一貫性を高める](https://platform.claude.com/docs/ja/test-and-evaluate/strengthen-guardrails/increase-consistency)：長く複雑なインタラクションの間でも、Claudeがスタイルを変えたりキャラクターから外れたりしないようにします。
* 個人識別情報（PII）を削除する：明示的に必要とされ許可されている場合を除き、応答からすべてのPIIを取り除きます。

#### ストリーミングで体感応答時間を短縮する

長くなる可能性のある応答を扱う場合、「streaming」（ストリーミング）を実装することでユーザーのエンゲージメントと満足度を向上させることができます。このシナリオでは、ユーザーは応答全体が生成されるのを待つのではなく、回答を段階的に受け取ります。

ストリーミングの実装方法は以下のとおりです。

1. [Anthropic Streaming API](https://platform.claude.com/docs/ja/build-with-claude/streaming)を使用してストリーミング応答をサポートします。
2. 受信するテキストのチャンクを処理するようにフロントエンドをセットアップします。
3. 各チャンクを到着するたびに表示し、リアルタイムのタイピングをシミュレートします。
4. 完全な応答を保存する仕組みを実装し、ユーザーがページを離れて戻ってきた場合でも閲覧できるようにします。

場合によっては、段階的な表示によって処理時間の長さの影響が軽減されるため、ストリーミングによってベースレイテンシの高いより高度なモデルを使用できるようになります。

#### チャットボットをスケールする

チャットボットの複雑さが増すにつれて、アプリケーションアーキテクチャもそれに合わせて進化させることができます。アーキテクチャにさらなるレイヤーを追加する前に、以下のより負担の少ない選択肢を検討してください。

* プロンプトを最大限に活用し、プロンプトエンジニアリングを通じて最適化していることを確認します。最も効果的なプロンプトを書くには[プロンプトエンジニアリングガイド](https://platform.claude.com/docs/ja/build-with-claude/prompt-engineering/overview)を使用してください。
* プロンプトに追加の[ツール](https://platform.claude.com/docs/ja/agents-and-tools/tool-use/overview)（[プロンプトチェーン](https://platform.claude.com/docs/ja/build-with-claude/prompt-engineering/claude-prompting-best-practices#chain-complex-prompts)を含めることができます）を追加し、必要な機能を実現できるかどうかを確認します。

チャットボットが非常に多様なタスクを処理する場合は、最初の顧客問い合わせをルーティングするために[個別のインテント分類器](https://platform.claude.com/cookbook/capabilities-classification-guide)の追加を検討するとよいでしょう。既存のアプリケーションの場合、これには顧客の問い合わせを分類器を経由して専門化された会話（独自のツールセットとシステムプロンプトを持つ）にルーティングする決定木を作成することが含まれます。なお、この方法ではClaudeへの追加の呼び出しが必要となり、レイテンシが増加する可能性があります。

### Claudeをサポートワークフローに統合する

これらの例はStreamlit環境内で呼び出し可能なPython関数に焦点を当ててきましたが、リアルタイムのサポートチャットボットとしてClaudeをデプロイするにはAPIサービスが必要です。

以下のようにアプローチできます。

1. APIラッパーを作成する：分類関数の周りにシンプルなAPIラッパーを開発します。たとえば、Flask APIやFast APIを使用してコードをHTTPサービスにラップできます。HTTPサービスはユーザー入力を受け取り、アシスタントの応答全体を返すことができます。したがって、サービスは以下の特性を持つことができます。

   * Server-Sent Events（SSE）：SSEにより、サーバーからクライアントへの応答のリアルタイムストリーミングが可能になります。これにより、LLMを扱う際にスムーズでインタラクティブな体験が提供されます。
   * キャッシング：キャッシングを実装することで、応答時間を改善し、不要なAPI呼び出しを削減できます。
   * コンテキストの保持：ユーザーがページを離れて戻ってきたときにコンテキストを維持することは、会話の継続性にとって重要です。

2. Webインターフェースを構築する：Claudeを活用したエージェントとやり取りするための、ユーザーフレンドリーなWeb UIを実装します。

## 次のステップ

<CardGroup cols={2}>
  <Card title="ツール使用" icon="wrench" href="https://platform.claude.com/docs/ja/agents-and-tools/tool-use/overview">
    ClaudeにAPIへのアクセスを与え、顧客に代わってアクションを実行できるようにします。
  </Card>

  <Card title="テストを開発する" icon="check" href="https://platform.claude.com/docs/ja/test-and-evaluate/develop-tests">
    定義した成功基準に対してサポートエージェントを測定するための評価を構築します。
  </Card>

  <Card title="ストリーミング" icon="bolt" href="https://platform.claude.com/docs/ja/build-with-claude/streaming">
    応答をストリーミングして、生成されるにつれて顧客が回答を確認できるようにします。
  </Card>

  <Card title="プロンプトエンジニアリング" icon="lightbulb" href="https://platform.claude.com/docs/ja/build-with-claude/prompt-engineering/overview">
    タスクのパフォーマンスを向上させるために、システムプロンプトと例を改良します。
  </Card>
</CardGroup>
