# 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 optimizes the article under fact-card constraints.
6. System runs quality gates.
7. Failed checks trigger targeted rewriting for up to two rounds.
8. User previews optimized content and QA report.
9. User downloads Markdown and a basic Word document.

## 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["Optimize Article"]
  D --> E["Quality Check"]
  E -->|Pass| F["Preview Result"]
  E -->|Fail| G["Targeted Rewrite"]
  G --> D
  F --> H["Download Markdown / Word"]
```

## 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.

### Optimized Result

Display:

- Optimized title.
- Summary.
- Optimized body.
- Image suggestions.
- Changed sections.

The UI should mark content that needs user confirmation.

### Quality Report

Display each gate as pass, warn, or fail, with evidence, reason, suggested fix, and target rewrite module.

### Export

Downloads:

- `optimized.md`
- `optimized.docx`
- `qa_report.json`

## 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["ArticleOptimizer"]
  D --> E["QualityInspector"]
  E -->|fail| F["TargetedRewriter"]
  F --> E
  E -->|pass/warn| G["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>`, `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 use deterministic local fallbacks so the MVP remains testable and usable without a live API key.
- 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.

### 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.

## 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.

```text
data/
  app.db
  exports/
    job_xxx/
      original.md
      optimized.md
      optimized.docx
      qa_report.json
```

### `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,
  "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"]
}
```

### `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 block export. Warnings allow export with visible confirmation prompts.

Failed checks trigger targeted rewrite for up to two rounds. After two failed rounds, the app stops rewriting 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 a structured QA report for the 10 quality gates.
6. Hard failures block export until fixed or manually reviewed.
7. User can download Markdown and a basic Word document.

## 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.
