Tenho algumas centenas de horas de aulas gravadas ensinando algoritmos e estruturas de dados, padrões de projeto, Domain-driven Design e Reputação e Marketing Pessoal. Também tenho algumas centenas de horas de sessões de mentoria em arquitetura de software.
Nas minhas masterclasses, sempre vou muito além do material preparado.
Todos esses vídeos possuem ideias incríveis, mas que não são fáceis de acessar, sem que, obviamente, alguém assista todos os vídeos novamente, estruturando conteúdo de maneira interessante.
Recentemente, a Google lançou uma ferramenta incrível chamada NotebookLM. Ela permite “conversar com documentos”. Mas, até então, estes documentos precisam ser textos.
Então, o caminho tem sido gerar versões em texto dos meus vídeos. Mas, como? A resposta é utilizar tecnologias de IA como a Whisper da OpenAI.
Para gerenciar as dependências de forma eficaz, recomendo criar um ambiente virtual usando conda. Primeiro, instale a última versão do Python e configure o ambiente:
conda create -n transcricao_env python=3.10
conda activate transcricao_env
Com o ambiente configurado, instale os pacotes necessários:
!pip install git+https://github.com/openai/whisper.git
!pip install ffmpeg-python
Em um notebook (ou script Python), começo importando os pacotes necessários e carregando o modelo do Whisper. A primeira execução consome um pouco mais de tempo:
import whisper
import ffmpeg
import os
model = whisper.load_model("base")
Em seguida, extraímos o áudio do vídeo. Isso é feito para facilitar a transcrição, uma vez que o Whisper trabalha diretamente com arquivos de áudio:
video_path = r"/Users/elemarjr/Downloads/mentoria_arquitetura_1.mp4"
audio_path = r"/Users/elemarjr/Downloads/mentoria_arquitetura_1.m4a"
ffmpeg.input(video_path).output(audio_path).run(overwrite_output=True)
Finalmente, obtemos a transcrição e a salvamos em um arquivo de texto:
# Obter a transcrição
output_path = r"/Users/elemarjr/Downloads/mentoria_arquitetura_1.txt"
result = model.transcribe(audio_path, language='pt')
# Salvar a transcrição em um arquivo de texto
with open(output_path, "w", encoding='utf-8') as f:
f.write(result['text'])
Pronto. Tenho um arquivo texto com todo o conteúdo da aula que, agora, pode ser usado como source no NotebookLM.
Para facilitar o uso do processo descrito acima, criei um utilitário de linha de comando em Python. Este script permite passar o caminho de um vídeo como argumento e obtém a transcrição do vídeo. Aqui está o código:
import whisper
import ffmpeg
import os
import sys
def transcribe_video(video_path):
# Define o caminho do arquivo de áudio e da transcrição
audio_path = os.path.splitext(video_path)[0] + '.m4a'
output_path = os.path.splitext(video_path)[0] + '.txt'
# Extrai o áudio do vídeo
ffmpeg.input(video_path).output(audio_path).run(overwrite_output=True)
# Carrega o modelo Whisper
model = whisper.load_model("base")
# Gera a transcrição
result = model.transcribe(audio_path, language='pt')
# Salva a transcrição em um arquivo de texto
with open(output_path, "w", encoding='utf-8') as f:
f.write(result['text'])
print(f"Transcrição salva em {output_path}")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Uso: python transcribe.py <caminho_para_o_video>")
sys.exit(1)
video_path = sys.argv[1]
transcribe_video(video_path)
Para usar este utilitário, salve o código acima em um arquivo chamado transcribe.py
e execute-o na linha de comando passando o caminho para o vídeo:
python transcribe.py /caminho/para/o/video.mp4
Pronto! Agora você tem um utilitário de linha de comando que transcreve vídeos automaticamente.
Durante o processo, alguns erros podem ocorrer. Aqui estão alguns comuns e como resolvê-los:
A transcrição de áudio e vídeo é extremamente útil para diversas aplicações, como:
Com essa abordagem, posso oferecer material de qualidade para quem estuda comigo, transformando horas de vídeo em documentos de texto utilizáveis, prontos para serem analisados e manipulados em ferramentas como o NotebookLM.
Se você deseja estudar comigo e acessar material de alta qualidade e contextualizado, entre em contato. Estou sempre pronto para compartilhar conhecimento e ajudar no seu desenvolvimento profissional.
WordPress é uma das plataformas mais populares do mundo para construção de Websites e blogs. A Ontologia da EximiaCo foi construída com WordPress.
Os consultores da EximiaCo colaboram com a produção de conteúdo que está devidamente organizado em classes.
O WordPress disponibiliza uma API RESTish que permite obtenção fácil de informações a respeito de um site.
import pandas as pd
import requests
from cachetools import cached, TTLCache
from datetime import datetime
class WordPress:
def __init__(self, url):
self.url = url
@cached(cache=TTLCache(maxsize=100, ttl=300))
def get_post_types(self):
endpoint = '/wp-json/wp/v2/types'
api_url = f'{self.url}{endpoint}'
response = requests.get(api_url)
post_types = response.json()
if response.status_code == 200:
data = [{'key': key, 'name': value['name']} for key, value in post_types.items()]
return pd.DataFrame(data)
else:
return pd.DataFrame()
@cached(cache=TTLCache(maxsize=100, ttl=300))
def get_users(self):
api_url = f'{self.url}/wp-json/wp/v2/users'
response = requests.get(api_url)
if response.status_code == 200:
users_data = response.json()
df = pd.DataFrame(users_data)
return df
else:
return pd.DataFrame()
@cached(cache=TTLCache(maxsize=100, ttl=300))
def get_posts(self, post_type_slug, start_date, end_date):
start_iso = (datetime.strptime(start_date, '%Y-%m-%d')
.replace(hour=0, minute=0, second=0)
.strftime('%Y-%m-%dT%H:%M:%S'))
end_iso = (datetime.strptime(end_date, '%Y-%m-%d')
.replace(hour=23, minute=59, second=59)
.strftime('%Y-%m-%dT%H:%M:%S'))
endpoint = f'/wp-json/wp/v2/{post_type_slug}'
api_url = f'{self.url}{endpoint}'
params = {
'after': start_iso,
'before': end_iso,
'date_gmt': ''
}
response = requests.get(api_url, params=params)
posts = response.json()
if response.status_code == 200 and isinstance(posts, list):
data = [
{'ID': post['id'],
'Title': post['title']['rendered'],
'Modified': post['modified'],
'Created': post['date_gmt'],
'AuthorId': post.get('author', 1)
} for post in posts]
df = pd.DataFrame(data)
return df
else:
return pd.DataFrame()
Tenho optado por retornar Dataframes do Pandas quase sempre. Isso torna a interação com os dados algo fácil.
Outra coisa que resolvi fazer nessa implementação foi utilizar um cache em memória mesmo. A ideia é reduzir a quantidade de “ataques” no servidor para informações que considero estáveis.
Boa parte das consultas que irei fazer contra meu WordPress serão relacionadas a períodos semanais – ou seja, começando em um domingo e encerrando no próximo sábado. Para tornar a especificação desses intervalos mais fácil, criei uma classe helper.
from datetime import timedelta, datetime
class Weeks:
@staticmethod
def get_week_dates(reference_date):
start_of_week = reference_date - timedelta(days=reference_date.weekday() + 1)
end_of_week = start_of_week + timedelta(days=6)
return start_of_week.strftime('%Y-%m-%d'), end_of_week.strftime('%Y-%m-%d')
@staticmethod
def get_current():
today = datetime.now()
return Weeks.get_week_dates(today)
@staticmethod
def get_previous(n = 1):
today = datetime.now()
last_week_date = today - timedelta(days=7 * n)
return Weeks.get_week_dates(last_week_date)
@staticmethod
def get_week_string_from_date(reference_date):
start, end = Weeks.get_week_dates(reference_date)
return Weeks.get_week_string_from_interval(start, end)
@staticmethod
def get_week_string_from_interval(start, end):
star_f = datetime.fromisoformat(start).strftime('%d/%m')
end_f = datetime.fromisoformat(end).strftime('%d/%m')
return f'{star_f} - {end_f}'
O WordPress é uma plataforma de propósito geral. A classe que desenvolvi consegue lidar bem com qualquer WordPress, mas deixa um bocado de interpretações a cargo do desenvolvedor.
Quase sempre, considero criar uma “camada semântica” sobre classes de propósito geral como a que desenvolvemos, adicionando significado específico para o contexto (domínio) em que estou trabalhando.
import pandas as pd
from datetime import datetime
from src.wordpress import WordPress
from src.weeks import Weeks
class Ontologia:
def __init__(self):
self.wp = WordPress('https://ontologia.eximia.co')
def get_classes(self):
df = self.wp.get_post_types()
classes = df[
~(df['key'].str.startswith('wp_') |
df['key'].str.startswith('nav_') |
df['key'].str.contains('jet-engine') |
df['key'].str.contains('assets') |
df['key'].str.contains('classes') |
df['key'].str.contains('page') |
df['key'].str.contains('post') |
df['key'].str.contains('links') |
df['key'].str.contains('media') |
df['key'].str.contains('attachment') |
df['key'].str.contains('visibility_preset') |
df['key'].str.endswith('_links') |
df['key'].str.endswith('-links')
)
]
return classes
def get_authors(self):
df = self.wp.get_users()
return df[~(df['name'].str.contains('admin'))]
def get_entries(self, start_date, end_date):
classes_df = self.get_classes()
entries_list = []
for index, row in classes_df.iterrows():
post_type_slug = row['key']
posts_df = self.wp.get_posts(post_type_slug, start_date, end_date)
posts_df['ClassKey'] = row['key']
posts_df['ClassName'] = row['name']
entries_list.append(posts_df)
final_df = pd.concat(entries_list, ignore_index=True)
final_df = final_df[~(final_df['AuthorId'] == 1)]
user_name_map = self.get_authors().set_index('id')['name'].to_dict()
final_df['AuthorName'] = final_df['AuthorId'].map(user_name_map)
return final_df
def get_summary_by_author(self, start_date, end_date):
entries_df = self.get_entries(start_date, end_date)
summary_df = entries_df.groupby('AuthorName').size().reset_index(name='Count')
start_date_formatted = pd.to_datetime(start_date).strftime('%d/%m')
end_date_formatted = pd.to_datetime(end_date).strftime('%d/%m')
date_column_title = f"{start_date_formatted} - {end_date_formatted}"
summary_df.columns = ['Author', date_column_title]
return summary_df
def get_summary_by_class(self, start_date, end_date):
entries_df = self.get_entries(start_date, end_date)
summary_df = entries_df.groupby('ClassName').size().reset_index(name='Count')
start_date_formatted = pd.to_datetime(start_date).strftime('%d/%m')
end_date_formatted = pd.to_datetime(end_date).strftime('%d/%m')
date_column_title = f"{start_date_formatted} - {end_date_formatted}"
summary_df.columns = ['Class', date_column_title]
return summary_df
def get_weekly_summary_by(self, by, alias, number_of_weeks):
start, _ = Weeks.get_previous(number_of_weeks)
_, end = Weeks.get_current()
entries = self.get_entries(start, end)
def week(row):
ref = datetime.fromisoformat(row["Created"])
return Weeks.get_week_string_from_date(ref)
entries["Week"] = entries.apply(week, axis=1)
result = pd.DataFrame(columns=[alias])
for i in range(number_of_weeks):
start, end = Weeks.get_previous(i)
week = Weeks.get_week_string_from_interval(start, end)
df = entries[entries['Week'] == week]
summary_df = df.groupby(by).size().reset_index(name='Count')
summary_df.columns = [alias, week]
result = pd.merge(summary_df, result, on=alias, how='outer')
result.fillna(0, inplace=True)
return result
Aqui, a classe semântica substitui a ideia de “usuários” por “autores”, “tipos de postagem” por “classes”, “postagem” por “entradas”.
Além disso, adicionei algumas consultas de sumarização.
Sempre digo que os repositórios são as “redes sociais” dos desenvolvedores. Nos repositórios, desenvolvedores compartilham código, acessam o código de outras pessoas. Mais do que isso, em uma organização de engenharia, repositórios são a “fonte da verdade”.
Recentemente, desenvolvi um notebook para que os consultores da EximiaCo fizessem análises sobre repositórios. A ideia era identificar arquivos modificados com maior frequência e os contribuidores mais ativos. Para isso, utilizei uma biblioteca, bem bacana, chamada GitPython.
! pip install GitPython --quiet
Abaixo uma método de apoio que retorna a relação de modificações em arquivos realizadas em um repositório nos últimos três meses.
import git
from datetime import datetime, timedelta
def get_commits_last_three_months(repo_path, branch_name):
repo = git.Repo(repo_path)
three_months_ago = datetime.now() - timedelta(days=90)
commits = []
# Iterar sobre os commits na branch especificada
for commit in repo.iter_commits(branch_name, since=three_months_ago.isoformat()):
for file_stat in commit.stats.files.items():
file_name, stats = file_stat
entry = safe_get(commit.tree, file_name)
lines = entry.data_stream.read().count(b'\n') + 1 if entry else 0
commit_info = {
'sha': commit.hexsha,
'author': commit.author.name,
'file': file_name,
'changes': stats['lines'],
'insertions' : stats['insertions'],
'deletions': stats['deletions'],
'bytes': entry.size if entry else 0,
'lines': lines
}
commits.append(commit_info)
return commits
O código em si é bastante simples. Além das informações que a própria biblioteca fornece, resolvi calcular também o número de linhas de cada arquivo em um commit específico.
A ideia completa de como utilizo esse código está no notebook.
Everhour é um sistema para registro de atividades popular adotado por empresas de todos os portes. Na EximiaCo, utilizamos o Everhour para registrar nossos engajamentos com nossos clientes.
O Everhour oferece uma API fácil de usar, baseada em HTTP.
Para obter a chave de acesso para a API do Everhour, você deve seguir os seguintes passos:
Lembre-se de manter sua chave de API segura e não compartilhá-la publicamente, pois ela dá acesso aos dados da sua conta no Everhour.
Seu usuário precisa ter acesso administrativo para poder obter esse acesso.
Para fazer uso da API, usaremos o pacote requests
. Para poder tratar os dados, usaremos o pandas
.
! pip install pandas requests
No código, é importante importar esses dois pacotes.
import pandas as pd
import requests
Vamos então iniciar nossa cruzada por obtenção de dados do Everhour. Para começar, vamos escrever código genérico para interagir com endpoints chaves.
def fetch_everhour(api_token, entity, params = {}):
url = f"https://api.everhour.com/team/{entity}"
headers = {
'Content-Type': 'application/json',
'X-Api-Key': api_token
}
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
return response.json()
else:
print (response)
return None
Você pode aprender um bocado sobre os endpoints do Everhour na documentação oficial.
def convert_to_dataframe(source):
if source:
df = pd.DataFrame(source)
return df
else:
return pd.DataFrame()
Por exemplo, o código abaixo resgata todas os usuários definidas no Everhour da organização.
api_token = "SEU-API-TOKEN"
users = convert_to_dataframe(fetch_everhour(api_token, "users"))
users.head()
Felizmente, Pandas ajuda na hora de entender quais são os dados que foram retornados.
Com os dados obtidos, interessante resgatar apenas informações mais interessantes.
users_f = users[['id', 'name', 'email']]
users_f
Agora que conhecemos os usuários, podemos obter também dados relacionados aos apontamentos que já foram realizados.
params = {
"from": "2024-04-01",
"to": "2024-04-16",
"limit": 10000,
"page": 1
}
times = convert_to_dataframe(fetch_everhour(api_token, "time", params))
Aqui é interessante observar a possibilidade de passar informações de intervalo.
Novamente, o Everhour fornece provavelmente mais informações do que necessitamos.
times_f = times[['user', 'date', 'time']]
times_f
O campo “user” possui um id.
De um lado, temos dia e volume de horas lançados. De outro, temos os dados dos usuários (pessoas que apontam horas). Vamos consolidar os dados em uma consulta só.
data = pd.merge(times_f, users_f, left_on="user", right_on="id", how="left")
summary = data[['name', 'time']]
summary.groupby("name").sum().plot(kind="bar")
Bacana, não?
No lugar de passar tanto trabalho com tantos dados, podemos criar uma classe que acelera o processo como um todo.
import requests
import pandas as pd
class EverhourClient:
def __init__(self, api_token):
self.api_token = api_token
self.base_url = "https://api.everhour.com/team/"
def fetch_everhour(self, entity, params={}):
url = f"{self.base_url}{entity}"
headers = {
'Content-Type': 'application/json',
'X-Api-Key': self.api_token
}
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
return response.json()
else:
print(response)
return None
def convert_to_dataframe(self, source):
if source:
return pd.DataFrame(source)
else:
return pd.DataFrame()
def get_all_users(self):
users_json = self.fetch_everhour("users")
users_df = self.convert_to_dataframe(users_json)
return users_df[['id', 'name', 'email']]
def get_appointments(self, start, end):
params = {
"from": start,
"to": end,
"limit": 10000,
"page": 1
}
times_json = self.fetch_everhour("time", params)
times_df = self.convert_to_dataframe(times_json)
return times_df[['id', 'user', 'date', 'time']]
def get_summary(self, start, end):
times_df = self.get_appointments(start, end)
users_df = self.get_all_users()
data = pd.merge(times_df, users_df, left_on="user", right_on="id", how="left")
summary = data[['name', 'time']]
return summary.groupby("name").sum()
Esse código torna a utilização da API algo muito mais fácil.
api_token = "SEU-API-TOKEN"
client = EverhourClient(api_token)
# Get all users
users = client.get_all_users()
print(users.head())
# Get appointments between dates
appointments = client.get_appointments("2024-04-01", "2024-04-16")
print(appointments.head())
# Get time summary between dates
summary = client.get_summary("2024-04-01", "2024-04-16")
print(summary)
Pipedrive é um sistema de CRM popular adotado por empresas de todos os portes. Na EximiaCo, utilizamos o PipeDrive para controlar a relação com nossos clientes.
O Pipedrive oferece uma API fácil de usar, baseada em HTTP.
Se você utiliza Pipedrive em sua organização e deseja interagir com seus dados, primeiro precisa obter a chave de acesso a API. Isso é fácil de fazer.
A chave pode ser encontrada nas configurações do aplicativo web do Pipedrive, em “Settings
> Personal preferences
> API
“. É importante não compartilhar essa chave com ninguém que você não confie, pois ela dá acesso aos dados da sua conta.
Para fazer uso da API, usaremos o pacote requests
. Para poder tratar os dados, usaremos o pandas
.
! pip install pandas requests
No código, é importante importar esses dois pacotes.
import pandas as pd
import requests
Vamos então iniciar nossa cruzada por obtenção de dados do Pipedrive. Para começar, vamos escrever código genérico para interagir com endpoints chaves.
def fetch_pipedrive(api_token, entity, start = 0, params = {}):
url = f"https://api.pipedrive.com/v1/{entity}"
params['api_token'] = api_token
params['limit'] = 500
params['start'] = start
response = requests.get(url, params=params)
if response.status_code == 200:
return response.json()
else:
return None
def convert_to_dataframe(source):
if source and 'data' in source:
df = pd.DataFrame(source['data'])
return df
else:
return pd.DataFrame()
Esse código busca informações a partir de um endpoint relevante dentro do Pipedrive. Abaixo a lista de referência:
Por exemplo, o código abaixo resgata todas as pipelines definidas no Pipedrive da organização.
api_token = "SEU-API-TOKEN"
pipelines = convert_to_dataframe(fetch_pipedrive(api_token, "pipelines"))
pipelines
Um problema potencial da consulta que implementamos até aqui é o limite de elementos retornados em uma única cosulta. Atualmente, estamos buscando o total possível (500, que é restrição da própria Pipedrive).
A solução é iterar na busca enquanto houverem páginas novas.
def has_next_page(data):
return data.get('additional_data', {}).get('pagination', {}).get('more_items_in_collection', False)
def fetch_next_page(api_token, entity, current_page):
if not has_next_page(current_page):
return None
next_start = current_page.get('additional_data', {}).get('pagination', {}).get('next_start')
return fetch_pipedrive(api_token, entity, next_start)
def get_all_deals(api_token):
data = fetch_pipedrive(api_token, 'deals')
df = convert_to_dataframe(data)
while has_next_page(data):
data = fetch_next_page(api_token, 'deals', data)
other_df = convert_to_dataframe(data)
df = pd.concat([df, other_df])
return df
No exemplo acima, temos o código que recupera todos os deals no Pipedrive consolidando tudo em um único dataframe.
deals = get_all_deals(api_token)
deals
Com as informações em dataframes podemos qualificar os dados e fazer análises refinadas. O uso das APIs garante sempre os “dados quentes”.
Um ponto a se considerar é a combinação dos dados do Pipedrive com outras fontes, mas esse é tema para outro post.
O YouTube oferece acesso gratuito às melhores palestras de eventos internacionais renomados. Assim, as principais limitações que enfrento para aproveitar tanto conteúdo de qualidade são minha capacidade e o tempo disponível, que está cada vez mais escasso.
Felizmente, utilizando criatividade, conhecimentos em Python e o Jupyter Notebook, consigo otimizar o tempo para capturar a essência do conteúdo de cada palestra.
Assumindo que você tem Python instalado e Jupyter funcionando, começo um novo notebook instalando os pacotes requests
e bs4
.
! pip install --upgrade --quiet requests bs4
O código que segue consegue extrair, a partir de um link, o título do vídeo, seu código e o nome do canal onde está publicado. Ele faz isso dividindo a URL para obter o código do vídeo, enviando uma solicitação para a URL, e, se bem-sucedido, analisa o HTML para encontrar os metadados do vídeo e do canal.
import requests
from bs4 import BeautifulSoup
# URL do vídeo no YouTube
video_url = 'https://www.youtube.com/watch?v=GYJ77F_8kq0'
# Extrai o código do vídeo da URL
video_code = video_url.split('=')[-1]
# Faz a solicitação para a página do vídeo
response = requests.get(video_url)
# Se a solicitação foi bem-sucedida
if response.status_code == 200:
# Analisa o HTML da página
soup = BeautifulSoup(response.text, 'html.parser')
# Extrai o nome do vídeo
# O título geralmente está dentro de uma tag <meta> com o atributo name="title"
video_title = soup.find('meta', {'name': 'title'})['content']
# Extrai o nome do canal
# O nome do canal geralmente está dentro de uma tag <meta> com o atributo itemprop="channelId"
channel_name = soup.find('link', {'itemprop': 'name'})['content']
print(f'Nome do vídeo: {video_title}')
print(f'Código do vídeo: {video_code}')
print(f'Canal: {channel_name}')
else:
print('Falha ao fazer a solicitação para a página do vídeo.')
Executando o código, obtenho, os dados que desejo.
Nome do vídeo: "Picasso, Geometry, Jupyter" by Ryan Herr
Código do vídeo: GYJ77F_8kq0
Canal: Strange Loop Conference
Já tenho os metadados de um vídeo, agora, desejo obter a transcrição. Para isso, vamos utilizar o pacote youtube-transcript-api
.
! pip install --upgrade --quiet youtube-transcript-api
O código para obter a transcrição é bem simples.
from youtube_transcript_api import YouTubeTranscriptApi
transcript = YouTubeTranscriptApi.get_transcript(video_code)
full_text = ' '.join([entry['text'] for entry in transcript])
print(full_text)
O resultado é a transcrição do vídeo que o YouTube disponibiliza para gerar legendas. É importante observar, no entanto, que nem todos os vídeos possuem transcrição.
hi I'm Ryan her and this talk is about Picasso and programming in 1945 Picasso
created a series of bulls where he started with a fairly realistic bull like you see
on the left and through a gradual progressive process he deconstructed the bull abstracting
it to its essence to a simplified geometric form so in 1945 Picasso did that in 2010 an
anonymous illustrator posted a meme on tumblr and reddit how to draw an owl again we see a
transition between two states step one draw some circles step two draw the rest of the owl
so the interesting thing when we compare these two sets of images well they're moving in
opposite directions right the the owl is moving from simple to detailed but the bull Picasso's Bulls actually moved in the opposite direction it went from more complex more detailed to
...
Agora que temos a transcrição do vídeo, podemos utilizar a API da OpenAI para “conversar” com a transcrição obtendo as informações que desejamos. Para isso, irei utilizar LangChain para facilitar a interação. Vamos instalar os pacotes langchain
, langchain-openai
, langchain-core
e langchain-community
.
! pip install --upgrade --quiet langchain langchain-openai langchain-core langchain-community
O código monta um modelo de prompt com os metadados e com a transcrição que obtemos anteriormente e a solicitação do usuário. Utilizo um modelo da OpenAI com uma janela de 128K que permite tratamento da transcrição completa, sem perdas. Também crio uma função helper para o modelo.
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
gpt4 = ChatOpenAI(model_name="gpt-4-0125-preview",temperature=0)
chat_with_document = ChatPromptTemplate.from_template(f"""
O texto que segue é uma transcrição do vídeo
intitulado {video_title} disponível no canal {channel_name}
Leia o texto atentamente e realize da forma mais completa
possível a tarefa também indicada abaixo.
Use, na resposta, apenas informações contidas no texto,
sem adicionar, em hipótese alguma, qualquer informação adicional.
Você responde em português, usando linguagem simples, com
vocabulário simples. Estrutura seu texto em
frases curtas, em parágrafos com não mais do que 300 caracteres.
TEXTO:
{{text}}
TAREFA:
{{task}}
SUA RESPOSTA:
""")
output_parser = StrOutputParser()
chat_with_document_chain = chat_with_document | gpt4 | output_parser
def do(task):
result = chat_with_document_chain.invoke({"text": full_text, "task": task})
print(result)
return result
Tudo pronto! Agora, vamos estudar. Comecemos, com um bom resumo.
summary = do("Forneça-me um resumo detalhado do que é discutido no vídeo.")
O resultado é um resumo, em português, do conteúdo que está na transcrição, que está em inglês.
O vídeo apresentado por Ryan Herr aborda a relação entre a arte de Picasso, a
programação e a busca pela simplificação e abstração tanto na arte quanto no
software. Em 1945, Picasso criou uma série de imagens de touros, começando
com uma representação realista e, progressivamente, simplificando-a até chegar
a uma forma geométrica simplificada. Esse processo de abstração de Picasso é
comparado a um meme de 2010 sobre desenhar uma coruja, mostrando a dificuldade
de simplificar ou detalhar imagens.
Ryan Herr explora como o processo de Picasso pode ser aplicado ao
desenvolvimento de software e matemática, onde a busca por abstrações concisas
e elegantes é constante. Ele menciona que Picasso, antes de criar sua série de
touros, enfrentou um bloqueio criativo enquanto trabalhava em outra pintura,
"Charnel-House". Picasso expressou o desejo de poder trabalhar em estados
progressivos sem finalizar a tela, algo que ele conseguiu explorar com a
série de touros usando a litografia, uma técnica que permite fazer alterações.
Herr utiliza papel vegetal para entender o processo de Picasso, traçando as
mudanças entre os estados dos touros. Ele então usa programação para analisar
computacionalmente essas mudanças, criando representações coloridas que mostram
o que foi adicionado, removido ou mantido entre cada estado. Esse processo
revela que inicialmente Picasso adicionou detalhes ao touro, mas progressivamente
simplificou a imagem, removendo elementos até chegar a uma forma quase linear.
Explorando ainda mais, Herr utiliza o algoritmo de simplificação de linhas
Douglas-Peucker para tentar recriar a abstração de Picasso computacionalmente,
resultando em uma forma que, embora diferente, oferece insights sobre o processo
de simplificação. Ele discute a dificuldade de capturar a abstração única de Picasso,
que combina elementos de abstração pictórica e icônica.
Por fim, Herr reflete sobre como a exploração computacional do processo de Picasso
não apenas oferece uma nova compreensão da arte de Picasso, mas também pode inspirar
desenvolvedores de software e matemáticos a enfrentar bloqueios criativos, sugerindo
que começar com uma versão mais complexa e simplificá-la progressivamente pode ser
uma estratégia eficaz para superar desafios criativos.
E esse foi só o começo…
Para interagir com outra palestra, basta-me substituir o link do vídeo que quero estudar e executar novamente o código.
A produção de resumos qualificados assim facilita a produção de anotações e me permite saber rapidamente quando interesse tenho em assistir uma palestra ou não.