IT技術ブログ

会社サイトとブログの著者を、同じ人物として構造化データで結ぶ。Person・Organizationの@idの決め方、sameAsに入れるURL、記事のauthorとProfilePageの指し先を、2つのサイトのJSON-LDを取りに行くスクリプトで確かめた記録

会社サイトと個人ブログで同じ著者・同じ会社を示すための構造化データの設計です。@idの置き場所、sameAsに選ぶURL、authorの指し先、ProfilePageの書き方と、2サイトを照合するスクリプトの結果を載せます。

この記事の結論:会社サイトとブログで同じ著者を示すには、人物と会社の@idを2つのサイトで同じIRIにそろえ、そのうえでsameAsとurlに相手側のプロフィールURLを入れ合います。@idだけに頼らず、Googleが著者の説明で挙げているurlとsameAs、それに画面上のリンクのrel="me"でも同じ関係を示します。このブログ(やまやまブログ)と会社サイト(bundlyze.co.jp)のJSON-LDを取りに行って照合したところ、必須の項目の抜けと結び目の食い違いは0件でした。そのうえで見つかった3つのずれ(rel="me"が片方向、LINE公式アカウントのURLの形の違い、会社サイトに@idが無い)は、2つのサイトの両側で直し、直した後の照合ですべてそろったことを確かめました。

この記事は、会社のサイトと、社員や代表の個人ブログの両方を持っている場合に、構造化データで「同じ人・同じ会社」を示すための実装の記録です。JSON-LDを1つの設定から生成する土台や、種類ごとの検証の手順は、構造化データのJSON-LD実装と検証の記事に書きました。ここでは、そのうえで2つのサイトをどう結ぶかに絞ります。コードはこのブログで動いているもので、照合のスクリプトは2026年10月5日に、公開中の2つのサイトに対して、直す前と直した後に流しています。

結ぶ相手は「人物」と「会社」の2つで、それぞれ本拠地のページを1つ決める

最初に、人物と会社のそれぞれについて、詳しい情報を置く本拠地のページを1つ決めます。ほかのページからはそこを指すだけにすると、名前や肩書きが場所ごとにずれにくくなります。

このブログと会社サイトでは、次のように分けています。

対象 本拠地のページ 構造化データ ほかのページからの指し方
人物(津嘉山) 会社サイトの執筆者ページ ProfilePage の mainEntity に Person 会社のコラムとブログの記事の author が、同じ @id を指す
人物(津嘉山) ブログの運営者ページ /about/ ProfilePage の mainEntity に Person(@id は会社サイトと同じ) ブログの記事の author の url が、このページを指す
会社(bundlyze) 会社サイトのトップ Organization 2つのサイトの Person の worksFor が、同じ @id で指す

人物のプロフィールページが2つあるのは、ブログと会社サイトのそれぞれに、その人を紹介するページがあるからです。ページは2つのまま残し、人物の @id だけは会社サイトの執筆者ページの側に1つ決めて、両方のページがその @id を使う形にしました。2つのプロフィールページは、sameAs でも互いに指し合っています。Googleの ProfilePage の説明でも、ブログの「自己紹介」のページと、会社サイトの従業員のページは、どちらも ProfilePage を使える例に挙がっています。

@idは2つのサイトで同じIRIにそろえるが、それだけには頼らない

@id は、人物や会社のノードに付ける識別子です。2つのサイトで同じ人物・会社には同じ文字列を使い、そのうえで、サイトをまたぐつながりを @id だけには任せません。

JSON-LD 1.1 の仕様は、グラフの外からノードを参照するには識別子が必要で、@id でノードを見分けると説明しています。あわせて、本当の意味でノードをつなぐには、その識別子を開いたときにそのノードの説明が得られるのが望ましい、とも書いています。そこで、人物の @id は、その人を紹介している会社サイトの執筆者ページのURLに #person を付けたIRIにし、会社の @id は、会社サイトのトップのURLに #organization を付けたIRIにしました。どちらも、開けばその人物・会社のページが出るURLで、自分たちが管理するドメインの下にあるので、他人の識別子と重なる心配もありません。

