File size: 5,179 Bytes
562df32
 
 
 
 
 
 
 
 
8764c67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import spaces


@spaces.GPU
def _zerogpu_noop():
    # ZeroGPU exige uma função decorada; o app roda todo em CPU.
    return None


"""Explorador estático do piloto de injeção de prompt (resultados agregados).

Os números vêm de dados/*.csv, copiados de results/SUMMARY.md do pacote
llm-injection-pilot, lidos como texto. Nada é recalculado aqui. Só mudam o
separador decimal (ponto para vírgula) e o formato do intervalo.
"""
from pathlib import Path

import gradio as gr
import pandas as pd

DADOS = Path(__file__).parent / "dados"


def _ler(nome):
    return pd.read_csv(DADOS / nome, dtype=str, keep_default_na=False)


def _virgula(s):
    return str(s).replace(".", ",")


def _ic(s):
    # "[70.9, 92.9]" vira "70,9 a 92,9"
    return _virgula(s.strip("[]").replace(", ", " a "))


def tabela_principal(arquivo, n_rotulo):
    d = _ler(arquivo)
    return pd.DataFrame(
        {
            "Modelo": d["cfg"],
            "Condição do prompt de sistema": d["condition"].map(
                {"baseline": "padrão", "hardened": "reforçado"}
            ),
            "Sucessos": d["k"] + " de " + d["n"],
            "ASR (%)": d["asr_%"].map(_virgula),
            "IC 95% Wilson (%)": d["ic95"].map(_ic),
        }
    ).rename(columns={"Sucessos": n_rotulo})


def tabela_t0():
    return tabela_principal("ic_t0.csv", "Sucessos (40 ataques)")


def tabela_t07():
    return tabela_principal("ic_t07.csv", "Sucessos (120 gerações)")


def tabela_criterios(temp="0"):
    d = _ler("asr_por_categoria.csv")
    d = d[d["temp"] == temp]
    return pd.DataFrame(
        {
            "Modelo": d["cfg"],
            "Condição": d["condition"].map({"baseline": "padrão", "hardened": "reforçado"}),
            "Categoria": d["category"].map({"direct": "direto", "indirect": "indireto"}),
            "n": d["n"],
            "Critério principal, casamento normalizado (%)": d["asr_principal"].map(_virgula),
            "String exata (%)": d["asr_exact_str"].map(_virgula),
        }
    ).reset_index(drop=True)


def filtrar_criterios(temp, modelo):
    t = tabela_criterios("0" if temp == "T=0" else "0.7")
    if modelo != "Todos":
        t = t[t["Modelo"] == modelo]
    return t.reset_index(drop=True)


MODELOS = ["Todos"] + sorted(_ler("ic_t0.csv")["cfg"].unique().tolist())

TEXTO_TOPO = """
# Piloto de injeção de prompt em dois modelos pequenos (CPU): resultados agregados

Este Space mostra só números agregados de um piloto pequeno e exploratório. Os ataques
e os textos dos prompts de ataque não são exibidos aqui.
"""

TEXTO_T0 = """
## Número principal: temperatura 0 (decodificação gulosa)

ASR é a taxa de ataque bem-sucedido, pelo critério principal (casamento normalizado),
sobre os 40 ataques por modelo e condição (16 diretos e 24 indiretos). Os dois modelos
não se comparam entre si.
"""

TEXTO_T07 = """
## Confirmação: temperatura 0,7 (3 seeds)

São 120 gerações por modelo e condição, que correspondem aos mesmos 40 ataques repetidos
em 3 seeds. As gerações não são independentes, então os intervalos abaixo ficam otimistas.
Use esta tabela como confirmação do T=0, não como número principal.
"""

TEXTO_CRITERIO = """
## Critério principal ao lado da string exata

O critério de sucesso foi corrigido depois de eu ver os resultados. A coluna de string
exata é o critério original e fica ao lado para a diferença ficar visível. Valores em
porcentagem, por categoria, como estão em results/SUMMARY.md.
"""

TEXTO_RESSALVAS = """
## Ressalvas

- Piloto pequeno e exploratório: 40 ataques por modelo e condição.
- Os ataques são próprios, escritos para este piloto. Não são um benchmark publicado.
- O critério de sucesso foi corrigido depois de ver os resultados. A string exata original
  está na tabela de critérios.
- Os dois modelos não se comparam entre si: tamanho e quantização diferentes (1,7B em Q8_0
  contra 3B em Q4_K_M) e amostra pequena.
- Reforçar o prompt de sistema não mudou o total em T=0 nos dois modelos. Com esta amostra
  não há diferença detectável, e isso não quer dizer que o reforço não funcione.
- Não é réplica do INFUSE e não se compara com os números dele: benchmark, juiz e modelos
  são outros.
- Execução em CPU, modo sem raciocínio (/no_think), 256 tokens no máximo por resposta.
- Nenhuma conclusão sobre a segurança de modelo algum.
"""

with gr.Blocks(title="Piloto de injeção de prompt: explorador") as demo:
    gr.Markdown(TEXTO_TOPO)
    gr.Markdown(TEXTO_T0)
    gr.Dataframe(value=tabela_t0(), interactive=False, wrap=True)
    gr.Markdown(TEXTO_T07)
    gr.Dataframe(value=tabela_t07(), interactive=False, wrap=True)
    gr.Markdown(TEXTO_CRITERIO)
    with gr.Row():
        sel_t = gr.Radio(["T=0", "T=0,7"], value="T=0", label="Temperatura")
        sel_m = gr.Dropdown(MODELOS, value="Todos", label="Modelo")
    saida = gr.Dataframe(value=filtrar_criterios("T=0", "Todos"), interactive=False, wrap=True)
    sel_t.change(filtrar_criterios, [sel_t, sel_m], saida)
    sel_m.change(filtrar_criterios, [sel_t, sel_m], saida)
    gr.Markdown(TEXTO_RESSALVAS)

if __name__ == "__main__":
    demo.launch()