AI エージェントとは、目標に向けて環境と相互作用しながらタスクをこなす知能システムです。AI エージェントを代表する特性は自律性と知性です。自律性は人間を介さず独立して自ら判断しタスクを遂行するう性質です。知性は思考能力や理解力、判断能力、知識の適応力の総称的な総称であり、人間らしい振る舞いをすることを重視しています。
AI エージェントを以下の二つでわかれます。
長期的な分析や計画をするタスクは学習の方が精度が良く、推論方針はリアルタイムな情報や社内情報への適用など、新しいタスクに即座に対応する必要がある場合に適しています。
まずは推論方式で作り、多くのユースケースで検証し、ビジネス的に価値があると判断できれば学習データを集めるフェーズに進むのがよい。
AI エージェントはユーザーの指示などから事前に与えられたプロフィール(Profile)に基づき、目標に向けて過去の経験をメモリ(Memory)から参照しながら計画(Planning)し、行動(Action)を行い環境(Environment)に作用します。
AI エージェントが目指す目標やその背景、AI エージェントが担う役割を指します。
| プロフィール | 概要 |
|---|---|
| 目標 | 達成すべきこと、目標に関する背景情報 |
| 基本情報 | 演じるペルソナの基本的な情報(職業、性別、年齢、年収、趣味、国籍など) |
| 心理的特性 | キャラクターの性格や行動パターン(社交的、楽観的、野心的など) |
| 社会的関係 | エージェントと他のエージェントの社会的関係(上司や部下、他のチームメイトの存在との関係性) |
| 特定の人物・集団 | 実在する特定の人物や集団(アニメや映画の人物、俳優、歴史上の人物、民族など) |
| スタイル | 話し方、口癖、方言、態度など |
## ツール呼び出し
ツールの役割を詳細に記述するほど、正確なツール選択をが可能になります。「対象行為をどのように行うか(手続き記憶)」と「事実や出来事を言葉で説明する知識(宣言的記憶)」の両側面が必要です。手続き記憶は魚のおろし方、包丁の使い方など、宣言的記憶は包丁は食べ物の調理に使う薄くて平たい刃物などを指す。
ツールを増やせばやすほど
LLM が自らが生成した内容をプロンプトに入れ、改善すべきことはないかを評価する方法です。
複数のモデルが互いに批評や意見交換を行いながら最終的に結論に達する方法。
評価プロンプトに人間が作成した評価基準リストを与えて修正点を見つける方法。
最終手段として人間によるフィードバックでプロセスを修正する方法。
論文では内在的な自己習性には限界があるとされています。自分の過ちに外部のフィードバックなしで気づくことができない。
なるべく外部フィードバックを使いましょう。
特にコンテンツ生成の自己修正は感性による部分があるため難しいですが、実用性のある明確な基準があれば評価ができます。
5回まで自己修正を許容した場合が精度が向上したという研究がある。まずは最大3回までの自己修正を設定するのが良い。
自己修正を入れるほど、回答までに時間がかかるので回答正答率とのトレードオフではある。外部フィードバックが与えられるポイントで自己修正を入れる方がよい。
人間との対話履歴や AI エージェントの行動履歴から情報を要約・抽出して保存するのを推奨。
まずは解決したい課題の業務プロセスを整理して下さい。意思決定が必要な箇所、ツールを必要とする箇所、作業が分かれる箇所、専門的な知識が必要となる箇所などでサブエージェントに切り出すポイントが見えてくる。
AI の出力を構造化データとして出力したいケースに利用できる。100% の精度で指定した構造化された出力ができたと報告されている。
response = client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "あなたは JSON を出力するようにせっけいされた便利なアシスタントです。"},
{"role": "assistant", "content": '{"winner": String}'},
{"role": "user", "content": "2020 年のワールドシリーズの優勝者は誰ですか?"}
]
)
以前に使用した入力トークンを再利用する機能。実行コストやレスポンス速度に大きな影響があるため、積極的に利用する方が良い。
LangChain を用いることで @tool デコレータを使用することで簡単に作成することができる。
@tool(args_schema=AddArgs)
def add(a: int, b: int) -> int:
"""
この Tool は 2 つの整数を引数として受け取り、それらの合計を返します。
Args:
a (int): 加算する最初の整数
a (int): 加算する 2 つ目の整数
Returns:
int: 2 つの整数の合計値
使用例:
入力: { "a": 3, "b": 5 }
出力: 8
"""
return a + b
どんな引数なのか、どういう返り値なのかを詳細に記述しましょう。
LLM を活用して、複数のエージェント間で情報をやり取りしながらタスクを進める複雑なワークフローを簡単に構築できます。
| 課題 | 内容 |
|---|---|
| 専門知識の理解 | 会計・人事などの専門知識が必要 |
| 質問の複雑さ | ・複数の質問事項を含む ・意図の読み取りに推測が必要 |
| 情報収集の困難さ | ・情報源が複数存在 ・必要な情報への到達が困難 |
| 回答品質の担保 | ・内容の正確性 ・適切な表現の選択 |
専門知識の理解、情報収集、回答品質の確保などは LLM が得意とする分野であるから。複雑な自然言語を理解し、文脈に応じた適切な回答を生成できる能力を持っている。また、大量の情報を効率的に処理し、必要な情報を素早く抽出して整理することも可能です。
まず問題をいくつかの小さいな問題に分割(計画)し、それぞれを個別に解決(行動)していくことで最終的な回答を得る方法です。これは分割統治法と呼ばれ、元の問題を管理しやすい小さな問題に分割し、それぞれの問題を解いて最後に統合することで元の問題を解くアプローチです。
この手法には以下のメリットがあります。
ポイントは
プロンプトにそのまま入れる長文脈言語モデルが一貫して RAG より高いパフォーマンスを示す。
RAG はロングコンテキストで圧倒的にコストパフォーマンスが高いという強みがある。
| ポイント | 説明 |
|---|---|
| チャンク化 | ドキュメントを適切に分割(チャンク化)しないと、検索時に意図した情報が得られないことがある。 大きすぎると不要な情報が混在し、小さすぎると文脈が失われる。 |
| メタデータの活用 | データ量が多い場合や類似の内容が多く含まれる場合は意図した検索結果が検索上位に来ないことがある。 メタデータ(作成日時、著者、カテゴリ、タグなど)を活用し、検索時に絞り込みを行うことで目的の情報を見つけやすくなる |
| ベクトル化の工夫 | 質問と回答ペアのみをベクトル化してインデックスを構築し、ユーザーの質問文としてベクトル化したもの同士で検索を行います。ペアとなる回答は結果の質問 ID などから辿れるようにしておくことでベクトル空間上で離れてしまう問題を緩和できる。 |
仮に検索インデックスが適切に構築されたとしても、適切な検索結果が得られない場合がある。
| 原因 | 説明 | 解決策 |
|---|---|---|
| クエリが曖昧 | ユーザーが入力する検索クエリが不明確または曖昧であると検索エンジンは適切な結果を返せません | ・クエリ拡張: ユーザーの入力クエリを LLM で拡張する手法 ・UI/UX による支援: 検索に有効な情報をユーザーが与えてくれるような仕組みを UI/UX から考える |
| 検索手法が不適切 | ベクトル検索が呈している場面でキーワード検索のみを使用すると、意味的な関連性を見逃す可能性がある | ベクトル検索とキーワード検索を適切に組み合わせたハイブリッド検索 |
ポイントは、
チェックポインターは、グラムのステートを保持するための仕組み。チェックポインターを指定しておくことで、ヒューマンフィードバックの際に処理が一時停止しても、フィードバックを得られた後にステートを復元して処理を再開することができる。
def _create_graph(self) -> CompiledStateGraph:
workflow = StateGraph(
state_schema=ResearchAgentState,
input=...,
output=...,
)
workflow.add_node("user_hearing", self.user_hearing)
....
return workflow.compile(checkpointer=MemorySaver())
ノードから Command オブジェクトを返すことで、遷移先のノードを動的に切り替えることができる。
def __call__(self, state: dict) -> ...:
messages = state.get("messages", [])
hearing = self.run(messages)
next_mode = (
"human_feedback" if hearing.is_need_human_feedback else "goal_setting"
)
return Command(
goto=next_node,
update={"hearing": hearing, "messages": message}
)
interrupt を利用することで、ノードの実行を一時中断して、ユーザーからのフィードバックを受け取ることができるようになる
def _human_feedback(self, state: ResearchAgentState) -> ...:
last_message = state["messages"][-1]
human_feedback = interrupt(last_message.content)
Send オブジェクトを活用すると複数のノードの呼び出しを一度に行うことが可能です。
for reading_result in reading_results:
gotos.append(
Send(
"analyze_paper",
PaperAnalyzerAgentInputState(
goal=state.get("goal", "")
reading_result=reading_result,
),
)
)
return Command(
goto=next_node,
update={"reading_results": reading_results}
)
AI エージェントの性能を高めるために人間の力が必要になります。また人間に AI エージェントを信頼してもらう UX を丁寧に設計する必要がある。
人間はチャットインターフェースを通じて、AI エージェントに指示を出したり、様子を確認することができます。
AI の性能を向上させるためにするためにプロンプトチューニングするのは老直がかかる作業です。労力を下げる工夫にプロンプトの初期設定を自動生成したり工夫があります。
AI エージェントの出力が不確実な要素を含み、最終出力に至るまでの過程を出力結果だけから把握することが困難だから。
分散トレーシングと同じように Trace で各 LLM やエージェントをモニタリングします。
毒メインエキスパートによる生成 AI の活用は、特定業務における AI エージェントの導入に先行すべきである。AI 活用によって業務効率を改善できない場合、AI エージェントによる特定業務の代行は難しいと判断されます。開発者が自ら業務代行として振る舞うことで業務解像度を上げることも可能。
AI エージェントでは複数のサブタスク間でエラーが伝播しやすいため、望ましくない出力や危険な動作を検知する「ガードレール」の設計が重要。ガードレールでは正規表現や文字数制限などのチェックから、LLM-as-a-Judge を用いたチェックまで様々な手法を採用することができる。これにより、「回答の正確性」などでハルシネーションの発生率やエラーの発生率を間接的に測定できる。