一方で、Googleの組織と記事の構造化データの説明には、@id を使ってサイトをまたいで同じ組織や人物だと照合する、という説明はありません。著者を見分ける手がかりとしてGoogleが挙げているのは、url と sameAs です。そのため、@id をそろえても、url と sameAs は省きません。

このブログの @id は、src/lib/schema.ts で次のように決めています。

src/lib/schema.ts(@id と sameAs の部分)ts
import { SITE, AUTHOR, COMPANY } from '../config/site';

const abs = (p: string) => new URL(p, SITE.url).href;

export const ORG_ID = `${COMPANY.url}#organization`; // https://www.bundlyze.co.jp/#organization
// 著者は会社サイトの著者ページと同じ @id を使い、2つのサイトの JSON-LD が同じ人物ノードを指すようにする
export const PERSON_ID = 'https://www.bundlyze.co.jp/column/author/tsukayama#person';
export const WEBSITE_ID = abs('/#website');
// 著者の公式プロフィール(X・Instagram・会社サイトの執筆者ページ)
const SAME_AS = [AUTHOR.x, AUTHOR.instagram, AUTHOR.profileUrl];

最初は、人物の @id をブログの運営者ページのURLに #person を付けた形(https://www.yamayamabloglink.com/about/#person)にしていました。会社サイトの側に @id が無かったので、ブログの中だけで通じる名札だったわけです。会社サイトが執筆者ページの人物に上の @id を付けたのに合わせて、ブログもその文字列に切り替えました。記事の author も、運営者ページの ProfilePage も、この1つの文字列を指します。

@id が同じでも、url はそれぞれのサイトのプロフィールページのままです。ブログの人物の url はブログの運営者ページ、会社サイトの人物の url は執筆者ページで、同じ人物について、2つのページがあることを示す形になっています。

sameAsには、本人が管理していて、開けば本人だと分かるURLだけを入れる

sameAs には、その人物や会社が自分で管理していて、開けば同じ相手だと確かめられるページのURLだけを並べます。数を増やすより、確かな相手のページだけに絞る方が、取り違えの元を作りません。

schema.org は sameAs を、その項目が何者かを紛れなく示す参照先のページのURLと定義しています。GoogleのProfilePageの説明でも、ほかのプロフィールやホームページへのURLとされています。この2つの説明から、選ぶときの基準を次のように決めました。

入れる 入れない
本人のSNSのプロフィール(X、Instagram) 記事一覧の2ページ目のような、中身が入れ替わるURL
会社サイトの執筆者ページ(ブログ側から) 他人が書いた紹介記事・インタビュー記事
ブログの運営者ページ(会社サイト側から) 検索結果のURL
会社の公式アカウント(Organization 側) 同じアカウントの別の形のURLの重ね書き

最後の行は、今回の照合で見つかったことです。会社のLINE公式アカウントを、ブログは lin.ee の友だち追加のURLで、会社サイトは line.me の形のURLで sameAs に入れていました。どちらも同じアカウントに着きますが、読む側から見ると、文字列が違うURLが2つあることになります。

そこで、構造化データでは会社サイトと同じ https://line.me/R/ti/p/@908dnlkk を使うように、ブログ側をそろえました。画面の「友だち追加」のボタンは今までどおり lin.ee のURLのままにして、sameAs に入れる値だけを別に持たせています。

src/config/site.ts と src/lib/schema.ts(LINE の部分)ts
// site.ts:会社のLINE公式アカウント(友だち追加のリンク。@908dnlkk)
line: 'https://lin.ee/o0zEQTW',
// 構造化データ(sameAs)では、会社サイトと同じ正規の形のURLを使う(短縮URLは使わない)
lineCanonical: 'https://line.me/R/ti/p/@908dnlkk',

// schema.ts:Organization の sameAs は正規の形だけ
sameAs: [COMPANY.lineCanonical],

記事のauthorは、プロフィールページの人物を@idとurlで指す

