Skip to content

Latest commit

 

History

History
393 lines (302 loc) · 16.1 KB

File metadata and controls

393 lines (302 loc) · 16.1 KB

shortcoder — Documentacao Completa

Visao Geral

shortcoder eh uma TUI (Terminal User Interface) no estilo classico dos anos 90, construida em AdvPL/TLPP e compilada com AdvPP.

Caracteristicas

  • Interfaceretrô: ASCII art, cores vintage (amarelo, ciano, verde, magenta), caixas delimitadas
  • Orquestracao de agentes: Roteamento automatico baseado no dominio da pergunta
  • 100% legivel: Texto quebrado corretamente, padding dinamico, caixas proporcionais
  • 3 agentes integrados:
    • ollama: LLM rapido (~1s) para tarefas gerais
    • mem0: Memoria persistente (<1s) para consultar/salvar fatos
    • ernesto: RAG + Memoria (>30s) para perguntas de dominio Protheus

Arquitetura

┌─────────────────────────────────────────────────────────────────┐
│                      shortcoder                             │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                   ORQUESTRADOR                           │   │
│  │  DetectAgent(cInput, cDefaultAgent)                      │   │
│  │    ├─ Keywords mem0 → agente mem0                       │   │
│  │    ├─ Keywords Protheus → agente ernesto (RAG)          │   │
│  │    └─ Padrão → agente ollama (LLM)                      │   │
│  └─────────────────────────────────────────────────────────┘   │
│                            │                                    │
│        ┌───────────────────┼───────────────────┐               │
│        │                   │                   │               │
│   ┌────▼────┐        ┌────▼────┐        ┌────▼────┐           │
│   │ ollama  │        │  mem0   │        │ ernesto │           │
│   │  (rapido)│        │(memoria)│        │  (RAG)  │           │
│   └────┬────┘        └────┬────┘        └────┬────┘           │
│        │                  │                  │                │
│   ┌────▼────┐        ┌────▼────┐        ┌────▼────┐           │
│   │ Ollama  │        │ ernesto │        │ ernesto │           │
│   │  :11434 │        │  :9081  │        │  :9081  │           │
│   └─────────┘        └─────────┘        └─────────┘           │
└─────────────────────────────────────────────────────────────────┘

Instalacao

Requisitos

  • AdvPP compilador (advplc) instalado em ~/.local/bin/
  • Ollama rodando em http://127.0.0.1:11434
  • ernesto-mem0 rodando em http://127.0.0.1:9081
  • Modelo lfm25-1b-uncensored:latest carregado no Ollama

Compilacao

cd ~/Projetos/shortcoder
advplc build shortcoder.prw -o shortcoder

Executcao

./shortcoder

Uso

Interface

╔══════════════════════════════════════════════════════════════════════════╗
║                                                                          ║
║                                SHORTCODER                                ║
║                                                                          ║
║                   [AI Coding Agent v1.0]                  ║
║                                                                          ║
╚══════════════════════════════════════════════════════════════════════════╝
┌──────────────────────────────────────────────────────────────────────────┐
│ MODEL:  [lfm25-1b-uncensored:latest]                                   │
│ SESSION:[user-20260802-...]                                            │
│ AGENT:  [OLLAMA]                                                       │
│ MEM0:   [default]                                                      │
└──────────────────────────────────────────────────────────────────────────┘
>

Comandos

Comando Descricao Agente
/agent Alterna entre ollama, mem0, ernesto, agnes e orchestrator
/model Lista e seleciona modelo
/mem0 list Lista memorias salvas mem0
/mem0 add <texto> Adiciona memoria persistente mem0
/mem0 clear Remove todas as memorias mem0
/history Mostra historico de turnos
/clear Nova sessao (limpa historico)
/help Esta ajuda
/tools selftest Auto-teste do harness de ferramentas do Agnes
/exit Sair do programa

Orquestracao Automatica

O sistema detecta automaticamente o agente mais adequado:

Entrada do Usuario Agente Selecionado Razao
"o que e A1_COD?" ernesto (RAG) Contem palavra-chave "campo"
"/mem0 list" mem0 Comando mem0 explicito
"2+2" ollama (LLM) Pergunta geral
"escreva um poema" ollama (LLM) Tarefa criativa
"como funciona SE1?" ernesto (RAG) Contem palavra-chave Protheus

Palavras-chave de Dominio (Protheus/AdvPL)

O sistema detecta automaticamente perguntas sobre:

  • Campos: campo, tabela, sx2, sx3, six
  • Formularios: formulario, rotina
  • Consultas: query, sql, trigger, indice
  • Modulos: protheus, advpl, tlpp, totvs, mata, finan
  • Tabelas: se1, sa1, sb1, sc1, sd1, sf1, sg1, sh1
  • Funcoes: d_e_l_e_t_, xfilial, fwexecstatement, tcquery, dbquery

Casal Agnes ↔ Ornith (Couple)

