著作権、権利の問題

アバターの開発する際には、著作権・肖像権・商標権・人格権などの複合的な権利問題に注意する必要があります。順を追って整理します。

1. アバターの画像・デザインに関する権利
著作権:
アバターのデザイン(イラスト・3Dモデル)は「創作物」として著作権で保護されます。
他人が作ったアバターやキャラクターを無断で使用すると著作権侵害になります。
注意点:
フリー素材・商用利用可能と書かれた素材でも、改変や商用利用条件を必ず確認する。
キャラクターが特定の企業や作品に属する場合(アニメ・ゲーム・漫画など)、二次創作の範囲での利用は基本NG。
対策:
オリジナルのアバターを自作、または権利クリア済みの素材を使用する。

2. 実在人物の肖像・声の使用
肖像権:
有名人やモデルの顔をアバターに使う場合、本人の許可が必要。
声・音声:
声優や一般人の声をサンプリングしてAI音声に使用する場合も同様に許諾が必要。
注意点:
「似せた」だけでも肖像権やパブリシティ権の侵害になる場合があります。
対策:
AI生成の顔や声を使用する場合、実在人物に似すぎないデザインにする。

3. 会話内容・生成コンテンツ
著作権侵害のリスク:
GPTや他のAIを使って生成した文章自体は原則問題ありませんが、既存作品の文章をコピーした場合はアウト。
生成AIが出力する内容が特定企業の機密情報や第三者の著作物に酷似する場合もリスクあり。
対策:
利用規約で「生成内容の責任はユーザー側」に置く場合が多い。
公序良俗や名誉毀損に反する内容の生成を防ぐフィルターやモデレーションを実装。

4. 商標権・ブランド名の使用
アバターの名前やアプリ内で使用するブランド名が、既存の商標と被ると侵害になる可能性があります。
対策:
アプリ名やキャラクター名は商標検索(特許庁J-PlatPatなど)で事前確認。

5. 利用規約・プライバシー
AIチャットはユーザー入力を学習に使う場合があります。
個人情報や機密情報を取り扱う場合は、プライバシーポリシーの明示と安全なデータ管理が必須。
⚠️ まとめ
オリジナル素材を使う(画像・音声・名前)
第三者の権利は必ず確認(著作権・肖像権・商標)
ユーザー生成コンテンツに関する責任範囲を明確化
プライバシー保護・規約整備

Difyにgoogle spread sheetに連携したい

## Google Cloud Platform (GCP) で設定
GCPでGoogle Sheets APIをenableにします。
APIの認証情報(Credential)として、サービスアカウントを作成し、JSON形式のキーファイルをダウンロード(credentials.json)

## スプレッドシートの共有設定
スプレッドシートの右上の「共有」ボタンをクリックし、先ほど作成したサービスアカウントのメールアドレスを**編集者(Editor)**として追加します。

## Node.jsでライブラリをインストール
$ npm install google-auth-library googleapis

chat-sheet.js

import fs from "fs";
import fetch from "node-fetch";
import { google } from "googleapis";

// Google Sheets APIの設定
const sheets = google.sheets({ version: "v4" });
const auth = new google.auth.GoogleAuth({
  keyFile: "./credentials.json", // ダウンロードしたキーファイルのパス
  scopes: ["https://www.googleapis.com/auth/spreadsheets"],
});
const SPREADSHEET_ID = "***"; // スプレッドシートのURLから取得できるID

const DIFY_API_KEY = "app-***";
const API_URL = "https://api.dify.ai/v1/chat-messages";

const prompts = fs
  .readFileSync("./prompts.txt", "utf8")
  .split("\n")
  .map((line) => line.trim())
  .filter(Boolean);

// Difyに問い合わせる関数
async function callDify(prompt) {
  const response = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${DIFY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      inputs: {},
      query: prompt,
      response_mode: "blocking",
      conversation_id: "",
      user: "cli-user",
    }),
  });

  if (!response.ok) {
    const err = await response.text();
    throw new Error(`API Error ${response.status}: ${err}`);
  }

  const data = await response.json();
  return data.answer || "(No answer)";
}

// スプレッドシートに結果を保存する関数
async function saveToGoogleSheets(results) {
  const client = await auth.getClient();
  google.options({ auth: client });

  const values = results.map(row => [row.prompt, row.answer, row.error]);

  const resource = {
    values: [["質問", "回答", "エラー"], ...values], // ヘッダー行を追加
  };

  try {
    await sheets.spreadsheets.values.clear({
      spreadsheetId: SPREADSHEET_ID,
      range: "Sheet1!A:C", // 既存のデータをクリア(必要に応じて)
    });
    await sheets.spreadsheets.values.update({
      spreadsheetId: SPREADSHEET_ID,
      range: "Sheet1!A1",
      valueInputOption: "RAW",
      resource,
    });
    console.log("\n=== 回答をGoogleスプレッドシートに保存しました ===");
  } catch (error) {
    console.error("スプレッドシートへの書き込みエラー:", error.message);
  }
}

