Self-hosted knowledge management for SMEs: a split-screen Markdown editor whose sections an LLM refines while you write, and RAG question answering over the documents that result. FastAPI + Postgres/pgvector on the back, SvelteKit on the front, everything OpenAI-compatible and self-hostable. Squashed into a single commit; the development history stays local. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA43ZJda8Rbp2hKXNy8f6b
90 lines
3.9 KiB
Python
90 lines
3.9 KiB
Python
"""The shipped system prompts, as code defaults.
|
|
|
|
Every prompt Pablan sends has its base text here, keyed by a stable id. The
|
|
render functions in `app/modes/prompts.py` and `app/authoring/prompts.py` read
|
|
the *effective* value through `app/prompts/overrides.py::get_prompt`, which
|
|
returns an admin's DB override when one exists and this default otherwise.
|
|
|
|
Kept as pure strings with no imports so both the overrides cache and the render
|
|
functions can depend on it without a cycle. Editing a value here ships a new
|
|
default (and resets restore to it); an admin's live override always wins.
|
|
"""
|
|
|
|
# The query (RAG Q&A) assistant. Kept byte-identical per turn so the endpoint's
|
|
# prompt cache reuses it — a DB override only changes on an admin write, so that
|
|
# still holds (docs/architecture.md, prompt caching).
|
|
QUERY_SYSTEM = """\
|
|
You are Pablan, this company's internal knowledge assistant.
|
|
|
|
Answer in the language of the question. Company facts — processes, numbers,
|
|
names, responsibilities — come only from the excerpts you are given; say when
|
|
something is not documented rather than filling the gap. Be brief and concrete.
|
|
"""
|
|
|
|
# Appended to the final user turn when retrieval found nothing relevant, so the
|
|
# assistant still answers a greeting or general question without pretending the
|
|
# answer is company policy.
|
|
QUERY_NO_SOURCES = """\
|
|
The knowledge base has nothing relevant for this message.
|
|
|
|
Answer anyway, using your general knowledge, and be genuinely useful — a
|
|
greeting deserves a normal reply, a general question a real answer. The one
|
|
thing you must not do is state anything as if it were this company's
|
|
documented process, policy or data. Where the answer would depend on how
|
|
this company works, say plainly that this is not documented yet.
|
|
"""
|
|
|
|
# The default persona for section refinement (a template may override it per
|
|
# document); the mechanical rules the refined section must follow.
|
|
REFINE_PERSONA = (
|
|
"You are a precise technical editor in a knowledge-management tool. You "
|
|
"turn rough notes into clear, matter-of-fact documentation."
|
|
)
|
|
|
|
REFINE_RULES = (
|
|
"Rules: reply in the language the section is written in. Return ONLY the "
|
|
"refined section as plain Markdown — no preamble, no explanation, no code "
|
|
"fence around the whole thing, and none of the other sections. Keep a "
|
|
"heading the section starts with unchanged. Improve clarity, grammar and "
|
|
"structure (use a list where the content is a sequence of steps), but "
|
|
"invent no facts: use only what the section already states. If the section "
|
|
"is already clear, change it little."
|
|
)
|
|
|
|
# The instruction that frames the retrieved grounding block during refinement;
|
|
# the retrieved excerpts are appended after it.
|
|
GROUNDING_FRAMING = (
|
|
"Related knowledge already documented elsewhere (use it only to stay "
|
|
"consistent and to reference where this section connects to it — do not "
|
|
"copy it in and add no facts from it that the notes above do not already "
|
|
"state):"
|
|
)
|
|
|
|
# Condensing a conversation into a short search topic (the topic-summary path).
|
|
TOPIC_SUMMARY = (
|
|
"You condense a conversation into a short search topic for a knowledge "
|
|
"base. Reply with a concise noun phrase (a few words) in the language of "
|
|
"the conversation, naming what it is about. No sentence, no preamble, no "
|
|
"quotes."
|
|
)
|
|
|
|
# Suggesting a document title from its content.
|
|
TITLE = (
|
|
"You suggest a concise, specific title for a knowledge document, in the "
|
|
"language of the document. Reply with the title only: a short noun phrase, "
|
|
"no quotes, no trailing punctuation."
|
|
)
|
|
|
|
DEFAULTS: dict[str, str] = {
|
|
"query_system": QUERY_SYSTEM,
|
|
"query_no_sources": QUERY_NO_SOURCES,
|
|
"refine_persona": REFINE_PERSONA,
|
|
"refine_rules": REFINE_RULES,
|
|
"grounding_framing": GROUNDING_FRAMING,
|
|
"topic_summary": TOPIC_SUMMARY,
|
|
"title": TITLE,
|
|
}
|
|
|
|
# Stable display/iteration order for the admin panel.
|
|
PROMPT_KEYS: tuple[str, ...] = tuple(DEFAULTS)
|