@rex0220/llm-task-router 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +6 -0
- package/LICENSE +21 -0
- package/README.ja.md +147 -0
- package/README.md +147 -0
- package/config/criteria/default.md +43 -0
- package/config/criteria/note.md +32 -0
- package/config/models.yaml +111 -0
- package/config/profiles/blog.yaml +9 -0
- package/config/profiles/note.yaml +11 -0
- package/config/profiles/qiita.yaml +13 -0
- package/config/profiles/zenn.yaml +9 -0
- package/dist/llm-task-router.js +1465 -0
- package/docs/api-smoke-test.md +291 -0
- package/docs/thin-model-router-design.md +1069 -0
- package/docs/thin-model-router-implementation-plan.md +479 -0
- package/package.json +74 -0
package/.env.example
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rex0220
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# llm-task-router
|
|
2
|
+
|
|
3
|
+
*[English README](README.md)*
|
|
4
|
+
|
|
5
|
+
複数ステップの記事作成ワークフロー(Qiita / Zenn / ブログ / note …)を駆動する、薄い ModelRouter の TypeScript 製 CLI です。
|
|
6
|
+
|
|
7
|
+
## インストール
|
|
8
|
+
|
|
9
|
+
グローバルインストールすると、単一バンドルの CLI(`dist/llm-task-router.js`)が `llm-task-router` コマンドとして使えます。
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# npm から(パッケージは scoped。インストール後のコマンド名は `llm-task-router` のまま)
|
|
13
|
+
npm install -g @rex0220/llm-task-router
|
|
14
|
+
|
|
15
|
+
# またはパックした tarball から
|
|
16
|
+
npm run build && npm pack
|
|
17
|
+
npm install -g ./rex0220-llm-task-router-<version>.tgz
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
CLI は `config/models.yaml`・`config/profiles/`・`config/criteria/`・`.env` を読み、`runs/` を書き出します。これらはすべて**カレントディレクトリ相対**です。作業ディレクトリに設定テンプレを展開するには `init` を使います。
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd my-articles
|
|
24
|
+
llm-task-router init # config/ と .env.example をここへコピー(既存は上書きしない。--force で上書き)
|
|
25
|
+
cp .env.example .env # APIキーを設定する
|
|
26
|
+
# config/models.yaml の model を実在するモデルIDに直す
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
以降はそのディレクトリ配下でどこでも実行できます。
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
llm-task-router --help
|
|
33
|
+
llm-task-router -v
|
|
34
|
+
llm-task-router article:create --topic "..."
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
APIキーは作業ディレクトリの `.env` に置きます。`config/models.yaml` は `providers.*.api_key_env` で個別のキー名を指定でき、未指定なら `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` といった標準名にフォールバックします。
|
|
38
|
+
|
|
39
|
+
## コマンド
|
|
40
|
+
|
|
41
|
+
引数なし(または `--help`)でコマンド一覧を表示します。`-v` / `--version` で版数を表示。各コマンドは `--help` に対応します(例: `llm-task-router article:export --help`)。
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# インラインのテーマ
|
|
45
|
+
llm-task-router article:create --topic "AIが解釈しやすい中間言語を設計する"
|
|
46
|
+
|
|
47
|
+
# 長文の指示はテキストファイルで(--topic / --topic-file はどちらか一方。両方指定はエラー)
|
|
48
|
+
llm-task-router article:create --topic-file topics/ai-ir.txt
|
|
49
|
+
|
|
50
|
+
# プロファイルで対象プラットフォームを変える(既定: qiita。profile は config/profiles/ にある)
|
|
51
|
+
llm-task-router article:create --topic-file topics/ai-ir.txt --profile zenn
|
|
52
|
+
|
|
53
|
+
llm-task-router article:resume --run 2026-06-16-example
|
|
54
|
+
llm-task-router article:review --run 2026-06-16-example
|
|
55
|
+
|
|
56
|
+
# final.md に自由な修正指示を反映(--instruction / --instruction-file はどちらか)
|
|
57
|
+
llm-task-router article:revise --run 2026-06-16-example --instruction "導入を短く。たとえ話を1つ追加"
|
|
58
|
+
|
|
59
|
+
# 別系統の審査役モデルで final.md を評価し、修正指示の草案を生成
|
|
60
|
+
llm-task-router article:evaluate --run 2026-06-16-example --min-severity major --criteria "正確性とコード例の動作を重視"
|
|
61
|
+
|
|
62
|
+
# 完成記事を任意のパスへ書き出す(例: Zenn リポジトリ)。--force で上書き。
|
|
63
|
+
llm-task-router article:export --run 2026-06-16-example --out ../zenn-content/articles/my-article.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`--topic-file` のとき `runId` はファイル名から生成されます(例: `ai-ir.txt` → `2026-06-16-ai-ir`)。`--run <runId>` で明示固定もできます。成果物は `runs/<runId>/` に保存されます。
|
|
67
|
+
|
|
68
|
+
## 開発
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install
|
|
72
|
+
cp .env.example .env
|
|
73
|
+
npm run build # 型チェック + CLI を dist/ にバンドル
|
|
74
|
+
npm test
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
開発中はビルドせずに `npm run article:*` スクリプト(`--` 区切りでフラグを渡す)や `npx tsx` でも実行できます。
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm run article:create -- --topic-file topics/ai-ir.txt --profile zenn
|
|
81
|
+
npx tsx src/index.ts article:create --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
作業コピーに対してグローバルの `llm-task-router` コマンドを使うにはリンクします。
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run build
|
|
88
|
+
npm link # `llm-task-router` がこのリポジトリを指すようになる
|
|
89
|
+
# ...
|
|
90
|
+
npm rm -g llm-task-router # 終わったら解除
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 記事プロファイル
|
|
94
|
+
|
|
95
|
+
`--profile <name>`(既定 `qiita`)で `config/profiles/<name>.yaml` を選びます。プロファイルは次を定義します。
|
|
96
|
+
|
|
97
|
+
- `platform` — 各ステップのプロンプトに織り込むラベル(`<platform>記事` / `<platform>向けMarkdown`)
|
|
98
|
+
- `style` — プラットフォームの作法(admonition 記法・front-matter の扱い等)。本文生成(draft / final / revise)に注入される
|
|
99
|
+
- `language` — 情報用
|
|
100
|
+
|
|
101
|
+
同梱プロファイル: `qiita` / `zenn` / `blog` / `note`。複製して自前のものを追加できます(例: `config/profiles/devto.yaml`)。`--platform <name>` はプロファイルのラベルだけを上書きします。解決後の `platform` と `style` は `meta.json` に保存され、`resume` / `review` / `revise` / `evaluate` が自動で引き継ぎます。
|
|
102
|
+
|
|
103
|
+
`article:revise` は、あなたの指示と現在の `final.md` から記事を書き直し、直前版を `final.bak.md` に退避します(`article:review` は `draft.md` から自動レビュー→書き直しをやり直すもので、自由指示は使いません)。
|
|
104
|
+
|
|
105
|
+
`article:export --run <runId> --out <path>` は run の `final.md` を指定先へコピーします(対象は `final.md` のみ)。秘密ファイル名(`.env*`)は拒否、ワークスペース外は警告、既存ファイルは `--force` 無しでは上書きしません。これは「任意の書き込み先を受け取らない」原則に対する**明示的でガード付きの例外**で、内部成果物は `runs/<runId>/` に閉じたままです。
|
|
106
|
+
|
|
107
|
+
`article:evaluate` は別系統の審査役モデル(`models.yaml` の `final_review` タスク。既定で本文の書き手と別プロバイダ)で現在の `final.md` を評価し、`runs/<runId>/` に3ファイルを書き出します。`final-review.json`(生スコアカード)、`final-review.md`(人が読むサマリ=判定・severity別件数・全指摘)、`revise-instruction.md`(`--min-severity` で絞った修正指示)。指示ファイルはローカル整形で生成され(追加APIコールなし)、確認・編集してから `article:revise --instruction-file` に渡せます。自動では書き直しません。`--criteria` / `--criteria-file` で評価の重点を指定できます。
|
|
108
|
+
|
|
109
|
+
評価観点は `config/criteria/` に置き、各プロファイルの `criteria_file` で対象に紐づきます。`article:evaluate` は run の profile(`meta.json` に保存)から観点を自動解決するため、通常は `--criteria-file` 不要です。
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
llm-task-router article:evaluate --run <runId> --min-severity minor
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
解決順: `--criteria`(インライン)> `--criteria-file`(明示上書き)> run の profile の `criteria_file` > なし。同梱: `config/criteria/default.md`(汎用の技術ルーブリック。`qiita`/`zenn`/`blog` が使用)と `config/criteria/note.md`(読みやすさ重視。`note` が使用)。一回だけ別観点で見たいときは `--criteria-file <path>` で上書きします。LLM-as-judge は実行ごとに揺れるため、対象ごとに観点を固定すると評価が一貫し比較しやすくなります。
|
|
116
|
+
|
|
117
|
+
## 実行推移の表示
|
|
118
|
+
|
|
119
|
+
すべてのコマンドは工程ごとの進捗を **stderr** に出し、`runId` / `final` のパスは **stdout** に出します(スクリプトが stdout を解析する際に進捗が混ざりません)。
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
[1/5] brief (article_brief) ...
|
|
123
|
+
[1/5] brief - done via openai/gpt-5.4 (2310ms, ~$0.0123)
|
|
124
|
+
[2/5] outline (outline) ...
|
|
125
|
+
[2/5] outline - done via anthropic/claude-opus-4-8 (4120ms, ~$0.0456)
|
|
126
|
+
total: ~$0.1240 (estimate)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
各行は実際に使われた provider/model・所要時間・概算コストを示し、最後に run 合計を出します。設定上の primary と異なる provider が出た場合はフォールバックが起きた印です。`article:resume` / `article:review` では完了済み工程を `skip (done)` と表示します。
|
|
130
|
+
|
|
131
|
+
コストはレスポンスの `usage`(トークン数)と `config/models.yaml` の `prices`(USD/1Mトークン)による**ローカル概算**で、表示のための追加APIコールはありません。単価未設定(または `0`)のモデルはコスト表示の対象外です。単価は変動するので最新に保ってください。
|
|
132
|
+
|
|
133
|
+
### 出力ガード
|
|
134
|
+
|
|
135
|
+
本文系の工程(draft / rewrite / revise)では、保存する Markdown に軽量なガードがかかります。
|
|
136
|
+
|
|
137
|
+
- **truncation 警告** — 出力が `max_tokens` / `max_output_tokens` で打ち切られた場合、`⚠` を表示します。`max_tokens` を増やして再実行する目安になります。
|
|
138
|
+
- **コードフェンス除去** — モデルが本文全体を ``` で囲んで返した場合、保存前に外側のフェンスを剥がします。文中の正当な(複数の)コードブロックには手を加えません。スキーマ工程は検証済み JSON を保存するため対象外です。
|
|
139
|
+
- **ラップ文検知(警告のみ)** — 本文が前置き(例:「以下は…改稿版です」)で始まる、または末尾が追加提案(例:「…で出し直せます」)になっている場合に `⚠` を表示します。検知は**文言パターン**ベース(「見出しで始まること」を要求しません)なので、最初の見出しの前にリード文が来る Zenn/note の正しい書き方では誤検知しません。自動で本文を編集はせず、修正は人に委ねます。
|
|
140
|
+
|
|
141
|
+
## セキュリティ方針
|
|
142
|
+
|
|
143
|
+
本ツールは CLI 専用です。HTTP API を公開せず、任意コード実行・任意URL取得を行わず、全文プロンプトをログに保存しません。エラーログは正規化され、APIキー・SDKの生レスポンス・ヘッダ・入力本文は含めません。
|
|
144
|
+
|
|
145
|
+
## モデルに関する注意
|
|
146
|
+
|
|
147
|
+
一部の provider/model の組み合わせは `temperature` 等の生成パラメータを受け付けません。Provider 実装は既知の未対応パラメータを送らないようにしています。`config/models.yaml` のモデル名は、実利用前に各 provider の最新APIで確認してください。
|
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# llm-task-router
|
|
2
|
+
|
|
3
|
+
*[日本語版 README](README.ja.md)*
|
|
4
|
+
|
|
5
|
+
TypeScript CLI for a thin ModelRouter that drives a multi-step article workflow (Qiita, Zenn, blog, …).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Global install builds a single bundled CLI (`dist/llm-task-router.js`) exposed as the `llm-task-router` command:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# from npm (the package is scoped; the installed command is still `llm-task-router`)
|
|
13
|
+
npm install -g @rex0220/llm-task-router
|
|
14
|
+
|
|
15
|
+
# or from a packed tarball
|
|
16
|
+
npm run build && npm pack
|
|
17
|
+
npm install -g ./rex0220-llm-task-router-<version>.tgz
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The CLI reads `config/models.yaml`, `config/profiles/`, `config/criteria/`, and `.env`, and writes `runs/`, all **relative to your current directory**. Scaffold the config templates into your working directory with `init`:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd my-articles
|
|
24
|
+
llm-task-router init # copies config/ and .env.example here (won't overwrite; use --force)
|
|
25
|
+
cp .env.example .env # then set your API keys
|
|
26
|
+
# edit config/models.yaml to set real model IDs
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then run it anywhere under that directory:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
llm-task-router --help
|
|
33
|
+
llm-task-router -v
|
|
34
|
+
llm-task-router article:create --topic "..."
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
API keys go in a `.env` in your working directory. `config/models.yaml` can refer to separated key names with `providers.*.api_key_env`; if omitted, providers fall back to standard names such as `OPENAI_API_KEY` and `ANTHROPIC_API_KEY`.
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
Run with no arguments (or `--help`) to list commands; `-v` / `--version` prints the version. Each command supports `--help` (e.g. `llm-task-router article:export --help`).
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Inline topic
|
|
45
|
+
llm-task-router article:create --topic "AIが解釈しやすい中間言語を設計する"
|
|
46
|
+
|
|
47
|
+
# Long instructions from a text file (--topic / --topic-file: provide exactly one; both is an error)
|
|
48
|
+
llm-task-router article:create --topic-file topics/ai-ir.txt
|
|
49
|
+
|
|
50
|
+
# Target a different platform via a profile (default: qiita). Profiles live in config/profiles/.
|
|
51
|
+
llm-task-router article:create --topic-file topics/ai-ir.txt --profile zenn
|
|
52
|
+
|
|
53
|
+
llm-task-router article:resume --run 2026-06-16-example
|
|
54
|
+
llm-task-router article:review --run 2026-06-16-example
|
|
55
|
+
|
|
56
|
+
# Apply a free-form revision instruction to final.md (--instruction / --instruction-file: pick one)
|
|
57
|
+
llm-task-router article:revise --run 2026-06-16-example --instruction "Make the intro shorter; add one analogy"
|
|
58
|
+
|
|
59
|
+
# Evaluate final.md with a separate judge model and generate a revision-instruction draft
|
|
60
|
+
llm-task-router article:evaluate --run 2026-06-16-example --min-severity major --criteria "Focus on accuracy and working code examples"
|
|
61
|
+
|
|
62
|
+
# Export the final article to a chosen path (e.g. a Zenn repo). --force to overwrite.
|
|
63
|
+
llm-task-router article:export --run 2026-06-16-example --out ../zenn-content/articles/my-article.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
With `--topic-file`, the `runId` is derived from the file name (e.g. `ai-ir.txt` → `2026-06-16-ai-ir`). Use `--run <runId>` to set it explicitly. Outputs are saved under `runs/<runId>/`.
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install
|
|
72
|
+
cp .env.example .env
|
|
73
|
+
npm run build # type-check + bundle the CLI to dist/
|
|
74
|
+
npm test
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
During development you can run the CLI without building via the `npm run article:*` scripts (note the `--` separator forwards flags) or `npx tsx`:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm run article:create -- --topic-file topics/ai-ir.txt --profile zenn
|
|
81
|
+
npx tsx src/index.ts article:create --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
To use the global `llm-task-router` command against your working copy, link it:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run build
|
|
88
|
+
npm link # makes `llm-task-router` resolve to this repo
|
|
89
|
+
# ...
|
|
90
|
+
npm rm -g llm-task-router # unlink when done
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Article profiles
|
|
94
|
+
|
|
95
|
+
`--profile <name>` (default `qiita`) selects a profile from `config/profiles/<name>.yaml`. A profile defines:
|
|
96
|
+
|
|
97
|
+
- `platform` — the label woven into every step prompt (`<platform>記事` / `<platform>向けMarkdown`)
|
|
98
|
+
- `style` — platform conventions (admonition syntax, front-matter rules, etc.) injected into the body-producing prompts (draft / final / revise)
|
|
99
|
+
- `language` — informational
|
|
100
|
+
|
|
101
|
+
Bundled profiles: `qiita`, `zenn`, `blog`. Copy one to add your own (e.g. `config/profiles/devto.yaml`). `--platform <name>` overrides just the label from the profile. The resolved `platform` and `style` are stored in `meta.json`, so `resume` / `review` / `revise` / `evaluate` reuse them automatically. Pair a profile with a matching `--criteria-file` for platform-specific evaluation.
|
|
102
|
+
|
|
103
|
+
`article:revise` rewrites `final.md` from your instruction and the current `final.md`, backing up the previous version to `final.bak.md`. (`article:review` instead re-runs the automatic review→rewrite from `draft.md` and ignores custom instructions.)
|
|
104
|
+
|
|
105
|
+
`article:export --run <runId> --out <path>` copies the run's `final.md` to a destination of your choice (only `final.md` is exported). It refuses secret filenames (`.env*`), warns when writing outside the workspace, and will not overwrite an existing file unless `--force` is given. This is the explicit, guarded exception to the "no arbitrary write destinations" rule — internal artifacts stay confined to `runs/<runId>/`.
|
|
106
|
+
|
|
107
|
+
`article:evaluate` reviews the current `final.md` with a separate judge model (the `final_review` task in `models.yaml`, defaulting to a different provider than the body writer) and writes three files to `runs/<runId>/`: `final-review.json` (raw scorecard), `final-review.md` (human-readable summary — verdict, per-severity counts, and all issues), and `revise-instruction.md` (actionable instructions filtered by `--min-severity`, `critical|major|minor|suggestion`, default `suggestion`). The instruction file is built locally (no extra API call) — review/edit it, then feed it to `article:revise --instruction-file`. It does not auto-rewrite. `--criteria` / `--criteria-file` focuses the evaluation on specific points.
|
|
108
|
+
|
|
109
|
+
Evaluation criteria live in `config/criteria/` and are associated with each profile via the profile's `criteria_file`. `article:evaluate` resolves the criteria automatically from the run's profile (stored in `meta.json`), so the usual command needs no `--criteria-file`:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
llm-task-router article:evaluate --run <runId> --min-severity minor
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Resolution order: `--criteria` (inline) > `--criteria-file` (explicit override) > the run profile's `criteria_file` > none. Bundled: `config/criteria/default.md` (general technical rubric, used by `qiita`/`zenn`/`blog`) and `config/criteria/note.md` (readability-focused, used by `note`). To use a different rubric for one run, pass `--criteria-file <path>`. Because LLM-as-judge results vary run to run, a fixed per-profile criteria file makes evaluations consistent and comparable.
|
|
116
|
+
|
|
117
|
+
## Progress Output
|
|
118
|
+
|
|
119
|
+
All commands print per-step progress to **stderr** while `runId` / `final` paths go to **stdout** (so scripts can parse stdout without mixing in progress lines).
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
[1/5] brief (article_brief) ...
|
|
123
|
+
[1/5] brief - done via openai/gpt-5.4 (2310ms, ~$0.0123)
|
|
124
|
+
[2/5] outline (outline) ...
|
|
125
|
+
[2/5] outline - done via anthropic/claude-opus-4-8 (4120ms, ~$0.0456)
|
|
126
|
+
total: ~$0.1240 (estimate)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Each line shows the provider/model actually used, the elapsed time, and an estimated cost; a run total is printed at the end. A provider different from the configured primary indicates a fallback. `article:resume` / `article:review` show already-completed steps as `skip (done)`.
|
|
130
|
+
|
|
131
|
+
Cost is a **local estimate** from the response's `usage` token counts and the `prices` in `config/models.yaml` (USD per 1M tokens) — no extra API call. Models without configured prices (or priced at `0`) are omitted from the cost output. Prices drift, so keep them current.
|
|
132
|
+
|
|
133
|
+
### Output Guards
|
|
134
|
+
|
|
135
|
+
For prose steps (draft / rewrite / revise), the saved Markdown gets two lightweight guards:
|
|
136
|
+
|
|
137
|
+
- **Truncation warning** — if the model output was cut off at `max_tokens` / `max_output_tokens`, the step prints a `⚠` warning so you can raise `max_tokens` and rerun.
|
|
138
|
+
- **Code-fence stripping** — if the model wrapped the entire document in a ``` fence, the outer fence is removed before saving. Legitimate inline/multiple code blocks are left untouched. Schema steps save validated JSON and are not affected.
|
|
139
|
+
- **Wrap-text detection (warn only)** — if the prose opens with a meta preamble (e.g. "以下は…改稿版です") or ends with follow-up offers (e.g. "…で出し直せます"), the step prints a `⚠` warning. Detection is phrase-based (not "must start with a heading"), so a legitimate lead paragraph before the first heading — normal for Zenn/note — does not trigger it. It does not auto-edit prose; the fix is left to you.
|
|
140
|
+
|
|
141
|
+
## Security Notes
|
|
142
|
+
|
|
143
|
+
This MVP is CLI-only. It does not expose an HTTP API, run arbitrary code, fetch arbitrary URLs, or store full prompts in logs. Error logs are normalized and should not include API keys, raw SDK responses, headers, or full input text.
|
|
144
|
+
|
|
145
|
+
## Model Notes
|
|
146
|
+
|
|
147
|
+
Some provider/model combinations do not accept generation parameters such as `temperature`. Provider implementations omit unsupported parameters where known, and model names in `config/models.yaml` should be verified against the current provider API before real use.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 技術記事 評価観点(共通デフォルト)
|
|
2
|
+
|
|
3
|
+
この内容は `article:evaluate` の審査役モデルに渡され、評価の重点になる。
|
|
4
|
+
profile の `criteria_file` から参照される共通デフォルト。対象ごとに変えたい場合は別ファイルを作り profile から参照する。
|
|
5
|
+
|
|
6
|
+
## 重点的に評価してほしい観点
|
|
7
|
+
|
|
8
|
+
### 1. 技術的正確性
|
|
9
|
+
- 事実誤り・用語の誤用・古い情報がないか。
|
|
10
|
+
- 断定が強すぎないか。未解明・諸説ある点は「まだ分かっていない」等で明示されているか。
|
|
11
|
+
|
|
12
|
+
### 2. コード・数値の再現性
|
|
13
|
+
- コード例がそのまま動くか(import漏れ・構文・APIの存在)。
|
|
14
|
+
- 計算値や出力例が実行結果と一致するか。一致しない場合は「概算」と明記されているか。
|
|
15
|
+
- 単位・桁・四捨五入の整合(特にkm/m等の換算)。
|
|
16
|
+
- ランタイム/バージョン前提(Node・TypeScript の target/lib 等)が明示され、使用APIの対応状況と整合しているか。
|
|
17
|
+
|
|
18
|
+
### 2.1 リソース解放・並行処理の片付け
|
|
19
|
+
- イベントリスナーの登録と解除が対になっているか(購読解除漏れ・MaxListenersExceededWarning のリスク)。
|
|
20
|
+
- タイマー(setTimeout 等)が確実に clear されるか、`finally` で後始末されているか。
|
|
21
|
+
- AbortController / AbortSignal の購読解除・abort 伝播・キャンセル理由の優先順位が正しいか。
|
|
22
|
+
- 例外パスと正常パスで後始末・打ち切り挙動が非対称になっていないか。
|
|
23
|
+
|
|
24
|
+
### 3. 用語の定義と一貫性
|
|
25
|
+
- 重要用語が初出で簡潔に定義されているか。
|
|
26
|
+
- 同じ対象の表記ゆれがないか(例: 名称の揺れ、英/日混在)。
|
|
27
|
+
|
|
28
|
+
### 4. 構成と流れ
|
|
29
|
+
- 見出しの論理が通っているか。重複・冗長な節がないか。
|
|
30
|
+
- 導入→本論→まとめの流れが対象読者にとって追いやすいか。
|
|
31
|
+
|
|
32
|
+
### 5. 読者適合とトーン
|
|
33
|
+
- 想定読者のレベルに合っているか(前提知識の置き方)。
|
|
34
|
+
- たとえ話が直感の助けになっているか、誤解を生んでいないか。
|
|
35
|
+
- 過度な煽り・冗長な前置き/後置きがないか。
|
|
36
|
+
|
|
37
|
+
### 6. Markdown体裁
|
|
38
|
+
- コードフェンスの開閉、太字(**)の開始/終了が対応しているか。
|
|
39
|
+
- 見出しレベル・リスト・リンクが崩れていないか。
|
|
40
|
+
|
|
41
|
+
## 出力の方針
|
|
42
|
+
- 各指摘は location(該当箇所)・problem(問題)・recommendation(具体的な直し方)を明確にすること。
|
|
43
|
+
- 重要度に応じて critical / major / minor / suggestion を厳密に区別すること。
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# note 記事 評価観点(読み物重視)
|
|
2
|
+
|
|
3
|
+
note(note.com)向け。一般読者も読む読み物として、わかりやすさと読後感を重視して評価する。
|
|
4
|
+
技術的正確性は保ちつつ、コードの厳密さより「伝わるか」を優先する。
|
|
5
|
+
|
|
6
|
+
## 重点的に評価してほしい観点
|
|
7
|
+
|
|
8
|
+
### 1. わかりやすさ(最優先)
|
|
9
|
+
- 専門用語をそのまま使っていないか。使う場合は初出で一言、やさしく言い換えているか。
|
|
10
|
+
- 前提知識のない読者でも流れを追えるか。
|
|
11
|
+
|
|
12
|
+
### 2. 読み物としての流れ・構成
|
|
13
|
+
- 導入で関心を引き、本論からまとめへ自然につながっているか。
|
|
14
|
+
- 重複・冗長・脱線がないか。一つの主題に沿っているか。
|
|
15
|
+
|
|
16
|
+
### 3. トーン・等身大さ
|
|
17
|
+
- 過度な煽り・断定・誇張がないか。
|
|
18
|
+
- 体験や具体例が等身大で、読後に「やってみよう」と思える余韻があるか。
|
|
19
|
+
|
|
20
|
+
### 4. 事実の確かさ
|
|
21
|
+
- 明らかな事実誤りや古い情報がないか(厳密な数値検証よりも、誤解を招く断定の有無を重視)。
|
|
22
|
+
- 不確かな点を断定していないか。
|
|
23
|
+
|
|
24
|
+
### 5. note の体裁
|
|
25
|
+
- `:::note` などプラットフォーム独自記法を使っていないか(標準 Markdown のみ)。
|
|
26
|
+
- 見出し・箇条書き・引用・太字が崩れていないか。
|
|
27
|
+
- コードや図に頼らず、文章で説明できているか(コードブロックの多用は減点気味に)。
|
|
28
|
+
|
|
29
|
+
## 出力の方針
|
|
30
|
+
- 各指摘は location(該当箇所)・problem(問題)・recommendation(具体的な直し方)を明確にすること。
|
|
31
|
+
- 重要度に応じて critical / major / minor / suggestion を厳密に区別すること。
|
|
32
|
+
- 読み物としての価値を損なう「わかりにくさ・冗長さ」は積極的に指摘すること。
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# モデルIDは利用時点の公式ドキュメント/ダッシュボードで必ず確認すること。
|
|
2
|
+
# - OpenAI: https://developers.openai.com/api/docs/pricing
|
|
3
|
+
# - Anthropic: https://docs.anthropic.com/en/docs/about-claude/models/overview
|
|
4
|
+
|
|
5
|
+
providers:
|
|
6
|
+
openai:
|
|
7
|
+
api_key_env: OPENAI_API_KEY_ARTICLE
|
|
8
|
+
anthropic:
|
|
9
|
+
api_key_env: ANTHROPIC_API_KEY_ARTICLE
|
|
10
|
+
|
|
11
|
+
# コスト概算用の単価(USD / 1M tokens)。価格改定でドリフトするため要定期確認。
|
|
12
|
+
# 取得時点: 2026-06(OpenAIは公式pricing、Anthropicはモデル概要に基づく概算)。
|
|
13
|
+
prices:
|
|
14
|
+
openai:
|
|
15
|
+
gpt-5.4:
|
|
16
|
+
input_usd_per_1m_tokens: 2.5
|
|
17
|
+
output_usd_per_1m_tokens: 15
|
|
18
|
+
gpt-5.4-mini:
|
|
19
|
+
input_usd_per_1m_tokens: 0.75
|
|
20
|
+
output_usd_per_1m_tokens: 4.5
|
|
21
|
+
anthropic:
|
|
22
|
+
claude-opus-4-8:
|
|
23
|
+
input_usd_per_1m_tokens: 5
|
|
24
|
+
output_usd_per_1m_tokens: 25
|
|
25
|
+
claude-sonnet-4-6:
|
|
26
|
+
input_usd_per_1m_tokens: 3
|
|
27
|
+
output_usd_per_1m_tokens: 15
|
|
28
|
+
|
|
29
|
+
defaults:
|
|
30
|
+
timeout_ms: 120000
|
|
31
|
+
|
|
32
|
+
tasks:
|
|
33
|
+
article_brief:
|
|
34
|
+
primary:
|
|
35
|
+
provider: openai
|
|
36
|
+
model: gpt-5.4
|
|
37
|
+
fallback:
|
|
38
|
+
- provider: anthropic
|
|
39
|
+
model: claude-opus-4-8
|
|
40
|
+
temperature: 0.4
|
|
41
|
+
max_tokens: 4000
|
|
42
|
+
|
|
43
|
+
outline:
|
|
44
|
+
primary:
|
|
45
|
+
provider: anthropic
|
|
46
|
+
model: claude-opus-4-8
|
|
47
|
+
fallback:
|
|
48
|
+
- provider: openai
|
|
49
|
+
model: gpt-5.4
|
|
50
|
+
temperature: 0.4
|
|
51
|
+
max_tokens: 4000
|
|
52
|
+
|
|
53
|
+
draft_markdown:
|
|
54
|
+
primary:
|
|
55
|
+
provider: openai
|
|
56
|
+
model: gpt-5.4
|
|
57
|
+
fallback:
|
|
58
|
+
- provider: anthropic
|
|
59
|
+
model: claude-sonnet-4-6
|
|
60
|
+
temperature: 0.6
|
|
61
|
+
max_tokens: 12000
|
|
62
|
+
timeout_ms: 180000
|
|
63
|
+
|
|
64
|
+
technical_review:
|
|
65
|
+
primary:
|
|
66
|
+
provider: anthropic
|
|
67
|
+
model: claude-opus-4-8
|
|
68
|
+
fallback:
|
|
69
|
+
- provider: openai
|
|
70
|
+
model: gpt-5.4
|
|
71
|
+
temperature: 0.2
|
|
72
|
+
max_tokens: 6000
|
|
73
|
+
|
|
74
|
+
rewrite:
|
|
75
|
+
primary:
|
|
76
|
+
provider: openai
|
|
77
|
+
model: gpt-5.4
|
|
78
|
+
fallback:
|
|
79
|
+
- provider: anthropic
|
|
80
|
+
model: claude-sonnet-4-6
|
|
81
|
+
temperature: 0.5
|
|
82
|
+
max_tokens: 12000
|
|
83
|
+
timeout_ms: 180000
|
|
84
|
+
|
|
85
|
+
# final.md の評価(審査役)。本文(rewrite)は openai 主体なので、別系統の anthropic を主審査に置く。
|
|
86
|
+
final_review:
|
|
87
|
+
primary:
|
|
88
|
+
provider: anthropic
|
|
89
|
+
model: claude-opus-4-8
|
|
90
|
+
fallback:
|
|
91
|
+
# opus 混雑時も審査役を Anthropic に保ち、本文(openai)との別系統性を維持する。
|
|
92
|
+
- provider: anthropic
|
|
93
|
+
model: claude-sonnet-4-6
|
|
94
|
+
- provider: openai
|
|
95
|
+
model: gpt-5.4
|
|
96
|
+
temperature: 0.2
|
|
97
|
+
max_tokens: 6000
|
|
98
|
+
|
|
99
|
+
markdown_format:
|
|
100
|
+
primary:
|
|
101
|
+
provider: openai
|
|
102
|
+
model: gpt-5.4-mini
|
|
103
|
+
temperature: 0.2
|
|
104
|
+
max_tokens: 8000
|
|
105
|
+
|
|
106
|
+
title_suggestions:
|
|
107
|
+
primary:
|
|
108
|
+
provider: openai
|
|
109
|
+
model: gpt-5.4-mini
|
|
110
|
+
temperature: 0.5
|
|
111
|
+
max_tokens: 1200
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 記事プロファイル: 一般的な技術ブログ
|
|
2
|
+
platform: ブログ
|
|
3
|
+
language: ja
|
|
4
|
+
criteria_file: config/criteria/default.md
|
|
5
|
+
style: |
|
|
6
|
+
一般的な技術ブログの作法に従う。本文は標準的な Markdown。
|
|
7
|
+
- プラットフォーム独自の記法(:::note など)は使わず、標準 Markdown のみで書く。
|
|
8
|
+
- コードブロックは言語指定つきのフェンスを使う。
|
|
9
|
+
- 見出しレベルは h2 から始め、文書全体を 1 つの h1 で始める。
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# 記事プロファイル: note(note.com)
|
|
2
|
+
platform: note
|
|
3
|
+
language: ja
|
|
4
|
+
criteria_file: config/criteria/note.md
|
|
5
|
+
style: |
|
|
6
|
+
note(note.com)の作法に従う。本文は素朴な Markdown。
|
|
7
|
+
- note のエディタは独自記法(:::note など)や凝った Markdown 装飾に対応しないため、標準的な見出し・段落・箇条書き・引用・太字のみを使う。
|
|
8
|
+
- コードブロックは言語指定つきのフェンスで書くが、装飾は最小限に留める(note ではシンタックスハイライトが限定的なため)。
|
|
9
|
+
- AIへの指示文(プロンプト)の例などは、コードフェンスにせず本文中に「」やかぎ括弧で短く引用する。読み物の流れを切らないため、フェンスは実際のコードに限る。
|
|
10
|
+
- 一般読者も読む前提で、専門用語は初出でやさしく言い換える。読み物として流れのある文章にする。
|
|
11
|
+
- 記事先頭の front-matter は本文に含めない。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 記事プロファイル: Qiita
|
|
2
|
+
# platform: プロンプトに使うプラットフォーム名(ラベル)
|
|
3
|
+
# language: 想定言語(任意・メモ用途)
|
|
4
|
+
# style: draft/final/revise の本文生成に注入する作法
|
|
5
|
+
|
|
6
|
+
platform: Qiita
|
|
7
|
+
language: ja
|
|
8
|
+
criteria_file: config/criteria/default.md
|
|
9
|
+
style: |
|
|
10
|
+
Qiita の作法に従う。本文は Markdown。
|
|
11
|
+
- コードブロックは言語指定つきのフェンスを使う。
|
|
12
|
+
- 補足や注意は `:::note info` / `:::note warn` の記法を使ってよい。
|
|
13
|
+
- 記事先頭の front-matter は本文に含めない。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 記事プロファイル: Zenn
|
|
2
|
+
platform: Zenn
|
|
3
|
+
language: ja
|
|
4
|
+
criteria_file: config/criteria/default.md
|
|
5
|
+
style: |
|
|
6
|
+
Zenn の作法に従う。本文は Markdown。
|
|
7
|
+
- 補足や注意は `:::message` / `:::details` の記法を使ってよい。
|
|
8
|
+
- 記事先頭の front-matter(title / emoji / type / topics / published)は本文に含めない。
|
|
9
|
+
- コードブロックは言語指定つきのフェンスを使う。
|