記事の author には、名前だけでなく、プロフィールページの人物と同じ @id、そのページの url、sameAs を入れます。そうすると、記事を読んだ側が「この著者は、あのプロフィールページの人だ」とたどれます。

このブログの記事の author は、次の形で出しています。

src/lib/schema.ts(記事の author の部分)ts
author: {
  '@type': 'Person',
  '@id': PERSON_ID,
  name: AUTHOR.name,            // 「津嘉山 洸」だけ。肩書きは入れない
  url: abs('/about/'),          // 本拠地のプロフィールページ
  jobTitle: AUTHOR.jobTitle,    // CTO
  image: abs(AUTHOR.avatar),
  worksFor: { '@type': 'Organization', '@id': ORG_ID, name: COMPANY.name, url: COMPANY.url },
  sameAs: SAME_AS,
},
// 個人ブログなので発行者も著者本人
publisher: { '@type': 'Person', '@id': PERSON_ID, name: AUTHOR.name, url: abs('/about/'), image: abs(AUTHOR.avatar) },

Googleの記事の構造化データの説明には、著者の書き方について次のような決まりがあります。

  • ページに著者として出している人は、全員を構造化データにも入れる
  • 著者が複数なら、1人ずつ別の author として書く
  • 型(@type)と、url か sameAs を使うことを強く勧める
  • author.name には名前だけを書く
  • 人なら Person、組織なら Organization を使い、Thing は使わない

publisher を会社ではなく本人にしているのは、このブログが会社の媒体ではなく、個人の発信だからです。会社サイトのコラムであれば、発行者として会社を指すのが実態に合います。著者と発行者を誰にするかは、実際にそのサイトを誰が運営しているかに合わせます。

プロフィールページは、ProfilePageのmainEntityで1人だけを指す

プロフィールページには ProfilePage を置き、mainEntity にそのページが紹介している1人を入れます。Googleの説明で必須なのは、mainEntity と、その人物の name の2つです。

ブログの運営者ページでは、ProfilePage と Person を別々の JSON-LD にして、ProfilePage の側からは @id だけで人物を指しています。

src/pages/about.astro(JSON-LD の部分)astro
jsonLd={[
  { '@context': 'https://schema.org', '@type': 'ProfilePage', mainEntity: { '@id': person()['@id'] } },
  person(),        // @id・name・jobTitle・worksFor・sameAs を持つ Person の本体
  organization(),
  breadcrumbs([{ name: '運営者について', url: '/about/' }]),
]}

会社サイトの執筆者ページの JSON-LD は、@id が付いた後に curl で取得すると次のとおりでした(説明文、得意分野、会社の別名とロゴ、記事の一覧は省いています)。

https://www.bundlyze.co.jp/column/author/tsukayama の ProfilePage(抜粋)json
{
  "@context": "https://schema.org",
  "@type": "ProfilePage",
  "url": "https://www.bundlyze.co.jp/column/author/tsukayama",
  "mainEntity": {
    "@type": "Person",
    "@id": "https://www.bundlyze.co.jp/column/author/tsukayama#person",
    "name": "津嘉山 洸",
    "jobTitle": "技術責任者(SEO・LLMO、広告運用、システム開発)",
    "url": "https://www.bundlyze.co.jp/column/author/tsukayama",
    "worksFor": {
      "@id": "https://www.bundlyze.co.jp/#organization",
      "@type": "Organization",
      "name": "株式会社bundlyze",
      "url": "https://www.bundlyze.co.jp",
      "sameAs": ["https://line.me/R/ti/p/@908dnlkk"]
    },
    "sameAs": ["https://www.yamayamabloglink.com/about/"]
  }
}

会社サイトの人物は sameAs でブログの運営者ページを指し、ブログの人物は sameAs で会社サイトの執筆者ページを指しています。人物と会社の @id も、ブログと同じ文字列です。これで、2つのプロフィールページが、同じ @id と、互いを指す sameAs の両方で結ばれる形になりました。

肩書きは、ブログでは「CTO」、会社サイトでは「技術責任者(SEO・LLMO、広告運用、システム開発)」と書き方が違います。どちらも同じ役割を指していて矛盾はしませんが、読む側の取り違えを減らすなら、片方の表記に寄せるか、同じ語を含めておく方が安全です。

