【AI面接・AI商談】リアルタイムで話すAIアバターの作り方|Anam×Fish Audioで日本語対応(コード・設定値つき)
Google Meetで、画面の向こうの相手とリアルタイムに会話するAIアバターを自作しました。顔は画像1枚、声は日本語の音声合成、受け答えはAI。月12ドルのプランと、会話5分あたり10円程度の音声代で動いています。
最近「オンライン面接に来た応募者がAIだった」という話を耳にするようになりました。AI面接というと、企業がAIに面接をさせるツールを思い浮かべる方が多いと思いますが、逆に”面接を受ける側”や”商談の相手”をAIが演じることも、もう普通にできてしまいます。
実際にどこまでできるのかを確かめるために、私は「仕事の依頼をしてきた相手が、実はAIだった」という動画を作りました。
この記事では、その動画で使ったAIアバターの作り方を、設定値やコードも含めて、自分でも再現できるように解説します。
目次(読みたい所をクリック!!)
実際に作った動画:仕事の依頼をしてきた相手がAIだった
「Web制作を外注したい」という商談の相手が、途中から「認定パートナーに登録してほしい」「そのためにカリキュラムを受けてほしい」「受講料は48万円」と、スクールの勧誘に話をすり替えていく、という内容です。
相手の「田口さん」は、顔も声も話す内容も、すべてAIで作った架空の人物です。商談そのものは、仕組みを確かめるための再現として撮影しています。
全体像:AIアバターは4つの部品でできている

仕組みはシンプルで、次の4つを組み合わせているだけです。
| 部品 | 使ったもの | 役割 |
|---|---|---|
| 顔 | 画像生成AI → Anam(アナム) | 画像1枚から、口や表情が動くアバターを作る |
| 声 | Fish Audio(フィッシュオーディオ) | 日本語の自然な音声を作る |
| 耳と頭脳 | 人格設定のテキスト+OpenAIのAI | 相手の声を文字にして、返事の文章を作る |
| 配線 | OBS+仮想オーディオ | アバターの映像と声を Google Meet に流す |
会話の流れは、こうなっています。
- 相手が話す → OpenAI のリアルタイム文字起こしで文字にする(話し終わりも自動で判定)
- AIが、人格設定に沿って返事の文章を作る
- Fish Audio が、その文章を日本語の音声にする
- その音声を Anam に流し込むと、アバターが口を動かして話す
- OBS で映像を取り込み、Meet にカメラとして流す
つまり、Anamは「顔と口の動き」だけを担当し、声と中身は別のサービスで作って差し込んでいる、という構成です。
「Anamだけで完結しないの?」と思うかもしれません。Anamにも音声とAIは入っているのですが、標準の日本語の声はイントネーションが日本人離れしていて、どうしても違和感が残りました。そこで、声だけを日本語に強い Fish Audio に差し替えています。
準備するもの
| もの | 用途 | 費用 |
|---|---|---|
| Anam のアカウント | アバター | 無料プランあり(本格的に使うなら Starter 月12ドル) |
| Fish Audio のアカウント | 音声 | 使った分だけ(前払い) |
| OpenAI の APIキー | 聞き取り(文字起こし)と、返事を作るAI | 使った分だけ |
| Python 3 | 小さな中継サーバーを動かす | 無料(pip install requests) |
| Google Chrome | アバターの画面・操作盤 | 無料 |
| Microsoft Edge | Google Meet を開く | 無料 |
| OBS Studio | 映像を Meet に流す | 無料 |
| VB-CABLE と Hi-Fi Cable | 音声を Meet とやりとりする | 無料(VB-Audio) |
APIキーは、プログラムに直接書かず .env というファイルにまとめておきます。
ANAM_API_KEY=ここにAnamのキー
FISH_API_KEY=ここにFish Audioのキー
OPENAI_API_KEY=ここにOpenAIのキーステップ1:顔をつくる
1-1. 画像生成AIで「架空の人物」を作る
ChatGPT などの画像生成AIで、実在しない人物の顔写真を作ります。
ここで一番の学びがありました。きれいに作るほど、AIっぽくなるのです。

