Skills MCP Model 博客 提交 Skills

DeepSeek Agent 開発チュートリアル

ゼロからインテリジェントエージェントを構築。ReAct推論パターン、Function Callingツール呼び出し、マルチエージェント協調、メモリ管理、計画と実行を習得。完全なPythonコードですぐに使えます。

学習を始める

AIエージェントとは何か?なぜ必要か?

AIエージェントは、環境を自律的に認識し、計画を立て、ツールを呼び出し、タスクを実行できるAIシステムです。従来のチャットボットとは異なり、エージェントは質問に答えるだけでなく、能動的に考え行動し、複雑なワークフローを完了できます。

Agent 概要

AI Agent の核心理念、アーキテクチャ構成要素、および従来の Chatbot との本質的な違いを理解することは、インテリジェントな Agent を構築するための理論的基盤です。

Agent vs 従来の Chatbot

比較項目 従来の Chatbot AI Agent
対話方式 単一ターンの Q&A、一問一答 多段階の推論、タスクチェーンを自律的に実行
能力の範囲 トレーニングデータに基づいてテキストを生成するのみ 外部ツール、API、データベースを呼び出し可能
記憶能力 コンテキストウィンドウ内の一時的な記憶 短期記憶 + 長期記憶(ベクトルストレージ)
タスクの複雑さ 簡単な対話、情報検索 多段階タスク、ワークフローの自動化
典型的なシナリオ カスタマーサービス Q&A、雑談、コンテンツ生成 データ分析、自動レポート、コード実行、プロセスオーケストレーション

Agent の4つの主要コンポーネント

コンポーネント 英語 役割 技術実装
大規模言語モデル LLM Agent の頭脳であり、推論と意思決定を担当 DeepSeek-Chat / DeepSeek-Reasoner
ツール Tools Agent の手足であり、具体的な操作を実行 Function Calling / API / コード実行エンジン
記憶 Memory 履歴情報の保存と検索 会話履歴 / ベクトルデータベース / 知識グラフ
計画 Planning タスクを分解し、実行戦略を策定 ReAct / Plan-and-Execute / Tree-of-Thought

Agent のワークフロー

  1. 知覚(Perception):ユーザー入力を受け取り、タスクの意図と目標を理解する
  2. 思考(Thinking):タスクを分析し、実行計画を立て、次の行動を決定する
  3. 行動(Action):ツールを呼び出して具体的な操作を実行する(検索、計算、API 呼び出しなど)
  4. 観察(Observation):ツールの実行結果を取得し、目標が達成されたか評価する
  5. ループ(Loop):観察結果に基づいて、実行を継続するか最終回答を出力するかを決定する

この「思考-行動-観察」ループが Agent の核となる動作パターンです。DeepSeek モデルの基礎知識については、DeepSeek モデルアーキテクチャの詳細 をご覧ください。

ReAct モード

ReAct(Reasoning + Acting)は、Agent の中核となる推論モードです。推論と行動を交互に行い、モデルが思考しながらツールを呼び出し、行動によって推論を検証します。

ReAct ループ: Thought - Action - Observation

ステップ 説明
Thought 現在の状態を分析し、次に何をするか決定する "今日の天気を調べて、傘を持っていくかどうかを決める必要がある"
Action 具体的なツール呼び出しを実行する get_weather("北京") 関数を呼び出す
Observation ツールの結果を取得し、タスクが完了したか評価する "北京は今日晴れ、気温25度、傘は不要"

DeepSeek + ReAct 完全実装

以下のコードは、DeepSeek モデルを使用して推論し、検索および計算ツールを呼び出す完全な ReAct Agent を示しています。

