職務経歴書はYAMLで自動生成する|保守はAIに任せる

職務経歴書はYAMLで自動生成する|保守はAIに任せる

履歴書と職務経歴書のファイルを開くたび、私は小さくため息をついていました。

転職の準備で一番つらいのは、書く内容そのものよりも、レイアウトの維持だったりします。

Excelのセル結合がずれ、Wordのページ送りが崩れ、一箇所直すと別の場所が動く。

そんな「見た目のメンテナンス」に時間を溶かしている自覚が、ずっとありました。

そこで私は、AIエージェントに丸ごと任せる前提で、履歴書と職務経歴書の管理方法そのものを作り直すことにしたのです。

きっかけは、文書の中身と見た目が同じファイルに固く結びついているという、ありふれた不便でした。

同じ悩みを抱えたまま、転職のたびに同じ苦労を繰り返している方も多いのではないでしょうか。

前提(最小限)

最初に、今回の土台を短く共有します。

作業はCoworkにローカルフォルダを渡して進めました。

Coworkは、Claudeにフォルダを預けて実務を任せられるエージェント機能です。

生成の中身はPython製で、テンプレートエンジンにJinja2、PDF化にヘッドレスChromiumのprint-to-PDFを使っています。

とはいえ、データと見た目を分離するという発想自体は環境を選びません。

手元がGoでもNode.jsでも、静的サイトジェネレータでも、同じ考え方がそのまま通用します。

つらさの正体は、はっきりしていました。

Excel/Wordの手メンテは、中身の更新とレイアウトの調整が毎回セットになるのが問題なのです。

住所が一行変わっただけで、なぜか表全体の高さがずれる。

この「一箇所いじると全部を見直すはめになる」構造こそ、私が断ち切りたかった相手でした。

方針はシンプルで、中身はYAMLに一本化し、見た目のテンプレートから完全に切り離すという一点に尽きます。

フォルダ設計とYAMLスキーマ

仕組みの背骨は、フォルダを役割ごとにきっぱり分けることでした。

散らかる転職フォルダの原因は、源泉データと生成物と応募先別の資料が同じ場所に混在するからだと考えています。

そこで、五つの階層に整理しました。

  • 10.master … 履歴書・職務経歴のマスターデータ(YAML)
  • 20.build … 生成スクリプトとテンプレート
  • 30.output … 生成されたPDF・HTMLの成果物
  • 40.applications … 応募先ごとに差し替える情報
  • 90.archive … 過去バージョンの退避先

この分離のうれしさは、源泉をいじるフォルダと、いじってはいけない生成物のフォルダが物理的に分かれる点にあります。

成果物は毎回まるごと作り直す前提なので、手で直すという誘惑そのものが、そもそも生まれません。

履歴書と職務経歴書、二つのYAML

マスターデータは二つに分けました。

一つは履歴書にあたるprofile.yaml、もう一つは職務経歴を収めるcareer.yamlです。

職務経歴は逆編年体で、配列の先頭が最新の案件になるよう持たせています。

案件の一つひとつは、期間・役割・技術環境・フェーズ・詳細を構造化して並べました。

projects:
  - name: Webシステム開発(例)
    period: { from: 2025-11, to: null }   # to: null = 現在継続中
    role: メンバー → リーダー
    env:
      言語/FW: Python
      DB: NoSQL / 全文検索
    phases: [基本設計, 詳細設計, 実装]
    details:
      - WebAPIの開発とデータストアのスキーマ移行
      - 実装フェーズに生成AIを取り入れ、開発期間を短縮

未確定の情報は消さずに「要確認」で残す

ここが地味に効いた工夫でした。

退職年月や現職の正式な社名など、その場で確定できない項目は必ず出てきます。

私はそれらを空欄で消すのではなく、YAMLの中に# 要確認というコメントで残しました。

さらにREADMEの未確認リストと同期させ、あとで人間が埋めるべき穴が、データを見ただけで分かるようにしています。

穴を隠さずデータに書いておく、この一手間が後工程をずいぶん楽にしてくれました。

HTML/CSSとヘッドレスChromeでPDFにする

データが整ったら、次は見た目を組む番です。

私はここで、専用のPDFライブラリを使わず、HTMLとCSSでレイアウトを作り、ブラウザの印刷機能でPDF化する方針を選びました。

理由は単純で、CSSなら段組みや余白を普段のWeb制作と同じ感覚で調整できるからです。

Jinja2のテンプレートにYAMLを流し込み、@pageルールでA4を定義します。

@page { size: A4; margin: 0 }
.page { height: 296.5mm; }                 /* A4の紙面ぴったりで改ページ */
body  { font-family: "Noto Serif CJK JP", serif; }  /* 履歴書は明朝 */