Ao selecionar ollama ou agnes (direto ou via /config set agent ...), o shortcoder tenta o agente escolhido primeiro; se a chamada falhar por um motivo transiente (timeout, erro HTTP, resposta sem conteudo, parse falho), o outro agente assume automaticamente e responde no lugar. A caixa de resposta mostra [COUPLE] <agente> indisponivel — respondido por <outro> para deixar a troca visivel — a experiencia nunca fica sem resposta, mas tambem nunca finge que nada aconteceu.

"Nao configurado" (por exemplo AGNES_API_KEY ausente) nao aciona o failover — e uma falha permanente de configuracao, nao uma queda momentanea, e mascara-la silenciosamente atras do Ornith so esconderia uma chave esquecida. Nesse caso o erro original (AGNES_API_KEY nao configurada) continua aparecendo normalmente.

Controlado por agents.couple_enabled (bool, padrao true). Desative com /config set agents.couple_enabled false para voltar ao comportamento de agente unico (falha mostra o erro e para por ai).

Ferramentas do Agnes

Quando agents.agnes.tools_enabled esta habilitado (padrao), o agente Agnes (selecionado via /agent → opcao 4, ou defaults.agent: "agnes") roda num loop de ferramentas: o modelo decide que ferramenta chamar, o shortcoder executa a ferramenta num job isolado (FWJOBSTARTFWJOBDONEFWJOBPOLL) e devolve o resultado de volta ao modelo, ate chegar na resposta final.

As 11 ferramentas disponiveis:

Ferramenta Descricao
read_file Le um arquivo dentro do workdir
write_file Cria/sobrescreve um arquivo dentro do workdir
edit_file Substitui a primeira ocorrencia de old_string por new_string
bash Executa um comando de shell no workdir
glob Lista arquivos que casam com um glob (via find)
grep Busca regex nos arquivos do workdir
list_dir Lista um diretorio (1 nivel)
rag_search Consulta a base vetorial AdvPL/Protheus (agents.ernesto.url)
mem0_search Busca memorias cross-agent (listagem por usuario)
web_fetch HTTP GET de uma URL
load_skill Carrega <skills_dir>/<name>/SKILL.md

Chaves de configuracao (agents.agnes.*): tools_enabled, tools_max_tokens, workdir, max_tool_iterations, tool_timeout, max_tool_output, max_history, load_skills, skills_dir. Todas tem fallback no ConfigGet, entao configs existentes funcionam sem migracao.

Modelo de seguranca

  • Caminhos: toda ferramenta de arquivo valida o caminho com IsWithinWorkdir(cWorkDir, cPath) antes de tocar no disco. Aceita absolutos sob o workdir e relativos (normalizados contra o workdir); rejeita .. acima do workdir e absolutos fora dele.
  • Shell: bash, glob e grep executam comandos no workdir (cd). O modelo controla o comando — sao confiaveis por convencao, nao por sandbox. Limitacao de seguranca documentada.
  • Nenhuma ferramenta expoe rede/UI: a VM do job retorna apenas string, e o loop exibe na VM principal.

Selftest

/tools selftest roda o auto-teste do harness: parse do JSON de definicoes, IsWithinWorkdir (aceite/recusa), roundtrip write/read/edit, shell tools e TruncateHistory. Tambem automatizado em tests/tools-tests.sh.


Arquitetura Tecnica

Funcoes Principais

DetectAgent(cInput, cDefaultAgent)

Orquestrador que decide qual agente usar baseado no conteudo.

Static Function DetectAgent(cInput, cDefaultAgent)
    Local cLower := Lower(cInput)
    
    // Regra 1: Comandos mem0 explicitos
    If Left(cLower, 10) == "/mem0 " .Or. cLower == "/mem0 list" ...
        Return "mem0"
    EndIf
    
    // Regra 2: Palavras-chave de dominio Protheus
    Local aProtheusKeywords := { "campo", "tabela", "sx2", ... }
    For i := 1 To Len(aProtheusKeywords)
        If aProtheusKeywords[i] $ cLower
            Return "ernesto"
        EndIf
    Next i
    
    // Regra 3: Padrão usa agente selecionado
    Return cDefaultAgent

WordWrap(cText, nMaxLen)

Quebra texto em linhas de tamanho maximo, respeitando palavras.

Static Function WordWrap(cText, nMaxLen)
    Local aLines := {}
    Local cWord, cLine := "", nWordLen, nLineLen
    Local aWords, i
    
    cText := StrTran(cText, Chr(10), " ")
    cText := StrTran(cText, Chr(13), " ")
    
    // Tokenizacao manual (StrTokArray nao existe no AdvPP)
    aWords := {}
    ...
    
    For i := 1 To Len(aWords)
        cWord := aWords[i]
        nWordLen := Len(cWord)
        nLineLen := Len(cLine)
        
        If nLineLen + nWordLen + (Empty(cLine) .And. 0 .Or. 1) <= nMaxLen
            If !Empty(cLine)
                cLine := cLine + " "
            EndIf
            cLine := cLine + cWord
        Else
            If !Empty(cLine)
                aAdd(aLines, cLine)
            EndIf
            cLine := cWord
        EndIf
    Next i
    
    If !Empty(cLine)
        aAdd(aLines, cLine)
    EndIf
    
