こんにちは、ステップアウトマーケティング代表の今宿裕昭です。
先日、note記事1本が2日でリサーチアプリになった話を書きました。今日はその続編です。今度の起点は、朝の日経新聞一面でした。
USBメモリの容量偽装品がネット通販に流通していて、出品審査をすり抜けている、という記事です。読んで思ったのは「これ、買う人が悪いんじゃなくて、そもそも“誰が売っているか”が見えにくいんだよな」ということでした。Amazonの商品ページって、大きく出ているのは商品名と価格で、「販売元」はずっと下の小さな行にある。僕自身、ちゃんと見ていませんでした。
朝、そう思って、昼にはアプリになっていました。「ダレウリ」──ポチる前に、販売元を3秒チェックする道具です。Webアプリだけでなく、ブックマークレット、iPhoneショートカット、Chrome拡張まで、半日で一通りできました。
前回は「コードを書かずに道具が作れる」という話でした。今回はもう一歩踏み込んで、作りながら実際に何を判断したのかという実装記録です。技術寄りの話になりますが、非エンジニアの方にも「AIと作るとき、人間は何を考えているのか」が伝わるように書きます。
プライバシー設計が、実装のかたちを決めた
最初にやりたかったのは、当然「商品URLを入れたら、勝手に販売元を調べてくれる」でした。サーバーがAmazonのページを見に行って、販売元情報を抜き出す。いちばん親切な形です。
でも、これはやめました。理由は3つ。Amazonの利用規約、機械アクセスへのブロック、そして他社サイトを自動取得することの法務リスク。個人の週末開発とはいえ、会社の名前で出す以上、ここは踏まない。
そこでMVP(最小限に動くもの)は「ユーザーが販売元情報を自分で貼り付ける」形にしました。地味です。30秒かかります。でも、サーバーがAmazonを触らないなら、規約もブロックも法務リスクも、そもそも発生しません。
問題は「貼り付けは面倒くさい」です。ここを一気に解いてくれたのが、ブラウザの仕様でした。
URLの # 以降(フラグメント)は、サーバーに送信されない。
これは仕様として決まっています。https://example.com/page#ここ の「ここ」は、ブラウザの中だけで完結し、サーバーには届かない。ページ内リンクのアンカーに使われるあの # です。
これを使えば、販売元情報をURLに乗せて渡しても、サーバーのログには残りません。実装はこうなりました。
// lib/prefill.ts
// Data arrives in the URL fragment (#s=<seller text>&u=<product url>),
// which the browser never sends to the server — the no-server-logs
// privacy design holds even for one-tap flows.
export function parsePrefillHash(hash: string): PrefillData {
const raw = hash.startsWith('#') ? hash.slice(1) : hash;
const params = new URLSearchParams(raw);
return {
sellerText: params.get('s')?.trim().slice(0, 10_000) || undefined,
url: params.get('u')?.trim() || undefined,
};
}
ダレウリを /#s=<販売元情報>&u=<商品URL> の形で開くと、ページはこのフラグメントを読んで自動でチェックし、読み終わったらアドレスバーからも消します。
面白いのは、この「1本のURL」に、あとの入口が全部乗ったことです。ブックマークレットも、iPhoneショートカットも、Chrome拡張も、やることは同じ。Amazonのページから販売元情報を集めて、/#s=...&u=... の形でダレウリを開くだけ。入口ごとに別のサーバー処理を書く必要がなく、しかもどれもサーバーにデータを残さない。プライバシー設計が、そのまま実装をシンプルにしてくれたわけです。