履歴書は明朝体、職務経歴書はゴシック体と、文書の性格でフォントを変えました。

証明写真のような画像も、base64のdata URLとしてHTMLへ直接埋め込み、外部ファイル参照をなくしています。

こうしておくと、HTMLファイル一枚だけで見た目が完結し、PDF化がどこでも安定して再現できるのがありがたいところ。

年齢や経過月数は「生成日基準」で自動計算する

手で更新したくない情報の筆頭が、時間で変わる数字でした。

満年齢も、各案件の「〜現在(9ヶ月)」という経過月数も、放っておけばすぐ古くなります。

そこで、これらは生成した日を基準にコードで計算し、データには開始年月だけを持たせました。

def period_str(pfrom, pto, asof):
    f = ym_parse(pfrom)
    t = ym_parse(pto)
    end = t if t else (asof.year, asof.month)  # 継続中は基準日まで
    months = (end[0]-f[0])*12 + (end[1]-f[1]) + 1
    # ... 年月と経過期間の文字列を組み立てる
    return f"{f[0]}年{f[1]}月〜..."

生成し直すだけで日付が今の値に更新される、この気持ちよさは手作業に戻れなくなるほどでした。

PDF化そのものは、Playwright経由のChromiumに任せています。

with sync_playwright() as pw:
    page = pw.chromium.launch().new_page()
    page.goto(src.resolve().as_uri())
    page.wait_for_load_state("networkidle")
    page.pdf(path=str(dst), format="A4",
             print_background=True, prefer_css_page_size=True)

ここで、知っておくと詰まらない仕様を一つ。

Playwrightのpage.pdf()は、ヘッドレスのChromiumでしか動かないという制約です。

FirefoxやWebKitで同じメソッドを呼ぶとエラーになるため、PDF生成の担当はChromiumに固定しました。

CSSの@pageサイズをそのまま活かすには、prefer_css_page_sizeをtrueにするのが要点になります。

詳しい挙動はPlaywright公式のPageリファレンスとMDNの@page解説が正確です。

志望動機だけ応募先ごとに差し替える

応募先が変わるたびに全部を書き直すのは、本末転倒でした。

そこで、応募先固有の情報は40.applicationsに寄せ、生成時に志望動機だけを差し込む口を用意しました。

コマンドで--motivationにファイルを渡すと、その応募先向けの一枚だけが出力されます。

本体のマスターは一切触らないので、履歴書の共通部分は常に一つの正解を保ったまま、応募先ごとの違いだけを上書きできる仕組みになりました。

タブ切替の確認用HTMLは、iframeのsrcdocに両文書をエスケープして丸ごと埋め込んでいます。

二文書を一つのファイルに内包しても、iframeで隔離されるためCSSの衝突が構造的に起きないのが利点です。

AIエージェントに保守を任せて分かったこと

この仕組みの本当の価値は、作った後にじわじわ効いてきました。

AIエージェントは、完成したPDFのバイナリを直接きれいに編集することはできません。

ところがマスターがYAMLだと、話がまるで変わります。

構造化されたテキストなら、エージェントは狙った箇所だけを正確に差分更新できるのです。

実際にあった場面を紹介します。

私が「住所が前のままだ」「ある資格の取得は今年の七月」と口頭レベルで伝えただけで、エージェントはYAMLの二行と資格の一行を書き換えました。

そして全成果物が再生成され、履歴書PDF・職務経歴書PDF・確認用HTMLの三つがすべて最新の内容にそろったのです。

PDFそのものは、私も含めて誰も手で触っていません。

ここで一つ、正直に書いておきます。

今回の構築と生成はCoworkのクラウドサンドボックス上で実行して成功したもので、私のWindows実機ではまだ動かしていません

サンドボックスにはNoto CJKフォントが入っていたため、手元のWindowsで再現するなら游明朝やメイリオへの読み替えが要ります。

それでも、人間が関与するのは事実の正誤を確かめる一点だけになりました。

レイアウトの番人から、データの校正者へと役割が変わった、これがいちばんの収穫でした。

最後に

今回いじったのは履歴書と職務経歴書ですが、効いた原理は文書の種類を問いません。

請求書でも報告書でも、定型のドキュメントには必ず「中身」と「見た目」が同居しています。

その二つを引き離し、中身は一つの正解(SSOT)として持ち、見た目はテンプレートに追い出す

すると更新作業はデータを直すだけになり、面倒な生成とレイアウトは、スクリプトやAIエージェントへ丸ごと預けられます。

データと見た目を分けた瞬間から、文書のメンテナンスは「編集」ではなく「監修」に変わるのだと実感しました。

もし手作業のドキュメント管理に疲れているなら、まずは一番よく直すファイルを一つ、データと見た目に割ってみるところから始めるのをおすすめします。

以上です。