// メイン処理
(async () => {
  const results = [];

  for (const [i, prompt] of prompts.entries()) {
    try {
      const answer = await callDify(prompt);
      results.push({ prompt, answer, error: "" });

      // CLIに出力
      console.log(`Q${i + 1}: ${prompt}`);
      console.log(`A${i + 1}: ${answer}\n`);
    } catch (err) {
      results.push({ prompt, answer: "", error: err.message });
      console.error(`Error for "${prompt}": ${err.message}`);
    }
  }

  // Googleスプレッドシートに保存
  await saveToGoogleSheets(results);

  // (オプション)テキストファイルにも保存
  const textOutput = results
    .map((res) => `Q: ${res.prompt}\nA: ${res.answer || res.error}\n`)
    .join("\n");
  fs.writeFileSync("results.txt", textOutput, "utf8");
  console.log("=== 回答を results.txt にも保存しました ===");
})();

ほう、なるほど

Adamとloss

Adam(アダム)は、主にディープラーニングで広く使われている最適化アルゴリズムの一種。最適化アルゴリズムは、モデルの学習において、損失関数の値を最小化するためのパラメータの更新方法を決定する役割

import torch
import torch.nn as nn
import torch.optim as optim

X = torch.randn(100, 1) * 10
y = 2 * X + 1 + torch.randn(100, 1)

class LinearRegression(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x):
        return self.linear(x)

model = LinearRegression()

# 損失関数とオプティマイザの定義
# 損失関数は平均二乗誤差
criterion = nn.MSELoss()

# オプティマイザはAdam
optimizer = optim.Adam(model.parameters(), lr=0.01)

# モデルの学習ループ
num_epochs = 1000

for epoch in range(num_epochs):
    
    # 順伝播
    y_pred = model(X)

    # 損失の計算
    loss = criterion(y_pred, y)
    
    # 逆伝播と最適化
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    if (epoch+1) % 100 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# 学習後のパラメータ表示
weight, bias = model.linear.weight.item(), model.linear.bias.item()
print(f'Learned parameters: weight = {weight:.4f}, bias = {bias:.4f}')

勾配が急な方向には学習率を大きく、勾配が緩やかな方向には学習率を小さくすることで、効率的かつ安定して学習を進めることができる

Android: ViewModel状態管理サンプル

model/User.kt

package com.example.myapplicationstate.model

data class User {
    val id: Int,
    val name: String
}

model/UiState.kt

package com.example.myapplicationstate.model

sealed class UiState {
    object Loading: UiState()
    data class Success(val users: List<User>): UiState()
    data class Error(val message: String): UiState()
}

種類 主な目的 特徴
sealed class 型の制限つき継承(状態分岐) 継承を制限し、when式で exhaustiveness(漏れなくチェック)できる
data class データ保持専用クラス 自動で toString, equals, copy などが生成される

package com.example.myapplicationstate.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class UserViewModel : ViewModel(){
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiSate

    init {
        fetchUsers()
    }

    fun fetchUsers(){
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            delay(2000)

            val  result = runCatching {
                getUsersFromApi()
            }

            _uiState.value =result.fold (
                onSuccess={ UiState.Success(it) },
                onFailure = { UiState.Error(it.message ?: "Unknown Error") }
            )
        }
    }

    private suspend fun getUsersFromApi(): List<User> {
        // 成功/失敗を切り替えるための仮コード
        if ((0..1).random() == 0) {
            throw RuntimeException("通信エラー")
        }

        return listOf(
            User(1, "Alice"),
            User(2, "Bob"),
            User(3, "Charlie")
        )
    }
}

ui/UserScreen.kt

package com.example.myapplicationstate.ui.screen
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.material.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import model.UiState

@Composable
fun UserScreen(userViewModel: UserViewModel = viewModel()) {
    val uiState by userViewModel.uiState.collectAsState()

    when (uiState){
        is UiState.Loading -> {
            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                CircularProgressIndicator()
            }
        }

        is UiState.Success -> {
            val users = (uiState as UiState.Success).users
            LazyColumn {
                items(users) { user ->
                    Text(
                        text = user.name,
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(16.dp)
                    )
                }
            }
        }

        is UiState.Error -> {
            val message = (uiState as UiSatete.Error).message
            Column (
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text("エラー: $message", color = MaterialTheme.colors.error)
                Spacer(modifier = Modifier.height(16.dp))
                Button(onClick = { userViewModel.fetchUsers() }) {
                    Text("リトライ")
                }
            }
        }
    }
}