画面のリンクにもrel=“me”を付けて、同じ関係を示す

構造化データの sameAs と同じ関係を、画面に出ているリンクの rel="me" でも示しておきます。読む側が JSON-LD を見ない場合でも、リンクから同じ人物だとたどれるようにするためです。

rel="me" は、ある人物についてのページから、同じ人物についての別のページへ張るリンクに付ける値です。microformats の説明では、2つのページが互いに rel="me" で指し合うと、同じ人物を表していると確かめられる、とされています。なお、Googleの構造化データの説明には rel="me" の扱いは出てこないので、検索の評価に効く、という期待で付けるものではありません。

会社サイトの執筆者ページには、ブログの運営者ページへのリンクに rel="me noopener" が付いていました。一方、ブログの運営者ページから会社サイトの執筆者ページへのリンクは、最初の照合の時点では rel="noopener" だけで、片方向でした。そこで、ブログの運営者ページにある2か所のリンクに me を足しました。

src/pages/about.astro(会社サイトの執筆者ページへのリンク)astro
<p class="profile-sec__text">会社では<a href={AUTHOR.profileUrl} target="_blank" rel="me noopener">bundlyzeのコラム</a>も書いています。</p>
<li><a href={AUTHOR.profileUrl} target="_blank" rel="me noopener">bundlyze コラムの執筆者ページ</a></li>

これで、2つのプロフィールページが rel="me" でも互いを指す形になりました。

2つのサイトのJSON-LDを取りに行き、結び目を機械で確かめる

2つのサイトにまたがる結び目は、片方だけを直すとすぐにずれます。そこで、両方の公開ページを取りに行って、決まりどおりに結べているかを確かめるスクリプトを作りました。

確かめる項目は次の6つです。1〜4で決まりに反するものは「問題」として数え、5と6は直すかどうかを人が判断するので「記録」として出します。

  1. 2つのプロフィールページに ProfilePage があり、mainEntity とその name があるか
  2. 記事の author が Person で、name に肩書きなどが混ざっておらず、url か sameAs があるか
  3. 記事の author の @id と、運営者ページの人物の @id が同じか
  4. 2つのサイトの人物が、sameAs で互いのプロフィールページを指しているか
  5. 画面のリンクの rel="me" が、どちら向きにあるか
  6. 人物と会社の @id・会社の sameAs・肩書きが、2つのサイトでそろっているか
check-identity.mjsjs
// ブログと会社サイトの JSON-LD を取りに行き、同じ人物・会社として結べているかを確かめる
// 使い方: node check-identity.mjs
const PAGES = {
  blogAbout: process.env.BLOG_ABOUT || "https://www.yamayamabloglink.com/about/",
  blogPost: "https://www.yamayamabloglink.com/structured-data-jsonld-implementation-check/",
  companyAuthor: "https://www.bundlyze.co.jp/column/author/tsukayama",
};

const norm = (u) => (u || "").replace(/\/+$/, ""); // 末尾スラッシュの違いは同じURLとみなす
const arr = (v) => (v == null ? [] : Array.isArray(v) ? v : [v]);
const types = (n) => arr(n?.["@type"]);

async function jsonLdOf(url) {
  const html = await (await fetch(url)).text();
  const blocks = [...html.matchAll(/<script[^>]*type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/g)];
  const nodes = [];
  for (const [, body] of blocks) {
    const d = JSON.parse(body); // 読めなければここで例外になり、止まる
    for (const n of arr(d["@graph"] ?? d)) nodes.push(n);
  }
  const links = [...html.matchAll(/<a\s[^>]*>/g)].map((m) => m[0]);
  return { nodes, links };
}

// ページ内の @id 参照を、同じページにある実体に置き換える
function resolve(ref, nodes) {
  if (ref && ref["@id"] && Object.keys(ref).length === 1) return nodes.find((n) => n["@id"] === ref["@id"]) ?? ref;
  return ref;
}

