Gå til hovedinnhold

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:

SeverityWeightExample
Neutral0Informational note
Minor1Slight tone inconsistency
Major5Wrong term used
Critical25Competitor 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:

recyclechanterm-lookupchantranslateLLMchanbrand-voice-checkLLMchanqaLLM

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/profile"

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/profile"
"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, terminology)

Starter packs

import "github.com/neokapi/neokapi/core/profile/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.