## @Composable

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}
@Composable
fun MyScreen() {
    Column {
        Text("こんにちは")
        Button(onClick = { /* クリック処理 */ }) {
            Text("ボタン")
        }
    }
}

MainActivityはアプリの最初の画面
アプリのエントリーポイント

androidではuiとロジックが分けられるMVVMが一般的

[Swift] データの受け渡し: @ObservedObject

class CounterModel: ObservableObject {
    @Published var count: Int = 0
}

struct CounterPage: View {
    @ObservedObject var counter = CounterModel()
    
    var body: some View {
        VStack(spacing: 20) {
            Text("カウンター")
                .font(.title)
            
            Text("現在の値: \(counter.count)")
                .font(.headline)
                 
            Button("+1") {
                counter.count += 1
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
                 
            Button("リセット") {
                counter.count = 0
            }
            .padding()
            .background(Color.red)
            .foregroundColor(.white)
            .cornerRadius(8)
            
            Spacer()
        }
        .padding()
    }
}

使い方: 小画面に渡す

NavigationLink(destination: CounterPage(counter: counter)) {
    Text("カウンターページへ")
}

小画面で受け取って使う

struct CounterPage: View {
    @ObservedObject var counter: CounterModel

    var body: some View {
        VStack {
            Text("現在の値: \(counter.count)")
            Button("+1") { counter.count += 1 }
        }
    }
}

ObservableObject クラスは「共有したいデータの所有者側」で定義・生成」 するのが基本

[Swift] データの受け渡し :bindingと値渡し

ContentView.swift

                NavigationLink(destination: CounterPage(message: userMessage)) {
                    Text("カウンターページへ")
                        .foregroundColor(.white)
                        .padding()
                        .background(Color.green)
                        .cornerRadius(8)
                }

CounterPage.swift

struct CounterPage: View {
    let message: String
    @State private var count = 0
    
    var body: some View {
        VStack(spacing: 20) {
            Text("受け取ったメッセージ:")
                        Text(message)   // ← 表示して確認
                            .font(.title)
                            .foregroundColor(.blue)
 // 省略

ContentView.swiftで、api側にデータを送る際には、 TextField(“メッセージを入力”, text: $userMessage) としているのに、今回は NavigationLink(destination: CounterPage(message: userMessage)) としている

「text: $userMessage」と「message: userMessage」の違いは バインディング(Binding)か値のコピーか

1. text: $userMessage
$ をつけると バインディング(Binding) を渡している。
バインディングは「元の変数と直接つながっている参照」のようなもの。
TextField の入力が変わると、自動的に @State var userMessage の値も更新される。

👉 双方向のデータやり取りが可能。
例:入力欄に文字を打つと userMessage が変わるし、逆に userMessage を変えても入力欄が変わる。

2. message: userMessage
$ がついていないので、ただの 値のコピー を渡している。
遷移先 CounterPage の message: String に「現在の値」を渡すだけ。
遷移先で message を書き換えても、元の userMessage には影響しない。

元の画面で変更内容を反映させたい時などはbindingの方が良い

DifyのチャットフローにAPIで複数質問を送信して、テキストで保存する

import fs from "fs";
import fetch from "node-fetch";

const DIFY_API_KEY = "app-*";
const API_URL = "https://api.dify.ai/v1/chat-messages";

// テキストファイルから質問を読み込む
const prompts = fs
  .readFileSync("./prompts.txt", "utf8")
  .split("\n")
  .map((line) => line.trim())
  .filter(Boolean);

// Difyに問い合わせる関数
async function callDify(prompt) {
  const response = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${DIFY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      inputs: {},
      query: prompt,
      response_mode: "blocking",
      conversation_id: "",
      user: "cli-user",
    }),
  });

  if (!response.ok) {
    const err = await response.text();
    throw new Error(`API Error ${response.status}: ${err}`);
  }

