add-converter

GitHub

指导如何为kgb/io/writers模块添加新的输出格式转换器(如CSV、RDF),涵盖架构理解、依赖配置及字段映射,提供基于GraphML的参考实现。

.agent/skills/add-converter/SKILL.md FabioYanezRomero/Knowledge-Graph-Builder

Trigger Scenarios

需要新增数据导出格式 实现自定义IO转换器

Install

npx skills add FabioYanezRomero/Knowledge-Graph-Builder --skill add-converter -g -y
More Options

Non-standard path

npx skills add https://github.com/FabioYanezRomero/Knowledge-Graph-Builder/tree/main/.agent/skills/add-converter -g -y

Use without installing

npx skills use FabioYanezRomero/Knowledge-Graph-Builder@add-converter

指定 Agent (Claude Code)

npx skills add FabioYanezRomero/Knowledge-Graph-Builder --skill add-converter -a claude-code -g -y

安装 repo 全部 skill

npx skills add FabioYanezRomero/Knowledge-Graph-Builder --all -g -y

预览 repo 内 skill

npx skills add FabioYanezRomero/Knowledge-Graph-Builder --list

SKILL.md

Frontmatter
{
    "name": "add-converter",
    "description": "Adds a new output format converter (e.g., CSV, RDF) to the IO writers module."
}

Adding a Converter

This skill documents how to add a new output format converter to kgb/io/writers/.

Overview

Converters transform JSON triples into various output formats for use with external tools. The system provides:

  • GraphML for graph analysis tools (Gephi, Cytoscape)
  • Extensible architecture for custom formats (CSV, RDF, etc.)

Architecture

                        IO Writers Module
    ┌───────────────────────────────────────────────────────────┐
    │                                                           │
    │  io/writers/__init__.py    ← Public exports               │
    │                                                           │
    │  io/writers/graphml.py     ← NetworkX GraphML format      │
    │  ├─ json_to_graphml()        Single file conversion       │
    │  └─ convert_json_directory() Batch conversion             │
    │                                                           │
    │  io/writers/csv.py         ← Your new format              │
    │  ├─ json_to_csv()                                         │
    │  └─ convert_csv_directory()                               │
    │                                                           │
    └───────────────────────────────────────────────────────────┘

Data Flow:
  list[Triple] → Validation → Field Mapping → Format Rendering → File

Key Files:

  • kgb/io/writers/graphml.py — Reference implementation (GraphML)
  • kgb/io/writers/__init__.py — Public exports
  • kgb/io/__init__.py — Top-level IO exports

Dependencies

Format Required Library Purpose
CSV csv (stdlib) Tabular export
GraphML networkx>=3.0 Graph format
RDF rdflib>=6.0 Semantic web

Field Mapping

Triple Field GraphML CSV RDF
head Source node head column Subject URI
tail Target node tail column Object URI
relation Edge label relation column Predicate URI
inference Edge attribute inference column Annotation

Step 1: Understand the Interface

The existing GraphML converter follows this pattern (in kgb/io/writers/graphml.py):

def json_to_graphml(
    triples: list[Triple] | list[dict[str, Any]],
    output_path: Path | str | None = None
) -> nx.DiGraph:
    """Convert triples to a NetworkX DiGraph (optionally saved as GraphML).

    - Validates/converts to Triple objects
    - Normalizes entity names (case-insensitive dedup)
    - Stores relation and inference as edge attributes
    - Uses inference.value (not str(inference)) for clean enum serialization
    """

Key implementation details from the reference:

  • Accept both list[Triple] and list[dict] inputs
  • Use Triple(**t) to validate dict inputs, skip invalid with warning
  • Entity name canonicalization via get_canonical_name() to avoid duplicates
  • Preserve inference as .value string ("explicit" / "contextual")

Step 2: Implement Your Converter

Create kgb/io/writers/csv.py:

"""CSV converter for knowledge graph triples."""

from __future__ import annotations
import csv
from pathlib import Path
from typing import Any

from pydantic import ValidationError
from ...domains import Triple


def json_to_csv(
    triples: list[Triple] | list[dict[str, Any]],
    output_path: Path | str,
    *,
    include_metadata: bool = True,
    delimiter: str = ","
) -> Path:
    """Convert triples to CSV edge list format."""
    if not triples:
        raise ValueError("Cannot convert empty triple list")

    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)

    # Validate and convert to Triple objects
    validated: list[Triple] = []
    for t in triples:
        try:
            if isinstance(t, Triple):
                validated.append(t)
            else:
                validated.append(Triple(**t))
        except ValidationError as e:
            print(f"Warning: Skipping invalid triple: {e}")
            continue

    if not validated:
        raise ValueError("No valid triples after validation")

    # Determine columns
    fieldnames = ["head", "relation", "tail"]
    if include_metadata:
        fieldnames.extend(["inference", "justification"])

    # Write CSV
    with open(output_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter=delimiter)
        writer.writeheader()

        for triple in validated:
            row = {
                "head": triple.head,
                "relation": triple.relation,
                "tail": triple.tail,
            }
            if include_metadata:
                row.update({
                    "inference": triple.inference.value,
                    "justification": triple.justification or "",
                })
            writer.writerow(row)

    return output_path