const problems = [];
const notes = [];
const ng = (m) => problems.push(m);

const got = {};
for (const [k, u] of Object.entries(PAGES)) got[k] = await jsonLdOf(u);

// 1. ProfilePage:mainEntity と その name が必須(Google の ProfilePage の説明)
for (const k of ["blogAbout", "companyAuthor"]) {
  const { nodes } = got[k];
  const pp = nodes.find((n) => types(n).includes("ProfilePage"));
  if (!pp) { ng(`${k}: ProfilePage が無い`); continue; }
  const main = resolve(pp.mainEntity, nodes);
  if (!main) ng(`${k}: ProfilePage に mainEntity が無い`);
  else if (!main.name && !main.alternateName) ng(`${k}: mainEntity に name が無い`);
  else notes.push(`${k}: ProfilePage → ${types(main).join()}「${main.name}」 @id=${main["@id"] ?? "(なし)"}`);
}

// 2. 記事の author:Person で、name は名前だけ、url か sameAs がある(Google の記事の説明)
{
  const post = got.blogPost.nodes.find((n) => types(n).some((t) => /Article|BlogPosting/.test(t)));
  for (const a of arr(post?.author)) {
    if (!types(a).includes("Person")) ng(`blogPost: author の型が Person でない`);
    if (/CTO|株式会社|代表|さん|様/.test(a.name || "")) ng(`blogPost: author.name に名前以外が入っている「${a.name}」`);
    if (!a.url && !arr(a.sameAs).length) ng(`blogPost: author に url も sameAs も無い`);
    notes.push(`blogPost: author @id=${a["@id"]} url=${a.url}`);
  }
}

// 3. ブログ内で、記事の author と運営者ページの Person が同じ @id か
const blogPerson = got.blogAbout.nodes.find((n) => types(n).includes("Person"));
{
  const post = got.blogPost.nodes.find((n) => types(n).includes("BlogPosting"));
  const ids = arr(post?.author).map((a) => a["@id"]);
  if (!ids.includes(blogPerson?.["@id"])) ng(`author の @id(${ids})と運営者ページの Person の @id(${blogPerson?.["@id"]})が違う`);
}

// 4. 相互の sameAs:ブログの Person → 会社の執筆者ページ、会社の Person → ブログの運営者ページ
const companyPerson = resolve(
  got.companyAuthor.nodes.find((n) => types(n).includes("ProfilePage"))?.mainEntity,
  got.companyAuthor.nodes,
);
const has = (list, url) => arr(list).map(norm).includes(norm(url));
if (!has(blogPerson?.sameAs, PAGES.companyAuthor)) ng("ブログの Person.sameAs に会社の執筆者ページが無い");
if (!has(companyPerson?.sameAs, PAGES.blogAbout)) ng("会社の Person.sameAs にブログの運営者ページが無い");

// 5. HTML のリンク:rel="me" が相手を指しているか(片方向でも記録する)
const relMe = (links, url) => links.some((t) => /rel="[^"]*\bme\b[^"]*"/.test(t) && t.includes(norm(url)));
notes.push(`会社の執筆者ページ → ブログ rel="me": ${relMe(got.companyAuthor.links, PAGES.blogAbout)}`);
notes.push(`ブログの運営者ページ → 会社の執筆者ページ rel="me": ${relMe(got.blogAbout.links, PAGES.companyAuthor)}`);

// 6. 人物と会社の @id がそろっているか、会社の sameAs の LINE の URL が同じか
const blogOrg = got.blogAbout.nodes.find((n) => types(n).includes("Organization"));
const companyOrg = companyPerson?.worksFor;
notes.push(`Person @id ブログ=${blogPerson?.["@id"]} / 会社=${companyPerson?.["@id"] ?? "(なし)"}`);
notes.push(`Organization @id ブログ=${blogOrg?.["@id"]} / 会社=${companyOrg?.["@id"] ?? "(なし)"}`);
notes.push(`Organization sameAs ブログ=${JSON.stringify(blogOrg?.sameAs)} / 会社=${JSON.stringify(companyOrg?.sameAs)}`);
notes.push(`Person jobTitle ブログ=${blogPerson?.jobTitle} / 会社=${companyPerson?.jobTitle}`);

