596 lines
18 KiB
Markdown
596 lines
18 KiB
Markdown
# GEO Agent Article Optimizer MVP Design
|
|
|
|
## Goal
|
|
|
|
Build a lightweight internal web tool for optimizing pasted GEO-related articles while preventing the issues observed in `GEO生成文章改动点(0422).docx`: industry drift, image-text mismatch, third-party voice in official articles, platform mismatch, incomplete company names, title/body grammar issues, hallucinated claims, inconsistent claims, context-insensitive sensitive-word handling, and useless content.
|
|
|
|
The first version validates the content quality loop before investing in batching, permissions, publishing integrations, or complex document parsing.
|
|
|
|
## MVP Scope
|
|
|
|
The MVP is a local web application:
|
|
|
|
1. User pastes title, body, image descriptions or image links, and selects the target platform.
|
|
2. System extracts a candidate fact card.
|
|
3. User confirms or edits the fact card.
|
|
4. Confirmed fact card is saved as a reusable local brand template.
|
|
5. System generates three optimization candidates under fact-card constraints:
|
|
`标准GEO版`, `去AI味版`, and `综合增强版`.
|
|
6. System runs quality gates for each candidate.
|
|
7. Failed checks trigger targeted rewriting for up to two rounds inside each
|
|
candidate path.
|
|
8. System scores each candidate for GEO quality and AI-flavor/style quality.
|
|
9. User compares all candidates side by side, with `综合增强版` highlighted as
|
|
the recommended default.
|
|
10. User downloads Markdown, Word, and QA artifacts for the selected candidate
|
|
or the full comparison.
|
|
|
|
## Explicitly Out Of Scope
|
|
|
|
- Account permissions.
|
|
- Multi-user collaboration.
|
|
- Publishing platform APIs.
|
|
- Batch queues.
|
|
- Direct `.docx` upload parsing.
|
|
- Complex Word template layout.
|
|
- Automatic use of unconfirmed facts.
|
|
|
|
## User Flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A["Input Article"] --> B["Auto Analyze"]
|
|
B --> C["Confirm Fact Card"]
|
|
C --> D["Generate Three Candidates"]
|
|
D --> E1["标准GEO版 QA"]
|
|
D --> E2["去AI味版 QA"]
|
|
D --> E3["综合增强版 QA"]
|
|
E1 --> F["Compare Results"]
|
|
E2 --> F
|
|
E3 --> F
|
|
F --> G["Select Candidate"]
|
|
G --> H["Download Markdown / Word / QA"]
|
|
```
|
|
|
|
## Page Areas
|
|
|
|
### Article Input
|
|
|
|
Fields:
|
|
|
|
- Title.
|
|
- Body.
|
|
- Image description or image link.
|
|
- Target platform: official site, media article, comparison review, recommendation list.
|
|
- User instructions.
|
|
|
|
The first version accepts pasted text instead of `.docx` upload to avoid early complexity around Word layout parsing.
|
|
|
|
### Fact Card Confirmation
|
|
|
|
The system extracts candidate facts, but they are not treated as truth until the user confirms them.
|
|
|
|
Fields:
|
|
|
|
- Company full name.
|
|
- Company short names.
|
|
- Brand names.
|
|
- Product names.
|
|
- Target industry.
|
|
- Target audience.
|
|
- Experience years.
|
|
- Core claims.
|
|
- Forbidden claims.
|
|
- Image topics.
|
|
- Uncertain items.
|
|
|
|
The user must resolve uncertain items before optimization starts.
|
|
|
|
### Candidate Comparison
|
|
|
|
Display:
|
|
|
|
- Candidate label: `标准GEO版`, `去AI味版`, or `综合增强版`.
|
|
- Method summary that explains what this candidate optimized for.
|
|
- Optimized title.
|
|
- Summary.
|
|
- Optimized body.
|
|
- QA status and failed/warned rule count.
|
|
- GEO score.
|
|
- AI-flavor/style score.
|
|
- Timing.
|
|
- Changed sections.
|
|
|
|
The UI should mark content that needs user confirmation and should make
|
|
`综合增强版` visually identifiable as the recommended default without hiding the
|
|
other two candidates.
|
|
|
|
### Quality Report
|
|
|
|
Display each gate as pass, warn, or fail, with evidence, reason, suggested fix, and target rewrite module.
|
|
|
|
### Export
|
|
|
|
Downloads:
|
|
|
|
- Selected candidate: `optimized.md`, `optimized.docx`, `qa_report.json`.
|
|
- Full comparison: `variant_comparison.json`.
|
|
- If all candidate files are exported together, use stable Chinese labels in the
|
|
metadata and ASCII-safe file names on disk, such as
|
|
`standard-geo.md`, `anti-ai-flavor.md`, and `integrated-enhanced.md`.
|
|
|
|
## Internal Agent Nodes
|
|
|
|
The product is delivered as a simple web app, but the internals are split into explicit workflow nodes.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
A["InputNormalizer"] --> B["FactExtractor"]
|
|
B --> C["UserConfirmedFactCard"]
|
|
C --> D["OptimizationVariantPlanner"]
|
|
D --> E1["标准GEO版 ArticleOptimizer"]
|
|
D --> E2["去AI味版 ArticleOptimizer"]
|
|
D --> E3["综合增强版 ArticleOptimizer"]
|
|
E1 --> F1["QualityInspector + StyleScorer"]
|
|
E2 --> F2["QualityInspector + StyleScorer"]
|
|
E3 --> F3["QualityInspector + StyleScorer"]
|
|
F1 -->|fail| G1["TargetedRewriter"]
|
|
F2 -->|fail| G2["TargetedRewriter"]
|
|
F3 -->|fail| G3["TargetedRewriter"]
|
|
G1 --> F1
|
|
G2 --> F2
|
|
G3 --> F3
|
|
F1 --> H["VariantComparator"]
|
|
F2 --> H
|
|
F3 --> H
|
|
H --> I["Exporter"]
|
|
```
|
|
|
|
### LLM Provider Integration
|
|
|
|
Workflow nodes use a local LLM client abstraction instead of calling a vendor API directly. The first implementation uses DeepSeek by default, while keeping the provider boundary open for later OpenAI-compatible providers.
|
|
|
|
Environment variables:
|
|
|
|
```text
|
|
LLM_PROVIDER=deepseek
|
|
DEEPSEEK_API_KEY=
|
|
DEEPSEEK_BASE_URL=https://api.deepseek.com
|
|
DEEPSEEK_MODEL=deepseek-v4-pro
|
|
DEEPSEEK_THINKING=disabled
|
|
```
|
|
|
|
Rules:
|
|
|
|
- `src/lib/llm/client.ts` exposes `generateText`, `generateJson<T>`,
|
|
`generateValidatedJson`, `isLlmConfigured`, and `getLlmProviderStatus`.
|
|
- `LLM_PROVIDER` defaults to `deepseek` when unset.
|
|
- DeepSeek is accessed through the OpenAI-compatible SDK with `baseURL` set to `https://api.deepseek.com`.
|
|
- `generateJson<T>` must use JSON output mode and prompts that explicitly require valid JSON only.
|
|
- Thinking mode is disabled by default for deterministic article rewrites and structured QA output.
|
|
- Missing credentials and provider failures surface as explicit API errors; the
|
|
workflow must not silently fall back when the user expects live LLM behavior.
|
|
- Provider errors are normalized inside the LLM client before they reach workflow nodes or API routes.
|
|
|
|
### InputNormalizer
|
|
|
|
Purpose: normalize page input into clean structured data.
|
|
|
|
Input:
|
|
|
|
- Title.
|
|
- Body.
|
|
- Image descriptions or links.
|
|
- Target platform.
|
|
- User instructions.
|
|
|
|
Output:
|
|
|
|
- `article_draft`
|
|
- `image_assets`
|
|
- `publish_context`
|
|
|
|
This node does not optimize content.
|
|
|
|
### FactExtractor
|
|
|
|
Purpose: extract candidate facts from the source article.
|
|
|
|
Output:
|
|
|
|
- `company_full_name`
|
|
- `company_short_name`
|
|
- `brand_names`
|
|
- `product_names`
|
|
- `target_industry`
|
|
- `target_audience`
|
|
- `experience_years`
|
|
- `core_claims`
|
|
- `forbidden_claims`
|
|
- `image_topics`
|
|
- `uncertain_items`
|
|
|
|
Low-confidence facts must go into `uncertain_items`.
|
|
|
|
### UserConfirmedFactCard
|
|
|
|
Purpose: provide hard constraints for all downstream nodes.
|
|
|
|
Rules:
|
|
|
|
- No downstream node may invent numbers, qualifications, clients, cases, or experience years outside the confirmed fact card.
|
|
- Company and product names must follow the fact card.
|
|
- Industry and audience must not drift from the fact card.
|
|
- Sensitive words must be handled by context, not removed mechanically.
|
|
|
|
### ArticleOptimizer
|
|
|
|
Purpose: improve title, summary, body, structure, and image suggestions under fact-card constraints.
|
|
|
|
Allowed:
|
|
|
|
- Improve fluency.
|
|
- Fix grammar.
|
|
- Adjust structure.
|
|
- Improve platform fit.
|
|
- Remove useless content.
|
|
- Improve transitions.
|
|
|
|
Forbidden:
|
|
|
|
- Invent claims.
|
|
- Change company or product names.
|
|
- Change industry.
|
|
- Add exaggerated marketing promises.
|
|
|
|
### OptimizationVariantPlanner
|
|
|
|
Purpose: create a fixed set of candidate methods for one confirmed fact card.
|
|
|
|
The first comparison version always creates exactly three candidates:
|
|
|
|
| Variant Key | Label | Method |
|
|
| --- | --- | --- |
|
|
| `standard_geo` | `标准GEO版` | Current GEO rewrite behavior: fact-card fidelity, platform fit, clear structure, and QA repair. |
|
|
| `anti_ai_flavor` | `去AI味版` | GEO rewrite plus localized Stop Slop rules to remove AI-flavored prose patterns. |
|
|
| `integrated_enhanced` | `综合增强版` | Balances GEO density, factual trust, platform fit, readability, and AI-flavor reduction. |
|
|
|
|
Candidate generation may run sequentially in the first implementation to avoid
|
|
provider rate-limit problems and to keep progress events understandable. The
|
|
API response still returns the three candidates together for side-by-side
|
|
comparison.
|
|
|
|
### Stop Slop / AI-Flavor Rules
|
|
|
|
The `hardikpandya/stop-slop` repository is an MIT-licensed writing skill rather
|
|
than an installable runtime dependency. The original rules are mostly English
|
|
writing rules, so the product should not copy them blindly into Chinese article
|
|
optimization.
|
|
|
|
The first implementation uses localized guidance derived from the rule intent:
|
|
|
|
- Remove template openers and filler transitions, such as generic "本文将",
|
|
"值得注意的是", "从某种意义上", and repeated "通过...实现..." chains.
|
|
- Replace empty business language with concrete claims already present in the
|
|
fact card.
|
|
- Avoid formulaic contrast paragraphs that first list what something is not and
|
|
then reveal what it is.
|
|
- Avoid slogan-like short fragments that sound like pull quotes instead of
|
|
article prose.
|
|
- Prefer active, specific Chinese sentences with clear actors when the source
|
|
facts identify an actor.
|
|
- Vary paragraph length and sentence rhythm without using theatrical emphasis.
|
|
|
|
If substantial text from the upstream Stop Slop files is copied into this
|
|
repository, add the MIT license notice to the committed artifact. A distilled
|
|
Chinese rule set written for this product can live in source without vendoring
|
|
the upstream repository.
|
|
|
|
### QualityInspector
|
|
|
|
Purpose: convert the document's issue list into executable quality gates.
|
|
|
|
Each check returns:
|
|
|
|
- `status`: `pass`, `warn`, or `fail`.
|
|
- `evidence`: source or optimized text snippet.
|
|
- `reason`: why the check passed or failed.
|
|
- `suggested_fix`: how to fix it.
|
|
- `target_agent`: rewrite target when failed.
|
|
|
|
### TargetedRewriter
|
|
|
|
Purpose: fix only failed checks.
|
|
|
|
Examples:
|
|
|
|
- Rewrite only the title for title quality failures.
|
|
- Adjust only the affected paragraph for body quality failures.
|
|
- Normalize company names for fact consistency failures.
|
|
- Delete or mark unsupported claims for hallucination risk.
|
|
- Warn instead of rewriting when image-text confidence is low.
|
|
|
|
### StyleScorer
|
|
|
|
Purpose: score each candidate for AI-flavor risk and naturalness without turning
|
|
style concerns into hard QA failures.
|
|
|
|
The first version returns a 0-50 score with five 0-10 dimensions:
|
|
|
|
| Dimension | Question |
|
|
| --- | --- |
|
|
| `directness` | Does the article state concrete points instead of announcing them? |
|
|
| `rhythm` | Do sentence and paragraph lengths vary naturally? |
|
|
| `specificity` | Are claims concrete and tied to the fact card? |
|
|
| `trust` | Does the prose avoid exaggerated intimacy, sweeping claims, and unsupported certainty? |
|
|
| `density` | Can obvious filler be removed without losing meaning? |
|
|
|
|
Scores below 35 should show a visible warning on the candidate card. They should
|
|
not block export.
|
|
|
|
### GeoScore
|
|
|
|
Purpose: reuse the existing GEO article performance rubric to score each
|
|
candidate for fact integrity, platform fit, search intent fit, answer density,
|
|
trust signal quality, and readability.
|
|
|
|
The score is computed from the candidate article plus its QA report. It is a
|
|
comparison aid for the three candidates, not a publishing guarantee.
|
|
|
|
### VariantComparator
|
|
|
|
Purpose: assemble the candidate list for the API response and frontend.
|
|
|
|
Each candidate result includes:
|
|
|
|
- `variant_key`
|
|
- `variant_label`
|
|
- `method_summary`
|
|
- `article`
|
|
- `qa_report`
|
|
- `geo_score`
|
|
- `style_score`
|
|
- `rewrite_rounds`
|
|
- `timing`
|
|
- `is_recommended`
|
|
|
|
`integrated_enhanced` is recommended by default unless it has a hard QA failure
|
|
and another candidate does not.
|
|
|
|
## Quality Gates
|
|
|
|
| Rule ID | Issue Prevented | First Version Behavior |
|
|
| --- | --- | --- |
|
|
| `industry_alignment` | Industry drift | Compare article against fact-card industry and audience. |
|
|
| `image_text_match` | Image-text mismatch | Compare image descriptions/topics with nearby article sections. |
|
|
| `voice_consistency` | Third-party voice in official articles | Enforce platform-specific tone. |
|
|
| `platform_fit` | Wrong article type for platform | Compare style and structure against target platform. |
|
|
| `company_name_integrity` | Incomplete company name | Compare against confirmed company full name and allowed short names. |
|
|
| `title_quality` | Title grammar issues | Detect awkward, keyword-stuffed, or semantically broken titles. |
|
|
| `body_quality` | Body grammar issues | Detect long sentences, unclear references, and broken logic. |
|
|
| `hallucination_risk` | Fabricated or misleading claims | Reject claims not traceable to the confirmed fact card. |
|
|
| `claim_consistency` | Inconsistent years/products/services | Scan and normalize repeated factual claims. |
|
|
| `context_sensitive_terms` | Blind sensitive-word deletion | Warn when wording needs context-aware handling. |
|
|
|
|
### Hard Fail
|
|
|
|
- Incomplete or inconsistent company name.
|
|
- New numbers, qualifications, customer cases, or other claims outside the fact card.
|
|
- Clear industry drift.
|
|
- Severe title grammar failure.
|
|
- Conflicting experience years, product names, or service names.
|
|
|
|
### Warn
|
|
|
|
- Low-confidence image-text match.
|
|
- Uncertain sensitive-word context.
|
|
- Weak platform fit.
|
|
- Overly promotional or low-density paragraphs.
|
|
|
|
### Auto Fix
|
|
|
|
- Body grammar.
|
|
- Useless content.
|
|
- Third-party voice when platform is official site.
|
|
|
|
## Data Model
|
|
|
|
The first version uses local SQLite plus an export folder.
|
|
|
|
The first implementation can reuse the existing `optimized_articles` and
|
|
`qa_reports` tables by saving each candidate as a normal article revision with
|
|
variant metadata inside the JSON payload. The matching QA report is saved
|
|
against that revision. A separate comparison-run table is out of scope until the
|
|
product needs historical side-by-side comparison beyond the immediate job
|
|
result.
|
|
|
|
```text
|
|
data/
|
|
app.db
|
|
exports/
|
|
job_xxx/
|
|
optimized.md
|
|
optimized.docx
|
|
qa_report.json
|
|
variant_comparison.json
|
|
standard-geo.md
|
|
anti-ai-flavor.md
|
|
integrated-enhanced.md
|
|
```
|
|
|
|
### `brand_template`
|
|
|
|
Reusable confirmed brand facts.
|
|
|
|
```json
|
|
{
|
|
"id": "brand_xxx",
|
|
"brand_name": "Brand",
|
|
"company_full_name": "Company Ltd.",
|
|
"company_short_names": ["Company"],
|
|
"product_names": ["Product"],
|
|
"target_industries": ["GEO optimization"],
|
|
"target_audience": ["Marketing teams"],
|
|
"verified_claims": ["More than ten years of industry experience"],
|
|
"forbidden_claims": ["Do not claim industry first without proof"],
|
|
"tone_rules": {
|
|
"official_site": "brand first-person or official voice",
|
|
"media": "objective third-party voice"
|
|
},
|
|
"updated_at": "2026-06-16T10:00:00+08:00"
|
|
}
|
|
```
|
|
|
|
### `article_job`
|
|
|
|
One optimization task.
|
|
|
|
```json
|
|
{
|
|
"id": "job_xxx",
|
|
"brand_template_id": "brand_xxx",
|
|
"source_title": "Original title",
|
|
"source_body": "Original body",
|
|
"image_inputs": [
|
|
{
|
|
"type": "description",
|
|
"content": "Product dashboard screenshot"
|
|
}
|
|
],
|
|
"publish_platform": "official_site",
|
|
"status": "qa_failed",
|
|
"created_at": "2026-06-16T10:05:00+08:00"
|
|
}
|
|
```
|
|
|
|
### `fact_card`
|
|
|
|
Confirmed facts for one job.
|
|
|
|
```json
|
|
{
|
|
"job_id": "job_xxx",
|
|
"source": "auto_extract_then_user_confirmed",
|
|
"company_full_name": "Company Ltd.",
|
|
"product_names": ["Product"],
|
|
"target_industry": "GEO optimization",
|
|
"publish_intent": "official article",
|
|
"locked_claims": ["More than ten years of industry experience"],
|
|
"uncertain_items": [],
|
|
"confirmed_by_user": true
|
|
}
|
|
```
|
|
|
|
### `optimized_article`
|
|
|
|
One revision of optimized content.
|
|
|
|
```json
|
|
{
|
|
"job_id": "job_xxx",
|
|
"revision": 2,
|
|
"variant_key": "integrated_enhanced",
|
|
"variant_label": "综合增强版",
|
|
"method_summary": "同时优化GEO信息密度、事实可信度、平台适配和表达自然度。",
|
|
"title": "Optimized title",
|
|
"summary": "Optimized summary",
|
|
"body_markdown": "Optimized body in Markdown",
|
|
"image_suggestions": [
|
|
{
|
|
"source": "image_1",
|
|
"suggestion": "Use product dashboard screenshot; avoid unrelated people photos"
|
|
}
|
|
],
|
|
"changed_sections": ["title", "first paragraph"],
|
|
"geo_score": {
|
|
"total": 8.1,
|
|
"rationale": "事实一致性与答案密度较好,平台适配仍需复核。"
|
|
},
|
|
"style_score": {
|
|
"total": 42,
|
|
"dimensions": {
|
|
"directness": 8,
|
|
"rhythm": 8,
|
|
"specificity": 9,
|
|
"trust": 8,
|
|
"density": 9
|
|
},
|
|
"warnings": []
|
|
}
|
|
}
|
|
```
|
|
|
|
### `qa_report`
|
|
|
|
Quality checks for one revision.
|
|
|
|
```json
|
|
{
|
|
"job_id": "job_xxx",
|
|
"revision": 2,
|
|
"overall_status": "warn",
|
|
"checks": [
|
|
{
|
|
"rule_id": "hallucination_risk",
|
|
"status": "pass",
|
|
"evidence": "No new unsupported factual claims found",
|
|
"reason": "All factual claims are traceable to the confirmed fact card",
|
|
"target_agent": null
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Fact Extraction
|
|
|
|
Optimization is disabled until the user resolves uncertain facts.
|
|
|
|
Examples:
|
|
|
|
- Only a company short name is found.
|
|
- Multiple product names appear.
|
|
- Multiple experience-year claims appear.
|
|
- Target industry is unclear.
|
|
- Image description is missing.
|
|
|
|
### QA Failure
|
|
|
|
Hard failures do not block export in the current product direction. The app must
|
|
preserve export files and show visible warnings so the user can review the
|
|
candidate manually.
|
|
|
|
Failed checks trigger targeted rewrite for up to two rounds inside each
|
|
candidate. After two failed rounds, the app stops rewriting that candidate and
|
|
shows manual review fields.
|
|
|
|
## Acceptance Criteria
|
|
|
|
The MVP is complete when:
|
|
|
|
1. User can paste title, body, image descriptions, and target platform.
|
|
2. System can extract a fact card and require user confirmation.
|
|
3. Confirmed fact card can be saved and reused as a local brand template.
|
|
4. System can generate an optimized article without changing confirmed facts.
|
|
5. System can generate exactly three labeled candidates: `标准GEO版`,
|
|
`去AI味版`, and `综合增强版`.
|
|
6. System can generate a structured QA report for the 10 quality gates for each
|
|
candidate.
|
|
7. System can score and compare the three candidates for AI-flavor/style quality.
|
|
8. QA failures and low style scores remain visible but do not remove export links.
|
|
9. User can download Markdown, a basic Word document, QA JSON, and a comparison
|
|
JSON artifact.
|
|
|
|
## Minimum Test Samples
|
|
|
|
Prepare at least five sample articles:
|
|
|
|
1. Industry drift sample.
|
|
2. Incorrect or incomplete company name sample.
|
|
3. Title grammar sample.
|
|
4. Conflicting experience-year sample.
|
|
5. Image-text mismatch sample.
|
|
|
|
These samples cover the most important risks from the source document and keep the first validation loop focused.
|