もうひとつ、細かいですが電話番号。貼り付けたテキストに電話番号が混じることがあるので、送信前にブラウザ側で [PHONE_REDACTED] に置き換えます。そのうえでサーバー側でも、もう一度同じ処理をかけます。二重防御です。
// lib/parseSellerText.ts — クライアントで消した後、サーバーでも消す
const { text, found } = redactPhoneLike(rawText);
そしてClaude APIに講評文を書いてもらう時も、生のテキストは渡しません。渡すのは匿名化した特徴量だけです。
// lib/critique.ts — buildCritiquePayload
// 個人名・住所・URL・セラーID・電話番号は送らない
return {
sellerCountry: parsed.countryGuess, // 'JP' | 'CN' | 'other' | 'unknown'
hasTokushohoLikeInfo: parsed.hasTokushohoLikeInfo === 'present',
categoryRisk: parsed.categoryRisk ?? null,
signal: result.signal,
flags: result.flags.map((flag) => flag.id),
};
AIが受け取るのは「所在地は日本国外らしい」「特商法の表記は見当たらない」といった、すでに判定済みの特徴だけ。誰の店かは、AIには分かりません。
Chrome拡張は、権限を2つに絞った
Chrome拡張は、便利さと引き換えに「あなたの見ているページを全部読める」ものが多い。だから審査も厳しいし、ユーザーも身構えます。
ダレウリの拡張は、要求する権限をこの2つだけにしました。
// extension/manifest.json
"permissions": ["activeTab", "scripting"],
activeTab は「ユーザーがボタンを押した、そのタブだけ」を一時的に見る権限。scripting は「収集スクリプトを1回実行する」権限。ホスト権限(特定サイトへの常時アクセス)はなし。常駐するコンテンツスクリプトもなし。
構成としては「押した瞬間だけ、いま見ているタブに収集関数を注入する」形です。
// extension/background.js
chrome.action.onClicked.addListener((tab) => runCheck(tab));
async function runCheck(tab) {
const injected = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: collectFromPage, // 押した瞬間だけ注入される
args: [SITE_ORIGIN],
});
// 収集結果を /#s=...&u=... で新しいタブに開く(前述のフラグメント)
}
普段は何もしていない。ボタンを押した瞬間だけ、いまのタブに入って販売元情報を集め、終わったら消える。この設計には副産物がありました。審査の通しやすさと、プライバシーの説明のしやすさが、両立するんです。
Chrome Web Storeの審査では「なぜこの権限が要るのか」を単一用途で説明します。ダレウリは「ユーザーがボタンを押した時だけ、表示中のAmazonページから販売元情報を読み取り、確認ツールに渡す」の一文で済む。常時アクセスを要求していないから、説明にごまかしが要らない。ユーザーへの説明も同じ一文で足ります。権限を絞ることは、審査対策であると同時に、正直さのコストを下げる設計でした。
AIが書いたコードを、どう信じるか
ここからが本題かもしれません。コードをほとんど書いていない、ということは、書いたコードを自分では完全には検証できないということです。ここをどう埋めるか。
やったのは2つ。ひとつは、別のAIエージェントにレビューさせること。code-reviewer(コードのバグ・品質チェック役)と design-reviewer(UIチェック役)に、書いたコードを何度も通しました。指摘が出る→直す→また通す、のループです。
実際に見つかって直した不具合を、正直に並べます。どれも、僕一人では見落としていたものです。
1. 日本の住所を、中国の住所と誤判定していた
販売元の所在地から国を推定する処理で、最初は「市」「村」という字が入っていると海外と判定する雑な正規表現が混じっていました。横浜市も、〇〇村も、日本の住所です。これは誤りでした。修正後は、日本の住所には出てこない字(簡体字の「县」「镇」など)や、明示的な省名だけを手がかりにしています。
// lib/parseSellerText.ts
// Standalone 市/村 must never count as a China signal — Japanese
// addresses use them too (横浜市, 〇〇村).
しかも「日本国内配送」のような接客文言に釣られないよう、都道府県名があれば最優先で日本と判定し、迷ったら unknown(不明)に倒す。断定より、分からないと言うほうを優先しました。
2. 情報不足のフラグが、二重に加点されていた
販売元情報が読み取れない時、「事業者情報が確認できません」と「特商法の表示が見当たりません」の両方が立って、点数が二重に上がっていました。これだと、情報が無いというひとつの事実で、確認レベルが不当に跳ね上がる。修正して、どちらか一方だけが立つようにしました。
3. ボタンのカーソルが指マークにならない
これはdesign-reviewerが拾いました。Tailwind CSS v4のリセットが、ボタンのカーソルを標準の矢印に戻してしまう。細かいですが、押せる感じが出ない。1行足して直しました。
/* app/globals.css */
/* Tailwind v4 preflight はボタンを cursor: default にするため戻す */
button { cursor: pointer; }
4. iPhoneで入力欄をタップすると、画面が勝手に拡大する
iOS Safariには、文字サイズが16px未満の入力欄をタップすると自動でズームする挙動があります。貼り付け欄がこれに引っかかっていた。入力欄の文字を16px(text-base)に上げて解消しました。実機を触るまで気づけない類の罠です。
もうひとつの担保が、AIの生成文そのものを、出力後に機械検査することです。ダレウリの講評文はClaudeが書きますが、システムプロンプトで「使ってはいけない言葉」を指示しても、LLMが必ず守る保証はありません。だから、生成された文章を出す前に、もう一度コードで検査します。
// lib/critique.ts
export function sanitizeCritique(raw: string): string | undefined {
const text = raw.trim();
if (!text) return undefined;
if (text.length > 500) return undefined;
if (containsForbiddenTerm(text)) return undefined; // 禁止語が入っていたら捨てる
if (text.includes('```')) return undefined;
return text;
}
禁止語が混じっていたら、その講評文はまるごと捨てて、ルールベースの結果だけを表示します。**AIの指示遵守はあてにしない。守られなかった時に落ちる先を、コードで用意しておく。**これは、AIを製品に組み込むときの基本作法だと思っています。
AIが、はっきり弱かったところ
いいことばかり書くとフェアじゃないので、うまくいかなかった話を。
いちばん苦戦したのは、iPhoneショートカットの作り方でした。Safariの共有ボタンから起動して、ページの情報を取り出し、ダレウリを開く──という一連の手順を、僕はiPhoneの画面を見ながら組みます。その手順書をAIに書いてもらったのですが、4回、間違えました。
「Webページの内容を取得」を挟むとリッチテキストになって型が合わずエラーになる、とか。ショートカットのJavaScriptは非同期の完了を待てないから fetch は使えず、完成URLを completion() で返すしかない、とか。どれも、実機で実際にポチッと押してみないと分からないことでした。AIは実物のiPhoneを持っていないので、もっともらしい手順を推測で書いては外す、を繰り返した。
流れが変わったのは、僕がショートカットアプリの画面をスクリーンショットで見せた瞬間でした。「この画面のこのボタンだよ」と実物を見せたら、精度が跳ね上がった。最終的な手順書には、実機で確認したメモがそのまま残っています。
// lib/shortcut.ts のコメント(実機確認の記録)
// 「Webページの内容を取得」を挟むとリッチテキストになり型が合わずエラーになる(実機確認)。
// fetch は使わない(ショートカットのJSは非同期完了を待てないため)。
ここで学んだのは、「調べれば分かること」と「実物を触らないと分からないこと」の線引きです。
規約の解釈、正規表現の書き方、ブラウザのフラグメント仕様。こういう「調べれば分かること」は、AIはめっぽう強い。むしろ僕より詳しい。一方で、iPhoneの特定のアプリの、特定の画面の、特定の挙動。こういう「実物を触らないと分からないこと」は、AIは平気で外します。しかも、外していることに自信満々だったりする。

