Skip to main content

Motivation

AskMyDocs ships with safe defaults: a fresh install behaves like a competent hybrid-RAG service out of the box. But every enterprise deployment eventually needs to tune something — the embedding provider, the reranker’s trust gradient, the refusal threshold, the retention windows. This page is the map of what is configurable, where the knob lives, and what it does. It is not an exhaustive env dump (see .env.example for that) — it argues the knobs that change behaviour you can observe.

Theory: configuration is layered, not flat

A knob is never read directly from the environment by a service. The flow is always env var → config/*.php cast + default → service constructor / config() call. This indirection matters:
  • config/*.php is the single source of truth for the default and the cast ((float), (bool), (int)). An unset env var is not “null” — it is the documented default.
  • Tests and config:cache read the compiled config, not $_ENV. Reading env() outside config/*.php is a bug (it returns null once config is cached).
After changing .env in production, run php artisan config:clear (or re-run config:cache). A cached config ignores live .env edits — this is the single most common “my setting did nothing” report.

The config files

Configuration is split by concern. The files you will touch most:
FileOwns
config/ai.phpChat + embeddings providers, models, cost rates, agentic/MCP tool-calling
config/kb.phpRetrieval, chunking, reranking, canonical, graph, refusal gate, soft-delete, embedding cache
config/askmydocs.phpScheduler slots (cron + enable toggles), composite gates
config/admin.phpMaintenance command allow-list, confirm-token TTL, audit retention
config/chat-log.phpChat logging driver + retention
config/permission.php / config/rbac.phpSpatie roles, RBAC enforcement
config/filesystems.phpKB disk (kb) + raw disk (kb-raw)
Sister-package surfaces (connectors, mcp-pack, mcp, widget, laravel-flow, pii-redactor-admin, eval-harness, ai-act-compliance, evidence-risk-review, …) each ship their own config/*.php. See Sister packages.

Providers (config/ai.php)

Chat and embeddings are configured independently — Anthropic has no embeddings endpoint, so you mix providers freely.
AI_PROVIDER=openrouter          # chat: openai | anthropic | gemini | openrouter | regolo
AI_EMBEDDINGS_PROVIDER=openai   # embeddings: openai | gemini | openrouter | regolo
KB_EMBEDDINGS_DIMENSIONS=1536   # MUST match the embeddings model's output width
Each provider has a <NAME>_API_KEY, <NAME>_CHAT_MODEL, <NAME>_TEMPERATURE, <NAME>_MAX_TOKENS, and <NAME>_TIMEOUT, but the rest of the surface is provider-specific — Anthropic adds ANTHROPIC_API_VERSION and has no *_BASE_URL or embeddings model; OpenAI / Gemini / OpenRouter add *_BASE_URL and *_EMBEDDINGS_MODEL; Regolo exposes a REGOLO_CHAT_MODEL* cheapest/smartest matrix plus REGOLO_EMBEDDINGS_DIMENSIONS. The full per-provider matrix — including the dimension-safety auto-select order when AI_PROVIDER=anthropic — is documented in AI providers.
KB_EMBEDDINGS_DIMENSIONS is part of the database contract. Changing the embeddings model to a different width (OpenAI 1536 → Gemini 768 → Regolo 4096) requires resizing the vector(N) columns and flushing the embedding cache and re-indexing. See Troubleshooting.

Retrieval & reranking (config/kb.php)

The reranker is the trust gradient — it fuses several signals into one score, then applies a canonical boost and a status penalty. Defaults (kb.reranking.* and kb.canonical.*):
KnobEnvDefaultEffect
Candidate multiplierKB_RERANK_CANDIDATE_MULTIPLIER3Over-retrieve 3× the limit, then rerank down
Vector weightKB_RERANK_VECTOR_WEIGHT0.55Cosine similarity signal
Keyword weightKB_RERANK_KEYWORD_WEIGHT0.25Lexical overlap signal
Heading weightKB_RERANK_HEADING_WEIGHT0.05Heading-path match signal
Tag overlapKB_RERANK_TAG_OVERLAP_WEIGHT0.05Source-aware tag overlap
Preamble matchKB_RERANK_PREAMBLE_WEIGHT0.05Frontmatter/preamble signal
RecencyKB_RERANK_RECENCY_WEIGHT0.02Newer docs slightly favoured
Status activeKB_RERANK_STATUS_WEIGHT0.02Active-status nudge
Mention boostKB_RERANK_MENTION_BOOST_WEIGHT0.50@slug explicit mention boost
Canonical priorityKB_CANONICAL_PRIORITY_WEIGHT0.001Multiplies retrieval_priority (0–100)
Superseded penaltyKB_CANONICAL_SUPERSEDED_PENALTY0.40Demote superseded canonical docs
Deprecated penaltyKB_CANONICAL_DEPRECATED_PENALTY0.40Demote deprecated canonical docs
Archived penaltyKB_CANONICAL_ARCHIVED_PENALTY0.60Demote archived canonical docs
Auto-tier penaltyKB_CANONICAL_AUTO_TIER_PENALTY0.02Keep auto-compiled docs below human-curated
The deep treatment of the formula lives in the chat & retrieval and grounding & evidence tiers pages.

The refusal gate

The anti-hallucination firewall refuses to answer when retrieval is too weak — better a refusal than a fabrication. Three thresholds, all in config/kb.php:
KB_REFUSAL_MIN_SIMILARITY=0.45    # min raw cosine of the best chunk
KB_REFUSAL_MIN_RERANK_SCORE=0.25  # min post-rerank score of the best chunk
KB_REFUSAL_MIN_CHUNKS=1           # min number of qualifying chunks
See the anti-hallucination firewall page for why the refusal is modelled as a status, not an error.

Graph expansion & rejected-approach injection

Both features degrade to no-ops when a tenant has zero canonical docs — an existing consumer sees identical retrieval until it canonicalises.
# Graph expansion (1-hop walk from canonical seeds)
KB_GRAPH_EXPANSION_ENABLED=true
KB_GRAPH_EXPANSION_HOPS=1
KB_GRAPH_EXPANSION_DEPTH=2
KB_GRAPH_EXPANSION_MAX_NODES=20
KB_GRAPH_EXPANSION_EDGE_TYPES=depends_on,implements,decision_for,related_to,supersedes

# Rejected-approach injection (surface dismissed options under a ⚠ marker)
KB_REJECTED_INJECTION_ENABLED=true
KB_REJECTED_INJECTION_MAX_DOCS=3
KB_REJECTED_MIN_SIMILARITY=0.40

Deletion, retention & multi-tenancy

KB_SOFT_DELETE_ENABLED=true         # soft-delete hides rows from every read path
KB_SOFT_DELETE_RETENTION_DAYS=30    # kb:prune-deleted hard-deletes after this
KB_EMBEDDING_CACHE_RETENTION_DAYS=30
CHAT_LOG_RETENTION_DAYS=90
KB_PROJECT_ISOLATION_ENABLED=false  # opt-in per-project read scoping
RBAC_ENFORCED=true
KB_PROJECT_ISOLATION_ENABLED is default-off by design: the v8.9 isolation audit shipped it opt-in so existing deployments keep their behaviour. Cross-tenant isolation is always enforced (see multi-tenant isolation and security & threat model).

Worked example: switch chat to a local-EU Regolo model, keep OpenAI embeddings

# .env
AI_PROVIDER=regolo
REGOLO_API_KEY=rg_live_xxx
REGOLO_CHAT_MODEL=Llama-3.3-70B-Instruct

AI_EMBEDDINGS_PROVIDER=openai
OPENAI_API_KEY=sk-xxx
OPENAI_EMBEDDINGS_MODEL=text-embedding-3-small
KB_EMBEDDINGS_DIMENSIONS=1536        # unchanged — no column resize needed
php artisan config:clear              # drop any cached config
php artisan kb:rebuild-graph --sync   # optional: warm the graph
Because the embeddings provider and dimension are unchanged, no migration and no re-index are needed — only the chat path moves to Regolo.

Gotchas & operations

  • config:cache in production, config:clear after edits. Cached config ignores live .env.
  • Never call env() outside config/*.php. It returns null under config:cache.
  • CSV env vars (SANCTUM_STATEFUL_DOMAINS, CORS_ALLOWED_ORIGINS, KB_GRAPH_EXPANSION_EDGE_TYPES) are trimmed + filtered on parse — leading whitespace is tolerated, but keep them comma-separated with no stray quotes.
  • Default-off flags ship off. When you enable a flag (project isolation, PII redaction, agentic tool-calling, the MCP HTTP server), verify the enabled and disabled paths — both must be healthy (see feature-flag discipline).

Self-hosting

Install, migrate, build the SPA, run the worker + scheduler.

Scheduler & maintenance

Every scheduled job, its cron, and its enable toggle.

AI providers

The full provider matrix and the dimension-safety auto-select.

Troubleshooting

Embedding-dimension gotcha, health checks, common failures.