# chave liberada para uso até o final do semestre, com algumas restrições
import os
from dotenv import load_dotenv
# carregando a biblioteca da openai
from openai import OpenAI
from pydantic import BaseModel
import pandas as pd
# Carrega o arquivo .env
load_dotenv()
= OpenAI() client
11 Classificação de documentos usando OpenAI GPT-4o-mini
Neste notebook, vamos usar o modelo de linguagem GPT-4o-mini da OpenAI para classificar documentos em categorias. O GPT-4o-mini é um modelo de linguagem de última geração que pode ser usado para uma variedade de tarefas de processamento de linguagem natural, incluindo classificação de texto.
Começamos carregando o modelo GPT-4o-mini pré-treinado da OpenAI usando a biblioteca openai
e, em seguida, usamos o modelo para classificar documentos em categorias. Para isso, fornecemos ao modelo um documento de entrada e ele nos dá uma lista de categorias possíveis para o documento.
Para acessar o modelo GPT-4o-mini, você precisará de uma chave de API da OpenAI. Esse notebook já disponibiliza uma chave de API para você usar, mas você também pode obter sua própria chave de API se desejar. Lembre-se que liberar a chave de API é uma prática insegura, então não estamos fazendo isso pelo fluxo ideal.
# testando se a chave está funcionando
= client.chat.completions.create(
completion ="gpt-4o-mini",
model=[
messages"role": "system", "content": "You are a helpful assistant."},
{
{"role": "user",
"content": "Escreva um cordel curto sobre a disciplina de Ciência de Dados no Direito."
}
]
)
print(completion.choices[0].message.content)
**Cordel da Ciência de Dados no Direito**
No mundo virtual em que vivo,
A ciência desponta e reluz,
No Direito, uma nova prática,
Transforma a lei, traz nova luz.
Dados dançam, em trovas sutil,
E o advogado, astuto e audaz,
Com algoritmos faz o perfil,
Decifra o futuro, implacável e eficaz.
Na Justiça, as cifras ensinam,
Padrões e tendências a explorar,
Com análise e lógica divina,
Sentenças justas a se firmar.
Habeas data, é hora de ver,
O sigilo e os dados a fluir,
Com ética à frente a guiar,
O Direito vem só a evoluir.
Em estações de aprendizado,
Estudamos o que há de novo,
Na era digital, tão necessitado,
A Ciência de Dados é o nosso povo.
Por isso, advogados, atenção,
A tecnologia não vai parar,
Com dados em mãos, em colaboração,
A Justiça se faz mais a brilhar.
11.1 Controlando a temperatura
O modelo GPT-4o-mini tem um parâmetro chamado “temperatura” que controla a aleatoriedade das previsões do modelo. Quanto maior a temperatura, mais aleatórias as previsões do modelo serão. Vamos ver um exemplo com temperatura igual a 1, rodando duas vezes o mesmo pedido.
= client.chat.completions.create(
teste1 ="gpt-4o-mini",
model=[
messages"role": "system", "content": "You are a helpful assistant."},
{
{"role": "user",
"content": "Qual o tesouro do jurista (ou seja, o diferencial quando comparado a um profissional da estatística, por exemplo) em pesquisas que envolvem ciência de dados no direito? Responda em uma frase."
}
],=1.0
temperature
)
print(teste1.choices[0].message.content)
O tesouro do jurista em pesquisas que envolvem ciência de dados no direito reside na sua expertise em interpretar e aplicar normas jurídicas, contextualizando os resultados estatísticos dentro do arcabouço legal e ético pertinente.
Agora, rodamos novamente o mesmo pedido. Veja que o resultado é diferente!
= client.chat.completions.create(
teste2 ="gpt-4o-mini",
model=[
messages"role": "system", "content": "You are a helpful assistant."},
{
{"role": "user",
"content": "Qual o tesouro do jurista (ou seja, o diferencial quando comparado a um profissional da estatística, por exemplo) em pesquisas que envolvem ciência de dados no direito? Responda em uma frase."
}
],=1.0
temperature
)
print(teste2.choices[0].message.content)
O tesouro do jurista em pesquisas que envolvem ciência de dados no direito reside na sua capacidade de interpretar normas legais, contextos jurídicos e implicações éticas, além de aplicar conhecimentos jurídicos para analisar e contextualizar dados de forma crítica e fundamentada.
Agora, vamos rodar o mesmo pedido, mas com temperatura igual a 0, duas vezes. O que você acha que vai acontecer?
= client.chat.completions.create(
teste3 ="gpt-4o-mini",
model=[
messages"role": "system", "content": "You are a helpful assistant."},
{
{"role": "user",
"content": "Qual o tesouro do jurista (ou seja, o diferencial quando comparado a um profissional da estatística, por exemplo) em pesquisas que envolvem ciência de dados no direito? Responda em uma frase."
}
],=0.0
temperature
)
print(teste3.choices[0].message.content)
O tesouro do jurista em pesquisas que envolvem ciência de dados no direito reside na sua capacidade de interpretar e contextualizar dados dentro do arcabouço legal e ético, garantindo que as análises respeitem os princípios jurídicos e os direitos fundamentais.
= client.chat.completions.create(
teste4 ="gpt-4o-mini",
model=[
messages"role": "system", "content": "You are a helpful assistant."},
{
{"role": "user",
"content": "Qual o tesouro do jurista (ou seja, o diferencial quando comparado a um profissional da estatística, por exemplo) em pesquisas que envolvem ciência de dados no direito? Responda em uma frase."
}
],=0.0
temperature
)
print(teste4.choices[0].message.content)
O tesouro do jurista em pesquisas que envolvem ciência de dados no direito reside na sua capacidade de interpretar e contextualizar normas jurídicas, garantindo que a análise estatística seja aplicada de forma ética e relevante ao sistema legal.
11.2 Desenvolvendo prompts para anotação de documentos jurídicos
A anotação automática de documentos jurídicos utilizando modelos de linguagem aumenta significativamente a eficiência na anotação de documentos. No entanto, a qualidade dos resultados pode variar dependendo da formulação do prompt utilizado.
Trata-se de uma área em desenvolvimento, então ainda não temos uma resposta final sobre como formular os prompts. No entanto, algumas dicas podem ser úteis:
Escreva instruções claras e objetivas: O prompt deve ser o mais claro e específico possível. Experimente verificar com você ou outra pessoa se as instruções são compreensíveis e diretas.
Deixe claro quem é o assistente e qual seu papel: O modelo de linguagem é um assistente, e é importante deixar claro qual é o papel dele na tarefa. Por exemplo, “você é um assistente jurídico que realiza a análise de petições iniciais em processos cíveis, resumindo as informações mais importantes para posterior avaliação de profissionais da advocacia”.
Descreva o que o modelo deve esperar de entrada: Sempre que necessário, forneça contexto relevante ao modelo. Isso pode incluir a descrição do tipo de documento, o objetivo da anotação e as características específicas do caso em questão. Por exemplo, uma petição inicial de um processo é diferente de um tweet.
Especifique o formato de saída: Indicar o formato exato da resposta esperada é uma boa prática. Geralmente, pediremos para que a resposta seja em um JSON, e temos um parâmetro para forçar esse comportamento no modelo.
Inclua exemplos: Fornecer exemplos no prompt ajuda o modelo a entender o formato e o conteúdo esperado na resposta. O nome técnico disso é “one shot” ou “few shot” learning.
Teste com alguns casos: Sempre verifique se o prompt está funcionando com alguns casos antes de rodar para uma amostra grande, e faça ajustes caso necessário.
Pedir um resumo: Para tarefas complexas, pode ser útil pedir para o modelo de linguagem resumir a resposta em um parágrafo ou algumas frases, antes de realizar uma anotação em categorias. Isso pode aumentar a qualidade da resposta final.
Obs: você pode usar o ChatGPT para ajudar na criação de prompts. Isso economiza tempo e cria prompts eficazes.
= """
prompt_ruim Classifique o documento abaixo nessas variáveis:
- faz parte do escopo?
- quais drogas estão envolvidas?
- quantidade de maconha em gramas
- quantidade de cocaína em gramas
- quantidade de crack em gramas
- sexo da pessoa acusada
- reincidente
- decisão
- tipo de pena
- tempo da pena
"""
= """
prompt_otimizado Você é um assistente de inteligência artificial que auxilia na anotação de sentenças judiciais do Tribunal de Justiça de São Paulo em processos envolvendo tráfico de drogas.
Você receberá o texto da sentença e deverá retornar um arquivo JSON, seguindo as regras abaixo:
- O processo faz parte do escopo da pesquisa? O caso deve: Ser um caso relacionado a tráfico de drogas; Envolver porte de maconha, crack ou cocaína; Envolver apenas uma pessoa acusada.
- Quantidade de maconha em gramas: Preencha apenas os números. Coloque 0 se o caso não envolve essa droga. Use "," como separador decimal. Se a decisão não menciona a quantidade em gramas, faça a conversão, seguindo a regra: 1 porção = 2 gramas.
- Quantidade de cocaína em gramas: Preencha apenas os números. Coloque 0 se o caso não envolve essa droga. Use "," como separador decimal. Se a decisão não menciona a quantidade em gramas, faça a conversão, seguindo a regra: 1 porção ou pino = 0,5 grama.
- Quantidade de crack em gramas: Preencha apenas os números. Coloque 0 se o caso não envolve essa droga. Use "," como separador decimal. Se a decisão não menciona a quantidade em gramas, faça a conversão, seguindo a regra: 1 porção = 0,1 grama.
- Decisão: pode ser procedente (condenação), improcedente / punibilidade extinta, ou parcialmente procedente / advertência.
- Tipo de pena: pode ser fechado, semiaberto ou aberto.
- Tempo da pena (em meses): Preencha apenas os números. Converta o tempo em meses. Por exemplo, 2 anos e 7 meses = 31 meses.
Retorne um arquivo JSON com as seguintes informações:
{
"escopo": "sim/não",
"maconha": "sim/não",
"cocaina": "sim/não",
"crack": "sim/não",
"qtd_maconha": 0,
"qtd_cocaina": 0,
"qtd_crack": 0,
"sexo": "masculino/feminino/não informado",
"reincidente": "sim/não/não informado",
"decisao": "procedente/improcedente/parcialmente procedente",
"tipo_pena": "fechado/semiaberto/aberto",
"tempo": 0
}
"""
11.2.1 Aplicando prompt em um caso
= pd.read_csv("https://github.com/jtrecenti/main-cdad2/releases/download/data/drogas.csv")
drogas
drogas.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 991 entries, 0 to 990
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 file 991 non-null object
1 processo 991 non-null object
2 pagina 991 non-null int64
3 hora_coleta 0 non-null float64
4 duplicado 991 non-null bool
5 classe 990 non-null object
6 assunto 991 non-null object
7 magistrado 991 non-null object
8 comarca 991 non-null object
9 foro 991 non-null object
10 vara 991 non-null object
11 disponibilizacao 991 non-null object
12 julgado 991 non-null object
13 cd_doc 991 non-null object
dtypes: bool(1), float64(1), int64(1), object(11)
memory usage: 101.7+ KB
= '15005860220238260569'
processo
= drogas[drogas['processo']==processo]['julgado'].iloc[0]
julgado
print(julgado[:2000])
TRIBUNAL DE JUSTIÇA DO ESTADO DE SÃO PAULO COMARCA de Boituva Foro de Boituva 1ª Vara Rua Manoel dos Santos Freire, 161, Centro - CEP 18550-000, Fone: (15) 3263-2120, Boituva-SP - E-mail: boituva1@tjsp.jus.br 1500586-02.2023.8.26.0569 - lauda SENTENÇA Processo nº: 1500586-02.2023.8.26.0569 Classe - Assunto Ação Penal - Procedimento Ordinário - Tráfico de Drogas e Condutas Afins Autor: Justiça Pública Réu: ROSANA LEMES DE SOUZA Juiz(a) de Direito: Dr(a). Liliana Regina de Araujo Heidorn Abdala Vistos. ROSANA LEMES DE SOUZA foi denunciada como incursa no artigo 33, caput, c/c art. 40, inciso III, ambos da Lei 11.343/06, porque no dia 01 de julho de 2023, aproximadamente às 08h00min, na Estrada Tatuí/Iperó, Área Rural, Estabelecimento Prisional, na cidade de Iperó e comarca de Boituva, ROSANA LEMES DE SOUZA, qualificada a fls. 14 e indiciada a fls. 62, adquiriu e trazia consigo, para fornecer e entregar ao consumo de terceiros, 98,13g (noventa e oito gramas e treze centigramas) líquidos de maconha, acondicionados em 1 (uma) porção, conforme laudo pericial de fls. 31/33 e 106/108, substância entorpecente que determina dependência física e psíquica, sem autorização legal e regulamentar para tanto. Segundo o apurado, a denunciada estava no estabelecimento prisional de Iperó para realização de visitas, quando foi submetida à revista. Ao verificar o que tinha no inteiro de um pacote que a denunciada trazia consigo, foi localizada 1 (uma) porção de maconha no interior de 1 (um) absorvente. As circunstâncias em que se deram o flagrante, evidenciam a prática do tráfico de entorpecentes. Presa em flagrante, a ré foi beneficiada com a liberdade provisória mediante o cumprimento das medidas cautelares previstas no artigo 319, do C.P.P. (fl.89/91). Regularmente notificada (fl. 158), a ré apresentou defesa preliminar (fls.141/144). A denúncia foi recebida (fl. 150). Regularmente citada, foi realizada a instrução processual, com oitiva de duas testemunhas em comum e o interrogatório
print(julgado[-2000:])
prisional, nos termos do artigo 40, inciso III, da Lei 11.343/2006, fixando-a definitivamente em 01 (um) ano, 11 (onze) meses e 10 (dez) dias, e 193 (cento e noventa e três) dias-multa. Vislumbro que a acusada faz jus ao benefício previsto nos artigos 43 e nos seguintes do Código Penal, substituo a pena privativa de liberdade em prestação de serviço à comunidade e 10 dias multa, nos termos do artigo 44, § 2º, do Código Penal, que será executada perante o Juízo das Execuções Criminais. Em caso de descumprimento, fixo o regime aberto para o início do cumprimento da pena. Pelo exposto JULGO PROCEDENTE a presente pretensão punitiva, e o faço para CONDENAR a ré ROSANA LEMES DE SOUZA à pena de 01 (um) ano, 11 (onze) meses e 10 (dez) dias, e 193 (cento e noventa e três) dias-multa, substituída por prestação de serviço à comunidade, na forma acima disposta, por infração ao artigo 33, caput, c/c art. 40, inciso III, ambos da Lei 11.343/06. Defiro recurso em liberdade. São parcas as informações acerca da situação financeira da ré, por isso arbitro o valor do dia-multa no seu mínimo legal, calculado sobre o salário mínimo da data dos fatos e atualizado até o dia do efetivo pagamento. Nos termos do artigo 32, parágrafos 1º e 2º, da Lei 11.343/06, AUTORIZO a incineração dos entorpecentes apreendidos, se ainda não providenciado. Condeno a acusada ao pagamento das custas equivalentes a 100 UFESP's, nos termos do artigo 4º, inciso III, item 5, § 9º, alínea “a” da Lei nº 11.608, de 29 de dezembro de 2003, obrigação que fica suspensa em atenção ao disposto nos artigos 11 e 12 da Lei nº 1.060/50, posto que beneficiários da justiça gratuita, pois defendidos por força do convênio Defensoria Pública/OAB. Arbitro os honorários advocatícios no valor máximo previsto em convênio. Expeça-se a devida certidão. Publicada em audiência. Saem todos intimados. Cumpra-se. Boituva, 19 de agosto de 2024. DOCUMENTO ASSINADO DIGITALMENTE NOS TERMOS DA LEI 11.419/2006, CONFORME IMPRESSÃO À MARGEM DIREITA
def classificar_decisao (prompt, julgado):
= client.chat.completions.create(
completion ="gpt-4o-mini",
model=[
messages"role": "system", "content": prompt},
{"role": "user", "content": julgado}
{
],=0.0
temperature
)return completion.choices[0].message.content
= classificar_decisao(prompt_ruim, julgado)
res_ruim
print(res_ruim)
- faz parte do escopo? Sim
- quais drogas estão envolvidas? Maconha
- quantidade de maconha em gramas: 98,13g
- quantidade de cocaína em gramas: 0g
- quantidade de crack em gramas: 0g
- sexo da pessoa acusada: Feminino
- reincidente: Não
- decisão: Condenação
- tipo de pena: Prestação de serviço à comunidade
- tempo da pena: 1 ano, 11 meses e 10 dias
= classificar_decisao(prompt_otimizado, julgado)
res_otimizado
print(res_otimizado)
```json
{
"escopo": "sim",
"maconha": "sim",
"cocaina": "não",
"crack": "não",
"qtd_maconha": 98,
"qtd_cocaina": 0,
"qtd_crack": 0,
"sexo": "feminino",
"reincidente": "não",
"decisao": "procedente",
"tipo_pena": "aberto",
"tempo": 23
}
```
11.3 Exercício:
Teste o prompt otimizado para novos processos e compare com as classificações manuais
= '<processo aqui>'
processo = drogas[drogas['processo']==processo]['julgado'].iloc[0]
julgado
classificar_decisao(prompt_otimizado, julgado)
11.4 Aula 05 até aqui
11.5 Aula 06
Dá para melhorar um pouco mais ainda. Vamos forçar o resultado a um formato específico, que podemos transformar em um DataFrame python diretamente.
class JsonSchema(BaseModel):
str
escopo: str
maconha: str
cocaina: str
crack: float
qtd_maconha: float
qtd_cocaina: float
qtd_crack: str
sexo: str
reincidente: str
decisao: str
tipo_pena: float
tempo:
def classificar_decisao_df (prompt, julgado):
= client.beta.chat.completions.parse(
completion ="gpt-4o-mini",
model=[
messages"role": "system", "content": prompt},
{"role": "user", "content": julgado}
{
],=0.0,
temperature= JsonSchema
response_format
)return completion.choices[0].message.parsed.dict()
= classificar_decisao_df(prompt_otimizado, julgado)
res_otimizado_df
print(res_otimizado_df)
{'escopo': 'sim', 'maconha': 'sim', 'cocaina': 'não', 'crack': 'não', 'qtd_maconha': 98.0, 'qtd_cocaina': 0.0, 'qtd_crack': 0.0, 'sexo': 'feminino', 'reincidente': 'não', 'decisao': 'procedente', 'tipo_pena': 'aberto', 'tempo': 22.0}
É possível converter para um DataFrame diretamente, veja:
pd.DataFrame([res_otimizado_df])
escopo | maconha | cocaina | crack | qtd_maconha | qtd_cocaina | qtd_crack | sexo | reincidente | decisao | tipo_pena | tempo | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | sim | sim | não | não | 98.0 | 0.0 | 0.0 | feminino | não | procedente | aberto | 22.0 |
Agora, podemos fazer isso para vários casos, com um laço:
= [
casos '00029796520178260542',
'15289122520238260228',
'15071822120248260228',
'15003527020248260347',
'15027831120238260542',
'15010156220248260559'
]
= []
resultados
for caso in casos:
print(caso)
= drogas[drogas['processo'] == caso]['julgado'].iloc[0]
txt_caso = classificar_decisao_df(prompt_otimizado, txt_caso)
res
resultados.append(res)
= pd.DataFrame(resultados) df_resultados
00029796520178260542
15289122520238260228
15071822120248260228
15003527020248260347
15027831120238260542
15010156220248260559
df_resultados
escopo | maconha | cocaina | crack | qtd_maconha | qtd_cocaina | qtd_crack | sexo | reincidente | decisao | tipo_pena | tempo | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | sim | sim | sim | não | 63.0 | 7.0 | 0.0 | masculino | sim | parcialmente procedente | fechado | 84.0 |
1 | sim | sim | sim | não | 3.0 | 11.0 | 0.0 | masculino | não | procedente | fechado | 32.0 |
2 | sim | sim | sim | não | 51.4 | 18.0 | 0.0 | masculino | não | procedente | aberto | 20.0 |
3 | sim | não | sim | não | 0.0 | 13.0 | 0.0 | masculino | não | procedente | aberto | 8.0 |
4 | sim | sim | sim | sim | 6.0 | 9.0 | 4.0 | masculino | não | procedente | aberto | 20.0 |
5 | sim | sim | sim | não | 37.0 | 12.0 | 0.0 | masculino | não | parcialmente procedente | aberto | 32.0 |
Finalmente, podemos salvar o resultado em um arquivo CSV:
"resultados.csv", index = False) df_resultados.to_csv(