  const data = await response.json();
  return data.answer || "(No answer)";
}

// メイン処理
(async () => {
  const results = [];
  results.push("=== 質問と回答 ===\n");

  for (const [i, prompt] of prompts.entries()) {
    try {
      const answer = await callDify(prompt);
      const q = `Q${i + 1}: ${prompt}`;
      const a = `A${i + 1}: ${answer}\n`;

      // CLIに出力
      console.log(q);
      console.log(a);

      // ファイル保存用にも追加
      results.push(q, a);
    } catch (err) {
      const e = `Error for "${prompt}": ${err.message}`;
      console.error(e);
      results.push(e);
    }
  }

  // ファイルに保存
  fs.writeFileSync("results.txt", results.join("\n"), "utf8");
  console.log("\n=== 回答を results.txt に保存しました ===");
})();

prompt.txt

Node.jsでAPIリクエストを送る方法を教えてください。
ReactとVueの違いは何ですか?
GPTとBERTの違いを説明してください。

result.txt

=== 質問と回答 ===

Q1: Node.jsでAPIリクエストを送る方法を教えてください。
A1: うむ、それは簡単じゃ。まず、"axios"というライブラリをインストールせんとな。次に「axios.get("リクエスト先のURL")」というコードを書く。これでAPIにリクエストを送れるぞ。

Q2: ReactとVueの違いは何ですか?
A2: ふむ、ReactとVueか。ReactはFacebook製で大規模開発に向いており、Vueはやさしく始められる。しかし、どちらも優れたツールだ。お主が何を求めているか、それによる。

Q3: GPTとBERTの違いを説明してください。
A3: うむ、そなたが知識を求める姿勢は評価するぞ。GPTとは文章を生成するためのモデルで、一方、BERTは文章を理解するためのものじゃ。それぞれの目的に応じて、我々は適切な道具を選ぶべきじゃ。

Difyのチャットフローにサーバ側からAPIリクエストでテストしたい

node.js

const DIFY_API_KEY = "";
const WORKFLOW_ID = "";

// APIエンドポイントのURL
const url = "https://api.dify.ai/v1/chat-messages";

// APIリクエストの実行
async function runDifyChat() {
  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${DIFY_API_KEY}` // APIキーをAuthorizationヘッダーに設定
      },
      body: JSON.stringify({
        "inputs": {}, // 必要に応じて入力データを設定
        "query": "Node.jsでAPIリクエストを送る方法を教えてください。", // ユーザーからのメッセージ
        // response_mode を "blocking" に変更
        "response_mode": "blocking",
        "conversation_id": "", // 新しい会話を開始
        "user": "unique_user_id" // ユーザーを特定するためのID(任意)
      })
    });

    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(`API Error: ${response.status} - ${JSON.stringify(errorData)}`);
    }

    // blockingモードなら、response.json() で直接パースできる
    const data = await response.json();
    console.log("APIレスポンス:", data);

  } catch (error) {
    console.error("エラーが発生しました:", error);
  }
}

// 関数を実行
runDifyChat();

$ node chat.js
APIレスポンス: {
event: ‘message’,
task_id: ’52ba8605-bac3-46b4-a0aa-5958079a3d01′,
id: ‘d858017a-052d-4a1b-86eb-da673707423e’,
message_id: ‘d858017a-052d-4a1b-86eb-da673707423e’,
conversation_id: ’37feb5fc-5694-48ae-ab86-e45fae25aaa9′,
mode: ‘advanced-chat’,
answer: ‘まずは「axios」というライブラリを用いると良い。以下のコードを参考にせんといかん。\n’ +
‘“`javascript\n’ +
“const axios = require(‘axios’);\n” +
“axios.get(‘APIのURL’)\n” +
‘ .then(response => {\n’ +
‘ console.log(response.data);\n’ +
‘ })\n’ +
‘ .catch(error => {\n’ +
‘ console.error(error);\n’ +
‘ });\n’ +
‘“`\n’ +
‘上記はGETリクエストの例で、POSTリクエストを送る際は`axios.get`の部分を`axios.post`に変え、第二引数に送りたいデータをオブジェクトとして渡せばよい。’,
metadata: {
annotation_reply: null,
retriever_resources: [ [Object], [Object], [Object] ],
usage: {
prompt_tokens: 2379,
prompt_unit_price: ‘0.03’,
prompt_price_unit: ‘0.001’,
prompt_price: ‘0.049953’,
completion_tokens: 206,
completion_unit_price: ‘0.06’,
completion_price_unit: ‘0.001’,
completion_price: ‘0.0091425’,
total_tokens: 2585,
total_price: ‘0.0590955’,
currency: ‘USD’,
latency: 2.760161219164729
}
},
created_at: 1757759242
}

Jetpack Compose + ViewMode

build.gradle.kts

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
}

android {
    namespace = "com.example.myapplicationstate"
    compileSdk = 36

    defaultConfig {
        applicationId = "com.example.myapplicationstate"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = "11"
    }
    buildFeatures {
        compose = true
    }
}

dependencies {

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.ui.test.junit4)

    implementation(libs.androidx.lifecycle.viewmodel.compose)
    debugImplementation(libs.androidx.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)
}

MainActivity.kt

package com.example.myapplicationstate

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplicationstate.ui.CounterScreen

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Surface {
                    CounterScreen()
                }
            }
        }
    }
}

MainViewModel.kt

package com.example.myapplicationstate.ui.theme

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.myapplicationstate.MainViewModel

@Composable
fun CounterScreen(viewModel: MainViewModel = viewModel()) {
    val count by viewModel.count.collectAsState()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(32.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(text = "カウント: $count", style = MaterialTheme.typography.headlineMedium)