Return aLines

FormatTextForBox(cEsc, cText, nMaxLen, nBoxW)

Formata texto para caber dentro de caixa.

Static Function FormatTextForBox(cEsc, cText, nMaxLen, nBoxW)
    Local aLines, cLine, cResult := "", i, nLen
    
    aLines := WordWrap(cText, nMaxLen)
    
    For i := 1 To Len(aLines)
        cLine := aLines[i]
        nLen := Len(cLine)
        
        If nLen >= nMaxLen
            cResult := cResult + cEsc + "[2;37m" + Left(cLine, nMaxLen) + cEsc + "[0m" + Chr(10)
        Else
            cResult := cResult + cEsc + "[2;37m" + cLine + Replicate(" ", nMaxLen - nLen) + cEsc + "[0m" + Chr(10)
        EndIf
    Next i
    
Return cResult

Endpoints HTTP

Endpoint Metodo Uso Tempo
http://127.0.0.1:11434/v1/chat/completions POST Ollama LLM (local, ex: ornith-uncensored) 1s–vários min (CPU)
http://127.0.0.1:9081/memories/{user_id} GET Mem0 list <1s
http://127.0.0.1:9081/memories/{user_id} POST Mem0 add <2s
http://127.0.0.1:9081/memories/{user_id} DELETE Mem0 clear <1s
http://127.0.0.1:9081/v1/chat/completions POST Ernesto RAG >30s
https://apihub.agnes-ai.com/v1/chat/completions POST Agnes 2.5 Flash (remoto, requer AGNES_API_KEY) variavel

Limitacoes

  1. Tempo de resposta do modelo ernesto: O modelo RAG (ernesto-granite41-rag:latest) eh muito lento (>30s), causando timeout no HTTP client (timeout padrao: 30s). Modelos ollama locais pesados (ex: 9B em GPU pequena/CPU) tambem podem exigir agents.ollama.timeout bem maior que o padrao — veja agents.ollama.keep_alive no README para evitar recarregar o modelo entre mensagens.

  2. StrTokArray nao existe: O AdvPP nao possui a funcao StrTokArray, entao a tokenizacao de texto eh feita manualmente no WordWrap.

  3. Sem streaming: As respostas sao processadas em modo nao-streaming (aguarda resposta completa).

  4. Memoria limitada: O modelo lfm25-1b-uncensored tem contexto de 128k tokens, mas respostas longas podem ser truncadas.


Testes

Resultado dos Testes

A suite automatizada (tests/config-tests.sh + tests/tools-tests.sh) cobre config write-once, aliases de /config set, /reload, o harness de ferramentas do Agnes (selftest, IsWithinWorkdir, roundtrip write/read/edit) e o failover do casal Agnes ↔ Ornith (fallback "nao configurado" nao aciona failover; ambos os agentes falhando degrada graciosamente sem travar).

Total atual: 19 testes, 19 passed, 0 failed.

Detalhes por suite: config-tests.sh roda 15 testes (inicializacao sem config, compatibilidade retroativa de /timeout//help//history, write-once da config, /config set com aliases, /reload, troca de agente em memoria); tools-tests.sh roda 4 testes (selftest do harness, erro controlado do Agnes sem AGNES_API_KEY, gating por tools_enabled, e o Test 4 novo do casal: ambos os agentes falhando mostra o erro do primario sem travar).

Executar Testes

# Teste 1: Inicializacao
printf '/exit\n' | ./shortcoder

# Teste 2: Ajuda
printf '/help\n/exit\n' | ./shortcoder

# Teste 3: Resposta rapida
printf '2+2\n/exit\n' | ./shortcoder

# Teste 4: Memoria
printf '/mem0 add "teste"\n/mem0 list\n/exit\n' | ./shortcoder

# Teste 5: Orquestracao
printf 'o que e o campo A1_COD?\n/exit\n' | ./shortcoder

# Testes automatizados (config + harness de ferramentas)
./tests/config-tests.sh
./tests/tools-tests.sh

Arquivos

~/Projetos/shortcoder/
├── shortcoder              # Binario standalone (build local, nao versionado)
├── shortcoder.prw          # Entry point AdvPL (advplc build)
├── src/                    # Fontes AdvPL do harness (sc_*.prw)
├── tests/                  # config-tests.sh, tools-tests.sh
├── installer/              # Instalador Windows (NSIS)
├── README.md                # Instalacao, comandos, tabela de configuracao
├── DOCUMENTACAO.md          # Este arquivo
├── ARCHITECTURE.md          # Arquitetura da extensao RAG (protheus-rag)
├── PROTOTIPO.md             # Notas do prototipo com Mem0/LLM rapido
├── WIREFRAME.md             # Wireframe da TUI
└── TEST_RESULTS.md          # Relatorio historico de testes manuais

Referencias


Licenca

Codigo desenvolvido para uso interno no projeto shortcoder.