chore(template): sync SPEC.md
This commit is contained in:
parent
73fa97578d
commit
89f76cc286
171
SPEC.md
Normal file
171
SPEC.md
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
# AI Agent App — product specification
|
||||||
|
|
||||||
|
This is the contract the project is built against. It is written for the agent that scaffolds
|
||||||
|
the repository, but it doubles as the human-readable brief: every requirement below is meant to
|
||||||
|
be implementable and verifiable.
|
||||||
|
|
||||||
|
Values in angle brackets (`<Agent name>`, `<model>`, `<retrieval>`) come from the
|
||||||
|
project-creation dialog and are recorded in the project record; replace them as you read.
|
||||||
|
|
||||||
|
## 1. Goal
|
||||||
|
|
||||||
|
A chat application built around an LLM agent: a user holds a conversation, the agent streams its
|
||||||
|
answer token by token, may consult the workspace's own documents through `<retrieval>`, and may
|
||||||
|
call tools. Answers that use documents cite the source chunks. It must be **runnable
|
||||||
|
end-to-end on day one** (even with a mock provider and seeded documents), not a set of screens
|
||||||
|
wired together later.
|
||||||
|
|
||||||
|
Non-goals (do not build unless the must-have list says otherwise): multi-agent orchestration,
|
||||||
|
fine-tuning, a public model marketplace, voice I/O, autonomous background agents.
|
||||||
|
|
||||||
|
## 2. Roles
|
||||||
|
|
||||||
|
| Role | Can do |
|
||||||
|
|------|--------|
|
||||||
|
| End user | Create conversations, chat with streaming answers, upload attachments, see citations and their own usage. |
|
||||||
|
| Workspace admin | Manage the knowledge base (upload, re-index, delete documents), configure the agent (system prompt, model, temperature, tools) and review usage/cost. |
|
||||||
|
| Platform operator | Set provider credentials and per-tenant quotas, inspect provider health and the audit log. |
|
||||||
|
|
||||||
|
Authentication is required for every conversation and for the whole admin surface; there is no
|
||||||
|
anonymous chat. Provide a seeded admin account and a seeded user for local development.
|
||||||
|
|
||||||
|
## 3. Information architecture (routes)
|
||||||
|
|
||||||
|
- `/` — conversations list: newest-first, title, last message snippet, model, new-conversation
|
||||||
|
action.
|
||||||
|
- `/conversations/:conversationId` — one conversation: streaming chat, message history,
|
||||||
|
attachments, stop/regenerate, rename/delete.
|
||||||
|
- `/knowledge` — knowledge base: document list with status (uploaded → processing → indexed →
|
||||||
|
failed), upload, re-index, delete, per-document chunk count.
|
||||||
|
- `/knowledge/:documentId` — one document: metadata, ingestion progress, chunk preview.
|
||||||
|
- `/agent` — agent configuration: system prompt, model, temperature, retrieval on/off, tool
|
||||||
|
selection.
|
||||||
|
- `/usage` — usage and cost: tokens and cost per period, per conversation and per model.
|
||||||
|
- `/settings` — profile, provider keys (BYOK), default model, export/delete account data.
|
||||||
|
- `/login`, `/logout` (admin surface guarded by the admin role).
|
||||||
|
|
||||||
|
## 4. Data model
|
||||||
|
|
||||||
|
Minimum viable entities (add fields the requirements imply; keep them typed and validated):
|
||||||
|
|
||||||
|
- **User** — id, email, passwordHash, name, role (`user` | `admin` | `operator`), createdAt.
|
||||||
|
- **Conversation** — id, userId, title, model, systemPromptSnapshot, createdAt, updatedAt,
|
||||||
|
archivedAt?.
|
||||||
|
- **Message** — id, conversationId, role (`system` | `user` | `assistant` | `tool`), content,
|
||||||
|
status (`pending` | `streaming` | `complete` | `error` | `cancelled`), inputTokens,
|
||||||
|
outputTokens, model, toolCallId?, createdAt.
|
||||||
|
- **Attachment** — id, messageId, kind (`image` | `file`), filename, mimeType, sizeBytes,
|
||||||
|
storagePath.
|
||||||
|
- **Document** — id, workspaceId, filename, mimeType, sizeBytes, status (`uploaded` |
|
||||||
|
`processing` | `indexed` | `failed`), error?, chunkCount, uploadedBy, createdAt.
|
||||||
|
- **DocumentChunk** — id, documentId, ordinal, text, tokenCount, embeddingRef (vector column for
|
||||||
|
`pgvector`, else the external collection/point id), createdAt.
|
||||||
|
- **AgentConfig** — id, workspaceId, systemPrompt, model, temperature, topP, retrievalEnabled,
|
||||||
|
retrievalTopK, enabledToolIds[], updatedAt.
|
||||||
|
- **ToolDefinition** — id, workspaceId, name, description, parameters (JSON schema), handler,
|
||||||
|
sideEffecting (boolean), enabled.
|
||||||
|
- **ToolCall** — id, messageId, toolId, arguments (JSON), result (JSON), status (`pending` |
|
||||||
|
`awaiting_confirmation` | `running` | `succeeded` | `failed` | `denied`), latencyMs.
|
||||||
|
- **UsageRecord** — id, userId, conversationId?, messageId?, model, inputTokens, outputTokens,
|
||||||
|
costMinor, currency, createdAt.
|
||||||
|
|
||||||
|
Retrieval storage is chosen in the dialog: `pgvector` keeps `embeddingRef` as a vector column;
|
||||||
|
`Qdrant`/`OpenSearch` store the external collection and point id; `None (chat only)` disables
|
||||||
|
ingestion and citations. Keep this behind one retrieval interface so the choice is a
|
||||||
|
configuration, not a rewrite.
|
||||||
|
|
||||||
|
Embeddings and model calls always run **server-side**; the browser never holds a provider key.
|
||||||
|
|
||||||
|
## 5. Key flows
|
||||||
|
|
||||||
|
1. **Streamed reply.** The user sends a message; the server persists it, calls the model through
|
||||||
|
the provider abstraction, and streams tokens back over the response stream. The assistant
|
||||||
|
message is `streaming` while tokens arrive and `complete` when the stream ends. The user can
|
||||||
|
cancel mid-stream (`cancelled`, partial text kept); a provider error ends the message as
|
||||||
|
`error` with a retry action.
|
||||||
|
2. **Document ingestion.** The admin uploads a document; the server stores it, extracts text,
|
||||||
|
chunks it, embeds each chunk, writes it to `<retrieval>`, and marks the document `indexed`.
|
||||||
|
Progress is visible per document (status + chunk count); a failure records the error and the
|
||||||
|
document stays re-indexable.
|
||||||
|
3. **Retrieval-augmented answer.** When retrieval is enabled, the question is embedded, the top-k
|
||||||
|
chunks are fetched, and they are passed to the model as untrusted context. The answer includes
|
||||||
|
**citations** that map back to the source chunk (document + ordinal), shown next to the
|
||||||
|
message and linking to `/knowledge/:documentId`.
|
||||||
|
4. **Tool calling.** The model may request a tool. Read-only tools run immediately; a
|
||||||
|
side-effecting tool pauses the turn in `awaiting_confirmation` and the user confirms or denies
|
||||||
|
before it executes. The result is fed back to the model and the turn continues; every call and
|
||||||
|
its status is recorded on the message.
|
||||||
|
5. **Conversation lifecycle.** A conversation is auto-titled from its first exchange, can be
|
||||||
|
renamed, and can be deleted (with confirmation) along with its messages and usage links.
|
||||||
|
|
||||||
|
## 6. Functional requirements
|
||||||
|
|
||||||
|
- **Chat:** streaming token-by-token with visible typing state; stop/regenerate; markdown
|
||||||
|
rendering with code blocks; per-message status; retry after an error without losing history.
|
||||||
|
- **Provider abstraction:** one interface with `DeepSeek`, `OpenAI`, `Anthropic` and `BYOK`
|
||||||
|
implementations, plus a **mock provider** used when no key is configured, so the app runs
|
||||||
|
without secrets.
|
||||||
|
- **Knowledge base:** upload (multiple files), list with status and filters, re-index, delete;
|
||||||
|
extraction for at least text/PDF/markdown; chunk size and overlap documented.
|
||||||
|
- **Retrieval:** configurable top-k; citations on every grounded answer; a clear "no relevant
|
||||||
|
documents" state instead of an invented answer; retrieval can be toggled per workspace and per
|
||||||
|
conversation.
|
||||||
|
- **Tools:** a registry with JSON-schema parameters; per-workspace enable/disable; the
|
||||||
|
side-effecting confirmation step; timeouts on every handler.
|
||||||
|
- **Usage:** record input/output tokens and cost per request; aggregate by period, conversation
|
||||||
|
and model; surface remaining quota to the user.
|
||||||
|
- **Admin:** edit the system prompt, model, temperature and tools; guard the surface with the
|
||||||
|
admin role; no destructive action without confirmation.
|
||||||
|
- **Seed data:** one workspace, one admin, one user, a few documents already indexed, and a
|
||||||
|
couple of conversations so the app is presentable on first run.
|
||||||
|
|
||||||
|
## 7. Non-functional requirements
|
||||||
|
|
||||||
|
- **Secrets:** never expose model/API keys to the client — all provider calls are server-side and
|
||||||
|
proxied; keys live in server env only and are documented in `.env.example`; BYOK keys are
|
||||||
|
encrypted at rest and never returned to the browser.
|
||||||
|
- **Limits:** per-user and per-tenant rate limits and token/cost quotas enforced server-side; a
|
||||||
|
request over quota is rejected with a clear message, not a silent failure.
|
||||||
|
- **Untrusted input:** documents, tool output and retrieved chunks are untrusted data — never
|
||||||
|
concatenated into instructions, delimited and labelled as data in the prompt, and never allowed
|
||||||
|
to trigger side-effecting tools without the confirmation step.
|
||||||
|
- **Resilience:** provider timeouts, rate-limit and partial-stream failures degrade with a clear
|
||||||
|
message and a retry path; a dropped connection keeps the partial answer.
|
||||||
|
- **Context budget:** conversation history sent to the model is bounded with a documented
|
||||||
|
truncation/summarisation strategy; the full transcript is always kept server-side.
|
||||||
|
- **Observability:** structured logs for model calls, retrieval and tool calls (no prompt or key
|
||||||
|
material in logs); a health endpoint that reports provider reachability.
|
||||||
|
- **Accessibility:** semantic landmarks, labelled inputs, keyboard-operable chat, visible focus,
|
||||||
|
contrast at least AA, and an announced streamed message for screen readers.
|
||||||
|
|
||||||
|
## 8. Acceptance criteria (definition of done)
|
||||||
|
|
||||||
|
- [ ] Install, dev server, lint, typecheck, tests and production build all pass.
|
||||||
|
- [ ] A streamed answer works end-to-end: tokens appear progressively, cancellation stops the
|
||||||
|
stream, and a provider error shows a retry without losing history.
|
||||||
|
- [ ] A question over an ingested document returns a grounded answer with citations to the source
|
||||||
|
chunks.
|
||||||
|
- [ ] An unavailable provider degrades with a clear, actionable message instead of a blank screen.
|
||||||
|
- [ ] Model/API keys are never present in the client bundle or any client response.
|
||||||
|
- [ ] Usage (input/output tokens and cost) is recorded per request and visible in `/usage`.
|
||||||
|
- [ ] A side-effecting tool cannot run without an explicit user confirmation.
|
||||||
|
- [ ] Seeded data makes the app presentable immediately; `.env.example` documents every secret.
|
||||||
|
- [ ] README quickstart (install, run, test, env) is accurate; empty/loading/error states exist.
|
||||||
|
- [ ] CI runs install + lint + typecheck + tests + build.
|
||||||
|
|
||||||
|
## 9. Suggested build order
|
||||||
|
|
||||||
|
Follow this order and finish (and verify) a layer before starting the next:
|
||||||
|
|
||||||
|
1. **Scaffold** the chosen stack, install dependencies, get the dev server and the empty shell
|
||||||
|
running, set up env handling for model keys, and commit the skeleton.
|
||||||
|
2. **Streaming chat shell**: the message list, the composer and token streaming end-to-end with
|
||||||
|
the mock provider.
|
||||||
|
3. **Conversation persistence**: conversations and messages in the database, auto-titling,
|
||||||
|
rename/delete, history loading.
|
||||||
|
4. **Document ingestion + retrieval**: upload → chunk → embed → index over `<retrieval>`, and
|
||||||
|
retrieval-augmented answers with citations.
|
||||||
|
5. **Tool calling**: the tool registry, the run loop, and the confirmation step for side-effecting
|
||||||
|
tools.
|
||||||
|
6. **Usage + limits**: token/cost accounting, quotas and rate limits.
|
||||||
|
7. **Quality**: tests for the flows above, CI, README, `.env.example`, accessibility pass.
|
||||||
Loading…
Reference in a new issue