Skip to content

Commit 5436f7f

Browse files
authored
Merge pull request
KernelBot v2: retrieval strict, logs estruturados e config por .env.example
2 parents 273fa9b + 01fa53e commit 5436f7f

75 files changed

Lines changed: 2146 additions & 20673 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# =============================================================================
2+
# ACL (KernelBot) — variáveis de ambiente
3+
# Copie para .env e preencha os valores reais:
4+
# copy .env.example .env (Windows PowerShell: Copy-Item .env.example .env)
5+
# O ficheiro .env não deve ir para o Git (.gitignore).
6+
# =============================================================================
7+
8+
# --- OpenRouter (obrigatório) ---
9+
# Chave da API em https://openrouter.ai/keys — usada em todas as chamadas ao LLM.
10+
OPENROUTER_API_KEY=
11+
12+
# --- MySQL (obrigatório para o índice BM25 e ingestão) ---
13+
# Host do servidor MySQL (ex.: 127.0.0.1 ou localhost). Nao use 127.0.0.0 — costuma dar timeout.
14+
DB_HOST=
15+
# Porta TCP (padrão MySQL: 3306).
16+
DB_PORT=3306
17+
# Nome da base de dados onde está a tabela `knowledge`.
18+
DB_NAME=
19+
# Utilizador com permissão de leitura (e escrita se correres ingest).
20+
DB_USER=
21+
DB_PASSWORD=
22+
23+
# --- Contexto global (/content sem disciplina) ---
24+
# geral = BM25 em todos os silos e merge por score.
25+
# all = mesmo modo de nome alternativo aceite pelo código.
26+
ACL_GLOBAL_CONTEXT=geral
27+
28+
# --- Contexto fixado (pin na sessão) ---
29+
# Quantos turnos manter chunks fixados antes de voltar a buscar forte.
30+
ACL_PINNED_MAX_TURNS=5
31+
# Tamanho máximo em caracteres do texto fixado no pin.
32+
ACL_PINNED_MAX_CHARS=24000
33+
# Score normalizado abaixo do qual o pin é considerado "fraco" (legado / heurísticas de pin).
34+
ACL_PINNED_WEAK_SCORE=0.4
35+
36+
# --- Política de retrieval (modo strict — ver engine/retrieval.py) ---
37+
# Score BM25 bruto mínimo do melhor candidato; abaixo → hard stop (contexto insuficiente).
38+
ACL_RETRIEVAL_MIN_SCORE=1.5
39+
# Margem mínima entre 1.º e 2.º score; evita ambos muito próximos (ambiguidade).
40+
ACL_RETRIEVAL_MIN_SCORE_MARGIN=0.15
41+
# Cobertura mínima (0–1) dos termos informativos da query nos chunks escolhidos.
42+
ACL_RETRIEVAL_MIN_COVERAGE=0.34
43+
# Igual, mas termos "centrais" da query contam com peso 2× na métrica.
44+
ACL_RETRIEVAL_MIN_COVERAGE_WEIGHTED=0.34
45+
# Número mínimo de termos informativos na pergunta (strict).
46+
ACL_RETRIEVAL_MIN_TERMS=2
47+
# Quantos candidatos o SearchEngine devolve antes de build_decision (1–50).
48+
ACL_RETRIEVAL_CANDIDATE_K=8
49+
# Quantos chunks entram no prompt após passar os gates (1–20).
50+
ACL_RETRIEVAL_TOP_K=4
51+
# Máximo de chunks por fonte (slug) entre os selecionados — diversidade (1–10).
52+
ACL_RETRIEVAL_MAX_CHUNKS_PER_SOURCE=2
53+
54+
# --- Logging ---
55+
# text = linha legivel; json = uma linha JSON por evento ACL (grep/agregadores).
56+
# ACL_LOG_FORMAT=text
57+
# ACL_LOG_LEVEL=INFO
58+
# ACL_LOG_LEVEL=DEBUG # inclui evento retrieval_gates_inputs (metricas antes dos cortes)
59+
60+
# --- Não configurável por .env (definido em core/config.py) ---
61+
# Lista de modelos OpenRouter, URL base, timeout HTTP, textos de system/sticky vêm de ficheiros
62+
# em core/systemPrompt/ — ajusta lá ou no código se precisares de outro stack de modelos.

