Agent playbooks
A playbook holds what your AI agents should say and do, approved by a person:
Agent playbooks
A playbook holds what your AI agents should say and do, approved by a person:
- FAQs: a question and its approved answer.
- Skills: named markdown instructions for a task (how to issue a refund, how to escalate an outage), loaded by an agent only when the task needs them.
Agents discover both by meaning: they send a task or a user's question in their own words and get back the published FAQs and skills that fit. Every edit makes a new version, and agents only ever see the version a person published.
Agents, and the agent memory store you link, propose new FAQs with the evidence they rest on. The proposals wait in an inbox until an admin or owner accepts, edits or rejects them. Link a semantic cache and every published answer is also put there, so your application's cache lookup serves the approved text without asking a language model.
Agent playbooks are part of Kivi Synapse.
How it works
A playbook lives in one of your databases. Its settings (name, embedding model,
links) are kept by KiviDB Cloud; everything written to it is stored in that
database, under keys starting kividb:pb:{<playbook id>}::
| Key | Holds |
|---|---|
e:faq:<id>, e:skill:<name> | the entry: state, newest version, published version, who changed it and when |
v:faq:<id>, v:skill:<name> | its versions, newest 20 and the published one |
x:pub:faq:<id>, x:pub:skill:<name> | the published version, with its embedding: what discovery searches |
x:ent:<id> | the newest version of each FAQ that is not archived, for duplicate checks |
x:prop:<id> | one pending proposal, with its evidence |
faqs, skills, proposals | sorted sets for listing and the limits |
meta | counts of accepted, rejected and dropped proposals |
and one search index, kividb:pb:<playbook id>:idx, over the x: keys. A
published document exists only while its entry is published, so a draft or an
archived entry cannot be discovered.
States and versions
| State | Agents see it |
|---|---|
draft | no: written, never published |
published | yes, the published version (an edit after publishing is a new version agents do not see until it is published) |
archived | no; its versions are kept, and publishing it again brings it back |
Each save is a new version, numbered from 1. Publish any kept version to roll back to it. The newest 20 versions are kept, plus the published one; an older version is removed when a new one is saved.
Who may do what
| Viewer | Member | Admin, owner | |
|---|---|---|---|
| See the playbook's settings and counts | yes | yes | yes |
| Read FAQs, skills and proposals; discover | no | yes | yes |
| Write drafts, save versions, propose | no | yes | yes |
| Publish, archive, delete, review proposals, change settings | no | no | yes |
API keys carry the same roles. When the organization's audit log is on, settings changes, publishing, archiving, deleting and every review are recorded.
Embeddings
Discovery and duplicate checks compare embeddings. A playbook uses one of:
embed_provider | |
|---|---|
local (default) | all-MiniLM-L6-v2, run inside KiviDB Cloud, 384 dimensions. Nothing leaves KiviDB and there is no extra charge. |
kividb | our hosted embedding model (256, 512 or 1024 dimensions), billed per token as described in hosted models. |
openai | OpenAI with your key, e.g. text-embedding-3-small |
openai_compatible | any HTTPS endpoint with OpenAI's /embeddings shape, with your key |
Keys are stored encrypted and never returned. An endpoint's address is checked when saved and on every call, and private and internal addresses are refused. The model and size can change only while the playbook is empty.
API
Every call takes an API key (Authorization: Bearer kvdb_...) or a console
session, and names the database and the playbook:
/instances/{database_id}/playbooks/{playbook}.
Create a playbook
curl -X PUT https://api.kividb.io/instances/$DB/playbooks/support \
-H "Authorization: Bearer $KIVIDB_API_KEY" -H "Content-Type: application/json" \
-d '{
"description": "Approved answers for the support assistant",
"embed_provider": "local",
"memory_store": "support-memory",
"cache_namespace": "support-answers",
"proposal_interval_minutes": 60,
"dedupe_threshold": 0.9
}'
memory_store and cache_namespace are optional and must be in the same
database. GET the same path for its settings and counts, DELETE it to remove
the playbook and everything it stored (including its answers in the linked
cache). GET /playbooks lists every playbook in the organization.
FAQs
# A draft, version 1
curl -X POST https://api.kividb.io/instances/$DB/playbooks/support/faqs \
-H "Authorization: Bearer $KIVIDB_API_KEY" -H "Content-Type: application/json" \
-d '{"question": "How long does a refund take?",
"answer": "Refunds reach the original card within 5 business days of approval.",
"tags": ["billing"]}'
# {"faq": {"id": "3f9c1a0b7d2e4c51", "version": 1}, "state": "draft"}
# Publish it (admin or owner). Without "version", the newest is published.
curl -X POST https://api.kividb.io/instances/$DB/playbooks/support/faqs/3f9c1a0b7d2e4c51/publish \
-H "Authorization: Bearer $KIVIDB_API_KEY" -H "Content-Type: application/json" -d '{}'
# {"published_version": 1, "previous_version": null, "cache": {"namespace": "support-answers", "ok": true, ...}}
| Call | Does |
|---|---|
GET …/faqs?offset=&state= | a page of 50, most recently changed first |
POST …/faqs | a new draft {question, answer, tags?, note?} |
GET …/faqs/{id} | the entry and every version kept |
PUT …/faqs/{id} | a new version, same body as POST |
POST …/faqs/{id}/publish | {version?}: what agents see |
POST …/faqs/{id}/archive | hide it from agents (and remove it from the linked cache) |
DELETE …/faqs/{id} | delete it and its versions |
Skills
curl -X POST https://api.kividb.io/instances/$DB/playbooks/support/skills \
-H "Authorization: Bearer $KIVIDB_API_KEY" -H "Content-Type: application/json" \
-d '{"name": "issue-refund",
"description": "Step by step: check eligibility, issue the refund, tell the customer.",
"body": "# Issue a refund\n\n1. Check the order is under 30 days old.\n2. Refund it in Billing > Orders.\n3. Reply with the refund id.",
"tags": ["billing"]}'
A skill's name is 1-64 lowercase letters, digits, - or _, starting with a
letter, and cannot change. Its body is markdown, up to 64 KB. The calls are the
FAQ ones under …/skills/{name}, except:
| Call | Does |
|---|---|
GET …/skills/{name} | the published skill, whole: what an agent loads |
GET …/skills/{name}/versions | the entry and every version kept |
Skills are written or pasted as markdown; uploading a ZIP package is not supported.
Discover
curl -X POST https://api.kividb.io/instances/$DB/playbooks/support/discover \
-H "Authorization: Bearer $KIVIDB_API_KEY" -H "Content-Type: application/json" \
-d '{"task": "when will I get my money back?", "kinds": ["faq", "skill"], "top_k": 5, "min_score": 0.4}'
{
"results": [
{ "kind": "faq", "id": "3f9c1a0b7d2e4c51", "question": "How long does a refund take?",
"answer": "Refunds reach the original card within 5 business days of approval.",
"tags": ["billing"], "version": 1, "score": 0.8123 },
{ "kind": "skill", "name": "issue-refund",
"description": "Step by step: check eligibility, issue the refund, tell the customer.",
"tags": ["billing"], "version": 2, "score": 0.6011 }
],
"latency_ms": 14
}
kinds defaults to both, top_k to 5 (at most 20). score is cosine
similarity, 0 to 1. Only published versions are returned. For a skill, load the
body with GET …/skills/{name} when the agent decides it needs it.
Proposals
curl -X POST https://api.kividb.io/instances/$DB/playbooks/support/proposals \
-H "Authorization: Bearer $KIVIDB_API_KEY" -H "Content-Type: application/json" \
-d '{"question": "Can I change the delivery address after ordering?",
"answer": "Yes, until the parcel ships: Orders > Change address.",
"evidence": [{"source": "ticket 4471", "excerpt": "Changed the address before dispatch, customer confirmed."}],
"proposed_by": "support-bot"}'
# {"accepted": true, "proposal_id": "a1b2c3d4e5f60718"}
Evidence is required: one to ten {source, excerpt} items. A proposal at least
dedupe_threshold similar (default 0.9) to an FAQ that is not archived, or to a
pending proposal, is not added; the reply names the one it repeats:
{ "accepted": false, "duplicate_of": { "kind": "faq", "id": "3f9c1a0b7d2e4c51", "question": "How long does a refund take?", "score": 0.97 } }
| Call | Does |
|---|---|
GET …/proposals?offset= | the inbox, newest first, and dropped: how many the limit has removed |
POST …/proposals/{id}/accept | {question?, answer?, tags?, publish?}: make an FAQ from it, with your edits; publish: true publishes it at once |
POST …/proposals/{id}/reject | remove it |
An accepted FAQ's first version notes who proposed it and its evidence sources.
Proposals from agent memory
Link an agent memory store in the same
database and, every proposal_interval_minutes (15 to 1,440, default 60),
KiviDB reads the long-term memories and session summaries the store has added
since the last read (up to 50 memories and 20 sessions a run) and asks the
store's own language model for FAQ candidates. Each candidate must cite the
memories or summaries it rests on; those become its evidence, and one that
cites nothing it was given is dropped. At most five are proposed a run, and the
duplicate check above applies.
- A store without a language model proposes nothing; the console says so.
- A newly linked store is read from its oldest memory onward, a batch a run, and each memory is read once.
- If the model fails, the same material is tried again next run; after five failed runs it is skipped and the playbook shows that it was.
- Read memory now in the console's inbox (or
POST …/run, admin or owner) runs it at once.
Model calls use the store's settings: our hosted model, billed per token, or your provider with your key.
The approved-answer loop
Link a semantic cache namespace in the same database and:
- publishing an FAQ puts its question and answer in the cache, embedded by the namespace's own provider so lookups compare like with like;
- publishing a new version replaces that entry; archiving or deleting the FAQ removes it;
- entries are put with a year's expiry and refreshed daily, so an answer stays cached while it stays published;
- linking a namespace puts every published FAQ in it; unlinking takes them out.
A namespace without a provider (none) is given the playbook's own embedding of
the question, so it must have the playbook's size, and your lookups must use
the same model (all-MiniLM-L6-v2 for local). Cached entries carry
{"source": "agent_playbook", "playbook": …, "faq_id": …, "version": …} as
metadata. If a put fails, the publish still stands; the console shows the error
and the daily refresh tries again.
From an AI assistant
The KiviDB MCP server has list_playbooks, playbook_discover,
playbook_get_skill and playbook_propose. A typical agent loop: call
playbook_discover with the user's question; answer from a returned FAQ, or
load a returned skill with playbook_get_skill and follow it; when it learns
something that should be an FAQ, call playbook_propose with the evidence.
Playbooks are created and entries published in the console or over the API.
Limits
| Playbooks | 20 per database |
| FAQs | 2,000 per playbook (archived ones count; delete them to make room) |
| Skills | 200 per playbook, each up to 64 KB of markdown |
| Versions kept | the newest 20 per entry, and the published one |
| Pending proposals | 500 per playbook; past that the oldest are dropped and counted |
| Question / answer | 500 / 8,000 characters |
| Evidence | 1 to 10 items per proposal, each excerpt up to 2,000 characters |
| Discover | top_k up to 20 |
Not available on sharded databases yet. It uses the database's own memory, with no extra charge beyond the database.