console.log("--- 記録");
notes.forEach((m) => console.log("  " + m));
console.log(`--- 問題 ${problems.length}件`);
problems.forEach((m) => console.log("  NG " + m));
process.exitCode = problems.length ? 1 : 0;

URLの末尾のスラッシュは、比べる前にそろえています。会社サイトの執筆者ページのURLは末尾に / が無い形で、ブログのURLは / で終わる形なので、そのまま文字列で比べると、同じページなのに一致しないと判定してしまうためです。

照合の結果:問題は0件、見つかった3つのずれも両側で直した

2026年10月5日に、公開中の2つのサイトに対して流しました。最初に流したときの記録の欄のうち、ずれていた4行は次のとおりです。

直す前の node check-identity.mjs の出力(記録の一部)
  companyAuthor: ProfilePage → Person「津嘉山 洸」 @id=(なし)
  ブログの運営者ページ → 会社の執筆者ページ rel="me": false
  Organization @id ブログ=https://www.bundlyze.co.jp/#organization / 会社=(なし)
  Organization sameAs ブログ=["https://lin.ee/o0zEQTW"] / 会社=["https://line.me/R/ti/p/@908dnlkk"]

ブログ側の rel="me" と LINE のURLを直し、会社サイトには人物と会社の @id が付き、ブログの人物の @id もそれに合わせました。2つのサイトの本番に反映されたことを、それぞれのHTMLで確かめてから、同じスクリプトをもう一度流した結果です。

直した後の node check-identity.mjs の出力(2026年10月5日 9時30分)
--- 記録
  blogAbout: ProfilePage → Person「津嘉山 洸」 @id=https://www.bundlyze.co.jp/column/author/tsukayama#person
  companyAuthor: ProfilePage → Person「津嘉山 洸」 @id=https://www.bundlyze.co.jp/column/author/tsukayama#person
  blogPost: author @id=https://www.bundlyze.co.jp/column/author/tsukayama#person url=https://www.yamayamabloglink.com/about/
  会社の執筆者ページ → ブログ rel="me": true
  ブログの運営者ページ → 会社の執筆者ページ rel="me": true
  Person @id ブログ=https://www.bundlyze.co.jp/column/author/tsukayama#person / 会社=https://www.bundlyze.co.jp/column/author/tsukayama#person
  Organization @id ブログ=https://www.bundlyze.co.jp/#organization / 会社=https://www.bundlyze.co.jp/#organization
  Organization sameAs ブログ=["https://line.me/R/ti/p/@908dnlkk"] / 会社=["https://line.me/R/ti/p/@908dnlkk"]
  Person jobTitle ブログ=CTO / 会社=技術責任者(SEO・LLMO、広告運用、システム開発)
--- 問題 0件
exit=0

決まりに反するもの(ProfilePage の必須の項目、author の書き方、@id の一致、相互の sameAs)は、直す前も後も0件でした。記録の欄から分かった3つのずれと、今の状態は次のとおりです。

見つかったこと 直す前 今の状態
rel="me" が片方向 会社サイト → ブログにはあり、ブログ → 会社サイトには無い 直した。ブログの運営者ページのリンクに me を足し、両方向になった
LINE公式アカウントのURLの形が違う ブログは lin.ee、会社サイトは line.me 直した。ブログの sameAs を会社サイトと同じ line.me の形にそろえた
会社サイトの人物と会社に @id が無い ブログが使う https://www.bundlyze.co.jp/#organization を、会社サイトは宣言していない 直した。会社サイトが人物と会社に @id を付け、ブログの人物の @id も同じIRIに切り替えた

残っているのは、肩書きの書き方の違い(ブログは「CTO」、会社サイトは「技術責任者(…)」)だけです。前に書いたとおり矛盾ではないので、この記事の時点ではそのままにしています。

