Skip to content

Commit edd9949

Browse files
committed
feat: 新しいハイプ表現検出ルールを追加
- "no-ai-hype-expressions"ルールを実装し、過度な誇張表現を検出 - READMEとテストケースを更新し、ルールの使用方法を明確化
1 parent d7bf880 commit edd9949

File tree

5 files changed

+589
-4
lines changed

5 files changed

+589
-4
lines changed

.github/copilot-instructions.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,5 @@ AIと人間の協力により、お互いの強みを活かした自然で読み
9090

9191
テストはUnit Testのみで確認を行う
9292

93-
- `textlint-tester`を使用して、ルールのテストを行う
93+
- `textlint-tester`を使用して、ルールのテストを行う
94+
- 実際にファイルを作成して、`textlint`コマンドを使ったテストは行わない

README.md

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,18 @@ AIが生成した文章によく見られる記述パターンを検出し、よ
77
### 1. no-ai-list-formatting
88
リストアイテムで機械的な印象を与える可能性のある記述パターンを検出します。
99

10-
#### 1-1. リストアイテムの強調パターン
10+
#### 1-1. リストアイテ#### no-ai-formal-expressions
11+
- `allows`: 指定したパターンにマッチする場合、エラーを報告しません
12+
- 文字列: `"許可したいテキスト"`
13+
- 正規表現: `"/パターン/フラグ"` (例: `"/以下のような.*/"`)
14+
15+
#### no-ai-hype-expressions
16+
- `allows`: 指定したパターンにマッチする場合、エラーを報告しません
17+
- 文字列: `"許可したいテキスト"`
18+
- 正規表現: `"/パターン/フラグ"` (例: `"/革命的な.*/"`)
19+
- `disableAbsolutenessPatterns`: `true`にすると絶対性・完全性を演出する表現の検出を無効にします
20+
- `disableAbstractPatterns`: `true`にすると抽象的・感覚的効果を演出する表現の検出を無効にします
21+
- `disabledPredictivePatterns`: `true`にすると権威的・予言的な表現の検出を無効にしますパターン
1122

1223
🔍 **検出される例:**
1324
```markdown
@@ -69,6 +80,73 @@ AIが機械的に使いがちな装飾的な絵文字の使用を検出します
6980
具体的には次のとおりです。
7081
```
7182

83+
### 3. no-ai-hype-expressions
84+
AIライティングで過度に使用されがちな誇張表現やハイプ的な表現を検出します。自然で読みやすい文章を促進するためのルールです。
85+
86+
#### 3-1. 絶対性・完全性を演出する表現
87+
88+
🔍 **検出される例:**
89+
```markdown
90+
革命的な技術で業界を変えます。
91+
これはゲームチェンジャーです。
92+
世界初のソリューションを提供します。
93+
究極のパフォーマンスを実現します。
94+
完全に問題を解決します。
95+
すべての課題を解決します。
96+
最高の品質を保証します。
97+
```
98+
99+
**推奨される表現:**
100+
```markdown
101+
効果的な技術で業界に変化をもたらします。
102+
これは大きな変化をもたらすでしょう。
103+
新しいソリューションを提供します。
104+
高いパフォーマンスを実現します。
105+
多くの問題を解決します。
106+
主要な課題を解決します。
107+
高い品質を保証します。
108+
```
109+
110+
#### 3-2. 抽象的・感覚的効果を演出する表現
111+
112+
🔍 **検出される例:**
113+
```markdown
114+
魔法のように動作します。
115+
奇跡的な結果を生み出します。
116+
可能性を解き放つソリューションです。
117+
AIを民主化するプラットフォームです。
118+
業務をスーパーチャージします。
119+
```
120+
121+
**推奨される表現:**
122+
```markdown
123+
スムーズに動作します。
124+
優れた結果を生み出します。
125+
新たな機会を創出するソリューションです。
126+
AIを利用しやすくするプラットフォームです。
127+
業務を効率化します。
128+
```
129+
130+
#### 3-3. 権威的・予言的な表現
131+
132+
🔍 **検出される例:**
133+
```markdown
134+
業界を再定義する革新です。
135+
未来を変える技術です。
136+
パラダイムシフトを起こします。
137+
不可避の変化が起こります。
138+
次世代のソリューションです。
139+
```
140+
141+
**推奨される表現:**
142+
```markdown
143+
業界に新しい視点をもたらす革新です。
144+
将来に影響を与える技術です。
145+
大きな変化を起こします。
146+
重要な変化が起こるでしょう。
147+
新しいソリューションです。
148+
```
149+
72150
## Install
73151

