输入角色名,生成可用 SOUL.generated.md,避免同名误判。
Built by Anchormind AI — screen-free AI learning companions that actually see your books.
核心价值: 解决 AniList/Jikan 同名角色混淆问题,通过强制消歧 + 语义验证,确保输出可直接用于 OpenClaw 角色扮演。
适合谁用:
- OpenClaw/AI Agent 用户想快速加载动漫角色
- 角色扮演社区需要标准化 SOUL.md
- 开发者想批量生成角色配置文件
3分钟上手: 安装 → 运行命令 → 获得角色文件。
- 🔍 Multi-source query: Parallel search across AniList GraphQL + Jikan (MyAnimeList) APIs
- 🎯 Forced disambiguation: Requires
--animehint for ambiguous names (prevents wrong character selection) - ✅ Semantic validation: 9-check validation system (structure + content quality)
- 🔄 Idempotent merge: Chapter-level merge with deduplication (same character won't be added twice)
- 💾 Smart caching: 24-hour SQLite cache with automatic retry logic
- 🎭 Wikiquote Fetcher: 3-phase台词抓取 (API → Browser → Local),带置信度评分与Speaker识别
- 🗣️ Voice-ready output: optional
--voice-promptblock with API-agnostic voice schema + calibration lines - Actual output example:
# Install via ClawHub
clawhub install anime-character-loader
# Or install from local clone
clawhub install ./anime-character-loaderThen use in OpenClaw:
@your_bot load_character "Kasumigaoka Utaha"
# Clone the repository
git clone https://github.com/colinchen4/anime-character-loader.git
cd anime-character-loader
# Install dependencies
pip install requests
# Run directly
python3 load_character.py "Kasumigaoka Utaha"This repository is now organized as a skill + CLI hybrid project:
anime-character-loader/
├─ load_character.py # legacy-compatible CLI wrapper
├─ src/anime_character_loader/ # structured package entrypoint + modules
│ ├─ cli.py
│ ├─ legacy.py # preserved behavior implementation snapshot
│ ├─ errors.py / models.py
│ ├─ sources/ disambiguation/ generator/ validator/ storage/
├─ tests/ # minimal regression tests
└─ SKILL.md # skill metadata and usage
Old command remains supported:
python3 load_character.py "Kasumigaoka Utaha"# Generate SOUL.md for a single character
python load_character.py "Kasumigaoka Utaha"
# Chinese names supported
python load_character.py "霞之丘诗羽"
# Preview without generating
python load_character.py "加藤惠" --info
# Generate with structured voice guidance for future TTS systems
python load_character.py "加藤惠" --anime "Saekano" --voice-promptCommon names like "Sakura" or "Rin" appear in multiple anime. The system requires an anime hint:
# ❌ Will fail - ambiguous name
python load_character.py "Sakura"
# ✅ Works - disambiguated
python load_character.py "Sakura" --anime "Fate"
python load_character.py "Sakura" --anime "Naruto"
# ✅ Or use --select to choose manually
python load_character.py "Sakura" --select 2Generate multiple characters into a single SOUL.md:
# Generate first character
python load_character.py "Katou Megumi" --anime "Saekano"
# Choose [2] MERGE
# Generate second character - will not duplicate if already exists
python load_character.py "Kasumigaoka Utaha" --anime "Saekano"
# Choose [2] MERGE - checks for duplicates before mergingMERGE mode features:
- Detects duplicates by character name + source work
- Updates existing character if content changed
- Atomic write (no partial/corrupted files)
After generation, choose how to load the character:
| Mode | Description |
|---|---|
| REPLACE | Replace existing SOUL.md |
| MERGE | Merge into existing SOUL.md (idempotent) |
| KEEP | Keep as SOUL.generated.md for manual review |
Query → Disambiguation → Generation → Validation → Output
↓ ↓ ↓ ↓ ↓
AniList Cross-source SOUL.md 9 checks SOUL.generated.md
+ Jikan consistency creation scoring + loading options
When both AniList and Jikan return results:
- Name similarity is calculated
- Work title similarity is calculated
- Combined confidence = weighted average + consistency bonus
When top 2 matches have similar scores (gap < 0.15), manual selection is forced.
| Check | Type | Description |
|---|---|---|
| contains_name | Basic | Character name present in output |
| contains_source | Basic | Source anime referenced |
| has_structure | Basic | Required sections (Identity/Personality/Boundaries) |
| content_length | Basic | Minimum 500 characters |
| no_placeholders | Basic | No TODO/FIXME/generic text |
| meaningful_background | Semantic | Background section has substance |
| specific_personality | Semantic | Personality traits are specific, not generic |
| speaking_style_details | Semantic | Speaking Style has concrete details |
| name_consistency | Semantic | Name variations are consistent |
Pass threshold: No errors + score ≥ 80/100
Scripts can check exit codes for specific error types:
| Code | Meaning | Example |
|---|---|---|
| 0 | Success | Character generated successfully |
| 10 | Network error | API timeout, no internet |
| 20 | Data error | No matches found, ambiguous name, low confidence |
| 30 | Validation error | Generated content failed quality checks |
| 40 | File error | Write failure, permission denied |
python load_character.py "Sakura"
if [ $? -eq 20 ]; then
echo "Need to specify --anime for disambiguation"
fiUse --voice-prompt to append a structured, API-agnostic voice block to the generated profile. This is designed for future companion / hardware pipelines without binding the project to a live TTS vendor yet.
Voice schema fields:
tonepaceemotion_rangepause_stylebanned_traitsdelivery_notes- calibration
sample_lines
Example:
## Voice Prompt
- character: Megumi Katou
- source_work: Saenai Heroine no Sodatekata
- tone: calm, composed, soft-spoken
- pace: measured and steady
- emotion_range: narrow-to-medium range; subtle shifts matter more than volume
- pause_style: short hesitant pauses before vulnerable or personal phrases
- banned_traits:
- robotic monotone
- overacted anime squealing
- delivery_notes:
- Keep delivery consistent with the dramatic tone of Saenai Heroine no Sodatekata.
### Voice Calibration Sample Lines
1. [neutral_greeting] Hello. I'm Megumi Katou. Let's keep this simple and honest.
- target: baseline identity and neutral cadence# Character Name
**Source:** Anime Title
## Identity
You are Character Name from Anime Title...
## Personality
- Trait 1
- Trait 2
## Speaking Style
- Style cue 1
- Style cue 2
## Boundaries
- Stay in character as...
---
*Generated by anime-character-loader*# Multi-Character SOUL
## Character A
[Identity, Personality, Speaking Style, Boundaries]
*Hash: abc123*
## Character B
[Identity, Personality, Speaking Style, Boundaries]
*Hash: def456*
## Character Selection Guide
- Choose Character A when...
- Choose Character B when...Edit load_character.py:
CONFIDENCE_THRESHOLD_HIGH = 0.8 # High confidence threshold
CONFIDENCE_THRESHOLD_MEDIUM = 0.6 # Medium confidence threshold
CONFIDENCE_THRESHOLD_LOW = 0.5 # Minimum acceptable
FORCE_SELECTION_THRESHOLD = 0.15 # Gap below this forces manual selectionCACHE_DURATION = timedelta(hours=24) # Cache expiration
MAX_RETRIES = 3 # API retry attempts| Source | Weight | Endpoint | Auth |
|---|---|---|---|
| AniList | 50% | https://graphql.anilist.co | None |
| Jikan | 30% | https://api.jikan.moe/v4 | None |
| Wikiquote | Fallback | MediaWiki API (zh.moegirl.org.cn) | None |
Three-phase quote fetching with confidence scoring:
- Phase 1 (API): MediaWiki API (
action=parse) for structured data - Phase 2 (Browser): Camofox fallback for Cloudflare bypass
- Phase 3 (Local):
data/quotes_database.jsonas seed corpus
Features:
- Speaker extraction:
Character: quote,(Character) quote,[Character] quote - Confidence scoring (0-1): section match + speaker match + text quality
- Deduplication: MD5-based
quote_id
当 Wiki 没有专门的 Quotes 区块时,提取角色描述作为补充素材:
- Source: Fandom (英文) + 萌娘百科 (中文)
- Content: Biography, Personality, Background 等章节摘录
⚠️ Important: 这是"角色描述摘录",不是原始台词- Usage: 作为
wikiquote的兜底/补充,提供角色背景上下文
Example:
from anime_character_loader.extractors.smart_excerpt_generator import generate_smart_excerpts
result = generate_smart_excerpts('泽村·斯潘塞·英梨梨', '路人女主')
# result['excerpts'][0]['text'] = "因为小学时被霸凌过,在外都会戴上大小姐的假面具..."
# result['excerpts'][0]['note'] = "⚠️ 来自Wiki角色描述,不是原始台词"- Source tracing:
source_type(api/browser/local/cache) +source_url
Note: "MediaWiki API抓取 + Playwright精准提取 + 本地语料兜底, 不保证所有页面结构一致".
Example:
from anime_character_loader.extractors.fandom_hybrid import fetch_quotes_fandom
result = fetch_quotes_fandom('Eriri Spencer Sawamura', 'Saekano')
# result['source_type'] = 'local' # when API/Browser fail
# result['quotes'][0] = {
# 'text': 'Baka! Chigau!',
# 'speaker': 'unknown',
# 'confidence': 0.8,
# 'quote_id': 'abc123',
# 'section': ''
# }| Scenario | Status | Notes |
|---|---|---|
| Japanese names (加藤恵) | ✅ Pass | AniList preferred |
| English names (Megumi Katou) | ✅ Pass | Jikan fallback |
| Mixed names (霞ヶ丘詩羽) | ✅ Pass | Unicode handling |
| Multi-character merge | ✅ Pass | Idempotent (3x merge = no duplicates) |
| Force disambiguation | ✅ Pass | "Sakura" + --anime "Fate" correctly identified |
| Cross-source validation | ✅ Pass | AniList + Jikan consistency check |
| Limitation | Workaround |
|---|---|
| Requires network | Cannot work offline |
| New anime delay | Latest characters may not be in databases |
| Niche characters | Use --anime to improve match rate |
| No Chinese API | Query uses Japanese/English names |
- Python 3.10+
- requests library
- Internet connection
This tool is part of the Anchormind ecosystem.
We build AI-native learning hardware — from character-driven study companions to interactive knowledge tools.
For product updates and enterprise solutions: anchormind-ai.com
MIT License
- Idempotent merge: Same character won't be added twice
- Cross-source scoring: AniList + Jikan consistency validation
- Exit codes: Standardized error codes for script integration
- Force selection: Manual selection required when top matches are close
- Forced disambiguation mode
- Semantic validation (9 checks)
- Multi-source query
- Atomic write with rollback