スクリプトが見落とさないかも確かめました。運営者ページの代わりに、ProfilePage の無いブログのトップページを指定して流すと、次のように問題が出て、終了コードが1になりました。

BLOG_ABOUT=https://www.yamayamabloglink.com/ node check-identity.mjs の出力(問題の部分)
--- 問題 2件
  NG blogAbout: ProfilePage が無い
  NG 会社の Person.sameAs にブログの運営者ページが無い
exit=1

このスクリプトで分かるのは、JSON-LD が読めるか、決めた項目と結び目がそろっているかまでです。型や項目の名前が schema.org の定義に合っているかは、別に Schema Markup Validator の画面で見ます。この記事を書いた時点で、Schema Markup Validator を外から呼ぶための公開のAPIの案内は見つけられなかったので、スクリプトには組み込んでいません。また、今回は Schema Markup Validator とリッチリザルト テストを、この2ページに対しては流していません。

会社と個人の情報を結ぶときに、先に決めておくこと

2つのサイトの構造化データを結ぶ作業は、コードを書く前に「どこを本拠地にするか」と「どのURLの形を正とするか」を決めておくと、後の手戻りが減ります。今回見つかった3つのずれも、どれもコードの誤りではなく、2つのサイトを別々に作ったときの決め事の違いから生まれたものでした。とくに @id は、片方のサイトだけで決めても意味が無く、2つのサイトが同じ文字列を宣言して初めてそろいます。

会社概要に何を書くと、AIや検索に会社を取り違えられにくいかは、会社のコラムの会社概要に書く項目の一覧と、AIに取り違えられない書き方にまとめています。株式会社bundlyzeでは、自社サイトでLLMOを実践しながら、構造化データとページの記載をそろえる作業を続けています。会社サイトとブログ、採用ページなど複数のサイトをまたいだ整え方は、SEO・LLMO対策の支援のページに進め方を載せています。

動作確認した環境

確認日は2026年10月5日です(直した後の照合は9時30分)。

確かめていないこと:

  • Schema Markup Validator とリッチリザルト テストでの確認(この2ページについては流していません)
  • Google がこの2つのプロフィールを同じ人物として扱っているか(外から確かめる方法がないため)

参照した公式ドキュメント(確認日:2026年10月5日)

よくある質問

@idを同じ文字列にすれば、別のサイトどうしでも同じ人物だと伝わりますか?

JSON-LDの仕組みのうえでは、同じIRIの@idは同じノードを指すので、2つのサイトで同じ文字列を使えば、データをまとめて読む側には1人の人物として結び付きます。ただ、Googleの組織と記事の構造化データの説明には、@idをサイトをまたいだ照合に使うとは書かれていません。このブログでは、人物と会社の@idを会社サイトと同じIRIにそろえたうえで、Googleが著者の説明で挙げているurlとsameAsにも、相手のプロフィールのURLを入れています。

sameAsには、どんなURLを入れればよいですか?

本人(または会社)が自分で管理していて、そのページを開けば同じ人物・会社だと分かるURLだけを入れます。このブログでは、本人のXとInstagram、会社サイトの執筆者ページを入れています。記事一覧の1ページや、他人が書いた紹介記事、検索結果のURLは入れません。同じアカウントに複数の形のURLがある場合は、2つのサイトで同じ形にそろえます。

記事のauthor.nameに肩書きや会社名を入れてもよいですか?

入れません。Googleの記事の構造化データの説明は、著者の名前の欄に肩書きや会社名、敬称などを足さないよう求めています。肩書きはjobTitle、勤め先はworksForに分けます。このブログでは、名前を「津嘉山 洸」だけにし、CTOはjobTitleに入れています。

Schema Markup Validatorは、スクリプトから自動で呼べますか?

この記事を書いた時点で、Schema Markup Validatorを外から呼ぶための公開のAPIの案内は見つけられませんでした。そのため、JSON-LDが読めるか・必要な項目があるか・2つのサイトで結び目がそろっているかはこの記事のスクリプトで確かめ、語彙の使い方が正しいかどうかは、画面にURLかコードを入れて1ページずつ見る分担にしています。