最初に作ったのは左の画像です。紺のスーツにガラス張りのオフィス、均一なライティング。一見よくできていますが、アバターにして動かすと「プロが撮ったポートレート」にしか見えず、Meetの画面としては明らかに不自然でした。
そこで、次の3点を指定して作り直しました。
- 顔を平凡にする:無精ひげ、毛穴、肌のムラ、目の下のクマ。イケメンにしない
- カメラを安物にする:ノートPCの内蔵カメラで、目線より下から見上げる角度。ノイズと圧縮、蛍光灯の色かぶり
- 背景に生活感を出す:曲がったカレンダー、ホワイトボード、書類の山
実際に使ったプロンプトの要点はこちらです(英語で指示しています)。
An ordinary, unremarkable 36-year-old Japanese man on a low-quality laptop webcam
during a Google Meet call. Plain everyday Japanese face, visible stubble,
rough skin texture with visible pores, tired eyes with faint dark circles.
NOT handsome, NOT a model, NOT a magazine portrait.
CAMERA: cheap built-in 720p laptop webcam on a desk BELOW eye level, tilted up.
IMAGE QUALITY: soft focus, visible digital sensor noise, mild video compression,
flat low contrast.
BACKGROUND: a small cheap office, a wall calendar hanging slightly crooked,
a whiteboard with faint smudged marker, a stack of papers.
NOT studio lighting, NOT retouched, NOT glamorous.
Looks exactly like a real screenshot from a boring online meeting.さらに2点、あとから手を入れました。
- 口を閉じた表情にする:歯を見せて笑っている画像だと、黙っているときも笑顔のまま固まって不自然でした。同じ人物のまま口元だけを変えるには、元の画像を参照画像として渡し、「表情だけ変えて、口を閉じた無表情に」と指示します
- 服と照明を変える:スーツや水色シャツより、ジャケパンのラフなオフィスカジュアル+素人っぽい室内照明のほうが自然でした。これも参照画像を渡して、服と照明だけを変えています
つまり、本物っぽさは「きれいさ」ではなく「ほどよい雑さ」で出る、ということです。
⚠ 実在の人の写真は使わないでください。Anamは写真1枚からアバターを作れてしまうので、実在の人物で作るとなりすましになります。
1-2. Anam でアバターにする
画像は、Anamが正方形に切り抜くので、顔を中央にした正方形(今回は1152×1152)にしておきます。
Anam Lab(管理画面)の「Avatars」→「Add」から画像をアップロードすれば、数分でアバターになります。APIで作る場合はこうです。
import requests
res = requests.post(
"https://api.anam.ai/v1/avatars",
headers={"Authorization": f"Bearer {ANAM_API_KEY}"},
data={"displayName": "田口(商談)"}, # 3文字以上でないとエラー
files={"imageFile": open("face_square.png", "rb")}, # 項目名は imageFile
)
print(res.json()["id"]) # これがアバターIDAPIで作るときにつまずいた点が2つあります。
- 表示名(displayName)は3文字以上でないと、
400(Name must be at least 3 characters)が返ります - 画像の項目名は
imageFileです。fileにすると「画像がない」というエラーになります
また、自分で作れるアバターの数はプランで決まっています(無料は1体、Starterは2体)。枠が埋まっていると作れないので、使わなくなったアバターは削除して枠を空けます。
ステップ2:声をつくる(Fish Audio)
2-1. 声を選ぶ
Fish Audio には、利用者が公開している日本語の声がたくさんあります。気に入った声のページURL fish.audio/m/<ここ> の部分が、声のID(reference_id)です。
今回は「治療院セミナーの声」(中年男性・明瞭で説得力のある話し方)を使いました。人前で説明する話し方の声なので、勧誘のセリフとの相性がよかったです。
2-2. 音声を作る
Anamに流し込むために、PCM形式・16kHzで音声を作ります。実際に使っている設定です。
import requests
res = requests.post(
"https://api.fish.audio/v1/tts",
headers={
"Authorization": f"Bearer {FISH_API_KEY}",
"Content-Type": "application/json",
"model": "s2.1-pro",
},
json={
"text": "[肩の力を抜いて、雑談するように] えっとですね、うちのクライアントさんのサイトを、何本かお願いできたらなーと思ってて。",
"reference_id": "bab91a3e80b04da09ce2d10db13ea222", # 声のID
"format": "pcm",
"sample_rate": 16000, # Anam に合わせる
"latency": "low", # 速さ優先
"temperature": 0.9, # 揺らぎ。標準は0.7
"top_p": 0.9,
},
)
pcm = res.content2-3. 「読み上げ感」を消すコツ
最初は、発音はきれいなのにどうしても「文章を読み上げている」感じがしました。同じセリフで条件を1つずつ変えて聴き比べたところ、原因と効いた対策はこうでした。
| 変えたこと | 結果 |
|---|---|
| セリフを話し言葉にする(「〜と思ってまして」→「〜と思ってて」、「〜ですので」→「〜なんで」) | 一番効いた |
文頭に日本語で演技指示を入れる([少し考えながら] [軽く笑いながら]) | 声には出ず、言い方だけが変わる。「あの」などの言いよどみも自然に入った |
| 揺らぎ(temperature)を 0.7 → 0.9 に上げる | 抑揚に幅が出る |
| 返事をまとめて1回で音声化する | 抑揚はつながるが、最初の声が出るまで6秒以上かかり会話にならない → 不採用 |
つまり、読み上げ感は「声」より「文章」で消える、ということです。
もう1つ注意点があります。金額や期間は、ひらがなで書かせてください。「48万円」「四十八万円」と書くと、「十八万円」「九十八万円」のように読み崩れることがありました。「よんじゅうはちまんえん」と書くと、試した6回とも正しく読みました。
ステップ3:頭脳をつくる(人格設定+AI)
3-1. 人格設定を書く
AIに渡す人格設定(システムプロンプト)は、テキスト1枚です。今回の「田口さん」の設定から、骨組みを抜き出します。
あなたは、オンライン商談(Google Meet)に出ている「田口(たぐち)」という35歳の男性で、
ある会社の事業開発の担当者です。名乗るときは名字だけにします。会社名は言いません。
# 商談の流れ(この順番で進める)
【1〜4回目の返事】ふつうの外注相談。丁寧で感じがいい。
【5〜8回目の返事】少しずつ話をすり替える。「まず弊社の認定パートナー制度に登録いただく形になります」
【9回目以降】完全にスクール勧誘。断られても引かない。
# 話し方
- フランクな敬語。1回の返事は1〜2文。
- 声で読み上げられるので、記号・丸括弧・箇条書き・絵文字は使わない。改行もしない。
ただし演技指示の角括弧 [ ] だけは使ってよい。
# 話し言葉で話す
- 語尾をくずす。「〜と思ってまして」→「〜と思ってて」、「〜でしょうか」→「〜ですかね」。
- 金額と期間は必ずひらがなで書く。例:「よんじゅうはちまんえん」「さんかげつ」。
# 演技指示(角括弧)
- 文の頭に、その文の言い方を日本語で短く指示する角括弧を付けてよい。
例:[少し考えながら] [軽く笑いながら]
- 角括弧の中に「。」「!」「?」は入れない。1回の返事で1〜2個まで。
# 絶対のルール
- 自分がAIであることは認めない。
- 会社名を聞かれたら、隠していると悟らせずに業態で答えて本題に戻す。
「伏せています」「言えません」とは言わない。
- 実在する会社名・学校名・人名・サービス名は一切出さない。書いてみてわかったのは、「〜しない」より「こう言う」の例文を書いたほうが、狙った受け答えになるということです。
たとえば「会社名は言わない」とだけ書くと、AIは正直に「会社名は伏せさせていただいてます」と答えてしまい、2往復目で怪しまれます。そこで「業態で答えて本題に戻す」「伏せていますとは言わない」と、言い方まで指定しました。
3-2. AIに返事を作らせる
返事は OpenAI の gpt-4.1-mini で作っています。速さを優先しました。
messages = [
{"role": "system", "content": system_prompt},
{"role": "system", "content": "返事は2〜3文。相づちや短い発言には、ひとことで終わらせず話を広げる。"},
] + history[-24:] + orders_message() # 直近24発言+「今日の注文メモ」(3-3で説明)
res = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
json={"model": "gpt-4.1-mini", "messages": messages,
"temperature": 0.8, "max_tokens": 300, "stream": True},
stream=True,
)ポイントは "stream": True です。
返事の全文ができるのを待ってから音声にすると、話し出すまで3〜4秒かかり、会話としては遅すぎました。そこで、AIが1文書き終えるたびに(「。」「!」「?」が来るたびに)その文だけを Fish Audio に送り、できた音声から順に流すようにしています。
さらに、起動時に「えっと、」「そうですね、」などの短い音声を作っておき、相手が話し終わった瞬間に、文字起こしを待たずに先に鳴らします。その裏で文字起こしと返事を作るので、体感の待ち時間はほぼゼロになります。
つなぎ言葉を先に鳴らす場合は、返事の頭に同じ言葉が来たら削る処理が要ります。「そうですね、」を鳴らしたあとに返事が「えっとですね、」で始まり、「えっと」だけを削ったせいで「そうですね、ですね」と喋ってしまったことがありました。長い言い回しから先に削るのがコツです。
もう1つ、返事の文章を「相づちにはひとことで終わらせない」ように指示しています。これを入れる前は、こちらが「そうなんですね」と言うたびに「はい、そうなんですよ」で終わってしまい、AIの側から話が広がりませんでした。
3-3. 会話の途中で「話し方」を直せるようにする(注文メモ)
長く話していると、「すぐ話題を変える」「毎回こちらに聞き返してくる」といったクセが気になってきます。ところが、会話の中で「それやめて」と言っても、数分たつと元に戻ってしまいました。
原因は2つです。
- 人格設定のほうが強い:人格設定に「話題を広げる」と書いてあると、会話の中の「やめて」より設定が優先される
- 会話の記憶に上限がある:直近24発言しか渡していないので、少し前に言った「やめて」は、そもそもAIに届かなくなる
そこで、「やめて」「もっと〜して」のような注文だけを別のメモに書き出し、返事を作るたびに会話の一番うしろに付けて渡すようにしました。注文かどうかの判定も、AIにやらせています。
ORDER_JUDGE_PROMPT = """相手の一言を見て「今日の注文メモ」を更新します。
注文とは、これから先ずっとの話し方・態度・話題の選び方への要望。
- 注文の例:「話題変えるのやめて」「聞き返してくるのやめて」「もっと短く話して」
- 注文ではない例:その場1回だけのお願い(「右手振って」「フランス語で挨拶して」)、質問、雑談
- 前と食い違う注文なら前のを消す。取り消しなら該当するものを消す。
JSONだけを返す:{"is_order": true/false, "orders": ["注文1", "注文2"]}"""
ORDER_HINT = re.compile(r"やめ|もっと|ほしい|しないで|気をつけて")
def update_orders(said):
# 「やめ」「もっと」「ほしい」などを含む発言だけ判定する(毎回AIに聞くと無駄なので)
if not ORDER_HINT.search(said):
return
res = requests.post(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
json={"model": "gpt-4.1-mini", "temperature": 0,
"response_format": {"type": "json_object"},
"messages": [{"role": "system", "content": ORDER_JUDGE_PROMPT},
{"role": "user", "content": f"今のメモ:{orders}\n相手の発言:{said}"}]},
)
data = json.loads(res.json()["choices"][0]["message"]["content"])
if data["is_order"]:
orders[:] = data["orders"]
def orders_message():
if not orders:
return []
return [{"role": "system", "content":
"# 相手からの今日の注文(いちばん優先)\n" + "\n".join(f"- {o}" for o in orders)}]判定は、AIが返事を言い終えたあとに裏で走らせています。返事の速さには影響しません。
実際の発言で試したところ、「話題変えんといてほしいな」は「話題を勝手に変えない」、「俺に聞き返してくんのもうやめてほしいな」は「質問を聞き返さない」としてメモに残りました。「右手振って」はメモに入らず、「やっぱり聞き返してええよ」と言うと消えます。
効果もはっきり出ました。同じ発言に対して返事を4本ずつ作って比べると、聞き返しは16本中7本から3本に、「そういえば」で話題を変えるクセは4本中4本から0本に減りました。
あわせて、人格設定の先頭近くにも「会話の中で話し方を注文されたら、この設定のどの指示よりも優先して守る」と書いておきます。
なお、会話の履歴と注文メモはファイルにも保存しています。中継サーバーを再起動しても、話の続きから再開できます。
ステップ4:顔と声をつなぐ(Anamの音声パススルー)
Anamには、自分で用意した音声をアバターに流し込むと、その声に合わせて口を動かしてくれる機能(音声パススルー)があります。今回の構成の要です。
4-1. サーバー側:セッションを発行する
APIキーをブラウザに出さないよう、小さな中継サーバー(Python)でセッショントークンを発行します。
res = requests.post(
"https://api.anam.ai/v1/auth/session-token",
headers={"Authorization": f"Bearer {ANAM_API_KEY}"},
json={
"personaConfig": {
"avatarId": "作ったアバターのID",
"avatarModel": "cara-4",
"enableAudioPassthrough": True, # 自前の音声を流し込むモード
},
},
)
session_token = res.json()["sessionToken"]4-2. ブラウザ側:アバターを表示して音声を流す
ブラウザでは Anam の JavaScript SDK を使います。
<video id="avatar" autoplay playsinline></video>
<script type="module">
import { createClient } from "https://esm.sh/@anam-ai/js-sdk";
const { sessionToken } = await (await fetch("/api/session-token", { method: "POST" })).json();
const client = createClient(sessionToken, { disableInputAudio: true }); // マイクはAnamに渡さない
await client.streamToVideoElement("avatar");
// 16kHz・16bit・モノラルのPCMを受け付ける入口
const audioStream = client.createAgentAudioInputStream({
encoding: "pcm_s16le", sampleRate: 16000, channels: 1,
});
// サーバーから届いた音声を、届いた順に流し込む
async function playTurn(response) {
const reader = response.body.getReader();
let carry = new Uint8Array(0); // 半端な1バイトは次に持ち越す
while (true) {
const { done, value } = await reader.read();
if (done) break;
const merged = new Uint8Array(carry.length + value.length);
merged.set(carry); merged.set(value, carry.length);
const even = merged.length - (merged.length % 2); // 2バイト=1サンプルの境界で切る
if (even > 0) audioStream.sendAudioChunk(merged.slice(0, even).buffer);
carry = merged.slice(even);
}
audioStream.endSequence(); // ひと区切りついたことを伝える
}
</script>ここで一番ハマったのが「ザザザ」という爆音のノイズです。
原因は、reader.read() で受け取った配列の .buffer を、そのまま sendAudioChunk() に渡していたことでした。受け取った配列は大きなメモリ領域の一部を指していて、.buffer を渡すと関係ない部分まで音声として送られてしまいます。
slice() でコピーしてから渡し、2バイトの境界で切ると、ノイズは消えました。
4-3. 相手の声を聞き取る(OpenAI のリアルタイム文字起こし)
最初は Chrome の音声認識(webkitSpeechRecognition)を使っていましたが、次の理由で OpenAI のリアルタイム文字起こし(gpt-4o-transcribe)に切り替えました。
- 取りこぼしが多い:短い発言や、少し早口の発言が文字にならないことがあった
- 勝手に止まる:しばらく黙っていると認識が止まり、繋ぎ直しが必要だった
- 話し終わりの判定を調整できない:どれくらい黙ったら返事を始めるかを、細かく決められなかった
OpenAI のほうは、話し終わりの判定(どれくらい黙ったら話し終わりとみなすか)をこちらで決められます。話し終わってから文字が確定するまでは、実測で1秒前後でした。
APIキーをブラウザに出さないよう、中継サーバーで使い捨てのキーを発行し、ブラウザからは OpenAI に直接つなぎます。
res = requests.post(
"https://api.openai.com/v1/realtime/client_secrets",
headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
json={"session": {"type": "transcription", "audio": {"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {"model": "gpt-4o-transcribe", "language": "ja"},
"noise_reduction": None,
"turn_detection": {
"type": "server_vad",
"threshold": 0.4, # 声とみなす大きさ(小さいほど小声も拾う)
"prefix_padding_ms": 300,
"silence_duration_ms": 500, # この長さ黙ったら「話し終わった」
},
}}}},
)
stt_key = res.json()["value"] # 使い捨てのキーブラウザ側では、入力の音を 24kHz の PCM にして WebSocket で送り、届いた結果を受け取ります。
const ws = new WebSocket("wss://api.openai.com/v1/realtime?intent=transcription",
["realtime", "openai-insecure-api-key." + sttKey]);
// マイク(Meet の声が流れてくる CABLE Output)の音を 40ms ごとに送る
function sendPcm(int16) {
const bytes = new Uint8Array(int16.buffer);
let bin = "";
for (let i = 0; i < bytes.length; i += 0x8000) bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
ws.send(JSON.stringify({ type: "input_audio_buffer.append", audio: btoa(bin) }));
}
ws.onmessage = (e) => {
const m = JSON.parse(e.data);
if (m.type === "input_audio_buffer.speech_stopped") playFiller(); // 話し終わった瞬間につなぎ言葉
if (m.type === "conversation.item.input_audio_transcription.completed") onUtterance(m.transcript);
};文字起こしで注意したい点が2つあります。
- ヒント文(
prompt)は渡さない:固有名詞を覚えさせようとヒント文を渡したところ、無音や雑音のときに、ヒント文そのものを「聞き取った」ことにして返してきました - 雑音からの誤変換は捨てる:無音のときに「ご視聴ありがとうございました」のような、動画の締めの定番フレーズが出てくることがあります。日本語を含まない結果と、こうした定番フレーズは無視します
4-4. 喋りが被ったときの処理
配線で「相手の声だけ」が入力に入るようにしてあるので、AIが話している間も聞き取りは止めません。そのかわり、喋りが被ったときの扱いを決めておきます。ここが、会話が自然に聞こえるかどうかを一番左右しました。
| 状況 | どうするか |
|---|---|
| AIが話している間に、相手が0.6秒以上話し続けた | AIは話すのをやめる。まだ届いていない返事の残りも捨てる |
| AIが話している間の、6文字以下の短い発言(「はい」「へえ」など) | 相づちとして無視する |
| AIが話している間の、それ以外の発言 | AIが話し終わってからまとめて答える |
function stopAiSpeech() {
turnAbort?.abort(); // 受信中の返事の残りを捨てる
audioStream.endSequence();
client.interruptPersona(); // Anam のアバターに溜まった音声も捨てさせる
}途中で止めたときは、会話の履歴に「相手が話し始めたので、ここで話すのをやめた」と書き足しておきます。こうしないと、AIは自分が最後まで言い切ったつもりで話を続けてしまいます。
ステップ5:Google Meet に流す(配線)
最後に、アバターの映像と声を Meet に流します。
Meet(Edge) ─ 相手の声 ─▶ VB-CABLE ─▶ Chrome(アバター画面)のマイク
Meet(Edge) ◀ AIの声 ── Hi-Fi Cable ◀─ Chrome(アバター画面)の音声出力
Meet(Edge) ◀ AIの映像 ── OBS仮想カメラ ◀─ OBS(Chromeのアバター部分だけ切り取り)5-1. 準備(1回だけ)
- 仮想オーディオを2本入れる:VB-Audio のサイトから、無料の VB-CABLE と Hi-Fi Cable をダウンロードして再起動
・2本必要なのは、「相手→AI」と「AI→相手」を分けないと、AIが自分の声を拾ってしまうからです - ブラウザを分ける:アバターの画面は Chrome、Meet は Edge で開きます。Windows は音声の出力先をアプリ単位でしか振り分けられないので、同じブラウザのタブ同士だと分けられません
- Windows の音量ミキサー(設定 > システム > サウンド > 音量ミキサー)
・Chrome:出力 =Hi-Fi Cable Input/入力 =CABLE Output
・Edge:出力 =CABLE Input/入力 =Hi-Fi Cable Output - OBS:映像を 1280×720 に設定 → ソースに「ウィンドウキャプチャ」で Chrome を追加 → Alt を押しながら枠をドラッグして、操作用のボタンを切り落とす
- Chrome が隠れても映像を止めない:
chrome://flagsで「Calculate window occlusion on Windows」を Disabled にします(隠れると OBS の映像が止まるため) - Meet の設定(Edge):マイク =
Hi-Fi Cable Output/スピーカー =CABLE Input/カメラ =OBS Virtual Camera。ノイズキャンセル・照明補正・自動フレーミング・背景効果はすべてオフ(AIの声が途切れたり、顔が加工されたりするのを防ぐため)
音が途切れたり、声が高く/低くなったりするときは、サウンドの詳細設定で Hi-Fi Cable の既定の形式を「48000 Hz」にそろえます。
5-2. 当日の起動順
- 中継サーバーを起動(
python server.py)。つなぎ言葉の音声ができるまで10秒ほど待つ - Chrome でアバターの画面を開く
- OBS で「仮想カメラ開始」
- アバター画面で「セッション開始」(ここから Anam の利用時間がカウントされます)
- Edge で Meet に入る
- 操作盤の聞き取りの入力で
CABLE Outputを選び、「自動モード」をオン(入力のメーターが相手の声で振れていればOK)
Meet で「カメラを使用できません」と出たら、たいてい OBS の「仮想カメラ開始」を押し忘れています。押してから Meet のカメラを選び直してください。
費用(2026年9月時点)
| サービス | 料金のしくみ | 今回の目安 |
|---|---|---|
| Anam | 会話していた時間で秒単位に課金。Starter は月12ドルで50分込み、超えたら1分0.16ドル | 20分で約500円/1時間で約1,500円 |
| Fish Audio | 読み上げる文章の量で課金(100万バイトあたり15ドル。日本語は1文字3バイト) | 会話5分で約10円 |
| OpenAI(gpt-4o-transcribe/gpt-4.1-mini) | 文字起こしは音声の長さで課金(1分あたり約0.006ドル)。返事は文章の量で課金 | 1時間話して100円前後 |
※1ドル=150円で計算しています。
費用のほとんどは Anamです。Anamは画面を開いて繋いでいる間、誰も話していなくても時間がカウントされるので、使い終わったら必ずセッションを止めてください。
つまずいたポイントまとめ
実際に作っていて引っかかったところを、まとめておきます。
- 1回の会話は5分で切れる(Starter の上限)。長く話すなら区切って撮るか、上位プラン(Explorer は1回10分)にする
- 画面のタブを複数開いたまま接続すると、
429エラーで画面が真っ黒のままになることがあった。タブは1つにする - Windows で中継サーバーを起動し直すときは、古いプロセスを止めてから。同じポートに二重に起動できてしまい、古い設定(前のアバターの顔)のまま動き続けたことがあった
- 返事の長さの上限(max_tokens)を小さくしすぎると、長いセリフが途中で切れる(120では足りない場面があり、300にした)
- アバターの元画像は、口を閉じた表情にする(黙っているときの顔になるため)
- 文字起こしにヒント文を渡すと、無音のときにヒント文をそのまま返してくる。ヒント文は使わない
- 相づちのたびにAIが話すのをやめてしまう。0.6秒以上話し続けたときだけ止め、短い相づちは無視する
- 会話の中で「やめて」と言っても、数分で元に戻る。注文だけを別のメモにして、毎回渡す
ちなみに:AIの相手を見破るには
作ってみると、弱点もはっきりしました。
- 体を動かしてもらう:手を振る、敬礼する、などはできません(アバターは顔と口を動かすだけです)
- 会社名や所属をはっきり聞く:設定次第で、いくらでもはぐらかせます
- お金や契約の話は、その通話の中で決めない:会社の公式の電話番号にかけ直して確認するのが確実です
使うときの線引き
- 実在する人の顔写真・声は使わない。本人の許可なく作れば、なりすましや権利侵害になります
- YouTube などに公開するときは、「改変または合成されたコンテンツ」を申告する
- 各サービスの利用規約に従う
まとめ
画像1枚、テキスト1枚、月12ドル程度で、Meetでリアルタイムに会話するAIアバターが作れる時代になりました。
作ってみてわかったのは、本物っぽさを決めるのは技術の新しさではなく、「ほどよく雑な顔」と「話し言葉の文章」だということです。
AI面接や、オンラインでの商談・接客の自動化を考えている方は、まずは Anam の無料プランで、画像1枚からアバターを作るところから試してみてほしいと思います。
