File size: 6,737 Bytes
ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f ec3b193 9db128f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# app.py — Isaac (CPU, Gemma 2 2B GGUF via llama-cpp-python)
import os
import json
import gradio as gr
from typing import List, Dict, Any
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from llama_cpp import Llama
# -------- Config --------
MODEL_PATH = os.environ.get("MODEL_PATH", "gemma-2-2b-it-Q4_K_M.gguf") # aponte para o GGUF local
N_CTX = int(os.environ.get("N_CTX", "4096"))
N_THREADS = int(os.environ.get("N_THREADS", "8"))
N_BATCH = int(os.environ.get("N_BATCH", "128"))
MAX_NEW_TOKENS = int(os.environ.get("MAX_NEW_TOKENS", "512"))
TEMPERATURE = float(os.environ.get("TEMPERATURE", "0.7"))
TOP_P = float(os.environ.get("TOP_P", "0.9"))
EPC_MD_PATH = os.environ.get("EPC_MD_PATH", "epct0-3.md")
SYSTEM_RULES = (
"Regras: 1) Sempre pergunte objetivo e contexto do pedido antes de responder; "
"2) Após entender o 'porquê/para quê', aplique um checklist filosófico (controle, impermanência, obstáculos, julgamentos, prioridades, papéis) e só então responda; "
"3) Explique a relação entre as diretrizes escolhidas e o plano; 4) Seja claro e conciso."
)
# -------- Carregar modelo (CPU) --------
llm = Llama(
model_path=MODEL_PATH,
chat_format="gemma", # usa o formato nativo de chat do Gemma
n_ctx=N_CTX,
n_threads=N_THREADS,
n_batch=N_BATCH,
verbose=False,
)
# -------- Parser + RAG leve --------
def parse_epct_table(md_path: str) -> List[Dict[str, Any]]:
rows = []
with open(md_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line.startswith("|") and not set(line).issubset(set("|:- ")):
parts = [c.strip() for c in line.strip("|").split("|")]
if len(parts) >= 4 and parts[0] != "Id":
rows.append({
"id": parts[0],
"resumo": parts[1],
"porques": parts[2],
"tags": parts[3]
})
return rows
DOCS = parse_epct_table(EPC_MD_PATH)
DOC_TEXTS = [f"{d['resumo']} {d['porques']} {d['tags']}" for d in DOCS]
VECTORIZER = TfidfVectorizer(stop_words="portuguese", max_features=4096)
DOC_MATRIX = VECTORIZER.fit_transform(DOC_TEXTS)
def retrieve_guidelines(query: str, k: int = 4) -> List[Dict[str, Any]]:
if not query.strip():
return []
qv = VECTORIZER.transform([query])
sims = cosine_similarity(qv, DOC_MATRIX)[0]
idxs = sims.argsort()[::-1][:k]
return [DOCS[i] for i in idxs]
def format_guidelines(items: List[Dict[str, Any]]) -> str:
if not items:
return "- (nenhuma diretriz selecionada)"
return "\n".join([f"- [{d['id']}] {d['resumo']} | Porquês: {d['porques']} | Tags: {d['tags']}" for d in items])
# -------- Mensagens e geração --------
def make_messages(initial_request: str, intent: str, selected_items: List[Dict[str, Any]]) -> List[Dict[str, str]]:
guidelines_block = format_guidelines(selected_items)
user_compound = (
f"Pedido original:\n{initial_request}\n\n"
f"Objetivo/porquê declarado:\n{intent}\n\n"
f"Diretrizes selecionadas (do corpus):\n{guidelines_block}\n\n"
"Tarefa:\n1) Produza uma 'Preparação' breve (itens do checklist aplicados ao caso).\n"
"2) Em seguida, produza 'Resposta' com passos claros e proporcionais ao objetivo.\n"
"3) Mostre a conexão entre as diretrizes e a recomendação.\n"
)
return [
{"role": "system", "content": SYSTEM_RULES},
{"role": "user", "content": user_compound},
]
def generate(messages: List[Dict[str, str]]) -> str:
out = llm.create_chat_completion(
messages=messages,
temperature=TEMPERATURE,
top_p=TOP_P,
max_tokens=MAX_NEW_TOKENS,
)
return out["choices"][0]["message"]["content"].strip()
# -------- Máquina de estados --------
def chat_turn(user_input: str, state: Dict[str, Any]):
if state is None or "stage" not in state:
state = {"stage": "await_intent", "initial_request": "", "intent": "", "items": []}
if state["stage"] == "await_intent":
state["initial_request"] = user_input
reply = ("Antes de responder, poderia explicar seu objetivo e contexto? "
"O que você quer alcançar e por quê isso é importante agora?")
state["stage"] = "collect_intent"
return reply, state
if state["stage"] == "collect_intent":
state["intent"] = user_input
items = retrieve_guidelines(user_input, k=4)
state["items"] = items
scaffold = [
"Preparação (rascunho):",
"- Controle: separe o que depende de você e foque no controlável.",
"- Perspectiva: identifique julgamentos que amplificam o problema.",
"- Obstáculos: antecipe contratempos realistas e pré-compromissos.",
"- Prioridades/Papéis: alinhe ação ao essencial e ao seu papel atual.",
"",
"Diretrizes candidatas:",
format_guidelines(items),
"",
"Se estiver de acordo, diga 'prossiga' para receber a resposta estruturada."
]
state["stage"] = "ready"
return "\n".join(scaffold), state
if state["stage"] == "ready":
if user_input.strip().lower() not in {"prossiga", "ok", "pode seguir", "pode prosseguir"}:
return "Confirme com 'prossiga' para gerar a resposta final baseada no preparo acima.", state
messages = make_messages(state["initial_request"], state["intent"], state["items"])
text = generate(messages)
state = {"stage": "await_intent", "initial_request": "", "intent": "", "items": []}
return text, state
state = {"stage": "await_intent", "initial_request": "", "intent": "", "items": []}
return "Vamos recomeçar: descreva seu pedido inicial.", state
# -------- UI --------
with gr.Blocks(title="Isaac — CPU (Gemma 2 2B GGUF)") as demo:
gr.Markdown("# Isaac — CPU (Gemma 2 2B)\nFluxo: Intenção → Preparação → Resposta.")
state = gr.State({"stage": "await_intent", "initial_request": "", "intent": "", "items": []})
chat = gr.Chatbot(height=480)
inp = gr.Textbox(label="Mensagem", placeholder="Descreva seu pedido...")
send = gr.Button("Enviar")
def ui_handle(user_msg, s):
reply, s2 = chat_turn(user_msg or "", s)
return chat + [[user_msg, reply]], s2, ""
send.click(ui_handle, [inp, state], [chat, state, inp])
inp.submit(ui_handle, [inp, state], [chat, state, inp])
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
|