74152
Install with [npm](https://www.npmjs.com/package/textlint-rule-preset-ai-writing):
@@ -102,6 +180,12 @@ Via `.textlintrc`(Recommended)
102180
},
103181
"no-ai-formal-expressions": {
104182
"allows": ["許可したいテキスト", "/正規表現パターン/"]
183+
},
184+
"no-ai-hype-expressions": {
185+
"allows": ["許可したいテキスト", "/正規表現パターン/"],
186+
"disableAbsolutenessPatterns": false,
187+
"disableAbstractPatterns": false,
188+
"disabledPredictivePatterns": false
105189
}
106190
}
107191
}

src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import noAiListFormatting from "./rules/no-ai-list-formatting";
22
import noAiFormalExpressions from "./rules/no-ai-formal-expressions";
3+
import noAiHypeExpressions from "./rules/no-ai-hype-expressions";
34

45
const preset = {
56
rules: {
67
"no-ai-list-formatting": noAiListFormatting,
7-
"no-ai-formal-expressions": noAiFormalExpressions
8+
"no-ai-formal-expressions": noAiFormalExpressions,
9+
"no-ai-hype-expressions": noAiHypeExpressions
810
},
911
rulesConfig: {
1012
"no-ai-list-formatting": true,
11-
"no-ai-formal-expressions": true
13+
"no-ai-formal-expressions": true,
14+
"no-ai-hype-expressions": true
1215
}
1316
};
1417

