Skip to content

Instantly share code, notes, and snippets.

@chottokun
Created October 15, 2025 14:45
Show Gist options
  • Select an option

  • Save chottokun/cb4f82832ad8f5dcd9028f5d802433ca to your computer and use it in GitHub Desktop.

Select an option

Save chottokun/cb4f82832ad8f5dcd9028f5d802433ca to your computer and use it in GitHub Desktop.
ace_lesson05.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"machine_shape": "hm",
"gpuType": "L4",
"authorship_tag": "ABX9TyM9342bnB+YKWutvX6HVsKh",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/chottokun/cb4f82832ad8f5dcd9028f5d802433ca/ace_lesson05.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"# Agentic Context Engineering\n",
"https://arxiv.org/html/2510.04618v1"
],
"metadata": {
"id": "7G-JqqxQy7mf"
}
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "lcbeSUylyzEb"
},
"outputs": [],
"source": [
"# 必要なパッケージ(oyama, ollama Python SDK, sentence-transformers, torch)のインストール\n",
"!pip install -q ollama sentence-transformers torch\n",
"!pip install -q git+https://github.com/HawkClaws/oyama.git ollama sentence-transformers torch"
]
},
{
"cell_type": "code",
"source": [
"#@title Ollama/Hugging Face GGUFモデル選択\n",
"use_huggingface = False #@param {type:\"boolean\"}\n",
"model_name = \"gpt-oss\" #@param {type:\"string\"}\n",
"\n",
"if use_huggingface:\n",
" model_path = \"hf.co/\" + model_name\n",
"else:\n",
" model_path = model_name\n",
"\n",
"print(f\"Selected model path: {model_path}\")"
],
"metadata": {
"id": "-x7aThT4zPee"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import time\n",
"import os\n",
"from oyama import oyama\n",
"\n",
"print(f\"\\n検証モデル {model_path} のプルを開始します...\")\n",
"# oyama.run() はモデルのプルとOllamaサーバーのセットアップを行います\n",
"MODEL_NAME = oyama.run(model_path)\n",
"print(f\"モデル {MODEL_NAME} のプルとセットアップが完了しました。\")"
],
"metadata": {
"id": "ZsmjUXu2zRDv"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import time\n",
"import os\n",
"from oyama import oyama\n",
"from ollama import Client\n",
"from sentence_transformers import SentenceTransformer\n",
"import torch\n",
"from pydantic import BaseModel, Field\n",
"from typing import List\n",
"\n",
"# Pydanticモデルの定義:抽出する洞察の構造\n",
"class Insight(BaseModel):\n",
" content: str = Field(..., description=\"再利用可能な教訓または観察事項\")\n",
" # 必要に応じて、洞察のタイプや関連度などのメタデータを追加できます\n",
" # type: str = Field(\"general\", description=\"洞察のタイプ(例: 'コーディング', '戦略', 'エラー処理')\")\n",
" # relevance: float = Field(1.0, description=\"推定される関連度スコア\")\n",
"\n",
"class InsightsList(BaseModel):\n",
" insights: List[Insight] = Field(..., description=\"抽出された洞察のリスト\")\n",
"\n",
"\n",
"class Generator:\n",
" \"\"\"\n",
" 目的:現在の進化型コンテキストを使用して、推論軌跡を生成し、新しいタスクを解決しようとします。\n",
" \"\"\"\n",
" def __init__(self, client, model_name):\n",
" self.client = client\n",
" self.model_name = model_name\n",
"\n",
" def generate_trajectory(self, context, query):\n",
" print(f\"Generating trajectory for query: '{query}'\")\n",
" prompt = f\"\"\"あなたはAIアシスタントです。以下のコンテキストを使用して、クエリを解決するための詳細な推論軌跡を生成してください。応答は日本語で行ってください。\n",
"コンテキスト:\n",
"{context}\n",
"\n",
"クエリ: \"{query}\"\n",
"\n",
"ステップバイステップの推論軌跡を生成してください。\n",
"\"\"\"\n",
" print(\"\\n--- Final Prompt Sent to LLM ---\")\n",
" print(prompt)\n",
" print(\"----------------------------------\\n\")\n",
" try:\n",
" # Ollama chat APIを使用\n",
" response = self.client.chat(\n",
" model=self.model_name,\n",
" messages=[\n",
" {\n",
" 'role': 'user',\n",
" 'content': prompt,\n",
" }\n",
" ]\n",
" )\n",
" return response['message']['content'] # chat APIの応答形式に対応\n",
" except Exception as e:\n",
" print(f\"Error generating trajectory: {e}\")\n",
" return \"推論軌跡の生成中にエラーが発生しました。\"\n",
"\n",
"class Reflector:\n",
" \"\"\"\n",
" 目的:ジェネレーターのパフォーマンスを批判的に分析し、実行可能な洞察を抽出します。\n",
" \"\"\"\n",
" def __init__(self, client, model_name):\n",
" self.client = client\n",
" self.model_name = model_name\n",
"\n",
" def reflect_on_trajectory(self, trajectory, feedback):\n",
" print(f\"Reflecting on trajectory with feedback: '{feedback}'\")\n",
" prompt = f\"\"\"以下の推論軌跡とフィードバックを分析してください。批判的な反省を提供してください。応答は日本語で行ってください。\n",
"推論軌跡:\n",
"{trajectory}\n",
"\n",
"フィードバック:\n",
"{feedback}\n",
"\n",
"何がうまくいったか、何がうまくいかなかったか、そしてその理由を特定してください。\n",
"\"\"\"\n",
" print(\"\\n--- Reflection Prompt Sent to LLM ---\")\n",
" print(prompt)\n",
" print(\"----------------------------------\\n\")\n",
" try:\n",
" # Ollama chat APIを使用\n",
" response = self.client.chat(\n",
" model=self.model_name,\n",
" messages=[\n",
" {\n",
" 'role': 'user',\n",
" 'content': prompt,\n",
" }\n",
" ]\n",
" )\n",
" return response['message']['content'] # chat APIの応答形式に対応\n",
" except Exception as e:\n",
" print(f\"Error reflecting on trajectory: {e}\")\n",
" return \"推論軌跡の反省中にエラーが発生しました。\"\n",
"\n",
" def distill_insights(self, reflection_output):\n",
" print(f\"Distilling insights from reflection into structured format...\")\n",
" prompt = f\"\"\"以下の反省結果から、具体的で再利用可能な教訓または洞察を抽出してください。出力は提供されたJSONスキーマに厳密に従ったJSONオブジェクト形式で行ってください。応答内容は日本語で記述してください。\n",
"反省結果:\n",
"{reflection_output}\n",
"\n",
"このJSONスキーマに厳密に従ってください:\n",
"{InsightsList.model_json_schema()}\n",
"\"\"\"\n",
" print(\"\\n--- Distillation Prompt Sent to LLM ---\")\n",
" print(prompt)\n",
" print(\"----------------------------------\\n\")\n",
" try:\n",
" # Ollama chat APIと構造化出力を使用\n",
" response = self.client.chat(\n",
" model=self.model_name,\n",
" messages=[\n",
" {\n",
" 'role': 'user',\n",
" 'content': prompt,\n",
" }\n",
" ],\n",
" format='json' # JSON形式での出力を要求\n",
" )\n",
" # JSON応答をパースしてPydanticモデルに検証\n",
" insights_data = response['message']['content']\n",
" insights_list_obj = InsightsList.model_validate_json(insights_data)\n",
" # Pydanticオブジェクトから洞察のリスト(文字列)を抽出\n",
" insights = [item.content for item in insights_list_obj.insights]\n",
" return insights\n",
" except Exception as e:\n",
" print(f\"Error distilling insights: {e}\")\n",
" print(f\"Raw response content: {response.get('message', {}).get('content', 'N/A')}\")\n",
" return [\"洞察の抽出中にエラーが発生しました。\"]\n",
"\n",
" def format_delta_entries(self, insights):\n",
" print(\"Formatting insights into delta entries...\")\n",
" # 抽出された洞察は既に意味のある単位になっていると仮定し、そのままdelta entryに変換\n",
" return [{\"content\": i, \"metadata\": {}} for i in insights]\n",
"\n",
"class Curator:\n",
" \"\"\"\n",
" 目的:リフレクターの洞察をメインコンテキストに統合し、その構造を維持し、冗長性を防ぎます。\n",
" \"\"\"\n",
" def __init__(self, client=None, model_name=None):\n",
" self.client = client\n",
" self.model_name = model_name\n",
"\n",
" def synthesize_delta(self, delta_entries, existing_context):\n",
" print(f\"Synthesizing delta from {len(delta_entries)} entries...\")\n",
" # 本格的な実装では、ここでデルタを既存コンテキストと照合・合成\n",
" return delta_entries\n",
"\n",
" def merge_context(self, existing_context, delta_entries):\n",
" print(\"Merging context...\")\n",
" new_context = existing_context.copy()\n",
" existing_content = {item['content'] for item in existing_context}\n",
" for entry in delta_entries:\n",
" if entry['content'] not in existing_content:\n",
" new_context.append(entry)\n",
" existing_content.add(entry['content'])\n",
" return new_context\n",
"\n",
" def perform_deduplication(self, context):\n",
" print(\"Performing deduplication...\")\n",
" # 本格的な実装では、埋め込みの類似度に基づいて重複を削除します\n",
" # 現在は簡易的にcontentの重複を削除\n",
" seen = set()\n",
" deduplicated = []\n",
" for item in context:\n",
" if item['content'] not in seen:\n",
" deduplicated.append(item)\n",
" seen.add(item['content'])\n",
" return deduplicated\n",
"\n",
" def prune_context(self, context):\n",
" print(\"Pruning context...\")\n",
" # 本格的な実装では、関連性や使用頻度に基づいて項目を削除します\n",
" # 現在は簡易的に項目数を制限しない\n",
" return context"
],
"metadata": {
"id": "eP2vVn5pzUrE"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import torch\n",
"from sentence_transformers import SentenceTransformer\n",
"\n",
"class ContextStore:\n",
" \"\"\"\n",
" 目的: 進化型コンテキストを構成する「項目」のコレクションを管理します。\n",
" \"\"\"\n",
" def __init__(self):\n",
" self.context = []\n",
"\n",
" def add_bullet(self, bullet_content, embedding=None, metadata=None):\n",
" if metadata is None: metadata = {}\n",
" bullet = {\"content\": bullet_content, \"embedding\": embedding, \"metadata\": metadata}\n",
" print(f\"Adding bullet: {bullet['content']}\")\n",
" self.context.append(bullet)\n",
"\n",
" def retrieve_bullets(self, query, top_k, embedding_model):\n",
" print(f\"Retrieving top {top_k} bullets for query: '{query}'\")\n",
" if not self.context or query is None:\n",
" return []\n",
"\n",
" query_embedding = embedding_model.encode(query, convert_to_tensor=True)\n",
" # Move item embeddings to the same device as the query embedding\n",
" item_embeddings = torch.stack([torch.tensor(item[\"embedding\"]).to(query_embedding.device) for item in self.context if item.get(\"embedding\") is not None])\n",
"\n",
" if item_embeddings.nelement() == 0: # Check if tensor is empty\n",
" return []\n",
"\n",
" # コサイン類似度を計算\n",
" cos_scores = torch.nn.functional.cosine_similarity(query_embedding, item_embeddings)\n",
" top_results = torch.topk(cos_scores, k=min(top_k, len(self.context)))\n",
"\n",
" return [self.context[i] for i in top_results.indices]\n",
"\n",
" def generate_and_store_embeddings(self, embedding_model):\n",
" print(\"Generating and storing embeddings for context items...\")\n",
" for item in self.context:\n",
" if item.get(\"content\") and item.get(\"embedding\") is None:\n",
" item[\"embedding\"] = embedding_model.encode(item[\"content\"]).tolist() # Store as list for easier serialization\n",
" print(\"Finished generating and storing embeddings.\")"
],
"metadata": {
"id": "LRcefmA0zVWq"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class ACEOrchestrator:\n",
" \"\"\"\n",
" 目的: ジェネレーター、リフレクター、キュレーター間のフローを調整し、適応サイクルを管理します。\n",
" \"\"\"\n",
" def __init__(self, generator, reflector, curator, context_store, embedding_model):\n",
" self.generator = generator\n",
" self.reflector = reflector\n",
" self.curator = curator\n",
" self.context_store = context_store\n",
" self.embedding_model = embedding_model\n",
"\n",
" def run_adaptation_cycle(self, query, feedback, mode, top_k=5):\n",
" print(f\"\\n--- Running adaptation cycle in {mode} mode for query: {query} ---\")\n",
" # 1. 関連コンテキストを取得\n",
" current_context_items = self.context_store.retrieve_bullets(query=query, top_k=top_k, embedding_model=self.embedding_model)\n",
" current_context_str = \"\\n\".join([f\"- {item['content']}\" for item in current_context_items])\n",
"\n",
" # 2. 推論軌跡を生成\n",
" trajectory = self.generator.generate_trajectory(current_context_str, query)\n",
"\n",
" # 3. 軌跡とフィードバックを反省\n",
" reflection_output = self.reflector.reflect_on_trajectory(trajectory, feedback)\n",
" insights = self.reflector.distill_insights(reflection_output)\n",
" delta_entries = self.reflector.format_delta_entries(insights)\n",
"\n",
" # 4. コンテキストをキュレーション\n",
" synthesized_delta = self.curator.synthesize_delta(delta_entries, current_context_items)\n",
" merged_context = self.curator.merge_context(self.context_store.context, synthesized_delta)\n",
" deduplicated_context = self.curator.perform_deduplication(merged_context)\n",
" pruned_context = self.curator.prune_context(deduplicated_context)\n",
"\n",
" # 5. コンテキストストアを更新し、埋め込みを再生成\n",
" self.context_store.context = pruned_context\n",
" self.context_store.generate_and_store_embeddings(self.embedding_model)\n",
"\n",
" print(\"--- Adaptation cycle finished. ---\")\n",
" return trajectory, self.context_store.context\n",
"\n",
" def run_offline_adaptation(self, dataset, initial_context, epochs, top_k=5):\n",
" print(f\"\\n--- Running offline adaptation for {epochs} epochs... ---\")\n",
" self.context_store.context = initial_context\n",
" self.context_store.generate_and_store_embeddings(self.embedding_model)\n",
"\n",
" for epoch in range(epochs):\n",
" print(f\"\\n--- Epoch {epoch + 1}/{epochs} ---\")\n",
" for data_point in dataset:\n",
" query = data_point.get(\"query\")\n",
" feedback = data_point.get(\"feedback\")\n",
" if query and feedback is not None:\n",
" self.run_adaptation_cycle(query, feedback, mode=\"offline\", top_k=top_k)\n",
" return self.context_store.context\n",
"\n",
" def run_online_adaptation(self, stream_of_tasks, initial_context, top_k=5):\n",
" print(\"\\n--- Running online adaptation... ---\")\n",
" self.context_store.context = initial_context\n",
" self.context_store.generate_and_store_embeddings(self.embedding_model)\n",
"\n",
" for task in stream_of_tasks:\n",
" query = task.get(\"query\")\n",
" feedback = task.get(\"feedback\")\n",
" if query and feedback is not None:\n",
" self.run_adaptation_cycle(query, feedback, mode=\"online\", top_k=top_k)\n",
" return self.context_store.context"
],
"metadata": {
"id": "kv8fxe5TzZM4"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"from ollama import Client\n",
"from sentence_transformers import SentenceTransformer\n",
"\n",
"# 埋め込みモデルのロード\n",
"try:\n",
" embedding_model = SentenceTransformer('all-MiniLM-L6-v2')\n",
" print(\"Embedding model loaded successfully.\")\n",
"except Exception as e:\n",
" print(f\"Error loading embedding model: {e}\")\n",
" embedding_model = None\n",
"\n",
"# コンポーネントのインスタンス化\n",
"ollama_client = Client()\n",
"context_store = ContextStore()\n",
"generator = Generator(ollama_client, MODEL_NAME)\n",
"reflector = Reflector(ollama_client, MODEL_NAME)\n",
"curator = Curator(ollama_client, MODEL_NAME)\n",
"\n",
"# オーケストレーターのインスタンス化\n",
"ace_orchestrator = ACEOrchestrator(generator, reflector, curator, context_store, embedding_model)\n",
"\n",
"# 初期コンテキストの追加と埋め込み生成\n",
"if embedding_model:\n",
" context_store.add_bullet(\"メモ化の良い戦略は、辞書を使って結果を保存することです。\")\n",
" context_store.add_bullet(\"Pythonの関数は、メモ化なしの再帰呼び出しでは遅くなることがあります。\")\n",
" context_store.add_bullet(\"動的計画法はフィボナッチ数列問題を効率的に解くために使えます。\")\n",
" context_store.add_bullet(\"Pythonでのエラー処理はtry-exceptブロックで行うことができます。\")\n",
" context_store.generate_and_store_embeddings(embedding_model)\n",
" print(\"\\nInitial context items added and embeddings generated.\")"
],
"metadata": {
"id": "DFuKBT-CzayQ"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# 単一適応サイクルの実行例\n",
"query = \"フィボナッチ数列を計算するPython関数をどう書くか?\"\n",
"feedback = \"生成された関数は大きな数に対して遅かった。メモ化を検討すべきだ。\"\n",
"\n",
"# 適応サイクルを実行\n",
"trajectory, updated_context = ace_orchestrator.run_adaptation_cycle(\n",
" query=query,\n",
" feedback=feedback,\n",
" mode=\"online\",\n",
" top_k=10\n",
")\n",
"\n",
"print(\"\\n--- Adaptation Cycle Output ---\")\n",
"print(f\"\\nGenerated Trajectory:\\n{trajectory}\")\n",
"print(\"\\nUpdated Context:\")\n",
"for item in updated_context:\n",
" print(f\"- {item.get('content', 'N/A')}\")"
],
"metadata": {
"id": "QbJj2M_-zcuX"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# オンライン適応の実行例\n",
"stream_of_tasks = [\n",
" {\"query\": \"Pythonでエラーを処理する方法は?\", \"feedback\": \"提案されたエラー処理はあまり具体的ではありませんでした。\"},\n",
" {\"query\": \"Pythonのロギングのベストプラクティスは?\", \"feedback\": \"ロギングの例は役立ちました。\"}\n",
"]\n",
"\n",
"# オンライン適応を実行(現在のコンテキスト状態から開始)\n",
"final_context_online = ace_orchestrator.run_online_adaptation(\n",
" stream_of_tasks=stream_of_tasks,\n",
" initial_context=context_store.context,\n",
" top_k=5\n",
")\n",
"\n",
"print(\"\\n--- Online Adaptation Final Context ---\")\n",
"for item in final_context_online:\n",
" print(f\"- {item.get('content', 'N/A')}\")"
],
"metadata": {
"id": "2LB9O6dCzezf"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# オフライン適応の実行例\n",
"offline_dataset = [\n",
" {\"query\": \"pandasでCSVファイルを読み込む方法は?\", \"feedback\": \"サンプルコードは明確で正しく動作しました。\"},\n",
" {\"query\": \"matplotlibでヒストグラムを描画するには?\", \"feedback\": \"プロットは成功裏に生成されました。\"},\n",
" {\"query\": \"numpyで記述統計量を計算する方法\", \"feedback\": \"説明が少し分かりにくかったです。\"}\n",
"]\n",
"\n",
"# オフライン適応を実行(現在のコンテキスト状態から開始)\n",
"final_context_offline = ace_orchestrator.run_offline_adaptation(\n",
" dataset=offline_dataset,\n",
" initial_context=context_store.context,\n",
" epochs=3,\n",
" top_k=10\n",
")\n",
"\n",
"print(\"\\n--- Offline Adaptation Final Context ---\")\n",
"for item in final_context_offline:\n",
" print(f\"- {item.get('content', 'N/A')}\")"
],
"metadata": {
"id": "D0_RtA_Ezfif"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# 単一適応サイクルの実行例\n",
"query = \"素数を計算するPython関数をどう書くか?\"\n",
"feedback = \"生成された関数は大きな数に対して遅かった。\"\n",
"\n",
"# 適応サイクルを実行\n",
"trajectory, updated_context = ace_orchestrator.run_adaptation_cycle(\n",
" query=query,\n",
" feedback=feedback,\n",
" mode=\"online\",\n",
" top_k=10\n",
")\n",
"\n",
"print(\"\\n--- Adaptation Cycle Output ---\")\n",
"print(f\"\\nGenerated Trajectory:\\n{trajectory}\")\n",
"print(\"\\nUpdated Context:\")\n",
"for item in updated_context:\n",
" print(f\"- {item.get('content', 'N/A')}\")"
],
"metadata": {
"id": "Oy8QSfu36zhU"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# 単一適応サイクルの実行例\n",
"query = \"フィボナッチ数列を計算するPython関数をどう書くか?\"\n",
"feedback = \"生成された関数は大きな数に対して遅かった。メモ化を検討すべきだ。\"\n",
"\n",
"# 適応サイクルを実行\n",
"trajectory, updated_context = ace_orchestrator.run_adaptation_cycle(\n",
" query=query,\n",
" feedback=feedback,\n",
" mode=\"online\",\n",
" top_k=10\n",
")\n",
"\n",
"print(\"\\n--- Adaptation Cycle Output ---\")\n",
"print(f\"\\nGenerated Trajectory:\\n{trajectory}\")\n",
"print(\"\\nUpdated Context:\")\n",
"for item in updated_context:\n",
" print(f\"- {item.get('content', 'N/A')}\")"
],
"metadata": {
"id": "4R8HH7NgFBWc"
},
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment