English / 中文
OpenSkald turns a local knowledge base into reviewable, publishable content. It reads Markdown/text notes, generates drafts with declarative prompt skills and an OpenAI-compatible LLM, stores state in JSONL memory, requires human approval by default, and publishes through platform plugins.
It is built for teams that want a small, inspectable content agent rather than a black-box SaaS workflow.
- FastAPI service and JSON-output CLI share the same runtime.
- No-credentials demo mode uses a deterministic local LLM provider.
- Supports local blog Markdown, WeChat Official Account, X threads, and Xiaohongshu notes.
- Built-in review queue keeps generated content in
pending_reviewuntil approval. - Publisher validation runs before real platform calls.
- Runtime memory tracks indexed articles, generated content, failures, reflections, metrics, skill proposals, and agent runs.
- APScheduler jobs can ingest knowledge, generate drafts, and publish approved content.
| Document | Purpose |
|---|---|
| Documentation Index | Complete documentation map |
| API Reference | REST endpoints, request bodies, responses, errors, and curl examples |
| Architecture | Module layout, container wiring, agents, publishers, memory, and extension points |
| Contributing | Local development workflow |
| Security | Credential handling and vulnerability reporting |
flowchart LR
K["Local knowledge files"] --> I["Ingest"]
I --> M["JSONL memory"]
M --> G["Generate drafts"]
S["YAML skills"] --> G
L["LLM provider"] --> G
G --> R["Human review"]
R --> P["Publish"]
P --> B["Blog"]
P --> W["WeChat"]
P --> X["X"]
P --> H["Xiaohongshu"]
Run the demo first. It needs no API key and uses examples/knowledge/.
uv sync --extra dev
bash scripts/demo.shRun the acceptance check:
bash scripts/check.shThe check runs pytest, Ruff, config validation, knowledge ingestion, status checks, publisher checks, content generation, and review queue listing.
uv sync --extra dev
cp config/config.yaml config/local.yaml
export DEEPSEEK_API_KEY="your-deepseek-api-key"
OPENVIKING_AGENT_CONFIG=config/local.yaml uv run uvicorn backend.app.main:app --reloadHealth and config:
curl http://localhost:8000/api/health
curl http://localhost:8000/api/status
curl http://localhost:8000/api/config/summaryGenerate, review, and publish:
curl -X POST http://localhost:8000/api/knowledge/ingest
curl -X POST http://localhost:8000/api/generate \
-H "Content-Type: application/json" \
-d '{"content_type":"daily_summary","platforms":["blog","x"]}'
curl "http://localhost:8000/api/review?status=pending_review"
curl -X POST http://localhost:8000/api/review/<content_id>/approve
curl http://localhost:8000/api/publish/blog/<content_id>/validate
curl -X POST http://localhost:8000/api/publish/blog/<content_id>See docs/API.md for all endpoints and examples.
uv run OpenSkald --config config/demo.yaml validate-config
uv run OpenSkald --config config/demo.yaml status
uv run OpenSkald --config config/demo.yaml knowledge-ingest
uv run OpenSkald --config config/demo.yaml generate-once \
--content-type daily_summary \
--platform blog \
--platform x
uv run OpenSkald --config config/demo.yaml review-list --status pending_review
uv run OpenSkald --config config/demo.yaml review-approve --content-id <content_id>
uv run OpenSkald --config config/demo.yaml publish-content --content-id <content_id>
uv run OpenSkald --config config/demo.yaml content-failures
uv run OpenSkald --config config/demo.yaml memory-search --query retrievalUseful inspection commands:
uv run OpenSkald --config config/demo.yaml config-summary
uv run OpenSkald --config config/demo.yaml content-summary
uv run OpenSkald --config config/demo.yaml memory-timeline --limit 10
uv run OpenSkald --config config/demo.yaml publisher-check-all
uv run OpenSkald --config config/demo.yaml skills-discover
uv run OpenSkald --config config/demo.yaml reflections-discoverConfig is loaded from --config, then OPENVIKING_AGENT_CONFIG, then
config/config.yaml. The environment variable name is historical and remains the active
config variable in the current code.
Important sections:
llm: provider, base URL, model, timeout, and API key environment variable.openviking: local knowledge path, include globs, and article limit.scheduler: cron jobs for ingestion, generation, and publishing.publishers: platform enablement, dry-run mode, account ID, and credentials env var.review: human approval gate and configured review queue path. Current generated content review state is stored with content inmemory.storage_path.memory: JSONL paths for content, skill proposals, and indexed articles.agent: runtime mode, workspace ID, reflection, and collaboration settings.
Config summaries are redacted: secret values are never returned by the API or CLI.
OpenSkald reads Markdown and text files from the configured knowledge path. Markdown can include front matter:
---
title: RAG Operations
tags:
- rag
- agents
url: https://example.com/source
---
# RAG Operations
Production retrieval needs memory, review, and reliable publishing checks.Generation uses indexed articles first. If the index is empty, it falls back to the configured knowledge path.
| Platform | Behavior |
|---|---|
blog |
Writes Markdown files to the configured local output directory |
wechat |
Publishes through WeChat Official Account APIs when dry_run: false |
x |
Posts one non-empty body line per tweet when dry_run: false |
xiaohongshu |
Uses an experimental creator-web cookie adapter when dry_run: false |
Production credential env vars must be JSON objects:
export WECHAT_PUBLISHER_CREDENTIALS='{"app_id":"wx_xxx","app_secret":"xxx","thumb_media_id":"xxx"}'
export X_PUBLISHER_CREDENTIALS='{"user_access_token":"USER_TOKEN_WITH_WRITE_SCOPE"}'
export XIAOHONGSHU_PUBLISHER_CREDENTIALS='{"cookie":"YOUR_CREATOR_COOKIE"}'Keep external publishers in dry_run: true until publisher-check succeeds and a real
account-level publish has been verified.
Skills are YAML prompt modules in backend/app/skills/<skill_name>/skill.yaml.
Platform-specific skills take priority over generic skills.
| Skill | Content Types | Platforms |
|---|---|---|
article_summary |
daily_summary, weekly_summary |
generic |
tech_analysis |
hot_topic_analysis, deep_technical_analysis |
generic |
blog_writer |
all content types | blog |
wechat_writer |
all content types | wechat |
x_writer |
all content types | x |
xiaohongshu_writer |
all content types | xiaohongshu |
Skill proposals are human-gated. Approved proposals create disabled draft skills, so a human still has to review and enable them deliberately.
docker compose up --buildThe container mounts ./config/config.yaml, ./knowledge, and ./data, validates config
before startup, serves on port 8000, and healthchecks /api/health.
- Keep
review.require_human_approval: truefor publishing accounts. - Run behind a TLS reverse proxy.
- Store secrets in environment variables or a secret manager.
- Persist
./data; it contains memory, review state, article indexes, and run records. - Enable real publishers one platform at a time after dry-run checks and manual review.
- Keep generated
data/and privateknowledge/out of Git unless publishing fixtures.
OpenSkald is released under the MIT License.