"""DeepSeek ReAct Agent — 完全実装""" import json import re from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.deepseek.com/v1", ) # ========== ツール定義 ========== def search_web(query: str) -> str: """検索エンジンを模擬し、クエリ結果を返す""" # 本番では実際の検索APIに置き換える mock_results = { "北京の天気": "北京は今日晴れ、気温22-28度、空気質は良好", "DeepSeek": "DeepSeek は深度求索公司が開発した大規模言語モデルで、1M コンテキストをサポート", } return mock_results.get(query, f"「{query}」に関する情報は見つかりませんでした") def calculate(expression: str) -> str: """安全な計算機""" try: # 数字と基本的な演算子のみ許可 if not re.match(r'^[\d\+\-\*\/\(\)\.\s]+$', expression): return "エラー: 式に不正な文字が含まれています" result = eval(expression) return str(result) except Exception as e: return f"計算エラー: {e}" # ツールレジストリ TOOLS = { "search": search_web, "calculate": calculate, } # ========== ReAct プロンプト ========== REACT_SYSTEM_PROMPT = """あなたはツールを使用できるインテリジェントなAgentです。厳密にReAct形式で回答してください。 利用可能なツール: - search(query): インターネットで情報を検索する - calculate(expression): 数学計算を実行する 回答形式(厳守): Thought: 現在の状況に対する思考と分析 Action: 実行する操作。形式は tool_name(arg) Observation: ツール実行の結果 ... (答えが得られるまで Thought/Action/Observation を繰り返す) Thought: 回答するのに十分な情報が得られた Final Answer: 最終回答 注意: 一度に実行できるActionは1つだけです。Observationを待ってから続行してください。""" # ========== ReAct ループ ========== def react_agent(user_query: str, max_steps: int = 5): """ReAct Agent メインループ""" messages = [ {"role": "system", "content": REACT_SYSTEM_PROMPT}, {"role": "user", "content": user_query}, ] for step in range(max_steps): print(f"\n--- Step {step + 1} ---") # DeepSeek モデルを呼び出す response = client.chat.completions.create( model="deepseek-chat", messages=messages, temperature=0.1, ) reply = response.choices[0].message.content print(reply) messages.append({"role": "assistant", "content": reply}) # 最終回答かどうかを確認 if "Final Answer:" in reply: final = reply.split("Final Answer:")[-1].strip() return final # Action を解析 action_match = re.search(r'Action:\s*(\w+)\(([^)]*)\)', reply) if action_match: tool_name = action_match.group(1) tool_arg = action_match.group(2).strip().strip('"').strip("'") if tool_name in TOOLS: observation = TOOLS[tool_name](tool_arg) print(f"Observation: {observation}") messages.append({ "role": "user", "content": f"Observation: {observation}", }) else: messages.append({ "role": "user", "content": f"エラー: 未知のツール '{tool_name}'", }) return "最大ステップ数に達したため、タスクを完了できませんでした。" # ========== テスト実行 ========== if __name__ == "__main__": query = "北京の今日の天気は?気温が25度を超えたら、30人それぞれが2本必要として、合計何本必要か計算して。" result = react_agent(query) print(f"\n{'='*60}\n最終回答: {result}\n{'='*60}")

ReAct モードの重要ポイント

  • 一度に1つのActionのみ実行し、Observationを待ってから続行する
  • ThoughtとActionは厳密にフォーマットを守り、正規表現で解析しやすくする
  • 無限ループを防ぐためにmax_stepsを設定し、通常5〜10ステップで十分
  • ツール関数はエラーハンドリングを適切に行い、意味のある結果を返す
  • DeepSeekの推論能力はReActモードに非常に適している

Function Calling ツール呼び出し

DeepSeekはネイティブのFunction Calling機能をサポートしており、モデルがツールを呼び出すタイミングを自動的に認識し、構造化された関数呼び出しパラメータを生成できます。これは手動でReAct形式を解析するよりも信頼性が高く、効率的です。

ツールスキーマの定義

OpenAI互換のFunction Calling形式でツールを定義します:

from openai import OpenAI import json client = OpenAI( api_key="sk-your-api-key", base_url="https://api.deepseek.com/v1", ) # ツールスキーマの定義(OpenAI Function Calling形式) tools = [ { "type": "function", "function": { "name": "get_weather", "description": "指定された都市のリアルタイムの天気情報を取得します", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "都市名(例:北京、上海、東京)", }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度単位、デフォルトはcelsius", }, }, "required": ["city"], }, }, }, { "type": "function", "function": { "name": "search_database", "description": "内部ナレッジベースで情報を検索します", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "検索キーワードまたは質問", }, "top_k": { "type": "integer", "description": "返却結果数、デフォルトは3", "default": 3, }, }, "required": ["query"], }, }, }, { "type": "function", "function": { "name": "send_email", "description": "メールを送信する", "parameters": { "type": "object", "properties": { "to": {"type": "string", "description": "受信者のメールアドレス"}, "subject": {"type": "string", "description": "メールの件名"}, "body": {"type": "string", "description": "メール本文"}, }, "required": ["to", "subject", "body"], }, }, }, ]

Function Calling Agent メインループ

# ツール実装 def get_weather(city: str, unit: str = "celsius") -> dict: """天気クエリのシミュレーション(本番では実際のAPIに置き換え)""" return { "city": city, "temperature": 26, "condition": "晴れ", "humidity": "45%", "unit": unit, } def search_database(query: str, top_k: int = 3) -> dict: return {"query": query, "results": [f"結果{i+1}: {query} に関する情報" for i in range(top_k)]} def send_email(to: str, subject: str, body: str) -> dict: return {"status": "sent", "to": to, "subject": subject} # ツールマッピングテーブル AVAILABLE_FUNCTIONS = { "get_weather": get_weather, "search_database": search_database, "send_email": send_email, } # ========== Function Calling Agent ========== def function_calling_agent(user_query: str, max_turns: int = 5): messages = [ {"role": "system", "content": "あなたはタスクを完了するためにツールを使用できるインテリジェントアシスタントです。中国語で回答してください。"}, {"role": "user", "content": user_query}, ] for turn in range(max_turns): response = client.chat.completions.create( model="deepseek-chat", messages=messages, tools=tools, tool_choice="auto", # モデルがツールを呼び出すか自動決定 ) assistant_message = response.choices[0].message # モデルがツールを呼び出す場合 if assistant_message.tool_calls: messages.append(assistant_message) for tool_call in assistant_message.tool_calls: func_name = tool_call.function.name func_args = json.loads(tool_call.function.arguments) print(f"[ツール呼び出し] {func_name}({func_args})") if func_name in AVAILABLE_FUNCTIONS: result = AVAILABLE_FUNCTIONS[func_name](**func_args) else: result = {"error": f"未知のツール: {func_name}"} print(f"[ツール結果] {result}") # ツール結果を会話に追加 messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result, ensure_ascii=False), }) else: # モデルが直接回答を返す return assistant_message.content return "最大ラウンド数に達しました。" # テスト result = function_calling_agent("北京の天気を調べて、admin@company.com に天気レポートを送信") print(f"\n最終結果:\n{result}")

Function Calling の利点

  • モデルがツール呼び出しの必要性を自動判断し、手動でのフォーマット解析が不要
  • パラメータは構造化された JSON で渡され、正規表現の解析エラーを回避
  • 複数ツールの並列呼び出しをサポート(tool_choice を "auto" に設定)
  • DeepSeek API は OpenAI Function Calling 形式と完全互換
  • 混合使用可能:モデルは1回の応答でツール呼び出しとテキスト生成を同時に行える

LangChain Agent 統合

LangChain は、ReAct ループとツール管理をカプセル化した高レベルな Agent 抽象化を提供します。LangChain を使用することで、Agent 開発はより簡潔で保守しやすくなり、複数の Agent タイプとツールの組み合わせをサポートします。

依存関係のインストール

pip install langchain langchain-openai langchain-community

LangChain Agent の作成

from langchain_openai import ChatOpenAI from langchain.agents import create_react_agent, AgentExecutor from langchain.tools import Tool from langchain.prompts import PromptTemplate from langchain_community.tools import WikipediaQueryRun from langchain_community.utilities import WikipediaAPIWrapper import requests # DeepSeek モデルの初期化 llm = ChatOpenAI( model="deepseek-chat", base_url="https://api.deepseek.com/v1", api_key="sk-your-api-key", temperature=0.1, ) # ========== カスタムツール開発 ========== # ツール1: 為替レート照会 def get_exchange_rate(currency_pair: str) -> str: """リアルタイム為替レートを照会。入力形式: USD/CNY または EUR/USD""" try: base, target = currency_pair.split("/") url = f"https://api.exchangerate-api.com/v4/latest/{base}" data = requests.get(url, timeout=5).json() rate = data["rates"][target] return f"1 {base} = {rate} {target}" except Exception as e: return f"為替レート照会失敗: {e}" # ツール 2: ファイル読み取り def read_file(filepath: str) -> str: """ローカルファイルの内容を読み取る""" try: with open(filepath, "r", encoding="utf-8") as f: content = f.read() if len(content) > 2000: return content[:2000] + "\n... (内容が長すぎるため、切り詰めました)" return content except Exception as e: return f"ファイルの読み取りに失敗: {e}" # ツール 3: Pythonコード実行 def execute_python(code: str) -> str: """Pythonコードを安全に実行し、結果を返す""" # 注意: 本番環境ではサンドボックス環境(Dockerなど)を使用してください import io, sys old_stdout = sys.stdout sys.stdout = buffer = io.StringIO() try: exec(code, {"__builtins__": { "print": print, "range": range, "len": len, "int": int, "str": str, "list": list, "dict": dict, "sum": sum, "sorted": sorted, "enumerate": enumerate, }}) result = buffer.getvalue() return result.strip() if result.strip() else "コードの実行に成功しました(出力なし)" except Exception as e: return f"実行エラー: {e}" finally: sys.stdout = old_stdout # ========== ツールの登録 ========== tools = [ Tool( name="為替レート照会", func=get_exchange_rate, description="リアルタイム為替レートを照会します。入力形式: USD/CNY または EUR/USD", ), Tool( name="ファイル読み取り", func=read_file, description="ローカルファイルの内容を読み取ります。入力はファイルパスです", ), Tool( name="Python実行", func=execute_python, description="Pythonコードを実行し、結果を返します。入力はPythonコードの文字列です", ), Tool( name="ウィキペディア", func=WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper(lang="zh")).run, description="中国語版ウィキペディアを検索します。入力は検索キーワードです", ), ] # ReActプロンプトテンプレート prompt = PromptTemplate.from_template("""あなたは以下のツールを使ってタスクを完了できるインテリジェントアシスタントです: {tools} 以下の形式で回答してください: Question: ユーザーの質問 Thought: 次に何をすべきか考える Action: ツール名 Action Input: ツールの入力パラメータ Observation: ツールの戻り結果 ... (Thought/Action/Action Input/Observation を繰り返すことができます) Thought: 十分な情報が得られました Final Answer: 最終回答 開始! Question: {input} Thought: {agent_scratchpad}""") # Agentと実行器の作成 agent = create_react_agent(llm=llm, tools=tools, prompt=prompt) agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, # 詳細な実行プロセスを出力 handle_parsing_errors=True, # 形式エラーを自動処理 max_iterations=8, # 最大反復回数 ) # Agentの実行 result = agent_executor.invoke({ "input": "USD/CNYの為替レートを照会し、1000ドルが何人民元に換算できるか計算してください", }) print(f"\n最終回答: {result['output']}")

AgentExecutorパラメータの説明

パラメータ 説明 推奨値
verbose 詳細な実行プロセスを出力するかどうか 開発時はTrue、本番ではFalse
handle_parsing_errors モデル出力の形式エラーを自動処理する True
max_iterations 無限ループを防ぐための最大反復回数 5-10
early_stopping_method 最大反復回数に達した後の処理方法 "generate"(最終回答を生成)

マルチエージェント協調

単一のエージェントの能力には限界があり、複雑なタスクには複数のエージェントが役割分担して協調する必要があります。CrewAIは現在最も人気のあるマルチエージェントフレームワークで、異なる役割のエージェントを定義し、プロセスに沿って協調してタスクを完了することができます。

CrewAIマルチエージェントアーキテクチャ

  • エージェント:役割、目標、バックストーリー、利用可能なツールを定義します
  • タスク:実行する具体的な作業を定義します。説明、期待される出力、割り当てられたエージェントを含みます
  • クルー:複数のエージェントを組織し、順次または階層的に協調させて一連のタスクを完了します
  • プロセス:タスクの実行順序を制御し、sequential(順次)とhierarchical(階層)をサポートします

CrewAI完全な例

# pip install crewai crewai-tools from crewai import Agent, Task, Crew, Process from crewai import LLM # DeepSeekを基盤モデルとして設定 deepseek_llm = LLM( model="deepseek/deepseek-chat", base_url="https://api.deepseek.com/v1", api_key="sk-your-api-key", ) # ========== エージェントの役割を定義 ========== # リサーチャー: 情報収集と分析を担当 researcher = Agent( role="シニアリサーチャー", goal="指定されたテーマについて深く調査し、包括的で正確な情報とデータを収集する", backstory="""あなたは10年間トップコンサルティング会社で働いてきた経験豊富なリサーチアナリストです。 情報を素早く収集し、重要なトレンドを特定し、データで見解を裏付けることに優れています。 あなたのレポートは常に構造が明確で、論拠が十分です。""", llm=deepseek_llm, verbose=True, allow_delegation=False, ) # ライター: コンテンツ作成を担当 writer = Agent( role="シニアコンテンツライター", goal="調査資料に基づいて、魅力的で専門的かつ正確なコンテンツを作成する", backstory="""あなたは複数の有名なテクノロジーメディアに寄稿してきたシニアテクノロジーライターです。 複雑な技術概念をわかりやすい記事に変換することに優れています。 あなたの記事は論理が明確で、データが正確で、読みやすいです。""", llm=deepseek_llm, verbose=True, allow_delegation=False, ) # レビュアー: 品質管理を担当 reviewer = Agent( role="コンテンツレビュアー", goal="コンテンツの正確性、読みやすさ、専門性をレビューし、出力品質を保証する", backstory="""あなたはトップ出版社で15年間働いてきたシニアエディターです。 文章に対して非常に高い基準を持ち、最も細かい誤りも見つけることができます。 あなたのレビュー基準には、事実の正確性、論理の一貫性、言語表現、フォーマットが含まれます。""", llm=deepseek_llm, verbose=True, allow_delegation=False, ) # ========== タスクを定義 ========== research_task = Task( description="""DeepSeekエージェント開発の最新動向を調査してください。以下を含みます: 1. DeepSeek APIのFunction Calling機能 2. LangChainとCrewAIのDeepSeekとの統合方法 3. 本番環境におけるエージェントのベストプラクティス 4. 他のLLM(GPT-4、Claude)とのエージェント機能の比較 詳細なデータと具体的な技術的詳細を提供してください。""", expected_output="主要な発見、技術比較、ベストプラクティスの推奨事項を含む構造化された調査レポート。500語以上", agent=researcher, ) writing_task = Task( description="""研究レポートに基づいて、開発者向けのDeepSeek Agent開発入門記事を執筆してください。 要件: 1. わかりやすい言葉で、Pythonの基礎知識がある開発者に適していること 2. 具体的なコード例とアーキテクチャ図の説明を含むこと 3. DeepSeekの独自の強みを強調すること 4. 文字数は800〜1000字に収めること""", expected_output="完全で、すぐに公開できる技術記事", agent=writer, ) review_task = Task( description="""記事の品質をレビューし、以下をチェックします: 1. 技術的な詳細が正確かどうか 2. コード例が実行可能かどうか 3. 論理が明確で一貫しているかどうか 4. 言語表現が専門的かどうか 問題がある場合は、具体的な場所を指摘し、修正提案を行ってください。""", expected_output="レビューレポート。合格/不合格の判定と具体的な修正提案を含む", agent=reviewer, ) # ========== Crewを編成して実行 ========== crew = Crew( agents=[researcher, writer, reviewer], tasks=[research_task, writing_task, review_task], process=Process.sequential, # 順次実行 verbose=True, ) result = crew.kickoff() print("\n=== 最終出力 ===\n", result)

マルチエージェント連携のベストプラクティス

  • 各エージェントは明確な役割を1つだけ担当し、責任の重複を避ける
  • タスクの説明は具体的にし、明確なexpected_output形式を含める
  • リサーチエージェントには検索ツール、実行エージェントにはコードツールを設定できる
  • 階層プロセスは複雑なプロジェクトに適しているが、追加のマネージャーエージェントが必要
  • allow_delegation=Falseを設定して、エージェント間でタスクを押し付け合わないようにする

Agentメモリ管理

メモリは、Agentを通常のChatbotと区別する重要な能力です。適切なメモリ管理により、Agentは過去の会話、ユーザーの好み、長期的な知識を記憶し、よりパーソナライズされ一貫性のある対話体験を提供できます。

3種類のメモリ

メモリタイプ 保存方法 ライフサイクル 適用シナリオ
短期メモリ メッセージリスト(コンテキストウィンドウ) 単一セッション 現在の会話コンテキスト、マルチターン対話
長期メモリ ベクトルデータベース / 知識グラフ セッションをまたいで永続化 ユーザーの好み、履歴知識、プロジェクト背景
作業メモリ 構造化データストレージ 単一タスク タスクの中間結果、実行状態

短期メモリ:会話履歴管理

from langchain.memory import ConversationBufferWindowMemory, ConversationSummaryMemory from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="deepseek-chat", base_url="https://api.deepseek.com/v1", api_key="sk-your-api-key", ) # 方法1: スライディングウィンドウメモリ — 直近Kターンの会話のみ保持 window_memory = ConversationBufferWindowMemory( k=5, # 直近5ターンのみ保持 return_messages=True, memory_key="chat_history", ) # 方法2: 要約メモリ — LLMを使用して過去の会話を自動要約 summary_memory = ConversationSummaryMemory( llm=llm, max_token_limit=500, # 要約は最大500トークン return_messages=True, memory_key="chat_history", ) # メモリを使用するAgent from langchain.agents import create_react_agent, AgentExecutor agent = create_react_agent(llm=llm, tools=tools, prompt=prompt) agent_with_memory = AgentExecutor( agent=agent, tools=tools, memory=window_memory, # メモリを注入 verbose=True, )

長期記憶:ベクトルストア実装

from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings from langchain.schema import Document import json, os, time # ベクトルストアの初期化 embeddings = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", model_kwargs={"device": "cpu"}, ) vectorstore = Chroma( persist_directory="./agent_memory", embedding_function=embeddings, collection_name="long_term_memory", ) class LongTermMemory: """長期記憶マネージャー""" def save_memory(self, content: str, metadata: dict = None): """記憶を保存""" doc = Document( page_content=content, metadata=metadata or {"timestamp": time.time()}, ) vectorstore.add_documents([doc]) print(f"記憶を保存しました: {content[:50]}...") def recall_memory(self, query: str, k: int = 3) -> str: """関連記憶を検索""" docs = vectorstore.similarity_search(query, k=k) if not docs: return "関連する履歴記憶がありません" memories = [f"- {d.page_content}" for d in docs] return "\n".join(memories) def clear_memory(self): """全記憶をクリア""" vectorstore.delete_collection() print("すべての記憶をクリアしました") # 使用例 memory = LongTermMemory() memory.save_memory("ユーザー設定: 簡潔な回答スタイルを好み、技術的な詳細はあまり求めない") memory.save_memory("プロジェクト背景: DeepSeek を中核エンジンとして使用する EC レコメンドシステムを開発中") relevant = memory.recall_memory("ユーザーの回答スタイルの好み") print(f"関連記憶:\n{relevant}")

ハイブリッド記憶アーキテクチャ

推奨される記憶アーキテクチャの組み合わせ:

  • 短期記憶: ConversationBufferWindowMemory(k=10)を使用し、最近の会話を保持
  • 長期記憶: ChromaDB ベクトルストアを使用し、ユーザー設定とプロジェクト知識を保存
  • 作業記憶: Python の辞書を使用し、単一タスク内で中間結果を渡す
  • 記憶検索: 各会話の前に自動的に関連する長期記憶を検索し、System Prompt に注入

計画と実行

複雑なタスクの場合、Agentはまずタスクを実行可能なサブタスクに分解し、段階的に実行する必要があります。Plan-and-Executeパターンは計画と実行を分離し、Agentがより複雑なマルチステップタスクを処理できるようにします。

Plan-and-Executeパターン

  1. Plan(計画フェーズ):LLMがタスクを分析し、詳細な実行計画(ステップリスト)を生成します
  2. Execute(実行フェーズ):Agentは計画に従って段階的に実行し、各ステップの結果を観察します
  3. Replan(再計画):ステップが失敗した場合や結果が期待に沿わない場合、後続の計画を動的に調整します
  4. Finalize(完了):すべてのステップの結果を集約し、最終回答を出力します

Plan-and-Executeの完全な実装

"""DeepSeek Plan-and-Execute Agent""" from openai import OpenAI import json client = OpenAI( api_key="sk-your-api-key", base_url="https://api.deepseek.com/v1", ) # ========== 計画フェーズ ========== PLANNER_PROMPT = """あなたはタスク計画の専門家です。ユーザーのタスクを明確な実行ステップに分解してください。 要件: 1. 各ステップは原子的な操作であり、それ以上分割できないこと 2. ステップ間には明確な依存関係があること 3. 異常な状況や代替案を考慮すること 4. JSON形式で出力すること 出力形式: { "task": "元のタスク説明", "steps": [ {"id": 1, "description": "ステップの説明", "depends_on": [], "expected_output": "期待される出力"}, {"id": 2, "description": "ステップの説明", "depends_on": [1], "expected_output": "期待される出力"} ] }""" def create_plan(task: str) -> dict: """実行計画を生成""" response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": PLANNER_PROMPT}, {"role": "user", "content": f"次のタスクの実行計画を作成してください:\n{task}"}, ], temperature=0.1, ) content = response.choices[0].message.content # JSONを抽出 if "```json" in content: content = content.split("```json")[1].split("```")[0] return json.loads(content) # ========== 実行フェーズ ========== EXECUTOR_PROMPT = """あなたはタスク実行の専門家です。現在のステップを実行し、結果を返してください。 現在のタスクの完全な計画: {plan} 過去の実行結果: {history} 現在のステップ: ステップ {step_id}: {step_description} このステップを実行して結果を返してください。ステップが失敗した場合は、理由を説明してください。""" def execute_step(plan: dict, step: dict, history: list) -> str: """単一のステップを実行""" response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": EXECUTOR_PROMPT.format( plan=json.dumps(plan, ensure_ascii=False, indent=2), history=json.dumps(history, ensure_ascii=False, indent=2) if history else "なし", step_id=step["id"], step_description=step["description"], )}, ], temperature=0.1, ) return response.choices[0].message.content # ========== メインループ ========== def plan_and_execute(task: str): """Plan-and-Execute メイン関数""" print("=" * 60) print(f"タスク: {task}") # フェーズ1: 計画 print("\n[計画フェーズ] タスクを分析中...") plan = create_plan(task) print(f"{len(plan['steps'])} 個の実行ステップを生成:") for s in plan["steps"]: print(f" ステップ {s['id']}: {s['description']}") # フェーズ2: 実行 print("\n[実行フェーズ] 実行を開始...") history = [] for step in plan["steps"]: print(f"\n--- 実行ステップ {step['id']}: {step['description']} ---") result = execute_step(plan, step, history) print(f"結果: {result[:200]}...") history.append({ "step_id": step["id"], "description": step["description"], "result": result, "status": "success" if "失敗" not in result else "failed", }) # Phase 3: 集計 print("\n[集計フェーズ] 最終レポートを生成中...") return history # テスト result = plan_and_execute("DeepSeek APIの価格戦略を分析し、競合比較レポートを作成する") print(f"\n実行完了、合計 {len(result)} ステップ完了")

動的プラン調整

あるステップの実行が失敗した場合、Agentに残りのステップを再計画させることができます。execute_stepが失敗を返した後、完了したステップの履歴を渡してcreate_planを呼び出し、モデルに新しい実行計画を生成させます。この適応能力は高度なAgentの特徴です。

RAG Agent

RAG Agentは、検索拡張生成(RAG)とAgentのツール呼び出し機能を組み合わせ、ナレッジベースからの情報検索と外部ツールの呼び出しの両方を可能にします。これはエンタープライズ向けAgentで最も一般的なアーキテクチャパターンです。

RAG Agentのアーキテクチャ

RAG Agentは2つの能力を統合します:

  • 知識検索:ベクトルデータベースから関連ドキュメントを取得し、正確なコンテキスト情報を提供
  • ツール呼び出し:外部APIの呼び出し、コード実行、データベースクエリなど
  • ハイブリッド意思決定:Agentがナレッジベースを検索すべきか、ツールを呼び出すべきかを自律的に判断

RAG Agentの完全な実装

from langchain_openai import ChatOpenAI from langchain.agents import create_react_agent, AgentExecutor from langchain.tools import Tool from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings from langchain.prompts import PromptTemplate llm = ChatOpenAI( model="deepseek-chat", base_url="https://api.deepseek.com/v1", api_key="sk-your-api-key", temperature=0.1, ) # ========== ナレッジベースの初期化(ベクトル検索ツール) ========== embeddings = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", model_kwargs={"device": "cpu"}, ) vectorstore = Chroma( persist_directory="./chroma_db", embedding_function=embeddings, ) def search_knowledge_base(query: str) -> str: """ナレッジベースで関連情報を検索""" docs = vectorstore.similarity_search(query, k=3) if not docs: return "ナレッジベースに関連情報が見つかりません" results = [] for i, doc in enumerate(docs): source = doc.metadata.get("source", "不明") results.append(f"[出典{i+1}: {source}]\n{doc.page_content[:300]}") return "\n\n---\n\n".join(results) # ========== RAG Agent ツールセットの定義 ========== rag_agent_tools = [ Tool( name="ナレッジベース検索", func=search_knowledge_base, description="内部ナレッジベースで情報を検索します。会社のドキュメント、製品情報、技術仕様を検索する必要がある場合にこのツールを使用します。入力は検索キーワードまたは質問です。", ), Tool( name="Python実行", func=execute_python, # 以前の関数を再利用 description="データ処理や計算のためにPythonコードを実行します。入力はPythonコード文字列です。", ), ] # RAG Agent 専用プロンプト RAG_AGENT_PROMPT = """あなたはナレッジベースのQ&Aエージェントであり、内部ナレッジベースを検索し、コードを実行できます。 利用可能なツール: {tools} ワークフロー: 1. 知識に関する質問には、優先的に「ナレッジベース検索」ツールを使用します 2. 計算が必要な場合は、「Python実行」ツールを使用します 3. 回答時には、ナレッジベースの出典を [出典X] の形式で引用してください 4. ナレッジベースに関連情報がない場合は、その旨を正直に述べてください 質問: {input} 思考: {agent_scratchpad}""" prompt = PromptTemplate.from_template(RAG_AGENT_PROMPT) # RAG Agent の作成 rag_agent = create_react_agent(llm=llm, tools=rag_agent_tools, prompt=prompt) rag_executor = AgentExecutor( agent=rag_agent, tools=rag_agent_tools, verbose=True, handle_parsing_errors=True, max_iterations=6, ) # RAG Agent のテスト result = rag_executor.invoke({ "input": "DeepSeek API の料金体系は?100万トークンでいくらかかりますか?", }) print(f"\n回答: {result['output']}")

RAG Agent の意思決定フロー

ユーザー質問タイプ Agent の決定 使用ツール
"API の呼び出し方は?" 知識に関する質問、ナレッジベースを検索 ナレッジベース検索
"100万トークンの費用を計算して" まず価格を検索し、次に計算 ナレッジベース検索 + Python実行
"DeepSeek と GPT-4 の比較" 知識に関する質問、両モデルの情報を検索 ナレッジベース検索(複数回呼び出し)

RAG ナレッジベース構築の詳細については、DeepSeek RAG ナレッジベース構築チュートリアル をご覧ください。

安全とガードレール

エージェントはツール呼び出し機能を持つため、危険な操作を実行する可能性があります。安全ガードレールはエージェントシステムの必須コンポーネントであり、エージェントが制御された範囲内で動作することを保証します。

エージェント安全の4層防御

防御層 役割 実装方法
入力検証 悪意のある入力をフィルタリングし、インジェクション攻撃を防ぐ 正規表現フィルタ + コンテンツモデレーションAPI
ツール権限制御 エージェントが呼び出せるツールとパラメータ範囲を制限する ホワイトリスト + パラメータ検証 + レート制限
出力フィルタリング 機密情報をフィルタリングし、データ漏洩を防ぐ 正規表現によるマスキング + 不適切な言葉のフィルタリング
監査ログ すべてのエージェント操作を記録し、追跡可能性を確保する 構造化ログ + データベース保存

安全ガードレールのコード実装

import re import logging from functools import wraps from datetime import datetime # ========== 1. 入力検証 ========== class InputGuard: """入力安全チェック""" # 危険なパターン DANGEROUS_PATTERNS = [ r'rm\s+-rf', # 削除コマンド r'DROP\s+TABLE', # SQLインジェクション r'__import__', # Pythonの危険なインポート r'os\.system', # システムコマンド r'eval\s*\(', # eval実行 r'exec\s*\(', # exec実行 ] MAX_INPUT_LENGTH = 10000 @classmethod def validate(cls, user_input: str) -> tuple[bool, str]: """入力を検証し、(安全かどうか, 理由) を返す""" if len(user_input) > cls.MAX_INPUT_LENGTH: return False, "入力長が制限を超えています" for pattern in cls.DANGEROUS_PATTERNS: if re.search(pattern, user_input, re.IGNORECASE): return False, f"危険なパターンを検出: {pattern}" return True, "OK" # ========== 2. ツール権限制御 ========== class ToolGuard: """ツール呼び出しの安全制御""" # ツールのホワイトリスト ALLOWED_TOOLS = {"search", "calculate", "get_weather"} # ツールごとの呼び出し回数制限 tool_call_count = {} MAX_CALLS_PER_TOOL = 10 MAX_TOTAL_CALLS = 30 @classmethod def check_tool_access(cls, tool_name: str, args: dict) -> tuple[bool, str]: """ツール呼び出しが許可されているか確認""" if tool_name not in cls.ALLOWED_TOOLS: return False, f"ツール '{tool_name}' はホワイトリストにありません" cls.tool_call_count[tool_name] = cls.tool_call_count.get(tool_name, 0) + 1 if cls.tool_call_count[tool_name] > cls.MAX_CALLS_PER_TOOL: return False, f"ツール '{tool_name}' の呼び出し回数が上限を超えました" total = sum(cls.tool_call_count.values()) if total > cls.MAX_TOTAL_CALLS: return False, "総呼び出し回数が上限を超えました" return True, "OK" # ========== 3. 出力フィルタリング ========== class OutputGuard: """出力の安全フィルタリング""" # 機密情報パターン SENSITIVE_PATTERNS = [ (re.compile(r'\b\d{15,19}\b'), '[銀行カード番号は非表示]'), (re.compile(r'\b1[3-9]\d{9}\b'), '[電話番号は非表示]'), (re.compile(r'\b[\w.-]+@[\w.-]+\.\w+\b'), '[メールアドレスは非表示]'), (re.compile(r'\b\d{6}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]\b'), '[身分証番号は非表示]'), ] @classmethod def sanitize(cls, output: str) -> str: """出力内容をサニタイズ""" sanitized = output for pattern, replacement in cls.SENSITIVE_PATTERNS: sanitized = pattern.sub(replacement, sanitized) return sanitized # ========== 4. 監査ログ ========== logging.basicConfig( filename='agent_audit.log', level=logging.INFO, format='%(asctime)s | %(levelname)s | %(message)s', ) def log_agent_action(action: str, detail: dict): """Agent操作ログを記録""" logging.info(f"{action} | {json.dumps(detail, ensure_ascii=False)}") # ========== 安全なAgentラッパー ========== class SafeAgent: """安全ガードレール付きAgentラッパー""" def __init__(self, agent_executor): self.executor = agent_executor def run(self, user_input: str) -> str: # 1. 入力検証 safe, reason = InputGuard.validate(user_input) if not safe: log_agent_action("INPUT_REJECTED", {"reason": reason}) return f"入力が拒否されました: {reason}" log_agent_action("INPUT_ACCEPTED", {"input": user_input[:100]}) # 2. Agentの実行 try: result = self.executor.invoke({"input": user_input}) output = result["output"] except Exception as e: log_agent_action("EXECUTION_ERROR", {"error": str(e)}) return "Agentの実行中にエラーが発生しました。後でもう一度お試しください。" # 3. 出力フィルタリング safe_output = OutputGuard.sanitize(output) log_agent_action("OUTPUT_GENERATED", {"output": safe_output[:100]}) return safe_output

セキュリティのベストプラクティス

  • Agentにファイルシステムやデータベースへの直接アクセス権限を与えず、制限付きAPIを使用する
  • コード実行ツールはサンドボックス環境(Dockerコンテナまたは制限付きPython)を使用する
  • すべての外部API呼び出しはプロキシを経由し、レートとアクセス範囲を制限する
  • Agentログを定期的に監査し、異常な動作を早期に発見する
  • System Promptを使用して、Agentにセキュリティ境界と禁止行為を明確に伝える

本番デプロイ

Agentを開発環境から本番環境に移行するには、完全なエンジニアリングソリューションが必要です。この章では、FastAPIを使用してAgentサービスを構築し、Dockerコンテナでデプロイし、監視とログシステムを設定する方法を紹介します。

FastAPI Agentサービス

# pip install fastapi uvicorn pydantic from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field from typing import Optional import uvicorn import time app = FastAPI( title="DeepSeek Agent API", version="1.0.0", description="DeepSeek Agent 智能体服务", ) # CORS設定 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) # ========== データモデル ========== class AgentRequest(BaseModel): query: str = Field(..., min_length=1, max_length=5000) session_id: Optional[str] = None max_steps: int = Field(default=5, ge=1, le=20) class AgentResponse(BaseModel): success: bool answer: str steps: int tools_used: list[str] execution_time: float session_id: Optional[str] # ========== APIエンドポイント ========== @app.get("/health") async def health_check(): return {"status": "healthy", "model": "deepseek-chat"} @app.post("/agent/run", response_model=AgentResponse) async def run_agent(request: AgentRequest): """Agentタスクを実行""" start_time = time.time() tools_used = [] try: # ここでAgent実行器を呼び出します # result = safe_agent.run(request.query) # サンプル返答 answer = f"Agentはクエリを処理しました: {request.query}" return AgentResponse( success=True, answer=answer, steps=3, tools_used=tools_used, execution_time=round(time.time() - start_time, 3), session_id=request.session_id, ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/agent/chat") async def chat(request: AgentRequest): """ストリーミングチャットエンドポイント(SSE)""" from fastapi.responses import StreamingResponse import asyncio async def generate(): # ストリーミング出力をシミュレート chunks = ["問題を分析中...", "ツールを呼び出し中...", "回答を生成中..."] for chunk in chunks: yield f"data: {chunk}\n\n" await asyncio.sleep(0.5) yield "data: [DONE]\n\n" return StreamingResponse(generate(), media_type="text/event-stream") # サービス起動 if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)

Dockerコンテナデプロイ

# Dockerfile FROM python:3.11-slim WORKDIR /app # 依存関係をインストール COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # アプリケーションコードをコピー COPY . . # ポートを公開 EXPOSE 8000 # サービス起動 CMD ["uvicorn", "agent_server:app", "--host", "0.0.0.0", "--port", "8000"] # requirements.txt # fastapi==0.115.0 # uvicorn[standard]==0.30.0 # openai==1.50.0 # langchain==0.3.0 # langchain-openai==0.2.0 # chromadb==0.5.0 # sentence-transformers==3.0.0 # pydantic==2.9.0 # docker-compose.yml # version: '3.8' # services: # agent: # build: . # ports: # - "8000:8000" # environment: # - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} # volumes: # - ./chroma_db:/app/chroma_db # - ./agent_audit.log:/app/agent_audit.log # restart: unless-stopped # ビルドと起動 # docker-compose up -d --build

監視とログ

# pip install prometheus-client from prometheus_client import Counter, Histogram, Gauge, generate_latest from fastapi import Response # 監視メトリクスの定義 agent_requests = Counter( 'agent_requests_total', 'Agent リクエスト総数', ['status'], ) agent_duration = Histogram( 'agent_request_duration_seconds', 'Agent リクエスト所要時間', buckets=[0.5, 1, 2, 5, 10, 30], ) tool_calls = Counter( 'agent_tool_calls_total', 'ツール呼び出し回数', ['tool_name'], ) active_sessions = Gauge( 'agent_active_sessions', 'アクティブセッション数', ) # Prometheus メトリクスエンドポイントの公開 @app.get("/metrics") async def metrics(): return Response(content=generate_latest(), media_type="text/plain")

本番環境チェックリスト

カテゴリ チェック項目 ツール/ソリューション
セキュリティ 入力検証、出力フィルタリング、ツール権限 SafeAgent ラッパー
監視 リクエスト量、レイテンシ、エラー率、ツール呼び出し Prometheus + Grafana
ログ リクエストログ、ツール呼び出しログ、エラーログ ELK / Loki / 構造化ログ
レート制限 API レート制限、並行制御 slowapi / Redis + トークンバケット
フォールトトレランス リトライ機構、デグラデーション戦略、ヘルスチェック tenacity + /health エンドポイント
デプロイ コンテナ化、ローリングアップデート、自動スケーリング Docker + K8s + HPA

デプロイ推奨事項

小規模アプリケーションの場合、単一マシンでの Docker Compose デプロイで十分です。本番グレードのアプリケーションでは、Prometheus + Grafana による監視と ELK によるログ収集を備えた K8s クラスタデプロイを推奨します。Agent サービスはステートレスであり、水平展開が可能ですが、LLM API の並行制限に注意する必要があります。

DeepSeek Agent開発よくある質問

ReActモードとFunction Calling、どちらを選ぶべきですか? +
Function Callingが推奨される最初の選択肢です。構造化JSONでパラメータを渡すため、正規表現解析の不確実性を回避でき、DeepSeek APIでネイティブにサポートされています。ReActモードはAgentの原理を学んだり理解したりするのに適しており、または高度にカスタマイズされた出力形式が必要な場合に使用します。本番環境では、Function Callingを優先してください。
Agentは最大でいくつのツールを呼び出せますか? +
理論上はハードな制限はありませんが、ツール数は5〜10個に抑えることをお勧めします。ツールが多すぎるとモデルの選択が難しくなり、エラー率が上がります。どうしても多くのツールが必要な場合は、ツールをカテゴリごとにグループ化し、各Agentが関連するツールのセットを担当するマルチAgentアーキテクチャを使用してください。DeepSeek APIのFunction Callingは10個以内のツールで最適に動作します。
Agentの実行が途中で止まってしまった場合はどうすればよいですか? +
max_iterationsまたはmax_stepsパラメータを設定することが、無限ループを防ぐ鍵です。また、AgentExecutorでhandle_parsing_errors=Trueを設定すると、形式エラーを自動的に処理できます。頻繁に止まる場合は、1) Promptが十分に明確か、2) ツールの説明が正確か、3) 適切なmax_iterations(推奨5〜10)が設定されているかを確認してください。
マルチAgentとシングルAgentはどう選べばよいですか? +
シングルAgentは明確で直線的なタスク(天気の確認、為替レートの計算など)に適しています。マルチAgentは複雑で分業と協力が必要なタスク(レポート作成、多段階データ分析など)に適しています。判断基準:タスクが一人で完了できるならシングルAgent、チームワークが必要ならマルチAgentを使用します。CrewAIは最も簡単なマルチAgent入門フレームワークです。
Agentのトークン消費は大きいですか?最適化するには? +
Agentのトークン消費は通常の会話よりも確かに高くなります。各推論で完全な会話履歴とツール定義を運ぶ必要があるためです。最適化の提案:1) ConversationSummaryMemoryを使用して履歴会話を圧縮する、2) ツールの説明を簡素化し冗長な情報を削除する、3) 適切なmax_iterationsを設定する、4) スライディングウィンドウでコンテキスト長を制限する、5) DeepSeek-V3の大きなコンテキストウィンドウを使用して圧縮オーバーヘッドを減らすことを検討する。
DeepSeek AgentとGPT-4 Agentの違いは何ですか? +
DeepSeekのFunction CallingはOpenAI形式と完全互換で、移行コストは非常に低いです。Agentタスクでは、DeepSeek-Chatは推論とツール呼び出しで優れた性能を発揮し、価格もGPT-4よりはるかに低いです。DeepSeek-Reasonerは複雑な推論タスクで独自の利点があります。主な違いはエコシステムです:OpenAIのAssistant APIはより完全なマネージドAgentソリューションを提供しますが、DeepSeekはより多くの自己構築が必要です。

DeepSeek関連チュートリアル

DeepSeekモデルの使用方法、デプロイ、エコシステムツールについてさらに学びます。

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

完全免费,取消任意时间。我们不会发送垃圾邮件。