だから、人間の役割は消えていません。実物を触って、スクリーンショットを撮って、「違う、これはこうだ」と現実を突きつける。この一手が、まだしばらくは要る。むしろ、そこだけやればよくなった、とも言えます。
名前を捨てて、全部を書き換えた
このツール、公開してから名前を変えています。最初は「ポチマエ」でした。
きっかけは、独自ドメインを取ろうとしたことです。買う直前に、念のため同じ名前が使われていないか調べたら、pochimae.com(商品レビューブログ)と pochimae.jp(ポチる前にセール情報を確認しよう、という買い物メディア)が、どちらも先に存在していました。ドメインが空いているかではなく、買い物という同じ土俵で、同じ名前が先に使われていた。このまま広めれば、自分で作った検索需要を、先住のサイトに渡しかねません。
「ダレウリ」に改名しました。誰が売っているか——ツールの機能そのものを名前にした。.com・.jp・.app すべて空いていて、商標も、J-PlatPatの称呼検索(単純文字列と類似の2種類・全区分)で0件でした。名前が資産になる余地がある。
問題は、「ポチマエ」がコードの隅々まで染み込んでいたことです。UIのワードマーク、ブックマークレットとショートカットとChrome拡張が開く先のURL、AI講評のプロンプト、動画のナレーション、README、記事の下書き。カタカナ・大文字・小文字を合わせて、140箇所ほどに散っていました。
やったことは、大きく3つです。
1. 名前とURLを、単一の出所にまとめた。 改名を機に lib/brand.ts を作り、ブランド名とサイトURLをそこ一箇所に集約しました。
// lib/brand.ts — 名前とURLの単一の出所。ここ以外にリテラルを書かない。
export const BRAND_NAME = 'ダレウリ';
export const SITE_ORIGIN = 'https://dareuri.app';
3つの入口(ブックマークレット・ショートカット・拡張)が、それぞれ別々に URL をハードコードしていたのを、この1ファイルを参照する形に直した。次に名前やドメインを変えるとき、書き換えるのはここだけで済みます(Chrome拡張の background.js だけは、拡張の制約で import できないので手で合わせる、というコメントを残しました)。
2. 素材の生成をスクリプト化した。 アイコンもストア画像もXカードも、最初は手作業で作っていました。改名の段になって、それらを作り直せないことに気づいた。作り方が残っていなかったからです。そこで scripts/make-assets.mjs を書いて、ブランド名を変えれば全部が再生成される状態にした。手作りの成果物は、名前を変えた瞬間に再現できない負債になる——これは、今回いちばん実感した教訓でした。
3. 「動いているから大丈夫」を疑った。 改名のコミットを本番にプッシュしたあと、Vercelのダッシュボードを見て血の気が引きました。本番のデプロイが、6回連続で失敗していたんです。動画を追加したコミットから、ずっと。原因は、Next.jsの型チェックが動画用の設定ファイル(Remotion)まで拾ってしまい、本番環境にはそのパッケージが入っていないので落ちる、というものでした。手元では動画用の依存が入っているのでビルドが通り、僕は「ローカルで通った=本番も通る」と読んでいた。幸い、Vercelは失敗したデプロイを本番に昇格させないので、訪問者にはずっと古い(動く)版が出ていて、実害はゼロでした。でも、気づいたのは偶然です。修正は設定1行でしたが、直したあと、手元の動画用依存をわざと退避して、本番と同じ条件でビルドが通ることを確かめてからプッシュし直しました。

