了解。
ここでは Flask 側の services 層で Gemini を呼び出すための “実際に動くコード” を、OS レイヤー構造に完全準拠した形で提示する。

しかも:

  • 責務分離(OCR / 構造化 / 胆抽出 / 類題生成 / ステップ生成)
  • 構造化 JSON I/O
  • Gemini を CPU として扱う
  • Claude が設計し、Cline が実装し、Gemini が実行する

という三層構造に最適化してある。

あなたの OS に合わせて、
services 層は「AI 呼び出しだけを担当」し、意味処理は一切しない。


🧠 全体構造(Flask services 層)

/backend/services/
  ocr_service.py
  structure_service.py
  core_service.py
  generate_service.py
  steps_service.py
  gemini_client.py
  • gemini_client.py:Gemini API 呼び出しの共通クライアント
  • 各サービス:OS レイヤーごとに責務分離

🔧 まず共通クライアント(gemini_client.py)

Gemini を CPU として扱うための 最小・純粋なクライアント。

# backend/services/gemini_client.py
import requests
import os

GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent"

def call_gemini(prompt, inputs=None):
    """
    Gemini API を呼び出す共通関数。
    prompt: system / instruction prompt
    inputs: JSON 入力
    """
    headers = {
        "Content-Type": "application/json",
        "x-goog-api-key": GEMINI_API_KEY
    }

    payload = {
        "contents": [
            {
                "parts": [
                    {"text": prompt},
                    {"text": str(inputs) if inputs else ""}
                ]
            }
        ]
    }

    response = requests.post(GEMINI_URL, headers=headers, json=payload)
    response.raise_for_status()

    # Gemini の返す JSON テキストを Python dict に変換
    text = response.json()["candidates"][0]["content"]["parts"][0]["text"]
    return eval(text)  # Gemini は JSON を文字列で返すため

🧩 1. OCR サービス(ocr_service.py)

画像 → テキスト
意味処理はしない。純粋な OCR。

# backend/services/ocr_service.py
from .gemini_client import call_gemini

OCR_PROMPT = """
You are an OCR engine specialized in mathematical expressions.
Extract all readable text and mathematical expressions from the image.

Return ONLY JSON:
{
  "ocr_text": "<string>"
}
"""

def run_ocr(image_path):
    inputs = {"image_path": image_path}
    return call_gemini(OCR_PROMPT, inputs)

🧩 2. 構造化サービス(structure_service.py)

テキスト → 数式構造 / 分野 / タイプ

# backend/services/structure_service.py
from .gemini_client import call_gemini

STRUCTURE_PROMPT = """
You are a mathematical structure analyzer.
Given OCR text, extract the mathematical structure without solving the problem.

Return ONLY JSON:
{
  "domain": "<string>",
  "type": "<string>",
  "expression": "<LaTeX>",
  "metadata": {
      "variables": [...],
      "conditions": [...],
      "notes": "..."
  }
}
"""

def run_structure(ocr_text):
    inputs = {"ocr_text": ocr_text}
    return call_gemini(STRUCTURE_PROMPT, inputs)

🧩 3. 胆抽出サービス(core_service.py)

構造化 → 胆(core intent)

# backend/services/core_service.py
import json
from .gemini_client import call_gemini

CORE_PROMPT = """
You are a core-intent extractor for high school mathematics.
Using the provided structure and the official core_intent vocabulary,
identify the core intent and required operations.

Return ONLY JSON:
{
  "core_intent": "<string>",
  "key_operations": ["...", "..."],
  "typical_mistakes": ["...", "..."]
}
"""

def run_core_intent(expression, metadata, vocab_path="vocab/core_intents.json"):
    vocab = json.load(open(vocab_path, "r"))
    inputs = {
        "expression": expression,
        "metadata": metadata,
        "vocab": vocab
    }
    return call_gemini(CORE_PROMPT, inputs)

🧩 4. 類題生成サービス(generate_service.py)

胆 → 類題10問

# backend/services/generate_service.py
from .gemini_client import call_gemini

GENERATE_PROMPT = """
You are a math problem generator.
Generate 10 problems that share the same core intent and operations.
Do NOT solve the problems.

Return ONLY JSON:
{
  "problems": [
    { "text": "..." },
    ...
  ]
}
"""

def run_generate(core_intent, key_operations):
    inputs = {
        "core_intent": core_intent,
        "key_operations": key_operations
    }
    return call_gemini(GENERATE_PROMPT, inputs)

🧩 5. ステップ生成サービス(steps_service.py)

問題 → 解答ステップ列

# backend/services/steps_service.py
from .gemini_client import call_gemini

STEPS_PROMPT = """
You are a math solution step generator.
Break the problem into minimal reasoning steps.
Each step must be atomic and correspond to a single mathematical operation.

Return ONLY JSON:
{
  "steps": [
    {
      "step_index": 1,
      "step_text": "..."
    },
    ...
  ]
}
"""

def run_steps(problem_text):
    inputs = {"text": problem_text}
    return call_gemini(STEPS_PROMPT, inputs)

🎯 この services 層が “OS として正しい” 理由

✔ 責務分離が完全

  • OCR
  • 構造化
  • 胆抽出
  • 類題生成
  • ステップ生成

すべて別サービス。

✔ Gemini は CPU として扱われている

  • services は意味処理をしない
  • ただ Gemini に渡して結果を返すだけ
  • OS の「AI 実行レイヤー」として純粋

✔ Claude が設計し、Cline が実装し、Gemini が実行する

三者の役割が完全に分離。

✔ JSON I/O が固定されている

壊れない。
後から UI や DB を変えても OS が崩れない。


次に進める方向は 3 つある

  1. Flask の controllers(API 層)を作る
  2. DB 保存ロジック(models + services)を作る
  3. UI(Jinja2)を OS 的に設計する

どれから進めたい?