Brand voice
Where terminology ensures you use the right words,
brand voice describes how you say them — the personality, formality, and
writing patterns that make content recognizable. neokapi captures a brand voice
as a machine-readable profile and runs it as one checkset over the same
content-verification engine that powers terminology,
do-not-translate, and placeholder integrity: every checker emits the same
findings into the same Block annotation, so brand voice is one check among
many, not a separate system. The Go library lives in core/brand/.
Used this way, brand voice keeps an AI assistant on-voice the way a test keeps code correct: load the profile into context (or expose it over MCP) so generated copy is on-voice from the first draft, then check anything that drifts and carry the same voice through every translation. The findings — the specific terms and rules that broke — are the substance; the 0–100 roll-up is a convenience, honest only when calibrated against a labeled set.
Brand voice with the CLI
The kapi brand command group works against a profile from a built-in starter
pack (--pack), the local brand store (--profile), or a standalone
git-shareable YAML file (--profile-file):
# Print the rendered guide (paste into an assistant, or pipe to a file)
kapi brand guide --pack friendly-dtc
# Score text: file argument, --input-text, or stdin. --min-score gates CI (exit 3).
kapi brand check --profile-file brand.yaml --min-score 80 release-notes.md
# Rewrite off-voice content (add --ai for tone/style as well as vocabulary)
kapi brand rewrite --profile-file brand.yaml --input-text "Leverage our solution"
# Manage profiles in the local store
kapi brand profiles
Both check and rewrite run a fast, offline rule-based vocabulary pass by
default; pass --ai to add an LLM analysis of tone, style, and clarity.
Voice profiles
A profile captures tone, style, and vocabulary as rules:
name: "Acme Corp"
description: "Professional yet approachable B2B SaaS voice"
tone:
personality: [knowledgeable, helpful, confident]
formality: neutral
emotion: warm
humor: light
style:
active_voice: true
sentence_length: medium
person_pov: second # "you" / "your"
contractions: sometimes
vocabulary:
preferred_terms:
- term: "workspace"
note: "Use instead of 'account' or 'organization'"
forbidden_terms:
- term: "leverage"
replacement: "use"
severity: minor
competitor_terms:
- term: "Slack"
replacement: "messaging platform"
severity: critical
examples:
- before: "Users can leverage the platform to achieve synergy."
after: "Your team can use the workspace to collaborate more effectively."
explanation: "Active voice, preferred terms, removed jargon"
category: style
Profiles support locale overrides (e.g. formal and third-person POV for
ja) and channel overrides (e.g. casual, frequent humor for
social_media). Channel overrides replace whole Tone/Style sections; locale
overrides merge individual fields.
Compliance scoring
Compliance is scored 0–100 across five dimensions — Tone, Style, Vocabulary, Clarity, and overall Brand compliance. Each finding reduces the score by its severity weight:
| Severity | Weight | Example |
|---|---|---|
Neutral | 0 | Informational note |
Minor | 1 | Slight tone inconsistency |
Major | 5 | Wrong term used |
Critical | 25 | Competitor term used |
Starter packs
Built-in packs provide ready-to-use starting points — professional-b2b,
friendly-dtc, technical-docs, marketing-blog, and customer-support —
each with tone settings, style rules, vocabulary constraints, and before/after
examples to customize.
Pipeline integration
The brand-voice-check tool runs in the pipeline alongside other tools:
It uses an LLM to analyze content against the profile and attaches compliance
scores and findings to each Block as annotations. The faster, rule-based
brand-vocab-check tool checks forbidden and competitor terms without LLM
calls. Brand vocabulary also flows through ordinary terminology tools —
preferred terms surface in term-lookup, forbidden/competitor terms trigger
term-enforce violations — so brand guardrails and terminology share one
enforcement path.
MCP integration
AI agents reach brand voice checking through the kapi mcp server:
{
"mcpServers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
Agents can score content for brand compliance with the brand_check MCP tool,
fetch the brand guide with brand_guide, and rewrite off-brand copy with
brand_rewrite. Server deployments
can expose an HTTP MCP endpoint so agents consume profiles and scoring without a
local CLI process.
Go library
BrandStore
type BrandStore interface {
CreateProfile(ctx context.Context, profile *VoiceProfile) error
GetProfile(ctx context.Context, id string) (*VoiceProfile, error)
UpdateProfile(ctx context.Context, profile *VoiceProfile) error
DeleteProfile(ctx context.Context, id string) error
ListProfiles(ctx context.Context, workspaceID string) ([]*VoiceProfile, error)
StoreScore(ctx context.Context, score *StoredScore) error
GetScores(ctx context.Context, projectID string, locale model.LocaleID) ([]*StoredScore, error)
GetScoreTrends(ctx context.Context, projectID string, days int) ([]*ScoreTrend, error)
StoreCorrection(ctx context.Context, correction *Correction) error
GetSuggestedRules(ctx context.Context, workspaceID string, minCount int) ([]*SuggestedRule, error)
Close() error
}
StoredScore, ScoreTrend, and the other unqualified types are declared in the
brand package; model.LocaleID is the BCP-47 locale type from
github.com/neokapi/neokapi/core/model.
The framework ships a SQLite backend (cli/storage/brand/sqlite.go) built on
the shared core/storage migration system, with JSON columns for the complex
tone/style/vocabulary fields. The interface is designed for extension — server
deployments can add a workspace-scoped PostgreSQL backend.
Scoring and resolution
import "github.com/neokapi/neokapi/core/brand"
findings := []brand.BrandVoiceFinding{
{Dimension: brand.DimensionVocabulary, Severity: brand.SeverityMajor,
Message: "Forbidden term: leverage", Suggestion: "use"},
{Dimension: brand.DimensionTone, Severity: brand.SeverityMinor,
Message: "Tone is too formal for this profile"},
}
score := brand.CalculateScore(findings) // score.Overall = 94 (100 - 5 - 1)
// ResolveProfile applies locale then channel overrides to a base profile
resolved := brand.ResolveProfile(base, "ja", "")
Pipeline tools
import (
aitool "github.com/neokapi/neokapi/core/ai/tools"
"github.com/neokapi/neokapi/core/brand"
"github.com/neokapi/neokapi/core/tools"
)
// LLM-based: structured findings scored via CalculateScore, attached as a
// BrandVoiceAnnotation plus brand-voice-score / brand-voice-findings properties
checkTool := aitool.NewBrandVoiceCheckTool(llmProvider, profile)
// Rule-based: fast forbidden/competitor-term enforcement, no LLM calls
vocabTool := tools.NewBrandVocabCheckTool(profile, termBase)
Starter packs
import "github.com/neokapi/neokapi/core/brand/packs"
names, _ := packs.List() // the five built-in pack names
profile, _ := packs.Load("professional-b2b")
all, _ := packs.LoadAll()
Packs are YAML files embedded via go:embed; each returns a
*brand.VoiceProfile ready to use or customize.
Content model integration
BrandVoiceAnnotation is a registered payload (brand-voice) stored as a
block-scoped annotation (AD-002),
the counterpart to positional overlays like term and entity. It is reached
through the block's Anno/SetAnno helpers and registered for wire/store
rehydration via model.RegisterPayload:
type BrandVoiceAnnotation struct {
ProfileID string `json:"profile_id"`
Score int `json:"score"` // 0-100 overall
Findings []BrandVoiceFinding `json:"findings"`
Position model.RunRange `json:"position"`
}
func (a *BrandVoiceAnnotation) AnnotationType() string { return "brand-voice" }
Profiles serialize as both JSON and YAML, so they can be authored by hand or
constructed programmatically as a *brand.VoiceProfile.