この半日で、僕は確認を二度サボっていました。ドメインの下調べと、デプロイが通ったかの確認。どちらも、実害が出る前にたまたま気づけただけです。ドメインは公開前に、デプロイはVercelが失敗版を昇格させない仕組みに、救われた。運が悪ければ、間違った名前で世に出て、壊れたページを配っていた。
技術記録として残しておきたいのは、この2つがAIには任せきれない種類の確認だった、という点です。「この名前は誰かが使っていないか」も「本番でビルドが本当に通るか」も、答えは僕のマシンの外にあります。ローカルのビルドが通ったこと、コードが正しく見えること——AIが確かめられるのはそこまでで、その先の現実は、人間が実物を叩いて確かめるしかない。iPhoneのショートカットで学んだことと、まったく同じでした。速く作れるようになったぶん、この「外を確かめる」工程だけが、くっきりと人間の側に残ります。
半日でできて、これから育てる
ダレウリは、まだ未完成です。Amazonのページ構成が変われば収集は外れるし、直販や国内セラーのページでの実地確認はこれからです。ブランド名から公開情報を照会する機能(Wikipediaや国税庁の法人番号など)も、設計だけ済ませて実装待ち。国税庁のAPIは発行に1か月半かかるので、先に申請だけ出しました。
でも、それでいいと思っています。前回の記事にも書いた通り、完璧な一発を狙わない。まず動くものを出して、使いながら直す。
朝の日経一面で「なんだかなあ」と思った小さな引っかかりが、昼にはポケットの中の道具になっている。派手なAI活用ではありません。でも、この「気になったことを、その日のうちに道具にできる」速度こそ、いま個人開発でいちばん面白いところだと、僕は思っています。
あなたが毎朝ニュースを見て「これ、なんとかならないの」と思うこと。それ、たぶん、その日のうちに形にできます。
引用元・参考リンク
- ダレウリ(本体)
- ダレウリ ソースコード(GitHub)
- Claude Code(Anthropic)
- MDN: URL のフラグメント(Fragment)
- Chrome 拡張機能 activeTab 権限(Chrome for Developers)
関連記事:
- note記事1本を読ませたら、2日でアプリになった──スタンフォード式「多視点リサーチ」を、コード0行でつくった話
- Claude Codeで経営を回す──一人法人が「開発・経営・コンテンツ」を1人で回す方法
- マーケティングエンジニアとは何か──博報堂29年の戦略家が、AIで「一人広告代理店」になった話
AIで、あなたの業務を“道具”に変える
ステップアウトマーケティングでは、中小企業のAI活用を、派手なツール導入ではなく「目の前の困りごとを道具に変える」ところから伴走しています。
「うちの“毎回めんどうな作業”も、なんとかできる?」という相談に、30分の無料壁打ちで一緒に答えを探します。売り込みはしません。