def convert_csv_directory(
    input_dir: Path | str,
    output_dir: Path | str,
    *,
    include_metadata: bool = True
) -> list[Path]:
    """Convert all JSON files to CSV format."""
    import json

    input_dir = Path(input_dir)
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    csv_files = []
    for json_file in input_dir.glob("*.json"):
        try:
            with open(json_file) as f:
                data = json.load(f)

            output_path = output_dir / f"{json_file.stem}.csv"
            json_to_csv(data, output_path, include_metadata=include_metadata)
            print(f"Converted: {json_file.name} -> {output_path.name}")
            csv_files.append(output_path)
        except ValueError as e:
            print(f"Skipped {json_file.name}: {e}")

    return csv_files

Step 3: Register in Module

Update kgb/io/writers/__init__.py:

from .graphml import json_to_graphml, convert_json_directory
from .csv import json_to_csv, convert_csv_directory

__all__ = [
    "json_to_graphml",
    "convert_json_directory",
    "json_to_csv",
    "convert_csv_directory",
]

Update kgb/io/__init__.py to export the new functions:

from .readers import load_records, detect_format, DataLoadError
from .writers import json_to_graphml, convert_json_directory, json_to_csv, convert_csv_directory

__all__ = [
    "load_records",
    "detect_format",
    "DataLoadError",
    "json_to_graphml",
    "convert_json_directory",
    "json_to_csv",
    "convert_csv_directory",
]

Step 4: Add CLI Support

Update the convert command in kgb/__main__.py to support the new format:

@app.command()
def convert(
    input_dir: Path = typer.Option(..., "--input", "-i", exists=True),
    output_dir: Optional[Path] = typer.Option(None, "--output", "-o"),
    format: str = typer.Option("graphml", "--format", "-f"),
):
    """Convert JSON triples to specified format."""
    from .io.writers import convert_json_directory, convert_csv_directory

    out_dir = output_dir or input_dir.parent / format

    if format == "graphml":
        files = convert_json_directory(input_dir, out_dir)
    elif format == "csv":
        files = convert_csv_directory(input_dir, out_dir)
    else:
        console.print(f"[red]Unknown format: {format}[/red]")
        raise typer.Exit(code=1)

    console.print(f"\n[green]Converted {len(files)} files to {format}[/green]")

Step 5: Verify

Check Import

python -c "from kgb.io.writers.csv import json_to_csv; print('OK')"

Unit Tests

def test_json_to_csv_basic(tmp_path):
    from kgb.io.writers.csv import json_to_csv
    from kgb.domains import Triple

    triples = [
        Triple(head="Alice", relation="knows", tail="Bob"),
        Triple(head="Bob", relation="works_at", tail="Acme"),
    ]

    output = tmp_path / "graph.csv"
    result = json_to_csv(triples, output)

    assert result.exists()

    import csv
    with open(result) as f:
        rows = list(csv.DictReader(f))

    assert len(rows) == 2
    assert rows[0]["head"] == "Alice"
    assert rows[0]["inference"] == "explicit"


def test_json_to_csv_empty_list(tmp_path):
    from kgb.io.writers.csv import json_to_csv
    import pytest

    with pytest.raises(ValueError, match="empty"):
        json_to_csv([], tmp_path / "empty.csv")


def test_json_to_csv_from_dicts(tmp_path):
    from kgb.io.writers.csv import json_to_csv
    import csv

    dicts = [{"head": "X", "relation": "r", "tail": "Y", "inference": "explicit"}]
    csv_path = tmp_path / "roundtrip.csv"
    json_to_csv(dicts, csv_path)

    with open(csv_path) as f:
        row = next(csv.DictReader(f))

    assert row["head"] == "X"
    assert row["relation"] == "r"
    assert row["tail"] == "Y"

Key Principles

Principle Implementation
Accept list[Triple] and list[dict] Use isinstance check with Triple(**t) validation
Use .value for enums triple.inference.value"explicit" (not "InferenceType.EXPLICIT")
Create Directories output_path.parent.mkdir(parents=True, exist_ok=True)
Skip Invalid Data Log warning and continue

Error Handling

Exception When Action
ValueError Empty input or no valid triples Fail with message
ValidationError Triple validation fails Log, skip, continue
FileNotFoundError Input directory doesn't exist Fail loudly

Files to Create/Modify

File Action
kgb/io/writers/csv.py Create — converter implementation
kgb/io/writers/__init__.py Modify — add imports
kgb/io/__init__.py Modify — add exports
kgb/__main__.py Modify — add format dispatch (optional)

Verification Checklist

  • Implementation validates Triple inputs
  • Uses inference.value for enum serialization
  • Tests pass (unit + round-trip)
  • Batch function for directory processing
  • Registered in kgb/io/writers/__init__.py
  • Exported in kgb/io/__init__.py
  • CLI format dispatch works (if added)

Version History

  • 588f0d9 Current 2026-07-25 05:34

Same Skill Collection

.agent/skills/add-augmentation-strategy/SKILL.md
.agent/skills/add-dataset-format/SKILL.md
.agent/skills/add-domain/SKILL.md
.agent/skills/add-llm-client/SKILL.md
.agent/skills/add-visualization/SKILL.md

Metadata

Files
0
Version
588f0d9
Hash
b4873646
Indexed
2026-07-25 05:34

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-28 22:09
浙ICP备14020137号-1 $Carte des visiteurs$