src/rules/no-ai-hype-expressions.ts

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import type { TextlintRuleModule } from "@textlint/types";
2+
import { matchPatterns } from "@textlint/regexp-string-matcher";
3+
4+
export interface Options {
5+
// If node's text includes allowed patterns, does not report.
6+
// Can be string or RegExp-like string ("/pattern/flags")
7+
allows?: string[];
8+
// Disable specific categories of hype expressions
9+
disableAbsolutenessPatterns?: boolean;
10+
disableAbstractPatterns?: boolean;
11+
disabledPredictivePatterns?: boolean;
12+
}
13+
14+
const rule: TextlintRuleModule<Options> = (context, options = {}) => {
15+
const { Syntax, RuleError, report, getSource, locator } = context;
16+
const allows = options.allows ?? [];
17+
const disableAbsolutenessPatterns = options.disableAbsolutenessPatterns ?? false;
18+
const disableAbstractPatterns = options.disableAbstractPatterns ?? false;
19+
const disabledPredictivePatterns = options.disabledPredictivePatterns ?? false;
20+
21+
// 絶対性と完全性を演出するハイプ表現
22+
const absolutenessPatterns = [
23+
{
24+
pattern: //g,
25+
message:
26+
"「革命的な」という表現は過度に誇張的である可能性があります。具体的な改善点を述べることを検討してください。"
27+
},
28+
{
29+
pattern: //g,
30+
message:
31+
"「ゲームチェンジャー」という表現は機械的な印象を与える可能性があります。具体的な変化を説明することを検討してください。"
32+
},
33+
{
34+
pattern: //g,
35+
message:
36+
"「世界初の」という表現は過度に強調的である可能性があります。事実に基づいた表現を検討してください。"
37+
},
38+
{
39+
pattern: //g,
40+
message: "「究極の」という表現は誇張的である可能性があります。より具体的で控えめな表現を検討してください。"
41+
},
42+
{
43+
pattern: //g,
44+
message:
45+
"「完全に」という絶対的な表現は過度に断定的である可能性があります。「多くの場合」などの表現を検討してください。"
46+
},
47+
{
48+
pattern: /[]/g,
49+
message:
50+
"「すべて」という包括的な表現は過度に断定的である可能性があります。「多くの」や「主な」などの表現を検討してください。"
51+
},
52+
{
53+
pattern: //g,
54+
message:
55+
"「完璧な」という表現は過度に理想化している可能性があります。具体的な利点を述べることを検討してください。"
56+
},
57+
{
58+
pattern: //g,
59+
message:
60+
"「最高の」という表現は主観的で誇張的である可能性があります。より客観的な評価を示すことを検討してください。"
61+
},
62+
{
63+
pattern: //g,
64+
message:
65+
"「最先端の」という表現は定型的である可能性があります。具体的な技術的特徴を説明することを検討してください。"
66+
}
67+
];
68+
69+
// 抽象的・感覚的効果を演出するハイプ表現
70+
const abstractPatterns = [
71+
{
72+
pattern: //g,
73+
message:
74+
"「魔法のように」という比喩的表現は現実味に欠ける可能性があります。具体的な仕組みを説明することを検討してください。"
75+
},
76+
{
77+
pattern: //g,
78+
message:
79+
"「奇跡的な」という表現は過度に感情的である可能性があります。具体的な成果を示すことを検討してください。"
80+
},
81+
{
82+
pattern: //g,
83+
message:
84+
"「驚異的な」という表現は誇張的である可能性があります。数値や事実に基づいた表現を検討してください。"
85+
},
86+
{
87+
pattern: //g,
88+
message:
89+
"「可能性を解き放つ」という抽象的な表現は曖昧である可能性があります。具体的な利益を説明することを検討してください。"
90+
},
91+
{
92+
pattern: //g,
93+
message:
94+
"「潜在能力を引き出す」という表現は抽象的である可能性があります。具体的な効果を説明することを検討してください。"
95+
},
96+
{
97+
pattern: //g,
98+
message:
99+
"「民主化する」という表現は技術文脈では曖昧である可能性があります。「利用しやすくする」などの具体的な表現を検討してください。"
100+
},
101+
{
102+
pattern: //g,
103+
message:
104+
"「スーパーチャージ」という表現は機械的な印象を与える可能性があります。具体的な改善内容を説明することを検討してください。"
105+
},
106+
{
107+
pattern: //g,
108+
message:
109+
"「驚嘆させる」という表現は過度に感情的である可能性があります。客観的な評価を示すことを検討してください。"
110+
}
111+
];
112+
113+
// 権威的・予言的なハイプ表現
114+
const predictivePatterns = [
115+
{
116+
pattern: //g,
117+
message:
118+
"「業界を再定義する」という表現は誇張的である可能性があります。具体的な変化を説明することを検討してください。"
119+
},
120+
{
121+
pattern: //g,
122+
message:
123+
"「未来を変える」という表現は大げさである可能性があります。具体的な改善点を述べることを検討してください。"
124+
},
125+
{
126+
pattern: //g,
127+
message:
128+
"「パラダイムシフト」という表現は定型的である可能性があります。具体的な変化を説明することを検討してください。"
129+
},
130+
{
131+
pattern: //g,
132+
message:
133+
"「不可避の」という表現は過度に断定的である可能性があります。「可能性が高い」などの表現を検討してください。"
134+
},
135+
{
136+
pattern: //g,
137+
message:
138+
"「新たな基準を設定」という表現は誇張的である可能性があります。具体的な改善内容を説明することを検討してください。"
139+
},
140+
{
141+
pattern: //g,
142+
message:
143+
"「次世代の」という表現は定型的である可能性があります。具体的な技術的進歩を説明することを検討してください。"
144+
},
145+
{
146+
pattern: //g,
147+
message:
148+
"「フロンティアを開拓」という比喩的表現は抽象的である可能性があります。具体的な取り組みを説明することを検討してください。"
149+
},
150+
{
151+
pattern: //g,
152+
message:
153+
"「根本的に変革」という表現は誇張的である可能性があります。具体的な変化を説明することを検討してください。"
154+
}
155+
];
156+
157+
return {
158+
[Syntax.Str](node) {
159+
const text = getSource(node);
160+
161+
// 許可パターンのチェック
162+
if (allows.length > 0) {
163+
const matches = matchPatterns(text, allows);
164+
if (matches.length > 0) {
165+
return;
166+
}
167+
}
168+
169+
// 各カテゴリのパターンをチェック
170+
const patterns = [
171+
...(disableAbsolutenessPatterns ? [] : absolutenessPatterns),
172+
...(disableAbstractPatterns ? [] : abstractPatterns),
173+
...(disabledPredictivePatterns ? [] : predictivePatterns)
174+
];
175+
176+
for (const { pattern, message } of patterns) {
177+
const matches = text.matchAll(pattern);
178+
for (const match of matches) {
179+
const index = match.index ?? 0;
180+
const matchRange = [index, index + match[0].length] as const;
181+
const ruleError = new RuleError(message, {
182+
padding: locator.range(matchRange)
183+
});
184+
report(node, ruleError);
185+
}
186+
}
187+
}
188+
};
189+
};
190+
191+
export default rule;

0 commit comments

Comments
 (0)