.github/workflows/main.yml

Lines changed: 0 additions & 46 deletions
This file was deleted.

.gitignore

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,40 @@
1+
<<<<<<< HEAD
12
.env
23

4+
.pytest_cache/
5+
36
**/__pycache__/
47
*.py[cod]
58
*$py.class
69
*.so
710

8-
cursor/
11+
content/
12+
.venv/
13+
.venv_readme_check/
14+
.ruff_cache/
15+
.mypy_cache/
16+
dist/
17+
build/
18+
*.egg-info/
19+
scripts/
20+
=======
21+
.env
22+
23+
.pytest_cache/
24+
25+
**/__pycache__/
26+
*.py[cod]
27+
*$py.class
28+
*.so
29+
30+
content/
31+
.venv/
32+
.venv_readme_check/
33+
.ruff_cache/
34+
.mypy_cache/
35+
dist/
36+
build/
37+
*.egg-info/
38+
scripts/
39+
40+
>>>>>>> 124ae17972dd35902cec901ff1819bd5be55891f

LICENSE

Lines changed: 0 additions & 21 deletions
This file was deleted.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<p>
1515
<strong>Se você é um recrutador:</strong> O código fala mais que mil palavras. Olhe a pasta <code>engine/</code>.<br />
1616
<strong>Se você é um curioso:</strong> Volte em breve. Ou dê um <code>python main.py</code> e descubra.
17-
</p>
17+
1818

1919
<br clear="all" />
2020
</div>

SQL/instructions_for_creation.md

Lines changed: 0 additions & 2 deletions
This file was deleted.

SQL/schema.sql

Lines changed: 0 additions & 10 deletions
This file was deleted.

api/routes.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,10 @@ async def chat(request: Request) -> StreamingResponse:
8181
log.info("🔄 Comando /reload recebido — reconstruindo índice BM25...")
8282
services.search_engine.rebuild()
8383
chunk_count = len(services.search_engine.chunks)
84-
db_count = sum(1 for c in services.search_engine.chunks if c.get("source", "").startswith("db:"))
85-
md_count = chunk_count - db_count
84+
silo_count = len(services.search_engine.discipline_ids)
8685
status = (
8786
f"Índice reconstruído: {chunk_count} chunk(s) total "
88-
f"({md_count} de arquivos .md + {db_count} do MySQL)."
87+
f"({silo_count} silo(s) do MySQL)."
8988
)
9089
log.info("✅ /reload concluído — %s", status)
9190

@@ -106,7 +105,11 @@ async def _reload_stream() -> AsyncGenerator[str, None]:
106105
)
107106

108107
return StreamingResponse(
109-
services.chat_provider.stream_response(built.messages, trace=built.trace),
108+
services.chat_provider.stream_response(
109+
built.messages,
110+
trace=built.trace,
111+
decision=built.decision,
112+
),
110113
media_type="text/event-stream",
111114
headers={
112115
"Cache-Control": "no-cache",

app/factory.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ def create_app(services: AppServices) -> FastAPI:
2424
async def lifespan(app: FastAPI):
2525
log.info("🚀 ACL iniciado e pronto para receber requisições.")
2626
yield
27-
services.observer.stop()
28-
services.observer.join()
29-
log.info("🛑 Watchdog encerrado. Servidor finalizado.")
27+
log.info("🛑 Servidor finalizado.")
3028

3129
app = FastAPI(title="ACL — Agente de Contexto Local", lifespan=lifespan)
3230
app.state.services = services
@@ -41,4 +39,5 @@ async def lifespan(app: FastAPI):
4139
app.mount("/src", StaticFiles(directory=str(src_dir)), name="src")
4240

4341
app.include_router(router)
42+
4443
return app

app/state.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
from dataclasses import dataclass
66

7-
from watchdog.observers import Observer
8-
97
from engine.chat_provider import ChatProvider
108
from engine.context import ContextManager
119
from engine.pinned_store import PinnedSessionStore
@@ -17,5 +15,4 @@ class AppServices:
1715
search_engine: SearchEngine
1816
context_manager: ContextManager
1917
chat_provider: ChatProvider
20-
observer: Observer
2118
pinned_store: PinnedSessionStore

0 commit comments

Comments
 (0)