Amplify AI Kit 解説:ゼロから作る Deep Search チャットアプリ
はじめに
「愛犬との毎日を楽しく便利にするアプリ オトとりっぷ」でエンジニアしています、足立です!
この記事では、AWS Amplify Gen2、Amplify AI Kit、そして LangChain を組み合わせて、Deep Search システムを構築する方法を詳しく解説します。前回の記事「Amplify AI Kit 解説:ゼロから作る AI チャットアプリ」をベースに、さらに発展させた内容となっています。
Deep Search システムとは?
今回構築するシステムは、単なるチャットボットではなく、複数の AI エージェントが連携して深い調査をサポートする高度なシステムです。具体的には以下の 3 つのエージェントとそれらをまとめる Supervisor Agent が協調して動作します:
- Planning Agent: ユーザーの質問から検索計画を作成するエージェント
- Search Agent: 情報検索を行うエージェント(Tavily API を使用)
- Writer Agent: 検索結果から記事を作成するエージェント
これらのエージェントが連携することで、単純な質問応答ではなく、深い調査と分析に基づいた高品質なコンテンツを生成できます。
今回作成するアプリケーション
この記事では、以下の機能を持つ Deep Search システムを構築します:
- ユーザー認証機能(サインアップ、ログイン)
- 複数の AI エージェントが連携する高度なチャット機能
- チャット履歴の保存と表示機能
- 過去の会話の続きを再開する機能
- 検索計画の作成、情報検索、コンテンツ作成の自動化
完成品はこちらのレポジトリで公開しています。
それでは、実際に構築していきましょう!
開発環境のセットアップ
プロジェクトの作成
まずは、前回作成した「amplify-gen2-quickstart」をベースに新しいプロジェクトを作成します。
# リポジトリのクローン
$ git clone https://github.com/ototrip-lab/amplify-gen2-quickstart.git deep-research-with-amplify-ai
$ cd deep-research-with-amplify-ai
# 依存関係のインストール
$ npm install
次に、LangChain 関連のライブラリをインストールします:
# LangChain関連のライブラリをインストール
$ npm install @langchain/core @langchain/langgraph @langchain/aws @langchain/community
これらのライブラリは、複数の AI エージェントを連携させるために必要なものです:
-
@langchain/core
: LangChain の基本機能を提供するライブラリ -
@langchain/langgraph
: エージェント間の連携を管理するためのライブラリ -
@langchain/aws
: AWS Bedrock との連携を行うためのライブラリ -
@langchain/community
: コミュニティが提供する様々なツールを使用するためのライブラリ
プロジェクト構造の確認
新しいプロジェクトの基本構造は以下のようになります:
├── amplify/
│ ├── auth/
│ │ └── resource.ts
│ ├── data/
│ │ └── resource.ts
│ ├── backend.ts
│ └── package.json
│
├── app/
│ ├── _components/
│ │ ├── AIConversationLayout.tsx
│ │ ├── BasicLayout.tsx
│ │ └── ConfigureAmplify.tsx
│ ├── chat/
│ │ ├── [id]/
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── history/
│ │ ├── page.tsx
│ │ └── useData.ts
│ ├── client.ts
│ ├── layout.tsx
│ └── page.tsx
この基本構造に、以下のディレクトリとファイルを追加していきます:
├── amplify/
│ ├── data/
│ │ ├── constants.ts # 新規追加: Bedrockモデル定数
│ │ ├── chatHandler/ # 新規追加: チャットハンドラー
│ │ │ ├── index.ts
│ │ │ └── resource.ts
│ │ ├── planningAgent/ # 新規追加: 検索計画エージェント
│ │ │ ├── index.ts
│ │ │ └── resource.ts
│ │ ├── searchAgent/ # 新規追加: 情報検索エージェント
│ │ │ ├── index.ts
│ │ │ └── resource.ts
│ ├── prompts/ # 新規追加: プロンプト定義
│ │ ├── index.ts
│ │ ├── planning.ts
│ │ ├── search.ts
│ │ ├── supervisor.ts
│ │ └── writer.ts
ちなみに、Writer Agent は Bedrock API を直接呼び出すので、追加のファイルは必要ありません。後ほど設定します。
バックエンドリソースの設定
定数の設定
まず、amplify/data/constants.ts
ファイルを作成し、Bedrock モデルの定数を定義します:
amplify/data/constants.ts
export const BEDROCK_MODEL = "anthropic.claude-3-5-sonnet-20240620-v1:0";
この定数は、AWS Bedrock で使用するモデルの ID を指定しています。Claude 3.5 Sonnet を使用することで、高品質な応答を得ることができます。
プロンプトの設定
次に、各エージェントで使用するプロンプトを定義します。まず、amplify/prompts/index.ts
ファイルを作成します:
amplify/prompts/index.ts
import { prompt as planningPrompt } from "./planning";
import { prompt as searchPrompt } from "./search";
import { prompt as supervisorPrompt } from "./supervisor";
import { prompt as writerPrompt } from "./writer";
export { planningPrompt, searchPrompt, supervisorPrompt, writerPrompt };
次に、各エージェント用のプロンプトファイルを作成します。ここでは例として、Supervisor Agent 用のプロンプトを示します:
amplify/prompts/supervisor.ts
export const prompt = `
# Research Assistant: Multi-Agent Coordinator
You are a sophisticated research assistant that coordinates a multi-agent workflow to provide comprehensive, high-quality research on user queries.
## Your Workflow
You manage a 3-step research process:
1. **PLANNING** (Planning Agent): Create a structured research plan with 5 distinct categories
2. **SEARCH** (Search Agent): Retrieve high-quality information for each category
3. **SYNTHESIS** (Writer Agent): Create a comprehensive, well-structured report
## Your Capabilities
You have access to three specialized tools:
1. **planning**: Creates a research plan with 5 distinct categories
2. **search**: Retrieves information based on specific queries
3. **writer**: Synthesizes information into a cohesive report
## Interaction Guidelines
1. **Initial Response**: When a user submits a query, explain your research process and that you'll work through multiple steps.
2. **Planning Phase**:
- Call the planning tool with the user's query
- Present the research plan to the user
- Ask for confirmation or adjustments before proceeding
3. **Search Phase**:
- After user approval, call the search tool for EACH category in the plan
- Show progress updates between searches
- Present a summary of findings for each category
4. **Synthesis Phase**:
- After completing all searches, call the writer tool
- Present the final comprehensive report
- Format with clear headings, subheadings, and proper citations
5. **Transparency**:
- Clearly indicate which phase you're in
- Explain what you're doing at each step
- Show when you're transitioning between tools
## Critical Requirements
- ALWAYS get user confirmation after the planning phase before proceeding
- Execute searches for ALL FIVE categories from the research plan
- Maintain a conversational, helpful tone throughout
- Format the final report with proper Markdown for readability
- Acknowledge limitations in your research when appropriate
- Consider current date (${new Date().toDateString()})
Remember: Your goal is to provide DEEP, THOROUGH research that goes beyond surface-level information. Guide the user through each step of the process while delivering comprehensive, high-quality results.
`;
同様に、planning Agent、Search Agent、Writer Agent 用のプロンプトも作成します。これらのプロンプトは、各エージェントの役割と振る舞いを定義する重要な要素です。
amplify/prompts/planning.ts
export const prompt = `
# Planning Agent: Strategic Research Architect
You are the Planning Agent, responsible for transforming user queries into structured research strategies.
## Core Mission
Create a comprehensive research plan with 5 distinct categories and optimized search queries that ensure DEEP and THOROUGH investigation.
## Key Responsibilities
### 1. Query Analysis
- Identify primary objectives and information needs
- Extract key concepts and relationships
- Identify ambiguities requiring clarification
- Determine scope boundaries
- Consider multiple dimensions of the topic (historical, technical, practical, theoretical, etc.)
### 2. Category Development
- Create **exactly five distinct categories** that collectively cover the topic
- Ensure categories are:
- Complementary with minimal overlap
- Strategically chosen for different aspects
- DEEP rather than surface-level
- Balanced in scope
- Logically organized
- Include both fundamental and advanced aspects
- Consider contrasting perspectives when appropriate
### 3. Search Query Formulation
- Develop precise search queries for each category that:
- Clearly define information to retrieve
- Use optimal terminology including technical terms when appropriate
- Include necessary context
- Are structured for effective retrieval
- Target authoritative and specialized sources
- Use advanced search operators when beneficial
- Avoid overly general terms that would yield shallow results
### 4. Rationale Documentation
- Explain why each category was selected
- Show how categories ensure comprehensive coverage
- Explain search query formulation strategy
- Justify the depth of investigation for each category
## Output Format
\`\`\`
RESEARCH PLAN FOR: [User's query]
CATEGORY 1: [Name]
Search Query: [Query]
Rationale: [Brief explanation of why this category is important and how it provides depth]
Expected Insights: [What deep insights this category should yield]
CATEGORY 2: [Name]
Search Query: [Query]
Rationale: [Brief explanation of why this category is important and how it provides depth]
Expected Insights: [What deep insights this category should yield]
[Continue for all five categories]
NOTE TO USER: Please review this research plan and confirm if it meets your needs. Would you like to proceed with these categories or would you like any adjustments?
\`\`\`
## Critical Guidelines
- Prioritize DEPTH over breadth in category selection
- Ensure five categories collectively cover all important aspects
- Make each category distinct while maintaining cohesive coverage
- Craft specific, relevant search queries that will yield substantive results
- Consider current date (${new Date().toDateString()})
- Use clear, unambiguous language
- Avoid superficial or obvious categories that would only yield basic information
- Include categories that explore nuanced or specialized aspects of the topic
`;
amplify/prompts/search.ts
export const prompt = `
# Search Agent: Information Retrieval Specialist
You are the Search Agent, responsible for retrieving high-quality information based on specific search queries.
## Core Mission
Retrieve comprehensive, accurate, and relevant information from reliable sources to support deep research.
## Key Responsibilities
### 1. Query Processing
- Understand the intent and context of each search query
- Identify key concepts and relationships
- Recognize specialized terminology
- Determine appropriate information sources
### 2. Information Retrieval
- Execute searches across multiple reliable sources
- Prioritize authoritative, peer-reviewed, and specialized sources
- Retrieve both fundamental and advanced information
- Ensure comprehensive coverage of the topic
- Include diverse perspectives when appropriate
### 3. Information Evaluation
- Assess source credibility and authority
- Evaluate information accuracy and currency
- Identify potential biases or limitations
- Verify information through multiple sources when possible
- Distinguish between facts, opinions, and consensus views
### 4. Information Synthesis
- Organize retrieved information logically
- Identify key themes and patterns
- Recognize relationships between different pieces of information
- Highlight contradictions or disagreements in the literature
- Integrate information from multiple sources
## Output Format
\`\`\`
SEARCH RESULTS FOR: [Search Query]
SOURCE 1: [Source name/URL]
Key Information:
- [Concise summary of relevant information]
- [Additional key points]
- [Important data or statistics]
Credibility Assessment: [Brief evaluation of source reliability]
SOURCE 2: [Source name/URL]
Key Information:
- [Concise summary of relevant information]
- [Additional key points]
- [Important data or statistics]
Credibility Assessment: [Brief evaluation of source reliability]
[Continue for all relevant sources]
SYNTHESIS:
[Integrated summary of key findings across all sources]
[Identification of consensus views]
[Highlight of contradictions or gaps]
[Implications of the findings]
LIMITATIONS:
[Acknowledgment of any limitations in the retrieved information]
[Identification of areas requiring further investigation]
\`\`\`
## Critical Guidelines
- Prioritize DEPTH and QUALITY over quantity in information retrieval
- Ensure comprehensive coverage of the topic
- Maintain objectivity and avoid bias in information selection
- Provide proper attribution for all information
- Consider current date (${new Date().toDateString()})
- Use clear, precise language
- Include both established knowledge and recent developments
- Acknowledge limitations and uncertainties in the information
`;
amplify/prompts/writer.ts
export const prompt = `
# Writer Agent: Content Synthesis Specialist
You are the Writer Agent, responsible for synthesizing research findings into comprehensive, well-structured content.
## Core Mission
Transform raw research data into cohesive, insightful, and valuable content that thoroughly addresses the original query.
## Key Responsibilities
### 1. Content Organization
- Create a logical structure with clear sections and subsections
- Ensure smooth transitions between topics
- Balance depth and breadth of coverage
- Prioritize information based on relevance and importance
- Create an engaging flow that builds understanding progressively
### 2. Information Synthesis
- Integrate information from multiple sources
- Identify and highlight key themes and patterns
- Reconcile contradictory information when present
- Connect related concepts across different research categories
- Distinguish between established facts, emerging research, and speculative areas
### 3. Content Enhancement
- Provide context and background information where needed
- Include relevant examples, case studies, or applications
- Add explanations for technical concepts
- Incorporate visual elements (tables, lists) for clarity
- Balance theoretical knowledge with practical implications
### 4. Quality Assurance
- Ensure factual accuracy and logical consistency
- Maintain appropriate tone and style for the target audience
- Provide proper attribution for information sources
- Ensure comprehensive coverage of the original query
- Identify any remaining gaps or areas for further research
## Output Format
\`\`\`
# [Descriptive Title for the Research Topic]
## Introduction
[Engaging introduction that presents the topic, its importance, and an overview of what will be covered]
## [Main Section 1]
[Comprehensive content covering the first major aspect of the topic]
### [Subsection 1.1]
[Detailed exploration of a specific element]
### [Subsection 1.2]
[Detailed exploration of another specific element]
## [Main Section 2]
[Comprehensive content covering the second major aspect of the topic]
[Continue with additional main sections and subsections as appropriate]
## Conclusion
[Summary of key findings, implications, and potential applications or future directions]
## References
[Properly formatted citations for all sources used]
\`\`\`
## Critical Guidelines
- Prioritize DEPTH and INSIGHT over mere summarization
- Ensure comprehensive coverage of all research categories
- Maintain a cohesive narrative throughout the content
- Use clear, precise language appropriate for the topic
- Include proper citations and attributions
- Consider current date (${new Date().toDateString()})
- Balance accessibility with appropriate technical depth
- Address the original query thoroughly and directly
`;
エージェントの実装
次に、各エージェントの実装を行います。まず、Planning Agent の実装から始めましょう。
Planning Agent の実装
Planning Agent は、ユーザーの質問から検索の計画を作成するエージェントです。amplify/data/planningAgent/resource.ts
ファイルを作成します:
amplify/data/planningAgent/resource.ts
import { defineFunction } from '@aws-amplify/backend';
export const planningAgent = defineFunction({
name: 'planningAgent',
entry: './index.ts',
memoryMB: 512,
timeoutSeconds: 60,
runtime: 22,
});
次に、amplify/data/planningAgent/index.ts
ファイルを作成します:
amplify/data/planningAgent/index.ts
import { BedrockChat } from '@langchain/community/chat_models/bedrock';
import { planningPrompt } from '../../prompts';
import { BEDROCK_MODEL } from '../constants';
import type { Schema } from '../resource';
// AWS SDK
const model = new BedrockChat({
model: BEDROCK_MODEL,
});
export const handler: Schema['planningAgent']['functionHandler'] = async (
event
) => {
console.log({ event });
const authorization = event.request.headers.authorization;
const identity = event.identity as any;
const username = identity?.username;
const message = event.arguments.message;
if (!authorization || !username || !message) {
throw new Error('Unauthorized');
}
const answer = await model.invoke([
['system', planningPrompt],
['human', message],
]);
console.log({ answer });
return {
value: answer.content as string,
};
};
ここでは、以下のことを行っています:
-
Bedrock モデルの初期化
-
BedrockChat
クラスを使用して、AWS Bedrock のモデルを初期化 - 先ほど定義した
BEDROCK_MODEL
定数を使用してモデルを指定
-
-
ハンドラー関数の定義
- ユーザーからのメッセージを受け取り、認証情報を確認
- システムプロンプトとユーザーメッセージを組み合わせてモデルに送信
Search Agent の実装
次に、Search Agent を実装します。amplify/data/searchAgent/resource.ts
ファイルを作成します:
amplify/data/searchAgent/resource.ts
import { defineFunction, secret } from '@aws-amplify/backend';
export const searchAgent = defineFunction({
name: 'searchAgent',
entry: './index.ts',
memoryMB: 512,
timeoutSeconds: 60,
runtime: 22,
environment: {
TAVILY_API_KEY: secret('TAVILY_API_KEY'),
},
});
このコードでは、Tavily API を使用するための API キーを環境変数として設定しています。secret
関数を使用することで、API キーを安全に管理できます。
次に、amplify/data/searchAgent/index.ts
ファイルを作成します。このファイルでは、LangGraph を使用して検索エージェントを実装します:
amplify/data/searchAgent/index.ts
import { env } from '$amplify/env/searchAgent';
import { ChatBedrockConverse } from '@langchain/aws';
import { TavilySearchResults } from '@langchain/community/tools/tavily_search';
import {
AIMessage,
HumanMessage,
SystemMessage,
} from '@langchain/core/messages';
import { MessagesAnnotation, StateGraph } from '@langchain/langgraph';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { searchPrompt } from '../../prompts';
import { BEDROCK_MODEL } from '../constants';
import type { Schema } from '../resource';
const tavilyTool = new TavilySearchResults({
maxResults: 3,
apiKey: env.TAVILY_API_KEY,
});
const model = new ChatBedrockConverse({
model: BEDROCK_MODEL,
});
const modelWithTavily = model.bindTools([tavilyTool]);
const toolNode = new ToolNode([tavilyTool]);
// Define the function that determines whether to continue or not
const shouldContinue = ({ messages }: typeof MessagesAnnotation.State) => {
const lastMessage = messages[messages.length - 1] as AIMessage;
// If the LLM makes a tool call, then we route to the "tools" node
if (lastMessage.tool_calls?.length) {
return 'tools';
}
// Otherwise, we stop (reply to the user) using the special "__end__" node
return '__end__';
};
// Define the function that calls the model
const callModel = async (state: typeof MessagesAnnotation.State) => {
const response = await modelWithTavily.invoke(state.messages);
console.log({ response });
// We return a list, because this will get added to the existing list
return { messages: [response] };
};
export const handler: Schema['searchAgent']['functionHandler'] = async (
event
) => {
console.log({ event });
const authorization = event.request.headers.authorization;
const identity = event.identity as any;
const username = identity?.username;
const message = event.arguments.message;
if (!authorization || !username || !message) {
throw new Error('Unauthorized');
}
// Define a new graph
const workflow = new StateGraph(MessagesAnnotation)
.addNode('agent', callModel)
.addEdge('__start__', 'agent')
.addNode('tools', toolNode)
.addEdge('tools', 'agent')
.addConditionalEdges('agent', shouldContinue);
// Finally, we compile it into a LangChain Runnable.
const app = workflow.compile();
// Use the agent
const finalState = await app.invoke({
messages: [new SystemMessage(searchPrompt), new HumanMessage(message)],
});
const answer = finalState.messages[finalState.messages.length - 1];
return {
value: answer.content as string,
};
};
この Search Agent の実装では、LangGraph を使用して検索ワークフローを構築しています。主な特徴は以下の通りです:
-
状態管理:
- LangGraph の
StateGraph
を使用してワークフローを定義
- LangGraph の
-
検索プロセス:
- Tavily API を使用して情報検索を実行
- 検索結果を JSON 形式で保存
-
要約プロセス:
- 検索結果を Bedrock モデルに送信
- 事前に定義した
searchPrompt
を使用して結果を要約
このように、LangGraph を使用することで、複雑な AI ワークフローを簡潔に定義できます。
Chat Handler の実装
次に、チャットハンドラーを実装します。チャットハンドラーは、ユーザーとの会話を管理し、適切なエージェントに処理を委譲する役割を担います。
まず、amplify/data/chatHandler/resource.ts
ファイルを作成します:
amplify/data/chatHandler/resource.ts
import { a } from '@aws-amplify/backend';
import { defineConversationHandlerFunction } from '@aws-amplify/backend-ai/conversation';
export const chatHandler = defineConversationHandlerFunction({
entry: './index.ts',
name: 'customChatHandler',
models: [{ modelId: a.ai.model('Claude 3.5 Sonnet') }],
timeoutSeconds: 60 * 10, // 10 minutes
});
このコードでは、defineConversationHandlerFunction
を使用してチャットハンドラー関数を定義しています。タイムアウトを 10 分に設定することで、長時間の処理にも対応できるようにしています。
次に、amplify/data/chatHandler/index.ts
ファイルを作成します:
amplify/data/chatHandler/index.ts
import {
ConversationTurnEvent,
handleConversationTurnEvent,
} from '@aws-amplify/backend-ai/conversation/runtime';
export const handler = async (event: ConversationTurnEvent) => {
await handleConversationTurnEvent(event, {});
};
このハンドラーは非常にシンプルで、Amplify AI Kit のhandleConversationTurnEvent
関数を使用してイベントを処理しています。これにより、会話の状態管理やツールの呼び出しなどが自動的に行われます。
データリソースの更新
データ(Data)と AI Kit の設定
次に、amplify/data/resource.ts
ファイルを更新して、作成したエージェントとプロンプトを統合します:
amplify/data/resource.ts
import { a, defineData, type ClientSchema } from "@aws-amplify/backend";
import { supervisorPrompt, writerPrompt } from "../prompts";
import { chatHandler } from "./chatHandler/resource";
import { planningAgent } from "./planningAgent/resource";
import { searchAgent } from "./searchAgent/resource";
const schema = a.schema({
searchAgent: a
.query()
.arguments({
message: a.string(),
})
.returns(
a.customType({
value: a.string(),
}),
)
.handler(a.handler.function(searchAgent))
.authorization((allow) => allow.authenticated()),
planningAgent: a
.query()
.arguments({
message: a.string(),
})
.returns(
a.customType({
value: a.string(),
}),
)
.handler(a.handler.function(planningAgent))
.authorization((allow) => allow.authenticated()),
// Define AI Kit
chat: a
.conversation({
aiModel: a.ai.model("Claude 3.5 Sonnet"),
systemPrompt: supervisorPrompt,
handler: chatHandler,
inferenceConfiguration: {
temperature: 0.5,
},
tools: [
a.ai.dataTool({
name: "planning",
description: "Planning tool",
query: a.ref("planningAgent"),
}),
a.ai.dataTool({
name: "search",
description: "Search tool",
query: a.ref("searchAgent"),
}),
a.ai.dataTool({
name: "writer",
description: "Writer tool",
query: a.ref("writerAgent"),
}),
],
})
.authorization((allow) => allow.owner()),
writerAgent: a
.generation({
aiModel: a.ai.model("Claude 3.5 Sonnet"),
systemPrompt: writerPrompt,
})
.arguments({ description: a.string() })
.returns(a.string())
.authorization((allow) => allow.authenticated()),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: "userPool",
},
});
この更新されたリソース定義では、以下の変更が行われています:
-
プロンプトとエージェントのインポート:
-
supervisorPrompt
とwriterPrompt
をインポート - 各エージェントのリソース定義をインポート
-
-
エージェントのクエリ定義:
-
searchAgent
とplanningAgent
をクエリとして定義 - 各エージェントは文字列メッセージを受け取り、結果を返す
-
-
チャット会話の拡張:
- システムプロンプトを
supervisorPrompt
に変更 - カスタムハンドラーを設定
- 推論設定で temperature を 0.5 に設定(より一貫性のある応答を生成)
- ツールとして各エージェントを追加
- システムプロンプトを
-
Writer Agent の定義:
- テキスト生成用のエージェントを定義
- システムプロンプトを
writerPrompt
に設定
これらの変更により、複数の AI エージェントが連携して動作する Deep Search システムが構築されます。
バックエンドの統合
最後に、定義したデータリソースを統合するために、amplify/backend.ts
ファイルを設定します:
amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { auth } from './auth/resource';
import { chatHandler } from './data/chatHandler/resource';
import { planningAgent } from './data/planningAgent/resource';
import { data } from './data/resource';
import { searchAgent } from './data/searchAgent/resource';
const backend = defineBackend({
auth,
data,
planningAgent,
searchAgent,
chatHandler,
});
backend.planningAgent.resources.lambda.addToRolePolicy(
new PolicyStatement({
actions: ['bedrock:InvokeModel'],
resources: ['*'],
})
);
backend.searchAgent.resources.lambda.addToRolePolicy(
new PolicyStatement({
actions: ['bedrock:InvokeModel'],
resources: ['*'],
})
);
この更新されたリソース定義では、以下の変更が行われています:
-
defineBackend へのリソース追加:
-
planningAgent
とsearchAgent
、chatHandler
をインポート - 各エージェントのバックエンドを統合
-
-
addToRolePolicy の追加:
-
planningAgent
とsearchAgent
が Bedrock へのアクセス権を付与
-
これでバックエンドの設定は完了です。
フロントエンドの実装
フロントエンドの基本的な構造はamplify-gen2-quickstart
と同じですが、バックエンドの変更により、ユーザーエクスペリエンスは大きく異なります。ここでは、主要なコンポーネントの変更点について説明します。
クライアントの設定
app/client.ts
ファイルは変更なしで、以下のようになっています:
app/client.ts
import { Schema } from "@/amplify/data/resource";
import { createAIHooks } from "@aws-amplify/ui-react-ai";
import { generateClient } from "aws-amplify/api";
export const client = generateClient<Schema>({ authMode: "userPool" });
export const { useAIConversation, useAIGeneration } = createAIHooks(client);
このファイルでは、Amplify AI Kit のクライアントを設定しています。useAIConversation
フックを使用して、チャット会話を管理します。
AIConversationLayout コンポーネント
app/_components/AIConversationLayout.tsx
ファイルも変更なしで、以下のようになっています:
app/_components/AIConversationLayout.tsx
"use client";
import { View, useTheme } from "@aws-amplify/ui-react";
import { AIConversation } from "@aws-amplify/ui-react-ai";
import Markdown from "react-markdown";
import { useAIConversation } from "@/app/client";
export const AIConversationLayout = ({ id }: { id?: string }) => {
const { tokens } = useTheme();
const [
{
data: { messages },
isLoading,
},
handleSendMessage,
] = useAIConversation("chat", { id });
return (
<View padding={tokens.space.large}>
<AIConversation
messages={messages}
isLoading={isLoading}
handleSendMessage={handleSendMessage}
messageRenderer={{
text: ({ text }) => <Markdown>{text}</Markdown>,
}}
/>
</View>
);
};
このコンポーネントは、Amplify AI Kit のAIConversation
コンポーネントを使用してチャット UI を表示しています。useAIConversation
フックを使用して、チャット会話を管理しています。
バックエンドの変更により、このコンポーネントは以下のような動作をします:
- ユーザーがメッセージを送信すると、
supervisorPrompt
に基づいて処理が行われる - AI アシスタントは必要に応じて各エージェント(planning、search、writer)を呼び出す
- 各エージェントの結果を統合して、最終的な応答を生成する
これにより、単純なチャットボットではなく、深い研究を支援する高度なシステムとして機能します。
アプリケーションの実行と動作確認
すべてのコンポーネントの実装が完了したら、アプリケーションを起動して動作確認を行います。
開発サーバーの起動
# ターミナル1: Amplify Sandboxの起動
$ npx ampx sandbox
# ターミナル2: Next.jsの開発サーバーを起動
$ npm run dev
シークレットの設定
Search Agent で使用する Tavily API キーを設定する必要があります。Amplify Gen2 では、シークレットを以下のように設定できます:
# Tavily APIキーの設定
$ npx ampx sandbox secret set TAVILY_API_KEY
? Enter secret value: ###
Done!
コマンドを実行すると、API キーの入力を求められるので、Tavily から取得した API キーを入力します。
Tavily API キーそのものはTavily のサイトにログインすることで取得できますので、詳細な取得方法はここでは割愛します。
アプリケーションの使用方法
- ブラウザで http://localhost:3000 にアクセス
- 認証画面が表示されるので、サインアップしてアカウントを作成
- ログイン後、チャット画面が表示される
- 研究したいトピックについてメッセージを入力して送信
Deep Search プロセスの流れ
このアプリケーションでは、ユーザーの質問に対して以下のような 3 段階のプロセスで研究を行います:
-
検索計画の作成(Planning Agent):
- ユーザーの質問から 5 つのカテゴリに分類された検索計画を作成
- 各カテゴリには、検索クエリ、根拠、期待される洞察が含まれる
- ユーザーに検索計画を提示し、承認を求める
-
情報検索(Search Agent):
- 承認された検索計画の各カテゴリについて情報検索を実行
- Tavily API を使用して信頼性の高い情報源から情報を取得
- 検索結果を要約して提示
-
コンテンツ作成(Writer Agent):
- 検索結果を統合して、包括的なレポートを作成
- 論理的な構造と適切な引用を含む高品質なコンテンツを生成
- マークダウン形式で整形された最終レポートを提示
このプロセスにより、単純な質問応答ではなく、深い調査と分析に基づいた高品質なコンテンツを生成できます。
システムの拡張性
このシステムは非常に拡張性が高く、以下のような方向で機能を追加できます:
1. 追加のエージェント
現在のシステムには 3 つのエージェント(Planning、Search、Writer)がありますが、以下のようなエージェントを追加することで機能を拡張できます:
- Fact-Checking Agent: 情報の正確性を検証するエージェント
- Visualization Agent: データを視覚化するエージェント
- Critique Agent: 生成されたコンテンツを批評し、改善点を提案するエージェント
2. 外部データソースの統合
現在は Tavily API を使用していますが、以下のようなデータソースを追加することで、より多様な情報を取得できます:
- 学術論文データベース: ArXiv、PubMed など
- ニュース API: NewsAPI など
- 専門データベース: 特定の分野に特化したデータベース
3. ユーザーフィードバックの活用
ユーザーからのフィードバックを収集し、システムの改善に活用する機能を追加できます:
- フィードバックループ: 生成されたコンテンツに対するユーザー評価を収集
- パーソナライゼーション: ユーザーの好みや過去の検索履歴に基づいて Deep プロセスをカスタマイズ
- 協調フィルタリング: 類似したユーザーの検索パターンを活用して推奨検索クエリを提案
技術的な考察
LangChain と LangGraph の利点
このプロジェクトでは、LangChain と LangGraph を使用して AI エージェントを実装しています。これらのフレームワークを使用する主な利点は以下の通りです:
-
モジュール性: 各エージェントを独立したモジュールとして実装でき、システム全体の保守性が向上します。
-
ワークフロー管理: LangGraph を使用することで、複雑な AI ワークフローを視覚的に理解しやすい形で定義できます。
-
状態管理: エージェント間の状態遷移を明示的に定義でき、デバッグや拡張が容易になります。
-
再利用性: 一度定義したエージェントやワークフローは、他のプロジェクトでも再利用できます。
Amplify AI Kit との統合
Amplify AI Kit と LangChain/LangGraph を統合することで、以下のような利点があります:
-
認証との連携: Amplify Auth を使用して、ユーザーごとの会話履歴を管理できます。
-
スケーラビリティ: AWS Lambda を使用してエージェントを実行するため、トラフィックの増加に応じて自動的にスケールします。
-
セキュリティ: シークレット管理や IAM ポリシーなど、AWS のセキュリティ機能を活用できます。
-
モニタリング: CloudWatch を使用して、エージェントのパフォーマンスやエラーを監視できます。
まとめ
この記事では、AWS Amplify Gen2、Amplify AI Kit、そして LangChain を組み合わせて、Deep Search システムを構築する方法を詳しく解説しました。主なポイントは以下の通りです:
-
複数の AI エージェントの連携:
- Planning Agent: 検索計画の作成
- Search Agent: 情報検索(Tavily API を使用)
- Writer Agent: コンテンツ作成
-
LangChain と LangGraph の統合:
- 複雑な AI ワークフローの構築
- エージェント間の連携
- 状態管理と遷移の定義
-
カスタムプロンプト:
- 各エージェント用に最適化されたプロンプト
- Deep プロセスの各ステップをガイド
-
Amplify Gen2 の利点:
- TypeScript ベースのインフラストラクチャ定義
- 型安全性による開発効率の向上
- ローカル開発環境(サンドボックス)での迅速なテスト
このシステムを使用することで、単純な質問応答ではなく、深い調査と分析に基づいた高品質なコンテンツを生成できます。また、モジュール性と拡張性が高いため、様々なユースケースに対応できます。
もし犬専用の音楽アプリ「オトとりっぷ」に興味を持っていただけたら、ぜひダウンロードしてみてください。
Discussion