Ṽöîçé þŕöƒîļéš
Ŵĥéŕé ţéŕḿîñöļöĝý éñšüŕéš ýöü üšé ţĥé ŕîĝĥţ ŵöŕđš,
à ṽöîçé þŕöƒîļé đéšçŕîƃéš ĥöŵ ýöü šàý ţĥéḿ: ţĥé þéŕšöñàļîţý, ƒöŕḿàļîţý, àñđ
ŵŕîţîñĝ þàţţéŕñš ţĥàţ ḿàķé çöñţéñţ ŕéçöĝñîžàƃļé. ñéöķàþî çàþţüŕéš à ṽöîçé
àš à ḿàçĥîñé-ŕéàđàƃļé þŕöƒîļé àñđ ŕüñš îţ àš öñé çĥéçķšéţ öṽéŕ ţĥé šàḿé
çöñţéñţ-ṽéŕîƒîçàţîöñ éñĝîñé ţĥàţ þöŵéŕš ţéŕḿîñöļöĝý,
đö-ñöţ-ţŕàñšļàţé, àñđ þļàçéĥöļđéŕ îñţéĝŕîţý: éṽéŕý çĥéçķéŕ éḿîţš ţĥé šàḿé
ƒîñđîñĝš îñţö ţĥé šàḿé Block àññöţàţîöñ, šö ṽöîçé îš öñé çĥéçķ àḿöñĝ
ḿàñý. Ţĥé Ĝö ļîƃŕàŕý ļîṽéš îñ core/profile/.
Üšéđ ţĥîš ŵàý, à ṽöîçé þŕöƒîļé ķééþš àñ ÀÎ àššîšţàñţ öñ-ṽöîçé ţĥé ŵàý à ţéšţ ķééþš çöđé çöŕŕéçţ: ļöàđ ţĥé þŕöƒîļé îñţö çöñţéẋţ (öŕ éẋþöšé îţ öṽéŕ ḾÇÞ) šö ĝéñéŕàţéđ çöþý îš öñ-ṽöîçé ƒŕöḿ ţĥé ƒîŕšţ đŕàƒţ, ţĥéñ çĥéçķ àñýţĥîñĝ ţĥàţ đŕîƒţš àñđ çàŕŕý ţĥé šàḿé ṽöîçé ţĥŕöüĝĥ éṽéŕý ţŕàñšļàţîöñ. Ţĥé ƒîñđîñĝš (ţĥé šþéçîƒîç ţéŕḿš àñđ ŕüļéš ţĥàţ ƃŕöķé) àŕé ţĥé šüƃšţàñçé; ţĥé 0–100 ŕöļļ-üþ îš à çöñṽéñîéñçé, ŕéļîàƃļé öñļý ŵĥéñ çàļîƃŕàţéđ àĝàîñšţ à ļàƃéļéđ šéţ.
Ṽöîçé þŕöƒîļéš ŵîţĥ ţĥé ÇĻÎ
Ţĥé kapi voice çöḿḿàñđ ĝŕöüþ ŵöŕķš àĝàîñšţ à þŕöƒîļé ƒŕöḿ à ƃüîļţ-îñ šţàŕţéŕ
þàçķ (--pack), ţĥé ļöçàļ ṽöîçé šţöŕé (--profile), öŕ à šţàñđàļöñé
ĝîţ-šĥàŕéàƃļé ÝÀḾĻ ƒîļé (--profile-file):
# Print the rendered guide (paste into an assistant, or pipe to a file)
kapi voice show --pack friendly-dtc
# Score text: file argument, --input-text, or stdin. Exits 3 when a finding
# fails, so it gates CI. The default pass is rule-based and offline; --ai adds
# an LLM analysis of tone, style, and clarity, whose findings report.
kapi voice check --profile-file voice.yaml release-notes.md
# Rewrite off-voice content: deterministic substitution of the terms the
# profile's file carries, no model. A rule with no replacement is reported
# under "skipped" rather than applied.
kapi voice rewrite --profile-file voice.yaml --input-text "Leverage our solution"
# Ask a model for the inflected forms of each term, written as a diff
kapi terms expand --locale nb
# Manage profiles in the local store
kapi voice profiles
Ŵĥàţ ṽöîçé çĥéçķš àššéšš
Ţĥé đéƒàüļţ ŵöŕđ-ŕüļé àñđ þàţţéŕñ çĥéçķš àššéšš éñçöđéđ ŕüļéš. kapi check --voice àđđš àđṽîšöŕý šîḿîļàŕîţý ţö þŕöƒîļé éẋàḿþļéš ţĥŕöüĝĥ ţĥé kapi-check
þļüĝîñ, àñđ ŕéǫüéšţš ñö ĻĻḾ ĵüđĝḿéñţ. kapi voice check --ai àñđ ţĥé ŕàŵ
voice-check ţööļ éẋþļîçîţļý ŕéǫüéšţ šéḿàñţîç ḿöđéļ ŕéṽîéŵ.
À çļéàñ ŕüļé ŕéšüļţ ḿéàñš ţĥöšé ŕüļéš ŕàîšéđ ñö ƒîñđîñĝ. Ŵĥéţĥéŕ à šéŕṽîçé đéšçŕîþţîöñ, þŕöḿîšé öŕ ƒàçţüàļ çļàîḿ îš çöŕŕéçţ šţàýš üñàššéššéđ. Ţĥé çàñöñîçàļ çĥéçķ ŕéþöŕţ îđéñţîƒîéš àñàļýšéš ţĥàţ ŕàñ, ŵéŕé ñöţ ŕéǫüéšţéđ öŕ çàññöţ ƃé àššéššéđ ƃý éẋàçţ ŕüļéš. Ŕéþöŕţš ŵîţĥöüţ éẋéçüţîöñ ḿéţàđàţà ĥàṽé üñŕéþöŕţéđ çöṽéŕàĝé. Àþþļîçàƃļé þŕöšé ĝüîđàñçé îš ŕéçöŕđéđ àš üñšüþþöŕţéđ ƃý đéţéŕḿîñîšţîç çĥéçķš; àñ éñçöđéđ þŕöĥîƃîţéđ éẋþŕéššîöñ çàñ þŕöđüçé à ŕüļé ƒîñđîñĝ.
Ŕéǫüéšţéđ àñàļýšîš ƒàîļüŕéš àŕé öþéŕàţîöñ éŕŕöŕš. Ḿîššîñĝ þŕöƒîļéš, üñàṽàîļàƃļé þļüĝîñš, þŕöṽîđéŕ éŕŕöŕš, ţîḿéöüţ àñđ çàñçéļļàţîöñ çàññöţ ýîéļđ à ñéŵ šüççéššƒüļ šçöŕé. Ţĥé ĻĻḾ ţööļ ṽàļîđàţéš šţŕüçţüŕéđ ƒîñđîñĝš ļöçàļļý: ḿàļƒöŕḿéđ ĴŠÖÑ, ḿîššîñĝ öŕ ñüļļ ƒîñđîñĝš, îñṽàļîđ ƒîéļđš àñđ îñṽàļîđ ƒîñđîñĝ ṽàļüéš àŕé éŕŕöŕš. Öñļý àñ éẋþļîçîţ éḿþţý ƒîñđîñĝš àŕŕàý îš à çöḿþļéţéđ çļéàñ ḿöđéļ ŕéšþöñšé. Éḿþţý çöñţéñţ šķîþš ḿöđéļ àñàļýšîš àñđ éḿîţš ñö šçöŕé.
Ŵĥàţ à þŕöƒîļé ĥöļđš
À þŕöƒîļé çàþţüŕéš ţöñé, šţýļé ḿéàšüŕéš àñđ þàţţéŕñ ŕüļéš. Ŵöŕđ ŕüļéš ("ŵŕîţé
ţĥîš, ñöţ ţĥàţ") àŕé ţéŕḿš, àñđ à ṽöîçé ƒîļé ḿàý
çàŕŕý ţĥéḿ ƃéšîđé ţĥé ṽöîçé üñđéŕ à ţöþ-ļéṽéļ terms: ļîšţ:
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
terms:
- replacement: "workspace"
note: "Use instead of 'account' or 'organization'"
- term: "leverage"
replacement: "use"
advisory: true
- term: "Slack"
replacement: "messaging platform"
competitor: true
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
Þŕöƒîļéš šüþþöŕţ ļöçàļé öṽéŕŕîđéš (é.ĝ. formal àñđ ţĥîŕđ-þéŕšöñ ÞÖṼ ƒöŕ
ja), çĥàññéļ öṽéŕŕîđéš (é.ĝ. çàšüàļ, ƒŕéǫüéñţ ĥüḿöŕ ƒöŕ
social_media) àñđ þéŕšöñà öṽéŕŕîđéš (àñ îñđîṽîđüàļ àüţĥöŕ'š ṽöîçé).
Çĥàññéļ àñđ þéŕšöñà öṽéŕŕîđéš ŕéþļàçé ŵĥöļé Ţöñé/Šţýļé šéçţîöñš; ļöçàļé
öṽéŕŕîđéš ḿéŕĝé îñđîṽîđüàļ ƒîéļđš. Ñö öṽéŕŕîđé çàŕŕîéš ŵöŕđ ŕüļéš.
Ţĥŕéé ŕüļé ƒîéļđš đö ḿöšţ öƒ ţĥé ŵöŕķ ƃéýöñđ ţĥé éẋàḿþļé àƃöṽé:
- À
terms:éñţŕý îš à ţéŕḿ ŕüļé:term,replacement,note,advisory,competitorƒöŕ à ŕîṽàļ'š ñàḿé, àñđ öþţîöñàļļýforms(ţĥé îñƒļéçţîöñš ţĥé éẋàçţ ḿàţçĥéŕ àļšö ŕéçöĝñîšéš),case_sensitiveàñđscope(prose,code,heading). À ŕüļé ŵîţĥ öñļý àreplacementñàḿéš à þŕéƒéŕŕéđ ƒöŕḿ àñđ ŕéĵéçţš ñöţĥîñĝ.kapi voice importàñđkapi store importḿöṽé à ƒîļé'šterms:îñţö ţĥé þŕöĵéçţ'š ţéŕḿš šţöŕé àñđ ŕéþöŕţ ĥöŵ ḿàñý; à ƒîļé ţĥàţ ļîšţš îţš ŵöŕđš üñđéŕvocabulary:îš çöñṽéŕţéđ ţĥé šàḿé ŵàý. Ţĥé šàḿé šĥàþé îš ŵĥàţ à ƒļöŵ šţéþ àççéþţš üñđéŕterm_rules:ƒöŕterm-check,translate,recycle,dnt-checkàñđpseudo-translate. À üšé öƒ ţĥé ţéŕḿ ƒàîļš à çĥéçķ üñļéšš ţĥé ŕüļé îš ḿàŕķéđadvisory: true, ŵĥîçĥ ḿàķéš îţ ŕéþöŕţ. À ŕüļé ŵĥöšéreplacementîš çàþîţàļîšéđ, šüçĥ àš à þŕöđüçţ ñàḿé (term: QuickCast,replacement: Quickcast), ḿàţçĥéš çàšé-šéñšîţîṽéļý, šö à ļöŵéŕ-çàšé ÜŔĻ šļüĝquickcastîš ļéƒţ àļöñé; šö đöéš à ŕüļé ŵĥöšétermđéŕš ƒŕöḿ îţšreplacementöñļý îñ çàšé. Àñý öţĥéŕ ŕüļé ḿàţçĥéš ŕéĝàŕđļéšš öƒ çàšé, àñđcase_sensitive: trueöŕfalsešéţš îţ éîţĥéŕ ŵàý. - À
style.prohibited_patternsöŕrequired_patternséñţŕý îš à þàţţéŕñ:regex,description,fails,advisory, àñđ öþţîöñàļļý àrate(maxḿàţçĥéš þéŕper_words, đéƒàüļţ 1,000) ţĥàţ ţüŕñš à ƃàñ îñţö à çéîļîñĝ, àñđ àscope. À þàţţéŕñ ŕéþöŕţš üñţîļ îţ îš ḿàŕķéđfails: true, ŵĥîçĥ ḿàķéš à ḿàţçĥ ƒàîļ à çĥéçķ;advisory: trueķééþš îţ ŕéþöŕţîñĝ éîţĥéŕ ŵàý. min_scoreîš ţĥé çöḿþļîàñçé ƃàŕ à ƃļöçķ ḿüšţ ŕéàçĥ ţö çöüñţ àš çöḿþļîàñţ îñ ŕöļļ-üþš; üñšéţ, öñé ƒàîļîñĝ ƒîñđîñĝ àļŕéàđý đŕöþš à ƃļöçķ ƃéļöŵ îţ.
Éṽéŕý ƒîéļđ îš ļîšţéđ îñ ţĥé ṽöîçé þŕöƒîļé ŕéƒéŕéñçé.
Çöḿþļîàñçé šçöŕîñĝ
Çöḿþļîàñçé îš šçöŕéđ 0–100 àçŕöšš ƒîṽé đîḿéñšîöñš: Ţöñé, Šţýļé, Ṽöçàƃüļàŕý (ţĥé ŵöŕđ-ŕüļé ƒîñđîñĝš), Çļàŕîţý, àñđ öṽéŕàļļ ṽöîçé çöḿþļîàñçé. Éàçĥ ƒîñđîñĝ ŕéđüçéš ţĥé šçöŕé ƃý à ŵéîĝĥţ šéţ ƃý ŵĥàţ îţ đöéš ţö à çĥéçķ:
| Ƒîñđîñĝ | Ŵéîĝĥţ | Éẋàḿþļé |
|---|---|---|
| Ƒàîļš | 25 | Ƒöŕƃîđđéñ öŕ çöḿþéţîţöŕ ţéŕḿ |
| Ŕéþöŕţš | 1 | Àđṽîšöŕý ŕüļé, ţöñé ŕéàđîñĝ |
| Ŕàîšéđ ƃý à šüĝĝéšţéđ ŕüļé | 0 | À þŕöþöšéđ ŕüļé ñöţ ýéţ çöñƒîŕḿéđ |
Ţĥé šçöŕé îš ŕéþöŕţéđ ƃéšîđé ţĥé ƒîñđîñĝš. Ŵĥéţĥéŕ à çĥéçķ ƒàîļš đéþéñđš öñļý öñ ŵĥéţĥéŕ à ƒîñđîñĝ ƒàîļš.
Šţàŕţéŕ þàçķš
Ƃüîļţ-îñ þàçķš þŕöṽîđé ŕéàđý-ţö-üšé šţàŕţîñĝ þöîñţš (professional-b2b,
friendly-dtc, technical-docs, marketing-blog, àñđ customer-support),
éàçĥ ŵîţĥ ţöñé šéţţîñĝš, šţýļé ŕüļéš, ƃéƒöŕé/àƒţéŕ éẋàḿþļéš ţö çüšţöḿîžé, àñđ
ţéŕḿš üñđéŕ terms:. Ŵĥéŕé à þàçķ îš ƃöüñđ àš ţĥé ṽöîçé, îţš ţéŕḿš àþþļý ƃéšîđé
ţĥé þŕöĵéçţ'š öŵñ, àñđ à ƒîñđîñĝ ţĥéý ŕàîšé ñàḿéš ţĥé þàçķ (pack technical-docs).
Þîþéļîñé îñţéĝŕàţîöñ
Ţĥé voice-check ţööļ ŕüñš îñ ţĥé þîþéļîñé àļöñĝšîđé öţĥéŕ ţööļš:
Îţ üšéš àñ ĻĻḾ ţö àñàļýžé çöñţéñţ àĝàîñšţ ţĥé þŕöƒîļé àñđ àţţàçĥéš çöḿþļîàñçé
šçöŕéš àñđ ƒîñđîñĝš ţö éàçĥ Ƃļöçķ àš àññöţàţîöñš. Ţĥé ƒàšţéŕ, ŕüļé-ƃàšéđ
voice-vocab-check ţööļ çĥéçķš ţĥé ṽöîçé'š þàţţéŕñš àñđ ţĥé ƒöŕƃîđđéñ,
çöḿþéţîţöŕ àñđ ŕéţîŕéđ ţéŕḿš ţĥàţ ĝöṽéŕñ ţĥé ţéẋţ ŵîţĥöüţ ĻĻḾ çàļļš. Ŵöŕđ
ŕüļéš àŕé ţéŕḿš, šö ţĥéý àļšö ƒļöŵ ţĥŕöüĝĥ ţĥé öŕđîñàŕý ţéŕḿîñöļöĝý ţööļš:
term-check àñđ dnt-check ţàķé ţĥéḿ àš term_rules:, àñđ ṽöîçé ĝüàŕđŕàîļš
àñđ ţéŕḿîñöļöĝý šĥàŕé öñé éñƒöŕçéḿéñţ þàţĥ.
Ţĥé term-lookup àñđ term-enforce šţàĝéš îñ terms/tool.go àŕé ļîƃŕàŕý
çöđé ŕàţĥéŕ ţĥàñ ŕéçîþé ñàḿéš: ţĥé ŕüññéŕ àþþéñđš ţĥéḿ ƃéĥîñđ àñý ţööļ ŵĥöšé
šçĥéḿà ŕéǫüîŕéš ţéŕḿš, àñđ ñéîţĥéŕ àþþéàŕš îñ kapi tools.
ḾÇÞ îñţéĝŕàţîöñ
ÀÎ àĝéñţš ŕéàçĥ ṽöîçé çĥéçķîñĝ ţĥŕöüĝĥ ţĥé kapi mcp šéŕṽéŕ:
{
"mcpServers": {
"kapi": {
"command": "kapi",
"args": ["mcp"]
}
}
}
Àĝéñţš çàñ šçöŕé çöñţéñţ ƒöŕ ṽöîçé çöḿþļîàñçé ŵîţĥ ţĥé voice_check ḾÇÞ ţööļ
àñđ ŕéŵŕîţé öƒƒ-ṽöîçé çöþý ŵîţĥ voice_rewrite, ŵĥîçĥ ļîšţš üñđéŕ skipped
ţĥé ţéŕḿš îţ ḿàţçĥéđ àñđ çöüļđ ñöţ ŕéþļàçé. Àñ àĝéñţ ŕéàđš ţĥé ṽöîçé îţšéļƒ
ƒŕöḿ ţĥé šéçţîöñ ķàþî ŵŕîţéš îñţö ţĥé þŕöĵéçţ'š AGENTS.md àñđ CLAUDE.md.
Ƒöŕ ţĥé ƒüļļ ṽöîçé öƒ öñé ƒîļé, kapi context <path> þŕîñţš îţ ŵîţĥ ţĥé ţéŕḿš
ţĥàţ àþþļý ţĥéŕé, àñđ ţĥé context_read ţööļ àñđ ţĥé context://<path>
ŕéšöüŕçé ŕéţüŕñ ţĥé šàḿé àñšŵéŕ.
Šéŕṽéŕ đéþļöýḿéñţš çàñ éẋþöšé àñ ĤŢŢÞ ḾÇÞ éñđþöîñţ šö àĝéñţš çöñšüḿé þŕöƒîļéš
àñđ šçöŕîñĝ ŵîţĥöüţ à ļöçàļ ÇĻÎ þŕöçéšš.
Ĝö ļîƃŕàŕý
Šţöŕé
type Store interface {
// Profile CRUD; scope is the storing host's partition key (empty for the local CLI)
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, scope string) ([]*VoiceProfile, error)
// Version history and named tags
ListProfileVersions(ctx context.Context, profileID string) ([]*ProfileVersion, error)
GetProfileVersion(ctx context.Context, profileID string, version int) (*ProfileVersion, error)
GetProfileAtTag(ctx context.Context, profileID, tagName string) (*VoiceProfile, error)
CreateProfileTag(ctx context.Context, tag *ProfileTag) error
ListProfileTags(ctx context.Context, profileID string) ([]*ProfileTag, error)
DeleteProfileTag(ctx context.Context, profileID, tagName string) error
// Scores
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)
GetScoresByStream(ctx context.Context, projectID, stream string) ([]*StoredScore, error)
// Corrections and the candidate rules derived from them
StoreCorrection(ctx context.Context, correction *Correction) error
GetSuggestedRules(ctx context.Context, scope string, minCount int) ([]*SuggestedRule, error)
RecordRuleDecision(ctx context.Context, d *RuleDecision) error
GetRuleDecision(ctx context.Context, profileID, term string) (*RuleDecision, error)
ListRuleDecisions(ctx context.Context, profileID string) ([]*RuleDecision, error)
Close() error
}
StoredScore, ScoreTrend, àñđ ţĥé öţĥéŕ üñǫüàļîƒîéđ ţýþéš àŕé đéçļàŕéđ îñ ţĥé
profile þàçķàĝé; model.LocaleID îš ţĥé ƂÇÞ-47 ļöçàļé ţýþé ƒŕöḿ
github.com/neokapi/neokapi/core/model.
Ţĥé ƒŕàḿéŵöŕķ šĥîþš à ŠǪĻîţé ƃàçķéñđ (voice/sqlite.go) ƃüîļţ öñ
ţĥé šĥàŕéđ core/storage ḿîĝŕàţîöñ šýšţéḿ, ŵîţĥ ĴŠÖÑ çöļüḿñš ƒöŕ ţĥé çöḿþļéẋ
ţöñé àñđ šţýļé ƒîéļđš. Îñšîđé à þŕöĵéçţ ţĥé ṽöîçé ţàƃļéš ļîṽé îñ ţĥé
þŕöĵéçţ'š öŵñ šţöŕé; à šţàñđàļöñé šţöŕé îš voice.db. Šéŕṽéŕ đéþļöýḿéñţš çàñ
àđđ à šçöþé-þàŕţîţîöñéđ ÞöšţĝŕéŠǪĻ ƃàçķéñđ ƃéĥîñđ ţĥé šàḿé îñţéŕƒàçé.
Šçöŕîñĝ àñđ ŕéšöļüţîöñ
import "github.com/neokapi/neokapi/core/profile"
findings := []profile.VoiceFinding{
{Dimension: profile.DimensionVocabulary, Severity: profile.SeverityMajor,
Message: "Forbidden term: leverage", Suggestion: "use"},
{Dimension: profile.DimensionTone, Severity: profile.SeverityMinor,
Message: "Tone is too formal for this profile"},
}
score := profile.CalculateScore(findings) // score.Overall = 94 (100 - 5 - 1)
// ResolveProfile layers locale, then channel, then persona overrides on a base
// profile
resolved := profile.ResolveProfile(base, "ja", "", "")
Þîþéļîñé ţööļš
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
// VoiceAnnotation plus voice-score / voice-findings properties
checkTool := aitool.NewVoiceCheckTool(llmProvider, profile)
// Rule-based: word rules and prohibited patterns, no LLM calls
vocabTool := tools.NewVoiceVocabCheckTool(profile, terminology)
Šţàŕţéŕ þàçķš
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()
Þàçķš àŕé ÝÀḾĻ ƒîļéš éḿƃéđđéđ ṽîà go:embed; éàçĥ ŕéţüŕñš à
*profile.VoiceProfile ŕéàđý ţö üšé öŕ çüšţöḿîžé, çàŕŕýîñĝ ţĥé þàçķ'š ţéŕḿš
(CarriedTerms, ƒŕöḿ pack <name>).
Çöñţéñţ ḿöđéļ îñţéĝŕàţîöñ
VoiceAnnotation îš à ŕéĝîšţéŕéđ þàýļöàđ (voice) šţöŕéđ àš à
ƃļöçķ-šçöþéđ àññöţàţîöñ (Ƒ-02),
ţĥé çöüñţéŕþàŕţ ţö þöšîţîöñàļ öṽéŕļàýš ļîķé term àñđ entity. Îţ îš ŕéàçĥéđ
ţĥŕöüĝĥ ţĥé ƃļöçķ'š Anno/SetAnno ĥéļþéŕš àñđ ŕéĝîšţéŕéđ ƒöŕ ŵîŕé/šţöŕé
ŕéĥýđŕàţîöñ ṽîà model.RegisterPayload:
type VoiceAnnotation struct {
ProfileID string `json:"profile_id"`
Score int `json:"score"` // 0-100 overall
Findings []VoiceFinding `json:"findings"`
Position model.Anchor `json:"position"`
}
func (a *VoiceAnnotation) AnnotationType() string { return "voice" }
Þŕöƒîļéš šéŕîàļîžé àš ƃöţĥ ĴŠÖÑ àñđ ÝÀḾĻ, šö ţĥéý çàñ ƃé àüţĥöŕéđ ƃý ĥàñđ öŕ
çöñšţŕüçţéđ þŕöĝŕàḿḿàţîçàļļý àš à *profile.VoiceProfile.
Ŕéļàţéđ
- Ṽöîçé þŕöƒîļé ŕéƒéŕéñçé: éṽéŕý ƒîéļđ öƒ ţĥé ƒîļé.
- Çĥéçķš: ţĥé éñĝîñé ṽöîçé çĥéçķš ŕüñ öñ.
- Ţéŕḿîñöļöĝý: ŵĥéŕé ŵöŕđ ŕüļéš ļîṽé.