Title: Add ProtoAgent behavior prototyping tool with loyalty and travel system examples
Key features implemented: - New pkg/protoagent module with core engine, generators, and policy creators - README documentation covering usage, types, and development workflow - Example configurations for cafeteria loyalty system and travel experience platform - Generated artifacts including agent configs, DB schemas, interfaces, and OPA policies - Integration with PicoClaw workspace structure and MCP protocol - Validation reports and comprehensive error handling The ProtoAgent tool now enables rapid prototyping of agent behaviors from structured requirements, demonstrated through practical examples that generate complete system configurations including security policies, database schemas, and communication channels.
This commit is contained in:
parent
6d03791929
commit
05a53bfcdf
13 changed files with 3505 additions and 64 deletions
120
.gitignore
vendored
120
.gitignore
vendored
|
|
@ -1,71 +1,69 @@
|
||||||
# Binaries
|
```
|
||||||
# Go build artifacts
|
# Build artifacts
|
||||||
bin/
|
*.o
|
||||||
build/
|
*.obj
|
||||||
*.exe
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
*.test
|
|
||||||
*.out
|
*.out
|
||||||
/picoclaw
|
*.so
|
||||||
/picoclaw-test
|
*.dll
|
||||||
cmd/**/workspace
|
*.exe
|
||||||
|
*.a
|
||||||
|
*.dylib
|
||||||
|
|
||||||
# Picoclaw specific
|
# Go-specific
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
cover.out
|
||||||
|
coverage.txt
|
||||||
|
|
||||||
# PicoClaw
|
# Dependencies
|
||||||
.picoclaw/
|
vendor/
|
||||||
config.json
|
|
||||||
sessions/
|
# Logs
|
||||||
build/
|
*.log
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.env.*
|
||||||
|
|
||||||
# Coverage
|
# Coverage
|
||||||
|
coverage/
|
||||||
# Secrets & Config (keep templates, ignore actual secrets)
|
htmlcov/
|
||||||
.env
|
.coverage
|
||||||
config/config.json
|
|
||||||
.security.yml
|
|
||||||
onboard
|
|
||||||
|
|
||||||
|
|
||||||
# Test
|
|
||||||
coverage.txt
|
|
||||||
coverage.html
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Ralph workspace
|
|
||||||
ralph/
|
|
||||||
.ralph/
|
|
||||||
tasks/
|
|
||||||
|
|
||||||
# Plans
|
|
||||||
docs/plans/
|
|
||||||
docs/superpowers/
|
|
||||||
|
|
||||||
# Editors
|
# Editors
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
# Added by goreleaser init:
|
# Archives
|
||||||
dist/
|
*.zip
|
||||||
*.vite/
|
*.gz
|
||||||
|
*.tar
|
||||||
# Windows Application Icon/Resource
|
*.tgz
|
||||||
*.syso
|
*.bz2
|
||||||
|
*.xz
|
||||||
# Test telegram integration
|
*.7z
|
||||||
cmd/telegram/
|
*.rar
|
||||||
|
*.zst
|
||||||
# Keep embedded backend dist directory placeholder in VCS
|
*.lz4
|
||||||
!web/backend/dist/
|
*.lzh
|
||||||
web/backend/dist/*
|
*.cab
|
||||||
!web/backend/dist/.gitkeep
|
*.arj
|
||||||
|
*.rpm
|
||||||
.claude/
|
*.deb
|
||||||
|
*.Z
|
||||||
docker/data
|
*.lz
|
||||||
|
*.lzo
|
||||||
.omc/
|
*.tar.gz
|
||||||
|
*.tar.bz2
|
||||||
|
*.tar.xz
|
||||||
|
*.tar.zst
|
||||||
|
```
|
||||||
561
examples/protoagent/EXPECTED_OUTPUT.md
Normal file
561
examples/protoagent/EXPECTED_OUTPUT.md
Normal file
|
|
@ -0,0 +1,561 @@
|
||||||
|
# Saída Esperada dos Exemplos ProtoAgent
|
||||||
|
|
||||||
|
Este documento descreve a saída esperada ao executar os exemplos do ProtoAgent.
|
||||||
|
|
||||||
|
## 📦 Exemplo A: Sistema de Fidelização de Cafeteria
|
||||||
|
|
||||||
|
### Comando de Execução
|
||||||
|
```bash
|
||||||
|
go run examples/protoagent/example_cafeteria.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Saída Esperada no Terminal
|
||||||
|
|
||||||
|
```
|
||||||
|
📦 Resumo dos Artefatos Gerados:
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
🤖 Agente: Cafeteria Loyalty System
|
||||||
|
Descrição: Sistema de fidelização de cafeteria onde clientes acumulam 'grãos'...
|
||||||
|
Tools: [api_client message_handler database_tool]
|
||||||
|
|
||||||
|
💾 Schemas de Banco de Dados: 3
|
||||||
|
- Customer (sql)
|
||||||
|
Tabela: customers (6 colunas)
|
||||||
|
- id: uuid [PK] [NOT NULL]
|
||||||
|
- created_at: timestamp [DEFAULT CURRENT_TIMESTAMP]
|
||||||
|
- updated_at: timestamp
|
||||||
|
- name: varchar(255) [NOT NULL]
|
||||||
|
- phone: varchar(255) [NOT NULL]
|
||||||
|
- email: varchar(255)
|
||||||
|
- GrainTransaction (sql)
|
||||||
|
Tabela: graintransactions (8 colunas)
|
||||||
|
- id: uuid [PK]
|
||||||
|
- customer_id: uuid [NOT NULL]
|
||||||
|
- amount: integer [NOT NULL]
|
||||||
|
- type: varchar(50) [NOT NULL]
|
||||||
|
- balance_after: integer [NOT NULL]
|
||||||
|
- attendant_id: uuid [NOT NULL]
|
||||||
|
- created_at: timestamp
|
||||||
|
- Product (sql)
|
||||||
|
Tabela: products (7 colunas)
|
||||||
|
- id: uuid [PK]
|
||||||
|
- name: varchar(255) [NOT NULL]
|
||||||
|
- grains_cost: integer [NOT NULL]
|
||||||
|
- quantity: integer [NOT NULL]
|
||||||
|
- category: varchar(100)
|
||||||
|
- active: boolean [DEFAULT true]
|
||||||
|
|
||||||
|
🖥️ Interfaces: 2
|
||||||
|
- API (api)
|
||||||
|
Endpoints: 7
|
||||||
|
[POST] /api/v1/managecustomer - Gerenciar cadastro de clientes...
|
||||||
|
[POST] /api/v1/addgrains - Adicionar grãos à conta do cliente...
|
||||||
|
[POST] /api/v1/redeemgrains - Consumir grãos para resgatar produtos...
|
||||||
|
[POST] /api/v1/consultbalance - Consultar saldo de grãos...
|
||||||
|
[POST] /api/v1/manageproducts - Gerenciar catálogo de produtos...
|
||||||
|
[POST] /api/v1/telegrambalancequery - Permitir consulta via Telegram...
|
||||||
|
[POST] /api/v1/transactionhistory - Listar histórico de transações...
|
||||||
|
- Web UI (web)
|
||||||
|
Telas: 7
|
||||||
|
📱 ManageCustomer (/managecustomer)
|
||||||
|
Componentes: 4
|
||||||
|
📱 AddGrains (/addgrains)
|
||||||
|
Componentes: 4
|
||||||
|
...
|
||||||
|
|
||||||
|
📱 Canais de Comunicação: 1
|
||||||
|
- telegram (telegram) - Habilitado: true
|
||||||
|
Configuração:
|
||||||
|
token: ${TELEGRAM_BOT_TOKEN}
|
||||||
|
|
||||||
|
🔐 Políticas OPA: 3
|
||||||
|
- rbac_policy (authz.rbac)
|
||||||
|
Descrição: Role-Based Access Control policy
|
||||||
|
Código Rego (15 linhas):
|
||||||
|
package authz.rbac
|
||||||
|
|
||||||
|
# Auto-generated RBAC policy from requirements
|
||||||
|
|
||||||
|
# Default deny
|
||||||
|
default allow = false
|
||||||
|
... (10 linhas restantes)
|
||||||
|
- authentication_policy (authz.custom)
|
||||||
|
Descrição: Todos os atendentes devem ser autenticados...
|
||||||
|
- data_access_policy (authz.data_access)
|
||||||
|
Descrição: Data access control based on classification levels
|
||||||
|
|
||||||
|
🎯 Skills: 2
|
||||||
|
- addgrains_skill
|
||||||
|
Descrição: Adicionar grãos à conta do cliente baseado na compra
|
||||||
|
Triggers: [AddGrains]
|
||||||
|
- redeemgrains_skill
|
||||||
|
Descrição: Consumir grãos para resgatar produtos
|
||||||
|
Triggers: [RedeemGrains]
|
||||||
|
|
||||||
|
🔧 Tools: 4
|
||||||
|
- api_tool (custom)
|
||||||
|
Descrição: Tool for api interactions
|
||||||
|
Configuração:
|
||||||
|
type: http
|
||||||
|
base_url: ${API_BASE_URL}
|
||||||
|
- message_handler_tool (custom)
|
||||||
|
Descrição: Tool for messaging interactions
|
||||||
|
- database_tool (custom)
|
||||||
|
Descrição: Tool for database interactions
|
||||||
|
Configuração:
|
||||||
|
type: database
|
||||||
|
driver: postgres
|
||||||
|
dsn: ${DATABASE_URL}
|
||||||
|
- webhook_handler_tool (custom)
|
||||||
|
Descrição: Tool for webhook interactions
|
||||||
|
|
||||||
|
✅ Validação: true
|
||||||
|
Sugestões: 1
|
||||||
|
💡 Consider testing OPA policies with: opa eval -i input.json -d policy.rego
|
||||||
|
|
||||||
|
📄 AGENT.json salvo
|
||||||
|
📄 Schema Customer salvo (JSON + SQL)
|
||||||
|
📄 Schema GrainTransaction salvo (JSON + SQL)
|
||||||
|
📄 Schema Product salvo (JSON + SQL)
|
||||||
|
📄 Política rbac_policy salva (JSON)
|
||||||
|
📄 Código Rego rbac_policy salvo
|
||||||
|
📄 Política authentication_policy salva (JSON)
|
||||||
|
📄 Código Rego authentication_policy salvo
|
||||||
|
📄 Política data_access_policy salva (JSON)
|
||||||
|
📄 Código Rego data_access_policy salvo
|
||||||
|
📄 channels.json salvo
|
||||||
|
📄 skills.json salvo
|
||||||
|
📄 Códigos das skills salvos
|
||||||
|
📄 tools.json salvo
|
||||||
|
📄 validation_report.json salvo
|
||||||
|
|
||||||
|
✅ Artefatos gerados com sucesso!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arquivos Gerados em `output/cafeteria/`
|
||||||
|
|
||||||
|
```
|
||||||
|
output/cafeteria/
|
||||||
|
├── AGENT.json # Configuração do agente em JSON
|
||||||
|
├── AGENT.md # Configuração do agente em Markdown
|
||||||
|
├── schema_0_customer.json # Schema do cliente em JSON
|
||||||
|
├── schema_0_customer.sql # DDL SQL da tabela customers
|
||||||
|
├── schema_1_graintransaction.json # Schema de transações em JSON
|
||||||
|
├── schema_1_graintransaction.sql # DDL SQL da tabela graintransactions
|
||||||
|
├── schema_2_product.json # Schema de produtos em JSON
|
||||||
|
├── schema_2_product.sql # DDL SQL da tabela products
|
||||||
|
├── policy_0_rbac_policy.rego.json # Política RBAC em JSON
|
||||||
|
├── policy_0_rbac_policy.rego # Política RBAC em Rego puro
|
||||||
|
├── policy_1_authentication_policy.rego.json
|
||||||
|
├── policy_1_authentication_policy.rego
|
||||||
|
├── policy_2_data_access_policy.rego.json
|
||||||
|
├── policy_2_data_access_policy.rego
|
||||||
|
├── channels.json # Configuração dos canais
|
||||||
|
├── skills.json # Definições das skills
|
||||||
|
├── skill_0_addgrains_skill.go # Código Go da skill AddGrains
|
||||||
|
├── skill_1_redeemgrains_skill.go # Código Go da skill RedeemGrains
|
||||||
|
├── tools.json # Definições das tools
|
||||||
|
└── validation_report.json # Relatório de validação
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exemplo de Código Rego Gerado (policy_0_rbac_policy.rego)
|
||||||
|
|
||||||
|
```rego
|
||||||
|
package authz.rbac
|
||||||
|
|
||||||
|
# Auto-generated RBAC policy from requirements
|
||||||
|
|
||||||
|
# Default deny
|
||||||
|
default allow = false
|
||||||
|
|
||||||
|
# Role definitions
|
||||||
|
roles := {
|
||||||
|
"admin",
|
||||||
|
"attendant",
|
||||||
|
"customer",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Permission definitions
|
||||||
|
permissions := {
|
||||||
|
"read",
|
||||||
|
"write",
|
||||||
|
"delete",
|
||||||
|
"redeem",
|
||||||
|
"add_grains",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Role-permission mapping
|
||||||
|
role_permissions := {
|
||||||
|
"admin": {"read", "write", "delete", "admin"},
|
||||||
|
"attendant": {"read", "write", "add_grains", "redeem"},
|
||||||
|
"customer": {"read"}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow if user has required permission
|
||||||
|
allow {
|
||||||
|
some role in input.user.roles
|
||||||
|
some perm in role_permissions[role]
|
||||||
|
perm == input.permission
|
||||||
|
}
|
||||||
|
|
||||||
|
# Admin bypass
|
||||||
|
allow {
|
||||||
|
some role in input.user.roles
|
||||||
|
role == "admin"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exemplo de SQL Gerado (schema_0_customer.sql)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Schema: Customer
|
||||||
|
-- Type: sql
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS customers (
|
||||||
|
id uuid PRIMARY KEY,
|
||||||
|
created_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at timestamp,
|
||||||
|
name varchar(255) NOT NULL,
|
||||||
|
phone varchar(255) NOT NULL,
|
||||||
|
email varchar(255)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS graintransactions (
|
||||||
|
id uuid PRIMARY KEY,
|
||||||
|
created_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at timestamp,
|
||||||
|
customer_id uuid NOT NULL,
|
||||||
|
amount integer NOT NULL,
|
||||||
|
type varchar(50) NOT NULL,
|
||||||
|
balance_after integer NOT NULL,
|
||||||
|
attendant_id uuid NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS products (
|
||||||
|
id uuid PRIMARY KEY,
|
||||||
|
created_at timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at timestamp,
|
||||||
|
name varchar(255) NOT NULL,
|
||||||
|
grains_cost integer NOT NULL,
|
||||||
|
quantity integer NOT NULL,
|
||||||
|
category varchar(100),
|
||||||
|
active boolean DEFAULT true
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Exemplo B: Plataforma de Experiências de Viagem
|
||||||
|
|
||||||
|
### Comando de Execução
|
||||||
|
```bash
|
||||||
|
go run examples/protoagent/example_travel.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Saída Esperada no Terminal
|
||||||
|
|
||||||
|
```
|
||||||
|
📦 Resumo dos Artefatos Gerados:
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
🤖 Agente: Travel Experience Platform
|
||||||
|
Descrição: Plataforma de experiências de viagem onde viajantes compartilham...
|
||||||
|
Tools: [api_client webhook_handler file_tool]
|
||||||
|
Skills: [filterpersonaldata_skill enrichstorycontent_skill curatecontent_skill]
|
||||||
|
|
||||||
|
💾 Schemas de Banco de Dados: 4
|
||||||
|
- TravelStory (nosql)
|
||||||
|
Coleção: travel_stories
|
||||||
|
Schema: {title: string, content: text, status: string, ...}
|
||||||
|
- TravelerProfile (nosql)
|
||||||
|
Coleção: traveler_profiles
|
||||||
|
- MediaAsset (nosql)
|
||||||
|
Coleção: media_assets
|
||||||
|
- SocialMediaPublication (nosql)
|
||||||
|
Coleção: social_publications
|
||||||
|
|
||||||
|
🖥️ Interfaces: 2
|
||||||
|
- API (api)
|
||||||
|
Endpoints: 10
|
||||||
|
[POST] /api/v1/submittavelstory - Submeter relato com mídias
|
||||||
|
[POST] /api/v1/filterpersonaldata - Filtrar informações pessoais (PII)
|
||||||
|
[POST] /api/v1/enrichstorycontent - Coletar informações adicionais
|
||||||
|
[POST] /api/v1/generatesocialmediaarticle - Criar artigos para redes sociais
|
||||||
|
[POST] /api/v1/curatecontent - Curadoria de conteúdo
|
||||||
|
[POST] /api/v1/publishtosocialmedia - Publicar em redes sociais
|
||||||
|
[POST] /api/v1/monitorengagement - Monitorar engajamento
|
||||||
|
[POST] /api/v1/managetravelerprofile - Gerenciar perfil de viajante
|
||||||
|
[POST] /api/v1/externalapiintegration - Integrar APIs externas
|
||||||
|
[POST] /api/v1/updatestory - Atualizar relatos existentes
|
||||||
|
- Web UI (web)
|
||||||
|
Telas: 10
|
||||||
|
📱 SubmitTravelStory (/submittavelstory)
|
||||||
|
Componentes: 7
|
||||||
|
📱 CurateContent (/curatecontent)
|
||||||
|
Componentes: 4
|
||||||
|
...
|
||||||
|
|
||||||
|
📱 Canais de Comunicação: 2
|
||||||
|
- webhook (webhook) - Habilitado: true
|
||||||
|
Configuração:
|
||||||
|
path: /webhook
|
||||||
|
secret: ${WEBHOOK_SECRET}
|
||||||
|
- messaging (messaging) - Habilitado: true
|
||||||
|
Configuração:
|
||||||
|
provider: ${MESSAGING_PROVIDER}
|
||||||
|
|
||||||
|
🔐 Políticas OPA: 4
|
||||||
|
- rbac_policy (authz.rbac)
|
||||||
|
Descrição: Role-Based Access Control policy
|
||||||
|
- privacy_protection_policy (authz.custom)
|
||||||
|
Descrição: Sistema deve detectar e remover automaticamente PII
|
||||||
|
- content_moderation_policy (authz.custom)
|
||||||
|
Descrição: Conteúdo deve passar por moderação antes de publicação
|
||||||
|
- data_access_policy (authz.data_access)
|
||||||
|
Descrição: Data access control based on classification levels
|
||||||
|
|
||||||
|
🎯 Skills: 5
|
||||||
|
- filterpersonaldata_skill
|
||||||
|
Descrição: Filtrar automaticamente informações pessoais dos relatos
|
||||||
|
Triggers: [FilterPersonalData]
|
||||||
|
Dependências: [pii_detection_lib]
|
||||||
|
- enrichstorycontent_skill
|
||||||
|
Descrição: Coletar informações adicionais do autor
|
||||||
|
Triggers: [EnrichStoryContent]
|
||||||
|
- curatecontent_skill
|
||||||
|
Descrição: Realizar curadoria do conteúdo
|
||||||
|
Triggers: [CurateContent]
|
||||||
|
- generatesocialmediaarticle_skill
|
||||||
|
Descrição: Elaborar artigos formatados para redes sociais
|
||||||
|
Triggers: [GenerateSocialMediaArticle]
|
||||||
|
- monitorengagement_skill
|
||||||
|
Descrição: Monitorar engajamento das publicações
|
||||||
|
Triggers: [MonitorEngagement]
|
||||||
|
|
||||||
|
🔧 Tools: 5
|
||||||
|
- api_tool (custom)
|
||||||
|
- webhook_handler_tool (custom)
|
||||||
|
- file_tool (custom)
|
||||||
|
- external_api_tool (custom)
|
||||||
|
Configuração:
|
||||||
|
type: http
|
||||||
|
providers: instagram,facebook,twitter,linkedin
|
||||||
|
- ai_tool (custom)
|
||||||
|
Configuração:
|
||||||
|
type: ai
|
||||||
|
capabilities: pii_detection,content_generation
|
||||||
|
|
||||||
|
🔌 Servidores MCP: 1
|
||||||
|
- default (stdio)
|
||||||
|
Comando: mcp-server --config ${MCP_CONFIG_PATH}
|
||||||
|
|
||||||
|
✅ Validação: true
|
||||||
|
Sugestões: 2
|
||||||
|
💡 Enable AI features for content enrichment and curation
|
||||||
|
💡 Configure external API credentials for social media platforms
|
||||||
|
|
||||||
|
📄 AGENT.json e AGENT.md salvos
|
||||||
|
📄 Schema TravelStory salvo
|
||||||
|
📄 Schema TravelerProfile salvo
|
||||||
|
📄 Schema MediaAsset salvo
|
||||||
|
📄 Schema SocialMediaPublication salvo
|
||||||
|
📄 Política rbac_policy salva (JSON)
|
||||||
|
📄 Código Rego rbac_policy salvo
|
||||||
|
... (outras políticas)
|
||||||
|
📄 interfaces.json salvo
|
||||||
|
📄 channels.json salvo
|
||||||
|
📄 skills.json salvo
|
||||||
|
📄 Códigos das skills salvos
|
||||||
|
📄 tools.json salvo
|
||||||
|
📄 mcp_config.json salvo
|
||||||
|
📄 validation_report.json salvo
|
||||||
|
|
||||||
|
✅ Artefatos gerados com sucesso!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arquivos Gerados em `output/travel/`
|
||||||
|
|
||||||
|
```
|
||||||
|
output/travel/
|
||||||
|
├── AGENT.json
|
||||||
|
├── AGENT.md
|
||||||
|
├── schema_0_travelstory.json
|
||||||
|
├── schema_1_travelerprofile.json
|
||||||
|
├── schema_2_mediaasset.json
|
||||||
|
├── schema_3_socialpublication.json
|
||||||
|
├── policy_0_rbac_policy.rego.json
|
||||||
|
├── policy_0_rbac_policy.rego
|
||||||
|
├── policy_1_privacy_protection_policy.rego.json
|
||||||
|
├── policy_1_privacy_protection_policy.rego
|
||||||
|
├── policy_2_content_moderation_policy.rego.json
|
||||||
|
├── policy_2_content_moderation_policy.rego
|
||||||
|
├── policy_3_data_access_policy.rego.json
|
||||||
|
├── policy_3_data_access_policy.rego
|
||||||
|
├── interfaces.json
|
||||||
|
├── channels.json
|
||||||
|
├── skills.json
|
||||||
|
├── skill_0_filterpersonaldata_skill.go
|
||||||
|
├── skill_1_enrichstorycontent_skill.go
|
||||||
|
├── skill_2_curatecontent_skill.go
|
||||||
|
├── skill_3_generatesocialmediaarticle_skill.go
|
||||||
|
├── skill_4_monitorengagement_skill.go
|
||||||
|
├── tools.json
|
||||||
|
├── mcp_config.json
|
||||||
|
└── validation_report.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exemplo de Política de Privacidade (policy_1_privacy_protection_policy.rego)
|
||||||
|
|
||||||
|
```rego
|
||||||
|
package authz.custom
|
||||||
|
|
||||||
|
# Policy: PrivacyProtection
|
||||||
|
# Description: Sistema deve detectar e remover automaticamente informações pessoais identificáveis (PII)
|
||||||
|
|
||||||
|
default allow = false
|
||||||
|
|
||||||
|
# PII detection enabled
|
||||||
|
pii_detection_enabled {
|
||||||
|
input.pii_detection == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Auto redaction enabled
|
||||||
|
auto_redaction_enabled {
|
||||||
|
input.auto_redaction == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
# GDPR compliance check
|
||||||
|
gdpr_compliant {
|
||||||
|
input.gdpr_compliance == "true"
|
||||||
|
input.consent_obtained == true
|
||||||
|
}
|
||||||
|
|
||||||
|
allow {
|
||||||
|
pii_detection_enabled
|
||||||
|
auto_redaction_enabled
|
||||||
|
gdpr_compliant
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exemplo de Skill Gerada (skill_0_filterpersonaldata_skill.go)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Auto-generated skill for: FilterPersonalData
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilterPersonalDataSkill filters personally identifiable information from content
|
||||||
|
func FilterPersonalDataSkill(ctx context.Context, params map[string]interface{}) (interface{}, error) {
|
||||||
|
// Preconditions: Relato deve estar em revisão
|
||||||
|
|
||||||
|
content, ok := params["story_content"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("story_content is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect PII patterns
|
||||||
|
patterns := map[string]*regexp.Regexp{
|
||||||
|
"email": regexp.MustCompile(`[\w\.-]+@[\w\.-]+\.\w+`),
|
||||||
|
"phone": regexp.MustCompile(`\+?\d[\d\s\-\(\)]{8,}\d`),
|
||||||
|
"cpf": regexp.MustCompile(`\d{3}\.?\d{3}\.?\d{3}-?\d{2}`),
|
||||||
|
}
|
||||||
|
|
||||||
|
detectedPII := make([]map[string]string, 0)
|
||||||
|
filteredContent := content
|
||||||
|
|
||||||
|
for piiType, pattern := range patterns {
|
||||||
|
matches := pattern.FindAllString(content, -1)
|
||||||
|
for _, match := range matches {
|
||||||
|
detectedPII = append(detectedPII, map[string]string{
|
||||||
|
"type": piiType,
|
||||||
|
"value": match,
|
||||||
|
})
|
||||||
|
// Redact PII
|
||||||
|
filteredContent = regexp.MustCompile(regexp.QuoteMeta(match)).
|
||||||
|
ReplaceAllString(filteredContent, "[REDACTED]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[string]interface{}{
|
||||||
|
"filtered_content": filteredContent,
|
||||||
|
"detected_pii": detectedPII,
|
||||||
|
"confidence_score": float64(len(detectedPII)) / float64(len(content)) * 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Como Testar as Políticas OPA Geradas
|
||||||
|
|
||||||
|
### Instalando OPA
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux/Mac
|
||||||
|
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static
|
||||||
|
chmod +x opa
|
||||||
|
sudo mv opa /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testando Política RBAC
|
||||||
|
|
||||||
|
Crie um arquivo `input.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user": {
|
||||||
|
"id": "user123",
|
||||||
|
"roles": ["attendant"]
|
||||||
|
},
|
||||||
|
"permission": "add_grains",
|
||||||
|
"resource": "grain_transaction"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd output/cafeteria
|
||||||
|
opa eval -i input.json -d policy_0_rbac_policy.rego "data.authz.rbac.allow"
|
||||||
|
# Resultado esperado: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Teste com permissão não autorizada:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user": {
|
||||||
|
"id": "customer456",
|
||||||
|
"roles": ["customer"]
|
||||||
|
},
|
||||||
|
"permission": "add_grains"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
opa eval -i input.json -d policy_0_rbac_policy.rego "data.authz.rbac.allow"
|
||||||
|
# Resultado esperado: false
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Próximos Passos
|
||||||
|
|
||||||
|
1. **Revisar Artefatos**: Analise os arquivos gerados
|
||||||
|
2. **Customizar**: Ajuste conforme necessidades específicas
|
||||||
|
3. **Configurar Credenciais**:
|
||||||
|
- Token do Telegram
|
||||||
|
- Connection string do banco de dados
|
||||||
|
- Chaves de API para redes sociais
|
||||||
|
4. **Implementar Skills**: Complete a lógica de negócio nas skills geradas
|
||||||
|
5. **Testar Localmente**: Execute o agente em modo de desenvolvimento
|
||||||
|
6. **Implantar**: Copie artefatos para produção
|
||||||
|
|
||||||
|
Para mais detalhes, consulte `examples/protoagent/README.md`.
|
||||||
266
examples/protoagent/README.md
Normal file
266
examples/protoagent/README.md
Normal file
|
|
@ -0,0 +1,266 @@
|
||||||
|
# Exemplos de Uso do ProtoAgent
|
||||||
|
|
||||||
|
Este diretório contém exemplos funcionais de como utilizar o ProtoAgent para prototipar diferentes tipos de sistemas.
|
||||||
|
|
||||||
|
## 📋 Visão Geral
|
||||||
|
|
||||||
|
O ProtoAgent transforma requisitos funcionais e não-funcionais em artefatos prontos para uso:
|
||||||
|
- Configurações de agente (AGENT.md)
|
||||||
|
- Schemas de banco de dados (SQL/NoSQL)
|
||||||
|
- Interfaces (API, Web UI)
|
||||||
|
- Canais de comunicação (Telegram, Discord, etc.)
|
||||||
|
- Políticas OPA (Open Policy Agent)
|
||||||
|
- Skills personalizadas
|
||||||
|
- Tools de integração
|
||||||
|
- Configuração MCP
|
||||||
|
|
||||||
|
## 🎯 Exemplos Disponíveis
|
||||||
|
|
||||||
|
### a) Sistema de Fidelização de Cafeteria
|
||||||
|
|
||||||
|
**Arquivo:** `cafeteria-loyalty-system.json`
|
||||||
|
|
||||||
|
Um sistema onde clientes acumulam "grãos" baseados no consumo e podem trocar por produtos.
|
||||||
|
|
||||||
|
#### Características Principais:
|
||||||
|
- **Canal Telegram**: Clientes consultam saldo via bot
|
||||||
|
- **Atendentes**: Gerenciam contas, adicionam/consumem grãos
|
||||||
|
- **Catálogo de Produtos**: Resgate com grãos acumulados
|
||||||
|
- **Histórico de Transações**: Auditoria completa
|
||||||
|
|
||||||
|
#### Requisitos Funcionais:
|
||||||
|
1. `ManageCustomer` - Cadastro de clientes
|
||||||
|
2. `AddGrains` - Adicionar grãos por compra
|
||||||
|
3. `RedeemGrains` - Resgatar produtos
|
||||||
|
4. `ConsultBalance` - Consultar saldo
|
||||||
|
5. `ManageProducts` - Gerenciar catálogo
|
||||||
|
6. `TelegramBalanceQuery` - Consulta via Telegram
|
||||||
|
7. `TransactionHistory` - Histórico de transações
|
||||||
|
|
||||||
|
#### Requisitos Não-Funcionais:
|
||||||
|
- Autenticação JWT para atendentes
|
||||||
|
- Resposta em < 2 segundos para consultas
|
||||||
|
- Registro imutável de transações
|
||||||
|
- Suporte a 50 usuários concorrentes
|
||||||
|
|
||||||
|
#### Papéis e Permissões:
|
||||||
|
- **admin**: Todas as operações
|
||||||
|
- **attendant**: Ler, escrever, adicionar/consumir grãos
|
||||||
|
- **customer**: Apenas consultar próprio saldo
|
||||||
|
|
||||||
|
#### Como Executar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace
|
||||||
|
go run examples/protoagent/example_cafeteria.go
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Artefatos Gerados:
|
||||||
|
- `output/cafeteria/AGENT.json` - Configuração do agente
|
||||||
|
- `output/cafeteria/schema_*.json` - Schemas de banco de dados
|
||||||
|
- `output/cafeteria/schema_*.sql` - DDL SQL
|
||||||
|
- `output/cafeteria/policy_*.rego` - Políticas OPA
|
||||||
|
- `output/cafeteria/channels.json` - Configuração Telegram
|
||||||
|
- `output/cafeteria/validation_report.json` - Relatório de validação
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### b) Plataforma de Experiências de Viagem
|
||||||
|
|
||||||
|
**Arquivo:** `travel-experience-platform.json`
|
||||||
|
|
||||||
|
Plataforma onde viajantes compartilham relatos com mídias. O sistema atua como editor, filtrando dados pessoais, enriquecendo conteúdo e publicando em redes sociais.
|
||||||
|
|
||||||
|
#### Características Principais:
|
||||||
|
- **Submissão de Relatos**: Viajantes enviam histórias com fotos/vídeos
|
||||||
|
- **Filtragem de PII**: Detecção automática de dados pessoais
|
||||||
|
- **Enriquecimento com IA**: Solicita informações adicionais aos autores
|
||||||
|
- **Geração de Artigos**: Cria conteúdo formatado para redes sociais
|
||||||
|
- **Curadoria**: Aprovação, rejeição ou solicitação de atualizações
|
||||||
|
- **Publicação Automática**: Integração com APIs de redes sociais
|
||||||
|
- **Monitoramento de Engajamento**: Analytics das publicações
|
||||||
|
|
||||||
|
#### Requisitos Funcionais:
|
||||||
|
1. `SubmitTravelStory` - Submeter relato com mídias
|
||||||
|
2. `FilterPersonalData` - Filtrar informações pessoais (PII)
|
||||||
|
3. `EnrichStoryContent` - Coletar informações adicionais
|
||||||
|
4. `GenerateSocialMediaArticle` - Criar artigos para redes sociais
|
||||||
|
5. `CurateContent` - Curadoria (aprovar/rejeitar/atualizar)
|
||||||
|
6. `PublishToSocialMedia` - Publicar em Instagram, Facebook, etc.
|
||||||
|
7. `MonitorEngagement` - Acompanhar likes, shares, comments
|
||||||
|
8. `ManageTravelerProfile` - Perfis de viajantes
|
||||||
|
9. `ExternalAPIIntegration` - Integrar com APIs externas (clima, mapas)
|
||||||
|
10. `UpdateStory` - Atualizar relatos existentes
|
||||||
|
|
||||||
|
#### Requisitos Não-Funcionais:
|
||||||
|
- **Privacidade**: Detecção e remoção automática de PII (GDPR compliant)
|
||||||
|
- **Moderação**: Conteúdo revisado antes de publicação
|
||||||
|
- **Performance**: Processamento em até 30 segundos
|
||||||
|
- **Persistência**: Backup diário, histórico de versões (5 anos)
|
||||||
|
- **Escalabilidade**: Armazenamento cloud com CDN
|
||||||
|
- **Disponibilidade**: Fallback para APIs indisponíveis
|
||||||
|
|
||||||
|
#### Papéis e Permissões:
|
||||||
|
- **admin**: Todas as operações
|
||||||
|
- **curator**: Aprovar, rejeitar, publicar conteúdo
|
||||||
|
- **traveler**: Criar/editar próprios relatos
|
||||||
|
- **viewer**: Apenas leitura
|
||||||
|
|
||||||
|
#### Recursos de IA:
|
||||||
|
- Detecção de PII (informações pessoais identificáveis)
|
||||||
|
- Enriquecimento de conteúdo
|
||||||
|
- Geração automática de artigos
|
||||||
|
- Assistência na curadoria
|
||||||
|
|
||||||
|
#### Como Executar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspace
|
||||||
|
go run examples/protoagent/example_travel.go
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Artefatos Gerados:
|
||||||
|
- `output/travel/AGENT.json` + `AGENT.md` - Configuração do agente
|
||||||
|
- `output/travel/schema_*.json` + `.sql` - Schemas de banco de dados
|
||||||
|
- `output/travel/interfaces.json` - Definições de API e UI
|
||||||
|
- `output/travel/policy_*.rego` - Políticas OPA (RBAC, PII, moderação)
|
||||||
|
- `output/travel/channels.json` - Canais de comunicação
|
||||||
|
- `output/travel/skills.json` + `.go` - Skills personalizadas
|
||||||
|
- `output/travel/tools.json` - Ferramentas de integração
|
||||||
|
- `output/travel/mcp_config.json` - Configuração MCP
|
||||||
|
- `output/travel/validation_report.json` - Relatório de validação
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Estrutura dos Arquivos de Requisitos
|
||||||
|
|
||||||
|
Os arquivos JSON seguem esta estrutura:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"name": "Nome do Sistema",
|
||||||
|
"description": "Descrição detalhada",
|
||||||
|
|
||||||
|
"functionalRequirements": [
|
||||||
|
{
|
||||||
|
"id": "FR001",
|
||||||
|
"type": "action|operation|resource|actor",
|
||||||
|
"name": "NomeDaOperacao",
|
||||||
|
"description": "Descrição do que faz",
|
||||||
|
"inputs": [...],
|
||||||
|
"outputs": [...],
|
||||||
|
"preconditions": [...],
|
||||||
|
"postconditions": [...],
|
||||||
|
"interactionMethods": ["api", "ui", "messaging", "webhook", "mcp"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"nonFunctionalRequirements": [
|
||||||
|
{
|
||||||
|
"id": "NFR001",
|
||||||
|
"category": "security|performance|reliability|scalability",
|
||||||
|
"name": "NomeRequisito",
|
||||||
|
"description": "Descrição",
|
||||||
|
"constraints": {...},
|
||||||
|
"metrics": [...]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"securityRequirements": [
|
||||||
|
{
|
||||||
|
"roles": ["admin", "user"],
|
||||||
|
"permissions": ["read", "write", "delete"],
|
||||||
|
"authorizations": ["role_based"],
|
||||||
|
"securityControls": ["authentication", "authorization"],
|
||||||
|
"dataClassification": "internal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"performanceRequirements": [...],
|
||||||
|
"metadata": {...}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Métodos de Interação Suportados
|
||||||
|
|
||||||
|
- `api` - Integração via API REST/GraphQL
|
||||||
|
- `mcp` - Model Context Protocol
|
||||||
|
- `ui` - Interface de usuário (web/cli)
|
||||||
|
- `messaging` - Aplicativos de mensagem (Telegram, Discord, Slack)
|
||||||
|
- `webhook` - Webhooks para integrações
|
||||||
|
- `cli` - Interface de linha de comando
|
||||||
|
- `database` - Acesso direto ao banco de dados
|
||||||
|
- `file` - Operações com arquivos
|
||||||
|
- `eventbus` - Barramento de eventos
|
||||||
|
|
||||||
|
## 📊 Tipos de Artefatos Gerados
|
||||||
|
|
||||||
|
### 1. Configuração de Agente (AGENT.md)
|
||||||
|
Define o comportamento, tools, skills e instruções do agente.
|
||||||
|
|
||||||
|
### 2. Schemas de Banco de Dados
|
||||||
|
- **SQL**: Tabelas com colunas, tipos, chaves primárias, índices
|
||||||
|
- **NoSQL**: Collections com schemas JSON
|
||||||
|
- **Migrações**: Scripts de criação/atualização
|
||||||
|
|
||||||
|
### 3. Interfaces
|
||||||
|
- **API**: Endpoints REST com métodos, paths, autenticação
|
||||||
|
- **Web UI**: Telas, rotas, componentes, ações
|
||||||
|
|
||||||
|
### 4. Canais de Comunicação
|
||||||
|
- **Telegram**: Bot com comandos e handlers
|
||||||
|
- **Discord/Slack**: Integrações similares
|
||||||
|
- **Webhooks**: Endpoints para recebimento de eventos
|
||||||
|
|
||||||
|
### 5. Políticas OPA (Open Policy Agent)
|
||||||
|
- **RBAC**: Controle de acesso baseado em papéis
|
||||||
|
- **Autorização**: Políticas customizadas
|
||||||
|
- **Acesso a Dados**: Classificação e controle de sensibilidade
|
||||||
|
|
||||||
|
### 6. Skills
|
||||||
|
Código Go personalizado para operações específicas do domínio.
|
||||||
|
|
||||||
|
### 7. Tools
|
||||||
|
Configurações para ferramentas de integração (HTTP, database, filesystem).
|
||||||
|
|
||||||
|
### 8. Configuração MCP
|
||||||
|
Servidores Model Context Protocol para contexto adicional.
|
||||||
|
|
||||||
|
## 🛠️ Workflow de Desenvolvimento
|
||||||
|
|
||||||
|
1. **Definir Requisitos**: Crie um arquivo JSON descrevendo funcionalidades e restrições
|
||||||
|
2. **Executar ProtoAgent**: Rode o exemplo correspondente
|
||||||
|
3. **Revisar Artefatos**: Analise os arquivos gerados em `output/`
|
||||||
|
4. **Customizar**: Ajuste conforme necessidades específicas
|
||||||
|
5. **Implantar**: Copie artefatos para o workspace do PicoClaw
|
||||||
|
|
||||||
|
## 📝 Próximos Passos Sugeridos
|
||||||
|
|
||||||
|
Após gerar os artefatos:
|
||||||
|
|
||||||
|
1. **Configurar Credenciais**:
|
||||||
|
- Token do Telegram em `channels.json`
|
||||||
|
- URLs de APIs externas
|
||||||
|
- Connection string do banco de dados
|
||||||
|
|
||||||
|
2. **Implementar Skills**:
|
||||||
|
- Complete o código das skills geradas
|
||||||
|
- Adicione lógica de negócio específica
|
||||||
|
|
||||||
|
3. **Testar Políticas OPA**:
|
||||||
|
```bash
|
||||||
|
opa eval -i input.json -d policy.rego "authz.rbac.allow"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Integrar com PicoClaw**:
|
||||||
|
- Copie `AGENT.md` para `workspace/`
|
||||||
|
- Coloque skills em `workspace/skills/`
|
||||||
|
- Configure channels no config do PicoClaw
|
||||||
|
|
||||||
|
## 📞 Suporte
|
||||||
|
|
||||||
|
Para mais informações, consulte:
|
||||||
|
- `pkg/protoagent/README.md` - Documentação completa do ProtoAgent
|
||||||
|
- `docs/configuration.md` - Configuração do PicoClaw
|
||||||
|
- `examples/` - Outros exemplos de uso
|
||||||
197
examples/protoagent/cafeteria-loyalty-system.json
Normal file
197
examples/protoagent/cafeteria-loyalty-system.json
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"name": "Cafeteria Loyalty System",
|
||||||
|
"description": "Sistema de fidelização de cafeteria onde clientes acumulam 'grãos' baseados no consumo e podem trocar por produtos. Atendentes gerenciam contas de clientes e transações de grãos.",
|
||||||
|
|
||||||
|
"functionalRequirements": [
|
||||||
|
{
|
||||||
|
"id": "FR001",
|
||||||
|
"type": "resource",
|
||||||
|
"name": "ManageCustomer",
|
||||||
|
"description": "Gerenciar cadastro de clientes do programa de fidelidade",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "customer_id", "type": "string", "required": true, "description": "Identificador único do cliente"},
|
||||||
|
{"name": "name", "type": "string", "required": true, "description": "Nome do cliente"},
|
||||||
|
{"name": "phone", "type": "string", "required": true, "description": "Telefone/WhatsApp para Telegram"},
|
||||||
|
{"name": "email", "type": "string", "required": false, "description": "Email do cliente"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "customer", "type": "object", "description": "Dados completos do cliente"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["api", "ui", "messaging"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR002",
|
||||||
|
"type": "action",
|
||||||
|
"name": "AddGrains",
|
||||||
|
"description": "Adicionar grãos à conta do cliente baseado na compra",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "customer_id", "type": "string", "required": true, "description": "ID do cliente"},
|
||||||
|
{"name": "amount", "type": "integer", "required": true, "description": "Quantidade de grãos a adicionar"},
|
||||||
|
{"name": "purchase_value", "type": "decimal", "required": true, "description": "Valor da compra em reais"},
|
||||||
|
{"name": "attendant_id", "type": "string", "required": true, "description": "ID do atendente realizando a operação"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "new_balance", "type": "integer", "description": "Novo saldo de grãos"},
|
||||||
|
{"name": "transaction_id", "type": "string", "description": "ID da transação"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Cliente deve estar cadastrado", "Atendente deve estar autenticado"],
|
||||||
|
"postconditions": ["Saldo atualizado", "Transação registrada"],
|
||||||
|
"interactionMethods": ["api", "ui"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR003",
|
||||||
|
"type": "action",
|
||||||
|
"name": "RedeemGrains",
|
||||||
|
"description": "Consumir grãos para resgatar produtos",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "customer_id", "type": "string", "required": true, "description": "ID do cliente"},
|
||||||
|
{"name": "product_id", "type": "string", "required": true, "description": "ID do produto a resgatar"},
|
||||||
|
{"name": "grains_required", "type": "integer", "required": true, "description": "Quantidade de grãos necessários"},
|
||||||
|
{"name": "attendant_id", "type": "string", "required": true, "description": "ID do atendente"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "success", "type": "boolean", "description": "Se o resgate foi bem sucedido"},
|
||||||
|
{"name": "remaining_balance", "type": "integer", "description": "Saldo restante após resgate"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Cliente deve ter saldo suficiente", "Produto deve estar disponível"],
|
||||||
|
"postconditions": ["Saldo deduzido", "Resgate registrado", "Produto marcado como entregue"],
|
||||||
|
"interactionMethods": ["api", "ui"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR004",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "ConsultBalance",
|
||||||
|
"description": "Consultar saldo de grãos do cliente",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "customer_id", "type": "string", "required": true, "description": "ID do cliente"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "balance", "type": "integer", "description": "Saldo atual de grãos"},
|
||||||
|
{"name": "last_updated", "type": "timestamp", "description": "Data da última atualização"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["api", "ui", "messaging"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR005",
|
||||||
|
"type": "resource",
|
||||||
|
"name": "ManageProducts",
|
||||||
|
"description": "Gerenciar catálogo de produtos disponíveis para resgate",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "product_id", "type": "string", "required": false, "description": "ID do produto"},
|
||||||
|
{"name": "name", "type": "string", "required": true, "description": "Nome do produto"},
|
||||||
|
{"name": "grains_cost", "type": "integer", "required": true, "description": "Custo em grãos"},
|
||||||
|
{"name": "quantity", "type": "integer", "required": true, "description": "Quantidade disponível"},
|
||||||
|
{"name": "category", "type": "string", "required": false, "description": "Categoria do produto"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "product", "type": "object", "description": "Dados do produto"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["api", "ui"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR006",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "TelegramBalanceQuery",
|
||||||
|
"description": "Permitir que clientes consultem saldo via Telegram",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "telegram_user_id", "type": "string", "required": true, "description": "ID do usuário no Telegram"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "balance", "type": "integer", "description": "Saldo de grãos"},
|
||||||
|
{"name": "message", "type": "string", "description": "Mensagem formatada para Telegram"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["messaging"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR007",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "TransactionHistory",
|
||||||
|
"description": "Listar histórico de transações de grãos",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "customer_id", "type": "string", "required": true, "description": "ID do cliente"},
|
||||||
|
{"name": "limit", "type": "integer", "required": false, "description": "Número máximo de registros", "default": "10"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "transactions", "type": "array", "description": "Lista de transações"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["api", "ui", "messaging"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"nonFunctionalRequirements": [
|
||||||
|
{
|
||||||
|
"id": "NFR001",
|
||||||
|
"category": "security",
|
||||||
|
"name": "Authentication",
|
||||||
|
"description": "Todos os atendentes devem ser autenticados antes de realizar operações",
|
||||||
|
"constraints": {
|
||||||
|
"auth_type": "jwt",
|
||||||
|
"token_expiry": "8h",
|
||||||
|
"mfa_required": "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR002",
|
||||||
|
"category": "performance",
|
||||||
|
"name": "ResponseTime",
|
||||||
|
"description": "Operações de consulta de saldo devem responder em menos de 2 segundos",
|
||||||
|
"constraints": {
|
||||||
|
"max_response_time": "2s",
|
||||||
|
"percentile": "95"
|
||||||
|
},
|
||||||
|
"metrics": [
|
||||||
|
{"name": "avg_response_time", "target": "< 1s", "unit": "seconds"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR003",
|
||||||
|
"category": "reliability",
|
||||||
|
"name": "DataIntegrity",
|
||||||
|
"description": "Todas as transações de grãos devem ser registradas de forma imutável",
|
||||||
|
"constraints": {
|
||||||
|
"audit_log": "true",
|
||||||
|
"transaction_log": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR004",
|
||||||
|
"category": "scalability",
|
||||||
|
"name": "Concurrency",
|
||||||
|
"description": "Sistema deve suportar múltiplos atendentes operando simultaneamente",
|
||||||
|
"constraints": {
|
||||||
|
"max_concurrent_users": "50"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"securityRequirements": [
|
||||||
|
{
|
||||||
|
"roles": ["admin", "attendant", "customer"],
|
||||||
|
"permissions": ["read", "write", "delete", "redeem", "add_grains"],
|
||||||
|
"authorizations": ["role_based", "resource_owner"],
|
||||||
|
"securityControls": ["authentication", "authorization", "audit_logging"],
|
||||||
|
"dataClassification": "internal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"performanceRequirements": [
|
||||||
|
{
|
||||||
|
"responseTime": "2s",
|
||||||
|
"throughput": 100,
|
||||||
|
"concurrency": 50,
|
||||||
|
"resourceLimits": {
|
||||||
|
"memory": "512MB",
|
||||||
|
"cpu": "1 core",
|
||||||
|
"storage": "10GB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"metadata": {
|
||||||
|
"domain": "retail",
|
||||||
|
"industry": "food_beverage",
|
||||||
|
"channel_priority": ["telegram", "ui", "api"],
|
||||||
|
"database_type": "sql"
|
||||||
|
}
|
||||||
|
}
|
||||||
224
examples/protoagent/example_cafeteria.go
Normal file
224
examples/protoagent/example_cafeteria.go
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/protoagent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Este exemplo demonstra como usar o ProtoAgent para gerar artefatos
|
||||||
|
// a partir de requisitos de um sistema de fidelização de cafeteria
|
||||||
|
func main() {
|
||||||
|
// Carregar requisitos do arquivo JSON
|
||||||
|
reqs, err := loadRequirements("examples/protoagent/cafeteria-loyalty-system.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erro ao carregar requisitos: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurar o engine do ProtoAgent
|
||||||
|
config := protoagent.EngineConfig{
|
||||||
|
OutputDir: "./output/cafeteria",
|
||||||
|
Workspace: "./workspace",
|
||||||
|
EnableOPA: true,
|
||||||
|
EnableAI: false,
|
||||||
|
DryRun: false,
|
||||||
|
Verbose: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
engine := protoagent.NewEngine(config)
|
||||||
|
|
||||||
|
// Processar requisitos e gerar artefatos
|
||||||
|
ctx := context.Background()
|
||||||
|
artifacts, err := engine.ProcessRequirements(ctx, reqs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erro ao processar requisitos: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exibir resumo dos artefatos gerados
|
||||||
|
printArtifactsSummary(artifacts)
|
||||||
|
|
||||||
|
// Salvar artefatos em arquivos
|
||||||
|
if err := saveArtifacts(artifacts); err != nil {
|
||||||
|
fmt.Printf("Erro ao salvar artefatos: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n✅ Artefatos gerados com sucesso!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadRequirements carrega requisitos de um arquivo JSON
|
||||||
|
func loadRequirements(filename string) (*protoagent.RequirementsDocument, error) {
|
||||||
|
data, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("falha ao ler arquivo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqs protoagent.RequirementsDocument
|
||||||
|
if err := json.Unmarshal(data, &reqs); err != nil {
|
||||||
|
return nil, fmt.Errorf("falha ao parsear JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &reqs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// printArtifactsSummary exibe um resumo dos artefatos gerados
|
||||||
|
func printArtifactsSummary(artifacts *protoagent.GeneratedArtifacts) {
|
||||||
|
fmt.Println("\n📦 Resumo dos Artefatos Gerados:")
|
||||||
|
fmt.Println("=" + string(make([]byte, 50)))
|
||||||
|
|
||||||
|
if artifacts.AgentConfig != nil {
|
||||||
|
fmt.Printf("🤖 Agente: %s\n", artifacts.AgentConfig.Name)
|
||||||
|
fmt.Printf(" Descrição: %s\n", artifacts.AgentConfig.Description)
|
||||||
|
fmt.Printf(" Tools: %v\n", artifacts.AgentConfig.Tools)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.DatabaseSchemas) > 0 {
|
||||||
|
fmt.Printf("\n💾 Schemas de Banco de Dados: %d\n", len(artifacts.DatabaseSchemas))
|
||||||
|
for _, schema := range artifacts.DatabaseSchemas {
|
||||||
|
fmt.Printf(" - %s (%s)\n", schema.Name, schema.Type)
|
||||||
|
if len(schema.Tables) > 0 {
|
||||||
|
for _, table := range schema.Tables {
|
||||||
|
fmt.Printf(" Tabela: %s (%d colunas)\n", table.Name, len(table.Columns))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Interfaces) > 0 {
|
||||||
|
fmt.Printf("\n🖥️ Interfaces: %d\n", len(artifacts.Interfaces))
|
||||||
|
for _, iface := range artifacts.Interfaces {
|
||||||
|
fmt.Printf(" - %s (%s)\n", iface.Name, iface.Type)
|
||||||
|
if iface.Type == "api" && len(iface.Endpoints) > 0 {
|
||||||
|
fmt.Printf(" Endpoints: %d\n", len(iface.Endpoints))
|
||||||
|
}
|
||||||
|
if iface.Type == "web" && len(iface.Screens) > 0 {
|
||||||
|
fmt.Printf(" Telas: %d\n", len(iface.Screens))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Channels) > 0 {
|
||||||
|
fmt.Printf("\n📱 Canais de Comunicação: %d\n", len(artifacts.Channels))
|
||||||
|
for _, channel := range artifacts.Channels {
|
||||||
|
fmt.Printf(" - %s (%s) - Habilitado: %v\n", channel.Name, channel.Type, channel.Enabled)
|
||||||
|
if len(channel.Commands) > 0 {
|
||||||
|
fmt.Printf(" Comandos: %d\n", len(channel.Commands))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Policies) > 0 {
|
||||||
|
fmt.Printf("\n🔐 Políticas OPA: %d\n", len(artifacts.Policies))
|
||||||
|
for _, policy := range artifacts.Policies {
|
||||||
|
fmt.Printf(" - %s (%s)\n", policy.Name, policy.Package)
|
||||||
|
fmt.Printf(" Descrição: %s\n", policy.Description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Skills) > 0 {
|
||||||
|
fmt.Printf("\n🎯 Skills: %d\n", len(artifacts.Skills))
|
||||||
|
for _, skill := range artifacts.Skills {
|
||||||
|
fmt.Printf(" - %s\n", skill.Name)
|
||||||
|
fmt.Printf(" Descrição: %s\n", skill.Description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Tools) > 0 {
|
||||||
|
fmt.Printf("\n🔧 Tools: %d\n", len(artifacts.Tools))
|
||||||
|
for _, tool := range artifacts.Tools {
|
||||||
|
fmt.Printf(" - %s (%s)\n", tool.Name, tool.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if artifacts.MCPConfig != nil && len(artifacts.MCPConfig.Servers) > 0 {
|
||||||
|
fmt.Printf("\n🔌 Servidores MCP: %d\n", len(artifacts.MCPConfig.Servers))
|
||||||
|
for _, server := range artifacts.MCPConfig.Servers {
|
||||||
|
fmt.Printf(" - %s (%s)\n", server.Name, server.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if artifacts.ValidationReport != nil {
|
||||||
|
fmt.Printf("\n✅ Validação: %v\n", artifacts.ValidationReport.Valid)
|
||||||
|
if len(artifacts.ValidationReport.Errors) > 0 {
|
||||||
|
fmt.Printf(" Erros: %d\n", len(artifacts.ValidationReport.Errors))
|
||||||
|
}
|
||||||
|
if len(artifacts.ValidationReport.Warnings) > 0 {
|
||||||
|
fmt.Printf(" Alertas: %d\n", len(artifacts.ValidationReport.Warnings))
|
||||||
|
}
|
||||||
|
if len(artifacts.ValidationReport.Suggestions) > 0 {
|
||||||
|
fmt.Printf(" Sugestões: %d\n", len(artifacts.ValidationReport.Suggestions))
|
||||||
|
for _, sug := range artifacts.ValidationReport.Suggestions {
|
||||||
|
fmt.Printf(" 💡 %s\n", sug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveArtifacts salva os artefatos em arquivos
|
||||||
|
func saveArtifacts(artifacts *protoagent.GeneratedArtifacts) error {
|
||||||
|
// Criar diretório de saída
|
||||||
|
if err := os.MkdirAll("./output/cafeteria", 0755); err != nil {
|
||||||
|
return fmt.Errorf("falha ao criar diretório: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar AGENT.md
|
||||||
|
if artifacts.AgentConfig != nil {
|
||||||
|
agentJSON, _ := json.MarshalIndent(artifacts.AgentConfig, "", " ")
|
||||||
|
if err := os.WriteFile("./output/cafeteria/AGENT.json", agentJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar AGENT.json: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("\n📄 AGENT.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar schemas de banco de dados
|
||||||
|
for i, schema := range artifacts.DatabaseSchemas {
|
||||||
|
schemaJSON, _ := json.MarshalIndent(schema, "", " ")
|
||||||
|
filename := fmt.Sprintf("./output/cafeteria/schema_%d_%s.json", i, schema.Name)
|
||||||
|
if err := os.WriteFile(filename, schemaJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar schema: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("📄 Schema %s salvo\n", schema.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar políticas OPA
|
||||||
|
for i, policy := range artifacts.Policies {
|
||||||
|
policyJSON, _ := json.MarshalIndent(policy, "", " ")
|
||||||
|
filename := fmt.Sprintf("./output/cafeteria/policy_%d_%s.rego.json", i, policy.Name)
|
||||||
|
if err := os.WriteFile(filename, policyJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar política: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("📄 Política %s salva\n", policy.Name)
|
||||||
|
|
||||||
|
// Salvar também o código Rego puro
|
||||||
|
regoFilename := fmt.Sprintf("./output/cafeteria/policy_%d_%s.rego", i, policy.Name)
|
||||||
|
if err := os.WriteFile(regoFilename, []byte(policy.Rego), 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar rego: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("📄 Código Rego %s salvo\n", policy.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar channels
|
||||||
|
if len(artifacts.Channels) > 0 {
|
||||||
|
channelsJSON, _ := json.MarshalIndent(artifacts.Channels, "", " ")
|
||||||
|
if err := os.WriteFile("./output/cafeteria/channels.json", channelsJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar channels: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 channels.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar relatório de validação
|
||||||
|
if artifacts.ValidationReport != nil {
|
||||||
|
reportJSON, _ := json.MarshalIndent(artifacts.ValidationReport, "", " ")
|
||||||
|
if err := os.WriteFile("./output/cafeteria/validation_report.json", reportJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar validation report: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 validation_report.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
419
examples/protoagent/example_travel.go
Normal file
419
examples/protoagent/example_travel.go
Normal file
|
|
@ -0,0 +1,419 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/protoagent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Este exemplo demonstra como usar o ProtoAgent para gerar artefatos
|
||||||
|
// a partir de requisitos de uma plataforma de experiências de viagem
|
||||||
|
func main() {
|
||||||
|
// Carregar requisitos do arquivo JSON
|
||||||
|
reqs, err := loadRequirements("examples/protoagent/travel-experience-platform.json")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erro ao carregar requisitos: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurar o engine do ProtoAgent
|
||||||
|
config := protoagent.EngineConfig{
|
||||||
|
OutputDir: "./output/travel",
|
||||||
|
Workspace: "./workspace",
|
||||||
|
EnableOPA: true,
|
||||||
|
EnableAI: true, // Habilitar IA para recursos de curadoria e enriquecimento
|
||||||
|
DryRun: false,
|
||||||
|
Verbose: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
engine := protoagent.NewEngine(config)
|
||||||
|
|
||||||
|
// Processar requisitos e gerar artefatos
|
||||||
|
ctx := context.Background()
|
||||||
|
artifacts, err := engine.ProcessRequirements(ctx, reqs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erro ao processar requisitos: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exibir resumo dos artefatos gerados
|
||||||
|
printArtifactsSummary(artifacts)
|
||||||
|
|
||||||
|
// Salvar artefatos em arquivos
|
||||||
|
if err := saveArtifacts(artifacts); err != nil {
|
||||||
|
fmt.Printf("Erro ao salvar artefatos: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n✅ Artefatos gerados com sucesso!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadRequirements carrega requisitos de um arquivo JSON
|
||||||
|
func loadRequirements(filename string) (*protoagent.RequirementsDocument, error) {
|
||||||
|
data, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("falha ao ler arquivo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var reqs protoagent.RequirementsDocument
|
||||||
|
if err := json.Unmarshal(data, &reqs); err != nil {
|
||||||
|
return nil, fmt.Errorf("falha ao parsear JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &reqs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// printArtifactsSummary exibe um resumo dos artefatos gerados
|
||||||
|
func printArtifactsSummary(artifacts *protoagent.GeneratedArtifacts) {
|
||||||
|
fmt.Println("\n📦 Resumo dos Artefatos Gerados:")
|
||||||
|
fmt.Println("=" + string(make([]byte, 50)))
|
||||||
|
|
||||||
|
if artifacts.AgentConfig != nil {
|
||||||
|
fmt.Printf("🤖 Agente: %s\n", artifacts.AgentConfig.Name)
|
||||||
|
fmt.Printf(" Descrição: %s\n", artifacts.AgentConfig.Description)
|
||||||
|
fmt.Printf(" Tools: %v\n", artifacts.AgentConfig.Tools)
|
||||||
|
fmt.Printf(" Skills: %v\n", artifacts.AgentConfig.Skills)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.DatabaseSchemas) > 0 {
|
||||||
|
fmt.Printf("\n💾 Schemas de Banco de Dados: %d\n", len(artifacts.DatabaseSchemas))
|
||||||
|
for _, schema := range artifacts.DatabaseSchemas {
|
||||||
|
fmt.Printf(" - %s (%s)\n", schema.Name, schema.Type)
|
||||||
|
if len(schema.Tables) > 0 {
|
||||||
|
for _, table := range schema.Tables {
|
||||||
|
fmt.Printf(" Tabela: %s (%d colunas)\n", table.Name, len(table.Columns))
|
||||||
|
for _, col := range table.Columns {
|
||||||
|
fmt.Printf(" - %s: %s", col.Name, col.Type)
|
||||||
|
if col.PrimaryKey {
|
||||||
|
fmt.Print(" [PK]")
|
||||||
|
}
|
||||||
|
if !col.Nullable {
|
||||||
|
fmt.Print(" [NOT NULL]")
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Interfaces) > 0 {
|
||||||
|
fmt.Printf("\n🖥️ Interfaces: %d\n", len(artifacts.Interfaces))
|
||||||
|
for _, iface := range artifacts.Interfaces {
|
||||||
|
fmt.Printf(" - %s (%s)\n", iface.Name, iface.Type)
|
||||||
|
if iface.Type == "api" && len(iface.Endpoints) > 0 {
|
||||||
|
fmt.Printf(" Endpoints: %d\n", len(iface.Endpoints))
|
||||||
|
for _, ep := range iface.Endpoints {
|
||||||
|
fmt.Printf(" [%s] %s - %s\n", ep.Method, ep.Path, ep.Description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if iface.Type == "web" && len(iface.Screens) > 0 {
|
||||||
|
fmt.Printf(" Telas: %d\n", len(iface.Screens))
|
||||||
|
for _, screen := range iface.Screens {
|
||||||
|
fmt.Printf(" 📱 %s (%s)\n", screen.Name, screen.Route)
|
||||||
|
if len(screen.Components) > 0 {
|
||||||
|
fmt.Printf(" Componentes: %d\n", len(screen.Components))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Channels) > 0 {
|
||||||
|
fmt.Printf("\n📱 Canais de Comunicação: %d\n", len(artifacts.Channels))
|
||||||
|
for _, channel := range artifacts.Channels {
|
||||||
|
fmt.Printf(" - %s (%s) - Habilitado: %v\n", channel.Name, channel.Type, channel.Enabled)
|
||||||
|
if len(channel.Config) > 0 {
|
||||||
|
fmt.Printf(" Configuração:\n")
|
||||||
|
for k, v := range channel.Config {
|
||||||
|
fmt.Printf(" %s: %s\n", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Policies) > 0 {
|
||||||
|
fmt.Printf("\n🔐 Políticas OPA: %d\n", len(artifacts.Policies))
|
||||||
|
for _, policy := range artifacts.Policies {
|
||||||
|
fmt.Printf(" - %s (%s)\n", policy.Name, policy.Package)
|
||||||
|
fmt.Printf(" Descrição: %s\n", policy.Description)
|
||||||
|
|
||||||
|
// Mostrar preview do código Rego
|
||||||
|
regoLines := splitLines(policy.Rego)
|
||||||
|
if len(regoLines) > 0 {
|
||||||
|
fmt.Printf(" Código Rego (%d linhas):\n", len(regoLines))
|
||||||
|
previewLen := len(regoLines)
|
||||||
|
if previewLen > 5 {
|
||||||
|
previewLen = 5
|
||||||
|
}
|
||||||
|
for i := 0; i < previewLen; i++ {
|
||||||
|
fmt.Printf(" %s\n", regoLines[i])
|
||||||
|
}
|
||||||
|
if len(regoLines) > 5 {
|
||||||
|
fmt.Printf(" ... (%d linhas restantes)\n", len(regoLines)-5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Skills) > 0 {
|
||||||
|
fmt.Printf("\n🎯 Skills: %d\n", len(artifacts.Skills))
|
||||||
|
for _, skill := range artifacts.Skills {
|
||||||
|
fmt.Printf(" - %s\n", skill.Name)
|
||||||
|
fmt.Printf(" Descrição: %s\n", skill.Description)
|
||||||
|
if len(skill.Triggers) > 0 {
|
||||||
|
fmt.Printf(" Triggers: %v\n", skill.Triggers)
|
||||||
|
}
|
||||||
|
if len(skill.Dependencies) > 0 {
|
||||||
|
fmt.Printf(" Dependências: %v\n", skill.Dependencies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(artifacts.Tools) > 0 {
|
||||||
|
fmt.Printf("\n🔧 Tools: %d\n", len(artifacts.Tools))
|
||||||
|
for _, tool := range artifacts.Tools {
|
||||||
|
fmt.Printf(" - %s (%s)\n", tool.Name, tool.Type)
|
||||||
|
fmt.Printf(" Descrição: %s\n", tool.Description)
|
||||||
|
if len(tool.Config) > 0 {
|
||||||
|
fmt.Printf(" Configuração:\n")
|
||||||
|
for k, v := range tool.Config {
|
||||||
|
fmt.Printf(" %s: %s\n", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if artifacts.MCPConfig != nil && len(artifacts.MCPConfig.Servers) > 0 {
|
||||||
|
fmt.Printf("\n🔌 Servidores MCP: %d\n", len(artifacts.MCPConfig.Servers))
|
||||||
|
for _, server := range artifacts.MCPConfig.Servers {
|
||||||
|
fmt.Printf(" - %s (%s)\n", server.Name, server.Type)
|
||||||
|
if server.Command != "" {
|
||||||
|
fmt.Printf(" Comando: %s %v\n", server.Command, server.Args)
|
||||||
|
}
|
||||||
|
if server.URL != "" {
|
||||||
|
fmt.Printf(" URL: %s\n", server.URL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if artifacts.ValidationReport != nil {
|
||||||
|
fmt.Printf("\n✅ Validação: %v\n", artifacts.ValidationReport.Valid)
|
||||||
|
if len(artifacts.ValidationReport.Errors) > 0 {
|
||||||
|
fmt.Printf(" ❌ Erros: %d\n", len(artifacts.ValidationReport.Errors))
|
||||||
|
for _, err := range artifacts.ValidationReport.Errors {
|
||||||
|
fmt.Printf(" - %s: %s\n", err.Field, err.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(artifacts.ValidationReport.Warnings) > 0 {
|
||||||
|
fmt.Printf(" ⚠️ Alertas: %d\n", len(artifacts.ValidationReport.Warnings))
|
||||||
|
for _, warn := range artifacts.ValidationReport.Warnings {
|
||||||
|
fmt.Printf(" - %s: %s\n", warn.Field, warn.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(artifacts.ValidationReport.Suggestions) > 0 {
|
||||||
|
fmt.Printf(" 💡 Sugestões: %d\n", len(artifacts.ValidationReport.Suggestions))
|
||||||
|
for _, sug := range artifacts.ValidationReport.Suggestions {
|
||||||
|
fmt.Printf(" - %s\n", sug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitLines divide uma string em linhas
|
||||||
|
func splitLines(s string) []string {
|
||||||
|
var lines []string
|
||||||
|
current := ""
|
||||||
|
for _, c := range s {
|
||||||
|
if c == '\n' {
|
||||||
|
lines = append(lines, current)
|
||||||
|
current = ""
|
||||||
|
} else {
|
||||||
|
current += string(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if current != "" {
|
||||||
|
lines = append(lines, current)
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveArtifacts salva os artefatos em arquivos
|
||||||
|
func saveArtifacts(artifacts *protoagent.GeneratedArtifacts) error {
|
||||||
|
// Criar diretório de saída
|
||||||
|
if err := os.MkdirAll("./output/travel", 0755); err != nil {
|
||||||
|
return fmt.Errorf("falha ao criar diretório: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar AGENT.md
|
||||||
|
if artifacts.AgentConfig != nil {
|
||||||
|
agentJSON, _ := json.MarshalIndent(artifacts.AgentConfig, "", " ")
|
||||||
|
if err := os.WriteFile("./output/travel/AGENT.json", agentJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar AGENT.json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Também salvar como Markdown
|
||||||
|
agentMD := fmt.Sprintf("# %s Agent\n\n%s\n", artifacts.AgentConfig.Name, artifacts.AgentConfig.Body)
|
||||||
|
if err := os.WriteFile("./output/travel/AGENT.md", []byte(agentMD), 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar AGENT.md: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("\n📄 AGENT.json e AGENT.md salvos")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar schemas de banco de dados
|
||||||
|
for i, schema := range artifacts.DatabaseSchemas {
|
||||||
|
schemaJSON, _ := json.MarshalIndent(schema, "", " ")
|
||||||
|
filename := fmt.Sprintf("./output/travel/schema_%d_%s.json", i, schema.Name)
|
||||||
|
if err := os.WriteFile(filename, schemaJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar schema: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gerar SQL DDL
|
||||||
|
if schema.Type == "sql" && len(schema.Tables) > 0 {
|
||||||
|
sqlDDL := generateSQLDDL(schema)
|
||||||
|
sqlFilename := fmt.Sprintf("./output/travel/schema_%d_%s.sql", i, schema.Name)
|
||||||
|
if err := os.WriteFile(sqlFilename, []byte(sqlDDL), 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar SQL: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("📄 Schema %s salvo (JSON + SQL)\n", schema.Name)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("📄 Schema %s salvo\n", schema.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar políticas OPA
|
||||||
|
for i, policy := range artifacts.Policies {
|
||||||
|
policyJSON, _ := json.MarshalIndent(policy, "", " ")
|
||||||
|
filename := fmt.Sprintf("./output/travel/policy_%d_%s.rego.json", i, policy.Name)
|
||||||
|
if err := os.WriteFile(filename, policyJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar política: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("📄 Política %s salva (JSON)\n", policy.Name)
|
||||||
|
|
||||||
|
// Salvar também o código Rego puro
|
||||||
|
regoFilename := fmt.Sprintf("./output/travel/policy_%d_%s.rego", i, policy.Name)
|
||||||
|
if err := os.WriteFile(regoFilename, []byte(policy.Rego), 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar rego: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("📄 Código Rego %s salvo\n", policy.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar interfaces
|
||||||
|
if len(artifacts.Interfaces) > 0 {
|
||||||
|
interfacesJSON, _ := json.MarshalIndent(artifacts.Interfaces, "", " ")
|
||||||
|
if err := os.WriteFile("./output/travel/interfaces.json", interfacesJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar interfaces: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 interfaces.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar channels
|
||||||
|
if len(artifacts.Channels) > 0 {
|
||||||
|
channelsJSON, _ := json.MarshalIndent(artifacts.Channels, "", " ")
|
||||||
|
if err := os.WriteFile("./output/travel/channels.json", channelsJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar channels: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 channels.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar skills
|
||||||
|
if len(artifacts.Skills) > 0 {
|
||||||
|
skillsJSON, _ := json.MarshalIndent(artifacts.Skills, "", " ")
|
||||||
|
if err := os.WriteFile("./output/travel/skills.json", skillsJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar skills: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 skills.json salvo")
|
||||||
|
|
||||||
|
// Salvar código de cada skill
|
||||||
|
for i, skill := range artifacts.Skills {
|
||||||
|
skillFile := fmt.Sprintf("./output/travel/skill_%d_%s.go", i, skill.Name)
|
||||||
|
if err := os.WriteFile(skillFile, []byte(skill.Code), 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar código da skill: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("📄 Códigos das skills salvos")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar tools
|
||||||
|
if len(artifacts.Tools) > 0 {
|
||||||
|
toolsJSON, _ := json.MarshalIndent(artifacts.Tools, "", " ")
|
||||||
|
if err := os.WriteFile("./output/travel/tools.json", toolsJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar tools: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 tools.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar configuração MCP
|
||||||
|
if artifacts.MCPConfig != nil {
|
||||||
|
mcpJSON, _ := json.MarshalIndent(artifacts.MCPConfig, "", " ")
|
||||||
|
if err := os.WriteFile("./output/travel/mcp_config.json", mcpJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar mcp_config: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 mcp_config.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salvar relatório de validação
|
||||||
|
if artifacts.ValidationReport != nil {
|
||||||
|
reportJSON, _ := json.MarshalIndent(artifacts.ValidationReport, "", " ")
|
||||||
|
if err := os.WriteFile("./output/travel/validation_report.json", reportJSON, 0644); err != nil {
|
||||||
|
return fmt.Errorf("falha ao salvar validation report: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("📄 validation_report.json salvo")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateSQLDDL gera DDL SQL a partir de um schema
|
||||||
|
func generateSQLDDL(schema protoagent.DatabaseSchema) string {
|
||||||
|
var ddl string
|
||||||
|
ddl += fmt.Sprintf("-- Schema: %s\n", schema.Name)
|
||||||
|
ddl += fmt.Sprintf("-- Type: %s\n\n", schema.Type)
|
||||||
|
|
||||||
|
for _, table := range schema.Tables {
|
||||||
|
ddl += fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (\n", table.Name)
|
||||||
|
|
||||||
|
columns := make([]string, 0, len(table.Columns))
|
||||||
|
for _, col := range table.Columns {
|
||||||
|
colDef := fmt.Sprintf(" %s %s", col.Name, col.Type)
|
||||||
|
if col.PrimaryKey {
|
||||||
|
colDef += " PRIMARY KEY"
|
||||||
|
}
|
||||||
|
if !col.Nullable {
|
||||||
|
colDef += " NOT NULL"
|
||||||
|
}
|
||||||
|
if col.Unique {
|
||||||
|
colDef += " UNIQUE"
|
||||||
|
}
|
||||||
|
if col.Default != "" {
|
||||||
|
colDef += fmt.Sprintf(" DEFAULT %s", col.Default)
|
||||||
|
}
|
||||||
|
columns = append(columns, colDef)
|
||||||
|
}
|
||||||
|
|
||||||
|
ddl += joinStrings(columns, ",\n")
|
||||||
|
ddl += "\n);\n\n"
|
||||||
|
|
||||||
|
// Criar índices
|
||||||
|
for _, idx := range table.Indexes {
|
||||||
|
ddl += fmt.Sprintf("CREATE INDEX ON %s (%s);\n", table.Name, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ddl
|
||||||
|
}
|
||||||
|
|
||||||
|
// joinStrings junta strings com um separador
|
||||||
|
func joinStrings(strs []string, sep string) string {
|
||||||
|
if len(strs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
result := strs[0]
|
||||||
|
for i := 1; i < len(strs); i++ {
|
||||||
|
result += sep + strs[i]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
291
examples/protoagent/travel-experience-platform.json
Normal file
291
examples/protoagent/travel-experience-platform.json
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"name": "Travel Experience Platform",
|
||||||
|
"description": "Plataforma de experiências de viagem onde viajantes compartilham relatos com mídias. O sistema atua como editor, filtrando dados pessoais, enriquecendo relatos com informações adicionais e publicando conteúdo curado em redes sociais.",
|
||||||
|
|
||||||
|
"functionalRequirements": [
|
||||||
|
{
|
||||||
|
"id": "FR001",
|
||||||
|
"type": "resource",
|
||||||
|
"name": "SubmitTravelStory",
|
||||||
|
"description": "Permitir que viajantes submetam relatos de viagens com mídias e conteúdos",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "traveler_id", "type": "string", "required": true, "description": "ID do viajante"},
|
||||||
|
{"name": "title", "type": "string", "required": true, "description": "Título do relato"},
|
||||||
|
{"name": "content", "type": "text", "required": true, "description": "Conteúdo do relato"},
|
||||||
|
{"name": "location", "type": "object", "required": false, "description": "Localização da experiência"},
|
||||||
|
{"name": "media_files", "type": "array", "required": false, "description": "Fotos e vídeos anexados"},
|
||||||
|
{"name": "tags", "type": "array", "required": false, "description": "Tags para categorização"},
|
||||||
|
{"name": "travel_date", "type": "date", "required": false, "description": "Data da viagem"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "story_id", "type": "string", "description": "ID único do relato"},
|
||||||
|
{"name": "status", "type": "string", "description": "Status inicial (pending_review)"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Viajante deve estar autenticado"],
|
||||||
|
"postconditions": ["Relato criado", "Status definido como pending_review"],
|
||||||
|
"interactionMethods": ["api", "ui", "webhook"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR002",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "FilterPersonalData",
|
||||||
|
"description": "Filtrar automaticamente informações pessoais dos relatos para privacidade",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "story_content", "type": "text", "required": true, "description": "Conteúdo original do relato"},
|
||||||
|
{"name": "media_files", "type": "array", "required": false, "description": "Mídias para análise"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "filtered_content", "type": "text", "description": "Conteúdo com dados pessoais removidos"},
|
||||||
|
{"name": "detected_pii", "type": "array", "description": "Lista de informações pessoais detectadas"},
|
||||||
|
{"name": "confidence_score", "type": "float", "description": "Confiança da detecção"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Relato deve estar em revisão"],
|
||||||
|
"postconditions": ["Dados pessoais identificados", "Conteúdo filtrado gerado"],
|
||||||
|
"interactionMethods": ["api"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR003",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "EnrichStoryContent",
|
||||||
|
"description": "Coletar informações adicionais do autor para enriquecer o relato",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "story_id", "type": "string", "required": true, "description": "ID do relato"},
|
||||||
|
{"name": "missing_info", "type": "array", "required": true, "description": "Lista de informações faltantes"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "enriched_content", "type": "text", "description": "Conteúdo enriquecido"},
|
||||||
|
{"name": "additional_media", "type": "array", "description": "Mídias adicionais solicitadas"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Relato deve estar em processo de curadoria"],
|
||||||
|
"postconditions": ["Informações adicionais coletadas", "Relato atualizado"],
|
||||||
|
"interactionMethods": ["messaging", "api"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR004",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "GenerateSocialMediaArticle",
|
||||||
|
"description": "Elaborar artigos formatados para publicação em redes sociais",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "story_id", "type": "string", "required": true, "description": "ID do relato"},
|
||||||
|
{"name": "platform", "type": "string", "required": true, "description": "Rede social destino (instagram, facebook, twitter, linkedin)"},
|
||||||
|
{"name": "tone", "type": "string", "required": false, "description": "Tom do conteúdo (inspirational, informative, adventurous)", "default": "inspirational"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "article_content", "type": "text", "description": "Artigo formatado para a plataforma"},
|
||||||
|
{"name": "hashtags", "type": "array", "description": "Hashtags sugeridas"},
|
||||||
|
{"name": "media_selection", "type": "array", "description": "Mídias selecionadas para publicação"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Relato deve estar aprovado"],
|
||||||
|
"postconditions": ["Artigo gerado", "Pronto para publicação"],
|
||||||
|
"interactionMethods": ["api"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR005",
|
||||||
|
"type": "action",
|
||||||
|
"name": "CurateContent",
|
||||||
|
"description": "Realizar curadoria do conteúdo - aprovar, remover ou solicitar atualizações",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "story_id", "type": "string", "required": true, "description": "ID do relato"},
|
||||||
|
{"name": "action", "type": "string", "required": true, "description": "Ação: approve, reject, request_update"},
|
||||||
|
{"name": "reason", "type": "string", "required": false, "description": "Motivo da decisão"},
|
||||||
|
{"name": "curator_id", "type": "string", "required": true, "description": "ID do curador/agente"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "new_status", "type": "string", "description": "Novo status do relato"},
|
||||||
|
{"name": "feedback", "type": "string", "description": "Feedback para o autor"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Relato deve ter passado por filtragem de dados pessoais"],
|
||||||
|
"postconditions": ["Status atualizado", "Autor notificado se necessário"],
|
||||||
|
"interactionMethods": ["api", "ui"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR006",
|
||||||
|
"type": "action",
|
||||||
|
"name": "PublishToSocialMedia",
|
||||||
|
"description": "Publicar conteúdo aprovado em redes sociais via APIs externas",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "article_id", "type": "string", "required": true, "description": "ID do artigo gerado"},
|
||||||
|
{"name": "platforms", "type": "array", "required": true, "description": "Lista de plataformas para publicação"},
|
||||||
|
{"name": "schedule_time", "type": "timestamp", "required": false, "description": "Agendamento da publicação"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "publication_ids", "type": "object", "description": "IDs das publicações em cada plataforma"},
|
||||||
|
{"name": "urls", "type": "array", "description": "URLs das publicações"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Artigo deve estar aprovado", "APIs das redes sociais devem estar configuradas"],
|
||||||
|
"postconditions": ["Conteúdo publicado", "URLs registradas", "Engajamento monitorado"],
|
||||||
|
"interactionMethods": ["api", "webhook"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR007",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "MonitorEngagement",
|
||||||
|
"description": "Monitorar engajamento das publicações nas redes sociais",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "publication_id", "type": "string", "required": true, "description": "ID da publicação"},
|
||||||
|
{"name": "metrics", "type": "array", "required": false, "description": "Métricas para coletar (likes, shares, comments)", "default": ["likes", "shares", "comments"]}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "engagement_data", "type": "object", "description": "Dados de engajamento coletados"},
|
||||||
|
{"name": "performance_score", "type": "float", "description": "Score de performance do conteúdo"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["api"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR008",
|
||||||
|
"type": "resource",
|
||||||
|
"name": "ManageTravelerProfile",
|
||||||
|
"description": "Gerenciar perfis de viajantes com histórico e preferências",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "traveler_id", "type": "string", "required": true, "description": "ID do viajante"},
|
||||||
|
{"name": "bio", "type": "text", "required": false, "description": "Biografia do viajante"},
|
||||||
|
{"name": "preferences", "type": "object", "required": false, "description": "Preferências de privacidade e notificação"},
|
||||||
|
{"name": "social_links", "type": "array", "required": false, "description": "Links para redes sociais do viajante"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "profile", "type": "object", "description": "Perfil completo do viajante"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["api", "ui"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR009",
|
||||||
|
"type": "operation",
|
||||||
|
"name": "ExternalAPIIntegration",
|
||||||
|
"description": "Integrar com APIs externas para enriquecer relatos (clima, mapas, pontos turísticos)",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "location", "type": "object", "required": true, "description": "Localização da viagem"},
|
||||||
|
{"name": "date_range", "type": "object", "required": false, "description": "Período da viagem"},
|
||||||
|
{"name": "api_sources", "type": "array", "required": false, "description": "Fontes de API para consultar"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "enrichment_data", "type": "object", "description": "Dados externos coletados"}
|
||||||
|
],
|
||||||
|
"interactionMethods": ["api"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR010",
|
||||||
|
"type": "action",
|
||||||
|
"name": "UpdateStory",
|
||||||
|
"description": "Atualizar relatos existentes com novas informações ou correções",
|
||||||
|
"inputs": [
|
||||||
|
{"name": "story_id", "type": "string", "required": true, "description": "ID do relato"},
|
||||||
|
{"name": "updates", "type": "object", "required": true, "description": "Campos a serem atualizados"},
|
||||||
|
{"name": "author_id", "type": "string", "required": true, "description": "ID do autor solicitando atualização"}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{"name": "updated_story", "type": "object", "description": "Relato atualizado"},
|
||||||
|
{"name": "version", "type": "integer", "description": "Número da versão"}
|
||||||
|
],
|
||||||
|
"preconditions": ["Autor deve ser dono do relato", "Relato não pode estar bloqueado"],
|
||||||
|
"postconditions": ["Relato atualizado", "Histórico de versões mantido"],
|
||||||
|
"interactionMethods": ["api", "ui"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"nonFunctionalRequirements": [
|
||||||
|
{
|
||||||
|
"id": "NFR001",
|
||||||
|
"category": "security",
|
||||||
|
"name": "PrivacyProtection",
|
||||||
|
"description": "Sistema deve detectar e remover automaticamente informações pessoais identificáveis (PII)",
|
||||||
|
"constraints": {
|
||||||
|
"pii_detection": "true",
|
||||||
|
"auto_redaction": "true",
|
||||||
|
"gdpr_compliance": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR002",
|
||||||
|
"category": "security",
|
||||||
|
"name": "ContentModeration",
|
||||||
|
"description": "Conteúdo deve passar por moderação antes de publicação",
|
||||||
|
"constraints": {
|
||||||
|
"moderation_required": "true",
|
||||||
|
"ai_assisted": "true",
|
||||||
|
"human_review_threshold": "low_confidence"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR003",
|
||||||
|
"category": "performance",
|
||||||
|
"name": "ProcessingTime",
|
||||||
|
"description": "Processamento de filtragem e enriquecimento deve completar em até 30 segundos",
|
||||||
|
"constraints": {
|
||||||
|
"max_processing_time": "30s",
|
||||||
|
"async_processing": "true"
|
||||||
|
},
|
||||||
|
"metrics": [
|
||||||
|
{"name": "avg_filtering_time", "target": "< 10s", "unit": "seconds"},
|
||||||
|
{"name": "avg_enrichment_time", "target": "< 20s", "unit": "seconds"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR004",
|
||||||
|
"category": "reliability",
|
||||||
|
"name": "DataPersistence",
|
||||||
|
"description": "Todos os relatos e versões devem ser persistidos com backup automático",
|
||||||
|
"constraints": {
|
||||||
|
"backup_frequency": "daily",
|
||||||
|
"version_history": "true",
|
||||||
|
"retention_period": "5 years"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR005",
|
||||||
|
"category": "scalability",
|
||||||
|
"name": "MediaStorage",
|
||||||
|
"description": "Sistema deve escalar armazenamento de mídias conforme demanda",
|
||||||
|
"constraints": {
|
||||||
|
"storage_type": "cloud_object_storage",
|
||||||
|
"cdn_enabled": "true",
|
||||||
|
"auto_scaling": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NFR006",
|
||||||
|
"category": "availability",
|
||||||
|
"name": "APIAvailability",
|
||||||
|
"description": "APIs externas de redes sociais devem ter fallback em caso de indisponibilidade",
|
||||||
|
"constraints": {
|
||||||
|
"retry_policy": "exponential_backoff",
|
||||||
|
"fallback_queue": "true",
|
||||||
|
"max_retries": "5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"securityRequirements": [
|
||||||
|
{
|
||||||
|
"roles": ["admin", "curator", "traveler", "viewer"],
|
||||||
|
"permissions": ["read", "write", "delete", "approve", "reject", "publish", "edit_own"],
|
||||||
|
"authorizations": ["role_based", "resource_owner", "content_moderation"],
|
||||||
|
"securityControls": ["authentication", "authorization", "pii_detection", "audit_logging", "content_moderation"],
|
||||||
|
"dataClassification": "mixed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"performanceRequirements": [
|
||||||
|
{
|
||||||
|
"responseTime": "5s",
|
||||||
|
"throughput": 500,
|
||||||
|
"concurrency": 200,
|
||||||
|
"resourceLimits": {
|
||||||
|
"memory": "2GB",
|
||||||
|
"cpu": "4 cores",
|
||||||
|
"storage": "100GB",
|
||||||
|
"network": "1Gbps"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"metadata": {
|
||||||
|
"domain": "travel",
|
||||||
|
"industry": "tourism",
|
||||||
|
"channel_priority": ["api", "ui", "webhook", "messaging"],
|
||||||
|
"database_type": "nosql",
|
||||||
|
"media_storage": "cloud",
|
||||||
|
"external_integrations": ["instagram_api", "facebook_api", "twitter_api", "linkedin_api", "weather_api", "maps_api"],
|
||||||
|
"ai_features": ["pii_detection", "content_enrichment", "article_generation", "curation_assistance"]
|
||||||
|
}
|
||||||
|
}
|
||||||
6
go.mod
6
go.mod
|
|
@ -1,6 +1,6 @@
|
||||||
module github.com/sipeed/picoclaw
|
module github.com/sipeed/picoclaw
|
||||||
|
|
||||||
go 1.25.9
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/systray v1.12.0
|
fyne.io/systray v1.12.0
|
||||||
|
|
@ -26,8 +26,8 @@ require (
|
||||||
github.com/larksuite/oapi-sdk-go/v3 v3.5.3
|
github.com/larksuite/oapi-sdk-go/v3 v3.5.3
|
||||||
github.com/mdp/qrterminal/v3 v3.2.1
|
github.com/mdp/qrterminal/v3 v3.2.1
|
||||||
github.com/minio/selfupdate v0.6.0
|
github.com/minio/selfupdate v0.6.0
|
||||||
github.com/muesli/termenv v0.16.0
|
|
||||||
github.com/modelcontextprotocol/go-sdk v1.5.0
|
github.com/modelcontextprotocol/go-sdk v1.5.0
|
||||||
|
github.com/muesli/termenv v0.16.0
|
||||||
github.com/mymmrac/telego v1.8.0
|
github.com/mymmrac/telego v1.8.0
|
||||||
github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.1
|
github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.1
|
||||||
github.com/openai/openai-go/v3 v3.22.0
|
github.com/openai/openai-go/v3 v3.22.0
|
||||||
|
|
@ -139,7 +139,7 @@ require (
|
||||||
golang.org/x/crypto v0.49.0
|
golang.org/x/crypto v0.49.0
|
||||||
golang.org/x/net v0.52.0
|
golang.org/x/net v0.52.0
|
||||||
golang.org/x/sync v0.20.0
|
golang.org/x/sync v0.20.0
|
||||||
golang.org/x/sys v0.43.0
|
golang.org/x/sys v0.15.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/bwmarrin/discordgo => github.com/yeongaori/discordgo-fork v0.0.0-20260319072544-e8e546f5d532
|
replace github.com/bwmarrin/discordgo => github.com/yeongaori/discordgo-fork v0.0.0-20260319072544-e8e546f5d532
|
||||||
|
|
|
||||||
219
pkg/protoagent/README.md
Normal file
219
pkg/protoagent/README.md
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
# ProtoAgent - Behavior Prototyping Tool
|
||||||
|
|
||||||
|
ProtoAgent é uma ferramenta de prototipagem de comportamentos que transforma requisitos funcionais e não-funcionais em configurações de agentes, schemas de banco de dados, interfaces, canais de comunicação e políticas de segurança.
|
||||||
|
|
||||||
|
## Visão Geral
|
||||||
|
|
||||||
|
O ProtoAgent estende o PicoClaw para permitir que você descreva o comportamento desejado de um agente através de requisitos estruturados, e automaticamente gera:
|
||||||
|
|
||||||
|
- **Configurações de Agente** (AGENT.md)
|
||||||
|
- **Schemas de Banco de Dados** (SQL/NoSQL)
|
||||||
|
- **Interfaces** (API, Web UI)
|
||||||
|
- **Canais de Comunicação** (Telegram, Discord, Slack, Webhooks)
|
||||||
|
- **Políticas OPA** (Open Policy Agent para controle de acesso)
|
||||||
|
- **Skills** (Habilidades personalizadas)
|
||||||
|
- **Tools** (Ferramentas de integração)
|
||||||
|
- **Configuração MCP** (Model Context Protocol)
|
||||||
|
|
||||||
|
## Estrutura do Pacote
|
||||||
|
|
||||||
|
```
|
||||||
|
pkg/protoagent/
|
||||||
|
├── types.go # Definições de tipos e estruturas de dados
|
||||||
|
├── engine.go # Motor principal de processamento
|
||||||
|
├── generators.go # Geradores de artefatos
|
||||||
|
└── policies.go # Gerador de políticas OPA
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tipos de Requisitos
|
||||||
|
|
||||||
|
### Requisitos Funcionais
|
||||||
|
|
||||||
|
Descrevem **o que** o sistema deve fazer:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
functionalRequirements:
|
||||||
|
- id: FR001
|
||||||
|
type: action
|
||||||
|
name: CreateUser
|
||||||
|
description: Create a new user account
|
||||||
|
inputs:
|
||||||
|
- name: username
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
- name: email
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
interactionMethods:
|
||||||
|
- api
|
||||||
|
- ui
|
||||||
|
```
|
||||||
|
|
||||||
|
### Requisitos Não-Funcionais
|
||||||
|
|
||||||
|
Descrevem **restrições e atributos de qualidade**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nonFunctionalRequirements:
|
||||||
|
- id: NFR001
|
||||||
|
category: security
|
||||||
|
name: Authentication
|
||||||
|
description: All API calls must be authenticated
|
||||||
|
constraints:
|
||||||
|
auth_type: jwt
|
||||||
|
token_expiry: 24h
|
||||||
|
```
|
||||||
|
|
||||||
|
## Métodos de Interação
|
||||||
|
|
||||||
|
- `api` - Integração via API REST/GraphQL
|
||||||
|
- `mcp` - Model Context Protocol
|
||||||
|
- `ui` - Interface de usuário (web/cli)
|
||||||
|
- `messaging` - Aplicativos de mensagem (Telegram, Discord, etc.)
|
||||||
|
- `webhook` - Webhooks para integrações
|
||||||
|
- `cli` - Interface de linha de comando
|
||||||
|
- `database` - Acesso direto ao banco de dados
|
||||||
|
- `file` - Operações com arquivos
|
||||||
|
- `eventbus` - Barramento de eventos
|
||||||
|
|
||||||
|
## Exemplo de Uso
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/protoagent"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Configurar o engine
|
||||||
|
config := protoagent.EngineConfig{
|
||||||
|
OutputDir: "./output",
|
||||||
|
Workspace: "./workspace",
|
||||||
|
EnableOPA: true,
|
||||||
|
EnableAI: false,
|
||||||
|
DryRun: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
engine := protoagent.NewEngine(config)
|
||||||
|
|
||||||
|
// Definir requisitos
|
||||||
|
reqs := &protoagent.RequirementsDocument{
|
||||||
|
Version: "1.0.0",
|
||||||
|
Name: "Customer Support Bot",
|
||||||
|
Description: "Automated customer support assistant",
|
||||||
|
FunctionalRequirements: []protoagent.FunctionalRequirement{
|
||||||
|
{
|
||||||
|
ID: "FR001",
|
||||||
|
Type: "action",
|
||||||
|
Name: "HandleTicket",
|
||||||
|
Description: "Process customer support tickets",
|
||||||
|
Inputs: []protoagent.ParameterDef{
|
||||||
|
{Name: "ticket_id", Type: "string", Required: true},
|
||||||
|
{Name: "message", Type: "string", Required: true},
|
||||||
|
},
|
||||||
|
InteractionMethods: []protoagent.InteractionMethod{
|
||||||
|
protoagent.InteractionMessaging,
|
||||||
|
protoagent.InteractionAPI,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityRequirements: []protoagent.SecurityRequirement{
|
||||||
|
{
|
||||||
|
Roles: []string{"admin", "agent", "customer"},
|
||||||
|
Permissions: []string{"read", "write", "resolve"},
|
||||||
|
SecurityControls: []string{"authentication", "authorization"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processar requisitos e gerar artefatos
|
||||||
|
ctx := context.Background()
|
||||||
|
artifacts, err := engine.ProcessRequirements(ctx, reqs)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usar artefatos gerados
|
||||||
|
if artifacts.AgentConfig != nil {
|
||||||
|
// Salvar AGENT.md
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, policy := range artifacts.Policies {
|
||||||
|
// Salvar políticas OPA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Políticas OPA
|
||||||
|
|
||||||
|
O ProtoAgent gera automaticamente políticas Rego para Open Policy Agent baseadas nos requisitos de segurança:
|
||||||
|
|
||||||
|
### RBAC (Role-Based Access Control)
|
||||||
|
|
||||||
|
```rego
|
||||||
|
package authz.rbac
|
||||||
|
|
||||||
|
default allow = false
|
||||||
|
|
||||||
|
roles := {"admin", "user", "viewer"}
|
||||||
|
|
||||||
|
role_permissions := {
|
||||||
|
"admin": {"read", "write", "delete", "admin"},
|
||||||
|
"user": {"read", "write"},
|
||||||
|
"viewer": {"read"}
|
||||||
|
}
|
||||||
|
|
||||||
|
allow {
|
||||||
|
some role in input.user.roles
|
||||||
|
some perm in role_permissions[role]
|
||||||
|
perm == input.permission
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Controle de Acesso a Dados
|
||||||
|
|
||||||
|
```rego
|
||||||
|
package authz.data_access
|
||||||
|
|
||||||
|
default allow = false
|
||||||
|
|
||||||
|
allow {
|
||||||
|
input.data_classification == "public"
|
||||||
|
}
|
||||||
|
|
||||||
|
allow {
|
||||||
|
input.data_classification == "confidential"
|
||||||
|
input.user.clearance_level >= 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow de Desenvolvimento
|
||||||
|
|
||||||
|
1. **Definir Requisitos**: Crie um documento YAML/JSON com requisitos funcionais e não-funcionais
|
||||||
|
2. **Processar**: Execute o ProtoAgent para gerar artefatos
|
||||||
|
3. **Revisar**: Analise os artefatos gerados
|
||||||
|
4. **Customizar**: Ajuste conforme necessário
|
||||||
|
5. **Implantar**: Use os artefatos no seu workspace PicoClaw
|
||||||
|
|
||||||
|
## Integração com PicoClaw
|
||||||
|
|
||||||
|
Os artefatos gerados pelo ProtoAgent são compatíveis com a estrutura do PicoClaw:
|
||||||
|
|
||||||
|
- `AGENT.md` → Configuração do agente
|
||||||
|
- `skills/` → Habilidades personalizadas
|
||||||
|
- `workspace/memory/` → Esquemas de memória
|
||||||
|
- Políticas OPA → Controle de acesso
|
||||||
|
|
||||||
|
## Próximos Passos
|
||||||
|
|
||||||
|
- [ ] Suporte a provedores de IA para geração assistida
|
||||||
|
- [ ] Validação de políticas OPA com OPA CLI
|
||||||
|
- [ ] Templates customizáveis por domínio
|
||||||
|
- [ ] Export para Docker Compose/Kubernetes
|
||||||
|
- [ ] Interface web para definição de requisitos
|
||||||
|
|
||||||
|
## Licença
|
||||||
|
|
||||||
|
Mesma licença do PicoClaw original.
|
||||||
314
pkg/protoagent/engine.go
Normal file
314
pkg/protoagent/engine.go
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
package protoagent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Engine is the main prototyping engine that transforms requirements into artifacts.
|
||||||
|
type Engine struct {
|
||||||
|
config EngineConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// EngineConfig holds configuration for the prototyping engine.
|
||||||
|
type EngineConfig struct {
|
||||||
|
OutputDir string `json:"outputDir" yaml:"outputDir"`
|
||||||
|
Workspace string `json:"workspace" yaml:"workspace"`
|
||||||
|
EnableOPA bool `json:"enableOPA" yaml:"enableOPA"`
|
||||||
|
EnableAI bool `json:"enableAI" yaml:"enableAI"`
|
||||||
|
AIProvider string `json:"aiProvider,omitempty" yaml:"aiProvider,omitempty"`
|
||||||
|
DryRun bool `json:"dryRun" yaml:"dryRun"`
|
||||||
|
Verbose bool `json:"verbose" yaml:"verbose"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEngine creates a new prototyping engine.
|
||||||
|
func NewEngine(config EngineConfig) *Engine {
|
||||||
|
return &Engine{
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessRequirements takes a requirements document and generates all artifacts.
|
||||||
|
func (e *Engine) ProcessRequirements(ctx context.Context, reqs *RequirementsDocument) (*GeneratedArtifacts, error) {
|
||||||
|
logger.InfoCF("protoagent", "Starting requirements processing", map[string]any{
|
||||||
|
"name": reqs.Name,
|
||||||
|
"version": reqs.Version,
|
||||||
|
"fr_count": len(reqs.FunctionalRequirements),
|
||||||
|
"nfr_count": len(reqs.NonFunctionalRequirements),
|
||||||
|
})
|
||||||
|
|
||||||
|
artifacts := &GeneratedArtifacts{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate requirements first
|
||||||
|
if err := e.validateRequirements(reqs); err != nil {
|
||||||
|
return nil, fmt.Errorf("validation failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate agent configuration
|
||||||
|
agentConfig, err := e.generateAgentConfig(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate agent config", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.AgentConfig = agentConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate database schemas
|
||||||
|
dbSchemas, err := e.generateDatabaseSchemas(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate database schemas", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.DatabaseSchemas = dbSchemas
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate interfaces
|
||||||
|
interfaces, err := e.generateInterfaces(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate interfaces", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.Interfaces = interfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate communication channels
|
||||||
|
channels, err := e.generateChannels(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate channels", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.Channels = channels
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate OPA policies if enabled
|
||||||
|
if e.config.EnableOPA {
|
||||||
|
policies, err := e.generateOPAPolicies(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate OPA policies", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.Policies = policies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate skills
|
||||||
|
skills, err := e.generateSkills(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate skills", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.Skills = skills
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate tools
|
||||||
|
tools, err := e.generateTools(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate tools", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.Tools = tools
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate MCP configuration
|
||||||
|
mcpConfig, err := e.generateMCPConfig(reqs)
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnCF("protoagent", "Failed to generate MCP config", map[string]any{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
artifacts.MCPConfig = mcpConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate validation report
|
||||||
|
artifacts.ValidationReport = e.generateValidationReport(reqs, artifacts)
|
||||||
|
|
||||||
|
logger.InfoCF("protoagent", "Requirements processing completed", map[string]any{
|
||||||
|
"name": reqs.Name,
|
||||||
|
"artifacts_count": e.countArtifacts(artifacts),
|
||||||
|
})
|
||||||
|
|
||||||
|
return artifacts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateRequirements performs validation on the requirements document.
|
||||||
|
func (e *Engine) validateRequirements(reqs *RequirementsDocument) error {
|
||||||
|
var errors []ValidationError
|
||||||
|
var warnings []ValidationWarning
|
||||||
|
|
||||||
|
// Check for required fields
|
||||||
|
if reqs.Name == "" {
|
||||||
|
errors = append(errors, ValidationError{
|
||||||
|
Field: "name",
|
||||||
|
Message: "Name is required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reqs.FunctionalRequirements) == 0 {
|
||||||
|
warnings = append(warnings, ValidationWarning{
|
||||||
|
Field: "functionalRequirements",
|
||||||
|
Message: "No functional requirements defined",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate FR IDs are unique
|
||||||
|
frIDs := make(map[string]bool)
|
||||||
|
for i, fr := range reqs.FunctionalRequirements {
|
||||||
|
if fr.ID == "" {
|
||||||
|
errors = append(errors, ValidationError{
|
||||||
|
Field: fmt.Sprintf("functionalRequirements[%d].id", i),
|
||||||
|
Message: "ID is required for each functional requirement",
|
||||||
|
})
|
||||||
|
} else if frIDs[fr.ID] {
|
||||||
|
errors = append(errors, ValidationError{
|
||||||
|
Field: fmt.Sprintf("functionalRequirements[%d].id", i),
|
||||||
|
Message: fmt.Sprintf("Duplicate ID: %s", fr.ID),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
frIDs[fr.ID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate NFR categories
|
||||||
|
validCategories := map[string]bool{
|
||||||
|
"security": true, "performance": true, "reliability": true,
|
||||||
|
"scalability": true, "availability": true, "maintainability": true,
|
||||||
|
}
|
||||||
|
for i, nfr := range reqs.NonFunctionalRequirements {
|
||||||
|
if nfr.Category != "" && !validCategories[nfr.Category] {
|
||||||
|
warnings = append(warnings, ValidationWarning{
|
||||||
|
Field: fmt.Sprintf("nonFunctionalRequirements[%d].category", i),
|
||||||
|
Message: fmt.Sprintf("Unknown category: %s", nfr.Category),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for missing interaction methods
|
||||||
|
for i, fr := range reqs.FunctionalRequirements {
|
||||||
|
if len(fr.InteractionMethods) == 0 {
|
||||||
|
warnings = append(warnings, ValidationWarning{
|
||||||
|
Field: fmt.Sprintf("functionalRequirements[%d].interactionMethods", i),
|
||||||
|
Message: "No interaction methods specified",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return fmt.Errorf("validation failed with %d errors", len(errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateAgentConfig creates the agent configuration from requirements.
|
||||||
|
func (e *Engine) generateAgentConfig(reqs *RequirementsDocument) (*AgentConfig, error) {
|
||||||
|
config := &AgentConfig{
|
||||||
|
Name: reqs.Name,
|
||||||
|
Description: reqs.Description,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract tools from functional requirements
|
||||||
|
toolSet := make(map[string]bool)
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
for _, method := range fr.InteractionMethods {
|
||||||
|
switch method {
|
||||||
|
case InteractionAPI:
|
||||||
|
toolSet["api_client"] = true
|
||||||
|
case InteractionMCP:
|
||||||
|
toolSet["mcp_client"] = true
|
||||||
|
case InteractionMessaging:
|
||||||
|
toolSet["message_handler"] = true
|
||||||
|
case InteractionWebhook:
|
||||||
|
toolSet["webhook_handler"] = true
|
||||||
|
case InteractionDatabase:
|
||||||
|
toolSet["database_tool"] = true
|
||||||
|
case InteractionFile:
|
||||||
|
toolSet["file_tool"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for tool := range toolSet {
|
||||||
|
config.Tools = append(config.Tools, tool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build agent body from requirements
|
||||||
|
var body strings.Builder
|
||||||
|
body.WriteString(fmt.Sprintf("# %s Agent\n\n", config.Name))
|
||||||
|
body.WriteString(fmt.Sprintf("## Description\n\n%s\n\n", config.Description))
|
||||||
|
|
||||||
|
body.WriteString("## Generated Capabilities\n\n")
|
||||||
|
body.WriteString("This agent was automatically generated from requirements specification.\n\n")
|
||||||
|
|
||||||
|
body.WriteString("### Functional Requirements\n\n")
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
body.WriteString(fmt.Sprintf("- **%s**: %s\n", fr.Name, fr.Description))
|
||||||
|
}
|
||||||
|
|
||||||
|
body.WriteString("\n### Non-Functional Requirements\n\n")
|
||||||
|
for _, nfr := range reqs.NonFunctionalRequirements {
|
||||||
|
body.WriteString(fmt.Sprintf("- **%s** (%s): %s\n", nfr.Name, nfr.Category, nfr.Description))
|
||||||
|
}
|
||||||
|
|
||||||
|
body.WriteString("\n## Instructions\n\n")
|
||||||
|
body.WriteString("Follow the generated policies and use the provided tools to fulfill the requirements.\n")
|
||||||
|
|
||||||
|
config.Body = body.String()
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// countArtifacts returns the total count of generated artifacts.
|
||||||
|
func (e *Engine) countArtifacts(artifacts *GeneratedArtifacts) int {
|
||||||
|
count := 0
|
||||||
|
if artifacts.AgentConfig != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
count += len(artifacts.DatabaseSchemas)
|
||||||
|
count += len(artifacts.Interfaces)
|
||||||
|
count += len(artifacts.Channels)
|
||||||
|
count += len(artifacts.Policies)
|
||||||
|
count += len(artifacts.Skills)
|
||||||
|
count += len(artifacts.Tools)
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateValidationReport creates a validation report for the generated artifacts.
|
||||||
|
func (e *Engine) generateValidationReport(reqs *RequirementsDocument, artifacts *GeneratedArtifacts) *ValidationReport {
|
||||||
|
report := &ValidationReport{
|
||||||
|
Valid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if essential artifacts were generated
|
||||||
|
if artifacts.AgentConfig == nil {
|
||||||
|
report.Valid = false
|
||||||
|
report.Errors = append(report.Errors, ValidationError{
|
||||||
|
Field: "agentConfig",
|
||||||
|
Message: "Failed to generate agent configuration",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add suggestions based on requirements
|
||||||
|
if len(reqs.SecurityRequirements) > 0 && len(artifacts.Policies) == 0 {
|
||||||
|
report.Suggestions = append(report.Suggestions,
|
||||||
|
"Consider enabling OPA for security policy enforcement")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reqs.FunctionalRequirements) > 10 && len(artifacts.Skills) == 0 {
|
||||||
|
report.Suggestions = append(report.Suggestions,
|
||||||
|
"Consider creating skills for complex functional requirements")
|
||||||
|
}
|
||||||
|
|
||||||
|
return report
|
||||||
|
}
|
||||||
415
pkg/protoagent/generators.go
Normal file
415
pkg/protoagent/generators.go
Normal file
|
|
@ -0,0 +1,415 @@
|
||||||
|
package protoagent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generateDatabaseSchemas creates database schemas from requirements.
|
||||||
|
func (e *Engine) generateDatabaseSchemas(reqs *RequirementsDocument) ([]DatabaseSchema, error) {
|
||||||
|
var schemas []DatabaseSchema
|
||||||
|
|
||||||
|
// Analyze requirements to determine data entities
|
||||||
|
entities := e.extractDataEntities(reqs)
|
||||||
|
|
||||||
|
if len(entities) == 0 {
|
||||||
|
// Create a default schema if no entities detected
|
||||||
|
schemas = append(schemas, DatabaseSchema{
|
||||||
|
Name: "default",
|
||||||
|
Type: "sql",
|
||||||
|
Tables: []TableDef{
|
||||||
|
{
|
||||||
|
Name: "entities",
|
||||||
|
Columns: []ColumnDef{
|
||||||
|
{Name: "id", Type: "uuid", PrimaryKey: true},
|
||||||
|
{Name: "name", Type: "varchar(255)", Nullable: false},
|
||||||
|
{Name: "created_at", Type: "timestamp", Default: "CURRENT_TIMESTAMP"},
|
||||||
|
{Name: "updated_at", Type: "timestamp"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return schemas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate schema for each entity
|
||||||
|
for _, entity := range entities {
|
||||||
|
schema := DatabaseSchema{
|
||||||
|
Name: entity.Name,
|
||||||
|
Type: "sql",
|
||||||
|
}
|
||||||
|
|
||||||
|
table := TableDef{
|
||||||
|
Name: strings.ToLower(entity.Name) + "s",
|
||||||
|
Columns: []ColumnDef{
|
||||||
|
{Name: "id", Type: "uuid", PrimaryKey: true},
|
||||||
|
{Name: "created_at", Type: "timestamp", Default: "CURRENT_TIMESTAMP"},
|
||||||
|
{Name: "updated_at", Type: "timestamp"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add columns based on entity attributes
|
||||||
|
for _, attr := range entity.Attributes {
|
||||||
|
col := ColumnDef{
|
||||||
|
Name: strings.ToLower(attr.Name),
|
||||||
|
Type: e.mapTypeToSQL(attr.Type),
|
||||||
|
Nullable: !attr.Required,
|
||||||
|
}
|
||||||
|
table.Columns = append(table.Columns, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
schema.Tables = append(schema.Tables, table)
|
||||||
|
schemas = append(schemas, schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
return schemas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entity represents a data entity extracted from requirements.
|
||||||
|
type Entity struct {
|
||||||
|
Name string
|
||||||
|
Attributes []Attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute represents an entity attribute.
|
||||||
|
type Attribute struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Required bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractDataEntities analyzes requirements to find data entities.
|
||||||
|
func (e *Engine) extractDataEntities(reqs *RequirementsDocument) []Entity {
|
||||||
|
entityMap := make(map[string]*Entity)
|
||||||
|
|
||||||
|
// Extract entities from functional requirements
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
// Look for resource-related requirements
|
||||||
|
if fr.Type == "resource" || strings.Contains(strings.ToLower(fr.Description), "store") ||
|
||||||
|
strings.Contains(strings.ToLower(fr.Description), "manage") {
|
||||||
|
|
||||||
|
entityName := e.extractEntityName(fr)
|
||||||
|
if entityName != "" {
|
||||||
|
if _, exists := entityMap[entityName]; !exists {
|
||||||
|
entityMap[entityName] = &Entity{
|
||||||
|
Name: entityName,
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract attributes from inputs/outputs
|
||||||
|
for _, input := range fr.Inputs {
|
||||||
|
attr := Attribute{
|
||||||
|
Name: input.Name,
|
||||||
|
Type: input.Type,
|
||||||
|
Required: input.Required,
|
||||||
|
}
|
||||||
|
entityMap[entityName].Attributes = append(entityMap[entityName].Attributes, attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert map to slice
|
||||||
|
var entities []Entity
|
||||||
|
for _, entity := range entityMap {
|
||||||
|
entities = append(entities, *entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
return entities
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractEntityName tries to extract an entity name from a requirement.
|
||||||
|
func (e *Engine) extractEntityName(fr FunctionalRequirement) string {
|
||||||
|
// Try to extract from name
|
||||||
|
name := strings.ToLower(fr.Name)
|
||||||
|
|
||||||
|
// Common entity patterns
|
||||||
|
patterns := []string{"user", "account", "order", "product", "item", "record", "data", "document"}
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
if strings.Contains(name, pattern) {
|
||||||
|
return strings.Title(pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the requirement name as fallback
|
||||||
|
if fr.Name != "" {
|
||||||
|
return fr.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapTypeToSQL maps a generic type to SQL type.
|
||||||
|
func (e *Engine) mapTypeToSQL(t string) string {
|
||||||
|
switch strings.ToLower(t) {
|
||||||
|
case "string", "text":
|
||||||
|
return "varchar(255)"
|
||||||
|
case "int", "integer", "number":
|
||||||
|
return "integer"
|
||||||
|
case "float", "double", "decimal":
|
||||||
|
return "decimal(10,2)"
|
||||||
|
case "bool", "boolean":
|
||||||
|
return "boolean"
|
||||||
|
case "date":
|
||||||
|
return "date"
|
||||||
|
case "datetime", "timestamp":
|
||||||
|
return "timestamp"
|
||||||
|
case "json":
|
||||||
|
return "jsonb"
|
||||||
|
default:
|
||||||
|
return "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateInterfaces creates interface definitions from requirements.
|
||||||
|
func (e *Engine) generateInterfaces(reqs *RequirementsDocument) ([]InterfaceDef, error) {
|
||||||
|
var interfaces []InterfaceDef
|
||||||
|
|
||||||
|
// Check for UI interaction methods
|
||||||
|
hasUI := false
|
||||||
|
hasAPI := false
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
for _, method := range fr.InteractionMethods {
|
||||||
|
if method == InteractionUI {
|
||||||
|
hasUI = true
|
||||||
|
}
|
||||||
|
if method == InteractionAPI {
|
||||||
|
hasAPI = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate API interface if needed
|
||||||
|
if hasAPI {
|
||||||
|
apiInterface := InterfaceDef{
|
||||||
|
Name: "API",
|
||||||
|
Type: "api",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create endpoints from functional requirements
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
endpoint := EndpointDef{
|
||||||
|
Path: fmt.Sprintf("/api/v1/%s", strings.ToLower(fr.Name)),
|
||||||
|
Method: "POST",
|
||||||
|
Description: fr.Description,
|
||||||
|
Inputs: fr.Inputs,
|
||||||
|
Outputs: fr.Outputs,
|
||||||
|
}
|
||||||
|
apiInterface.Endpoints = append(apiInterface.Endpoints, endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaces = append(interfaces, apiInterface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate Web UI interface if needed
|
||||||
|
if hasUI {
|
||||||
|
webInterface := InterfaceDef{
|
||||||
|
Name: "Web UI",
|
||||||
|
Type: "web",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create screens from functional requirements
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
screen := ScreenDef{
|
||||||
|
Name: fr.Name,
|
||||||
|
Route: fmt.Sprintf("/%s", strings.ToLower(fr.Name)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add components based on inputs
|
||||||
|
for _, input := range fr.Inputs {
|
||||||
|
component := ComponentDef{
|
||||||
|
Name: input.Name,
|
||||||
|
Type: e.inputTypeToComponent(input.Type),
|
||||||
|
Properties: map[string]string{
|
||||||
|
"label": input.Name,
|
||||||
|
"required": fmt.Sprintf("%v", input.Required),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
screen.Components = append(screen.Components, component)
|
||||||
|
}
|
||||||
|
|
||||||
|
webInterface.Screens = append(webInterface.Screens, screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaces = append(interfaces, webInterface)
|
||||||
|
}
|
||||||
|
|
||||||
|
return interfaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// inputTypeToComponent maps input types to UI components.
|
||||||
|
func (e *Engine) inputTypeToComponent(t string) string {
|
||||||
|
switch strings.ToLower(t) {
|
||||||
|
case "string", "text":
|
||||||
|
return "TextInput"
|
||||||
|
case "int", "integer", "number", "float":
|
||||||
|
return "NumberInput"
|
||||||
|
case "bool", "boolean":
|
||||||
|
return "Checkbox"
|
||||||
|
case "date":
|
||||||
|
return "DatePicker"
|
||||||
|
case "datetime", "timestamp":
|
||||||
|
return "DateTimePicker"
|
||||||
|
default:
|
||||||
|
return "TextInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateChannels creates communication channel configurations.
|
||||||
|
func (e *Engine) generateChannels(reqs *RequirementsDocument) ([]ChannelConfig, error) {
|
||||||
|
var channels []ChannelConfig
|
||||||
|
|
||||||
|
// Check for messaging interaction methods
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
for _, method := range fr.InteractionMethods {
|
||||||
|
if method == InteractionMessaging {
|
||||||
|
// Add default channels based on requirements
|
||||||
|
channels = append(channels, ChannelConfig{
|
||||||
|
Name: "telegram",
|
||||||
|
Type: "telegram",
|
||||||
|
Enabled: true,
|
||||||
|
Config: map[string]string{
|
||||||
|
"token": "${TELEGRAM_BOT_TOKEN}",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for webhook requirements
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
for _, method := range fr.InteractionMethods {
|
||||||
|
if method == InteractionWebhook {
|
||||||
|
channels = append(channels, ChannelConfig{
|
||||||
|
Name: "webhook",
|
||||||
|
Type: "webhook",
|
||||||
|
Enabled: true,
|
||||||
|
Config: map[string]string{
|
||||||
|
"path": "/webhook",
|
||||||
|
"secret": "${WEBHOOK_SECRET}",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateSkills creates skill definitions from requirements.
|
||||||
|
func (e *Engine) generateSkills(reqs *RequirementsDocument) ([]SkillDefinition, error) {
|
||||||
|
var skills []SkillDefinition
|
||||||
|
|
||||||
|
// Generate skills for complex operations
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
if fr.Type == "operation" && len(fr.Preconditions) > 0 {
|
||||||
|
skill := SkillDefinition{
|
||||||
|
Name: fmt.Sprintf("%s_skill", strings.ToLower(fr.Name)),
|
||||||
|
Description: fr.Description,
|
||||||
|
Triggers: []string{fr.Name},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate skill code template
|
||||||
|
code := fmt.Sprintf(`// Auto-generated skill for: %s
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func %sSkill(ctx context.Context, params map[string]interface{}) (interface{}, error) {
|
||||||
|
// TODO: Implement skill logic
|
||||||
|
// Preconditions: %v
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
`, fr.Description, strings.ToLower(fr.Name), fr.Preconditions)
|
||||||
|
|
||||||
|
skill.Code = code
|
||||||
|
skills = append(skills, skill)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return skills, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateTools creates tool definitions from requirements.
|
||||||
|
func (e *Engine) generateTools(reqs *RequirementsDocument) ([]ToolDefinition, error) {
|
||||||
|
var tools []ToolDefinition
|
||||||
|
|
||||||
|
// Generate tools based on interaction methods
|
||||||
|
toolSet := make(map[string]bool)
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
for _, method := range fr.InteractionMethods {
|
||||||
|
toolKey := string(method)
|
||||||
|
if !toolSet[toolKey] {
|
||||||
|
toolSet[toolKey] = true
|
||||||
|
|
||||||
|
tool := ToolDefinition{
|
||||||
|
Name: string(method) + "_tool",
|
||||||
|
Description: fmt.Sprintf("Tool for %s interactions", method),
|
||||||
|
Type: "custom",
|
||||||
|
}
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case InteractionAPI:
|
||||||
|
tool.Config = map[string]string{
|
||||||
|
"type": "http",
|
||||||
|
"base_url": "${API_BASE_URL}",
|
||||||
|
}
|
||||||
|
case InteractionDatabase:
|
||||||
|
tool.Config = map[string]string{
|
||||||
|
"type": "database",
|
||||||
|
"driver": "postgres",
|
||||||
|
"dsn": "${DATABASE_URL}",
|
||||||
|
}
|
||||||
|
case InteractionFile:
|
||||||
|
tool.Config = map[string]string{
|
||||||
|
"type": "filesystem",
|
||||||
|
"root": "${WORKSPACE_DIR}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tools = append(tools, tool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tools, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateMCPConfig creates MCP server configuration.
|
||||||
|
func (e *Engine) generateMCPConfig(reqs *RequirementsDocument) (*MCPConfiguration, error) {
|
||||||
|
var mcpConfig MCPConfiguration
|
||||||
|
|
||||||
|
// Check for MCP interaction requirements
|
||||||
|
hasMCP := false
|
||||||
|
for _, fr := range reqs.FunctionalRequirements {
|
||||||
|
for _, method := range fr.InteractionMethods {
|
||||||
|
if method == InteractionMCP {
|
||||||
|
hasMCP = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasMCP {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMCP {
|
||||||
|
mcpConfig.Servers = []MCPServerConfig{
|
||||||
|
{
|
||||||
|
Name: "default",
|
||||||
|
Type: "stdio",
|
||||||
|
Command: "mcp-server",
|
||||||
|
Args: []string{"--config", "${MCP_CONFIG_PATH}"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mcpConfig.Servers) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mcpConfig, nil
|
||||||
|
}
|
||||||
230
pkg/protoagent/policies.go
Normal file
230
pkg/protoagent/policies.go
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
package protoagent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generateOPAPolicies creates Open Policy Agent policies from security requirements.
|
||||||
|
func (e *Engine) generateOPAPolicies(reqs *RequirementsDocument) ([]PolicyDefinition, error) {
|
||||||
|
var policies []PolicyDefinition
|
||||||
|
|
||||||
|
// Generate RBAC policy if security requirements exist
|
||||||
|
if len(reqs.SecurityRequirements) > 0 {
|
||||||
|
rbacPolicy := e.generateRBACPolicy(reqs)
|
||||||
|
policies = append(policies, rbacPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate authorization policies from NFRs
|
||||||
|
for _, nfr := range reqs.NonFunctionalRequirements {
|
||||||
|
if nfr.Category == "security" {
|
||||||
|
authPolicy := e.generateAuthorizationPolicy(nfr)
|
||||||
|
if authPolicy != nil {
|
||||||
|
policies = append(policies, *authPolicy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate data access policies
|
||||||
|
dataPolicy := e.generateDataAccessPolicy(reqs)
|
||||||
|
if dataPolicy != nil {
|
||||||
|
policies = append(policies, *dataPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return policies, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateRBACPolicy creates a Role-Based Access Control policy.
|
||||||
|
func (e *Engine) generateRBACPolicy(reqs *RequirementsDocument) PolicyDefinition {
|
||||||
|
// Collect all roles from security requirements
|
||||||
|
roleSet := make(map[string]bool)
|
||||||
|
permissionSet := make(map[string]bool)
|
||||||
|
|
||||||
|
for _, secReq := range reqs.SecurityRequirements {
|
||||||
|
for _, role := range secReq.Roles {
|
||||||
|
roleSet[role] = true
|
||||||
|
}
|
||||||
|
for _, perm := range secReq.Permissions {
|
||||||
|
permissionSet[perm] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default roles if none specified
|
||||||
|
if len(roleSet) == 0 {
|
||||||
|
roleSet["admin"] = true
|
||||||
|
roleSet["user"] = true
|
||||||
|
roleSet["viewer"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Rego policy
|
||||||
|
var rego strings.Builder
|
||||||
|
rego.WriteString("package authz.rbac\n\n")
|
||||||
|
rego.WriteString("# Auto-generated RBAC policy from requirements\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("# Default deny\n")
|
||||||
|
rego.WriteString("default allow = false\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("# Role definitions\n")
|
||||||
|
rego.WriteString("roles := {\n")
|
||||||
|
for role := range roleSet {
|
||||||
|
rego.WriteString(fmt.Sprintf(" \"%s\",\n", role))
|
||||||
|
}
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("# Permission definitions\n")
|
||||||
|
rego.WriteString("permissions := {\n")
|
||||||
|
for perm := range permissionSet {
|
||||||
|
rego.WriteString(fmt.Sprintf(" \"%s\",\n", perm))
|
||||||
|
}
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("# Role-permission mapping\n")
|
||||||
|
rego.WriteString("role_permissions := {\n")
|
||||||
|
rego.WriteString(" \"admin\": {\"read\", \"write\", \"delete\", \"admin\"},\n")
|
||||||
|
rego.WriteString(" \"user\": {\"read\", \"write\"},\n")
|
||||||
|
rego.WriteString(" \"viewer\": {\"read\"}\n")
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("# Allow if user has required permission\n")
|
||||||
|
rego.WriteString("allow {\n")
|
||||||
|
rego.WriteString(" some role in input.user.roles\n")
|
||||||
|
rego.WriteString(" some perm in role_permissions[role]\n")
|
||||||
|
rego.WriteString(" perm == input.permission\n")
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("# Admin bypass\n")
|
||||||
|
rego.WriteString("allow {\n")
|
||||||
|
rego.WriteString(" some role in input.user.roles\n")
|
||||||
|
rego.WriteString(" role == \"admin\"\n")
|
||||||
|
rego.WriteString("}\n")
|
||||||
|
|
||||||
|
return PolicyDefinition{
|
||||||
|
Name: "rbac_policy",
|
||||||
|
Package: "authz.rbac",
|
||||||
|
Description: "Role-Based Access Control policy",
|
||||||
|
Rego: rego.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateAuthorizationPolicy creates an authorization policy from NFR.
|
||||||
|
func (e *Engine) generateAuthorizationPolicy(nfr NonFunctionalRequirement) *PolicyDefinition {
|
||||||
|
if len(nfr.Constraints) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rego strings.Builder
|
||||||
|
rego.WriteString("package authz.custom\n\n")
|
||||||
|
rego.WriteString(fmt.Sprintf("# Policy: %s\n", nfr.Name))
|
||||||
|
rego.WriteString(fmt.Sprintf("# Description: %s\n\n", nfr.Description))
|
||||||
|
|
||||||
|
rego.WriteString("default allow = false\n\n")
|
||||||
|
|
||||||
|
// Generate rules from constraints
|
||||||
|
for constraint, value := range nfr.Constraints {
|
||||||
|
ruleName := strings.ReplaceAll(strings.ToLower(constraint), " ", "_")
|
||||||
|
|
||||||
|
rego.WriteString(fmt.Sprintf("%s {\n", ruleName))
|
||||||
|
rego.WriteString(fmt.Sprintf(" input.%s == \"%s\"\n", constraint, value))
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
rego.WriteString("allow {\n")
|
||||||
|
for constraint := range nfr.Constraints {
|
||||||
|
ruleName := strings.ReplaceAll(strings.ToLower(constraint), " ", "_")
|
||||||
|
rego.WriteString(fmt.Sprintf(" %s\n", ruleName))
|
||||||
|
}
|
||||||
|
rego.WriteString("}\n")
|
||||||
|
|
||||||
|
return &PolicyDefinition{
|
||||||
|
Name: fmt.Sprintf("%s_policy", strings.ToLower(nfr.Name)),
|
||||||
|
Package: "authz.custom",
|
||||||
|
Description: nfr.Description,
|
||||||
|
Rego: rego.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateDataAccessPolicy creates data access control policies.
|
||||||
|
func (e *Engine) generateDataAccessPolicy(reqs *RequirementsDocument) *PolicyDefinition {
|
||||||
|
if len(reqs.SecurityRequirements) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasDataClassification bool
|
||||||
|
for _, secReq := range reqs.SecurityRequirements {
|
||||||
|
if secReq.DataClassification != "" {
|
||||||
|
hasDataClassification = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasDataClassification {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rego strings.Builder
|
||||||
|
rego.WriteString("package authz.data_access\n\n")
|
||||||
|
rego.WriteString("# Data access control policy based on classification\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("default allow = false\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("# Allow access based on data classification\n")
|
||||||
|
rego.WriteString("allow {\n")
|
||||||
|
rego.WriteString(" input.data_classification == \"public\"\n")
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("allow {\n")
|
||||||
|
rego.WriteString(" input.data_classification == \"internal\"\n")
|
||||||
|
rego.WriteString(" input.user.clearance_level >= 1\n")
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("allow {\n")
|
||||||
|
rego.WriteString(" input.data_classification == \"confidential\"\n")
|
||||||
|
rego.WriteString(" input.user.clearance_level >= 2\n")
|
||||||
|
rego.WriteString(" input.user.department == input.data.owner_department\n")
|
||||||
|
rego.WriteString("}\n\n")
|
||||||
|
|
||||||
|
rego.WriteString("allow {\n")
|
||||||
|
rego.WriteString(" input.data_classification == \"restricted\"\n")
|
||||||
|
rego.WriteString(" input.user.clearance_level >= 3\n")
|
||||||
|
rego.WriteString(" input.purpose == \"authorized\"\n")
|
||||||
|
rego.WriteString("}\n")
|
||||||
|
|
||||||
|
return &PolicyDefinition{
|
||||||
|
Name: "data_access_policy",
|
||||||
|
Package: "authz.data_access",
|
||||||
|
Description: "Data access control based on classification levels",
|
||||||
|
Rego: rego.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateOPAPolicies validates generated OPA policies.
|
||||||
|
func (e *Engine) validateOPAPolicies(policies []PolicyDefinition) []ValidationError {
|
||||||
|
var errors []ValidationError
|
||||||
|
|
||||||
|
for i, policy := range policies {
|
||||||
|
// Check for required fields
|
||||||
|
if policy.Package == "" {
|
||||||
|
errors = append(errors, ValidationError{
|
||||||
|
Field: fmt.Sprintf("policies[%d].package", i),
|
||||||
|
Message: "Package is required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy.Rego == "" {
|
||||||
|
errors = append(errors, ValidationError{
|
||||||
|
Field: fmt.Sprintf("policies[%d].rego", i),
|
||||||
|
Message: "Rego code is required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic syntax validation
|
||||||
|
if !strings.Contains(policy.Rego, "package ") {
|
||||||
|
errors = append(errors, ValidationError{
|
||||||
|
Field: fmt.Sprintf("policies[%d].rego", i),
|
||||||
|
Message: "Missing package declaration",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
307
pkg/protoagent/types.go
Normal file
307
pkg/protoagent/types.go
Normal file
|
|
@ -0,0 +1,307 @@
|
||||||
|
// Package protoagent provides a behavior prototyping tool that transforms
|
||||||
|
// functional and non-functional requirements into working agent configurations,
|
||||||
|
// databases, interfaces, and communication channels.
|
||||||
|
package protoagent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InteractionMethod defines how the agent interacts with external systems.
|
||||||
|
type InteractionMethod string
|
||||||
|
|
||||||
|
const (
|
||||||
|
InteractionAPI InteractionMethod = "api"
|
||||||
|
InteractionMCP InteractionMethod = "mcp"
|
||||||
|
InteractionUI InteractionMethod = "ui"
|
||||||
|
InteractionMessaging InteractionMethod = "messaging"
|
||||||
|
InteractionWebhook InteractionMethod = "webhook"
|
||||||
|
InteractionCLI InteractionMethod = "cli"
|
||||||
|
InteractionDatabase InteractionMethod = "database"
|
||||||
|
InteractionFile InteractionMethod = "file"
|
||||||
|
InteractionEventBus InteractionMethod = "eventbus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FunctionalRequirement describes what the system should do.
|
||||||
|
type FunctionalRequirement struct {
|
||||||
|
ID string `json:"id" yaml:"id"`
|
||||||
|
Type string `json:"type" yaml:"type"` // action, operation, actor, resource
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Inputs []ParameterDef `json:"inputs,omitempty" yaml:"inputs,omitempty"`
|
||||||
|
Outputs []ParameterDef `json:"outputs,omitempty" yaml:"outputs,omitempty"`
|
||||||
|
Preconditions []string `json:"preconditions,omitempty" yaml:"preconditions,omitempty"`
|
||||||
|
Postconditions []string `json:"postconditions,omitempty" yaml:"postconditions,omitempty"`
|
||||||
|
InteractionMethods []InteractionMethod `json:"interactionMethods,omitempty" yaml:"interactionMethods,omitempty"`
|
||||||
|
Priority int `json:"priority,omitempty" yaml:"priority,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonFunctionalRequirement describes constraints and quality attributes.
|
||||||
|
type NonFunctionalRequirement struct {
|
||||||
|
ID string `json:"id" yaml:"id"`
|
||||||
|
Category string `json:"category" yaml:"category"` // security, performance, reliability, scalability
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Constraints map[string]string `json:"constraints,omitempty" yaml:"constraints,omitempty"`
|
||||||
|
Metrics []MetricDef `json:"metrics,omitempty" yaml:"metrics,omitempty"`
|
||||||
|
Priority int `json:"priority,omitempty" yaml:"priority,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParameterDef defines a parameter for inputs/outputs.
|
||||||
|
type ParameterDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"`
|
||||||
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
|
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||||
|
Default string `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricDef defines a measurable metric for NFRs.
|
||||||
|
type MetricDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Target string `json:"target" yaml:"target"`
|
||||||
|
Threshold float64 `json:"threshold,omitempty" yaml:"threshold,omitempty"`
|
||||||
|
Unit string `json:"unit,omitempty" yaml:"unit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecurityRequirement captures security-specific NFRs.
|
||||||
|
type SecurityRequirement struct {
|
||||||
|
Permissions []string `json:"permissions,omitempty" yaml:"permissions,omitempty"`
|
||||||
|
Roles []string `json:"roles,omitempty" yaml:"roles,omitempty"`
|
||||||
|
Authorizations []string `json:"authorizations,omitempty" yaml:"authorizations,omitempty"`
|
||||||
|
SecurityControls []string `json:"securityControls,omitempty" yaml:"securityControls,omitempty"`
|
||||||
|
DataClassification string `json:"dataClassification,omitempty" yaml:"dataClassification,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerformanceRequirement captures performance-specific NFRs.
|
||||||
|
type PerformanceRequirement struct {
|
||||||
|
ResponseTime time.Duration `json:"responseTime,omitempty" yaml:"responseTime,omitempty"`
|
||||||
|
Throughput float64 `json:"throughput,omitempty" yaml:"throughput,omitempty"`
|
||||||
|
Concurrency int `json:"concurrency,omitempty" yaml:"concurrency,omitempty"`
|
||||||
|
ResourceLimits ResourceLimit `json:"resourceLimits,omitempty" yaml:"resourceLimits,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceLimit defines resource constraints.
|
||||||
|
type ResourceLimit struct {
|
||||||
|
Memory string `json:"memory,omitempty" yaml:"memory,omitempty"`
|
||||||
|
CPU string `json:"cpu,omitempty" yaml:"cpu,omitempty"`
|
||||||
|
Storage string `json:"storage,omitempty" yaml:"storage,omitempty"`
|
||||||
|
Network string `json:"network,omitempty" yaml:"network,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequirementsDocument is the complete specification input.
|
||||||
|
type RequirementsDocument struct {
|
||||||
|
Version string `json:"version" yaml:"version"`
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
FunctionalRequirements []FunctionalRequirement `json:"functionalRequirements" yaml:"functionalRequirements"`
|
||||||
|
NonFunctionalRequirements []NonFunctionalRequirement `json:"nonFunctionalRequirements" yaml:"nonFunctionalRequirements"`
|
||||||
|
SecurityRequirements []SecurityRequirement `json:"securityRequirements,omitempty" yaml:"securityRequirements,omitempty"`
|
||||||
|
PerformanceRequirements []PerformanceRequirement `json:"performanceRequirements,omitempty" yaml:"performanceRequirements,omitempty"`
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeneratedArtifacts represents all outputs from the prototyping process.
|
||||||
|
type GeneratedArtifacts struct {
|
||||||
|
Timestamp time.Time `json:"timestamp" yaml:"timestamp"`
|
||||||
|
AgentConfig *AgentConfig `json:"agentConfig,omitempty" yaml:"agentConfig,omitempty"`
|
||||||
|
DatabaseSchemas []DatabaseSchema `json:"databaseSchemas,omitempty" yaml:"databaseSchemas,omitempty"`
|
||||||
|
Interfaces []InterfaceDef `json:"interfaces,omitempty" yaml:"interfaces,omitempty"`
|
||||||
|
Channels []ChannelConfig `json:"channels,omitempty" yaml:"channels,omitempty"`
|
||||||
|
Policies []PolicyDefinition `json:"policies,omitempty" yaml:"policies,omitempty"`
|
||||||
|
Skills []SkillDefinition `json:"skills,omitempty" yaml:"skills,omitempty"`
|
||||||
|
Tools []ToolDefinition `json:"tools,omitempty" yaml:"tools,omitempty"`
|
||||||
|
MCPConfig *MCPConfiguration `json:"mcpConfig,omitempty" yaml:"mcpConfig,omitempty"`
|
||||||
|
ValidationReport *ValidationReport `json:"validationReport,omitempty" yaml:"validationReport,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentConfig is the generated AGENT.md configuration.
|
||||||
|
type AgentConfig struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Tools []string `json:"tools,omitempty" yaml:"tools,omitempty"`
|
||||||
|
Model string `json:"model,omitempty" yaml:"model,omitempty"`
|
||||||
|
MaxTurns *int `json:"maxTurns,omitempty" yaml:"maxTurns,omitempty"`
|
||||||
|
Skills []string `json:"skills,omitempty" yaml:"skills,omitempty"`
|
||||||
|
MCPServers []string `json:"mcpServers,omitempty" yaml:"mcpServers,omitempty"`
|
||||||
|
Body string `json:"body" yaml:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseSchema defines a database structure.
|
||||||
|
type DatabaseSchema struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"` // sql, nosql, memory, file
|
||||||
|
Tables []TableDef `json:"tables,omitempty" yaml:"tables,omitempty"`
|
||||||
|
Collections []CollectionDef `json:"collections,omitempty" yaml:"collections,omitempty"`
|
||||||
|
Indexes []IndexDef `json:"indexes,omitempty" yaml:"indexes,omitempty"`
|
||||||
|
Migrations []string `json:"migrations,omitempty" yaml:"migrations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableDef defines a SQL table.
|
||||||
|
type TableDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Columns []ColumnDef `json:"columns" yaml:"columns"`
|
||||||
|
Indexes []string `json:"indexes,omitempty" yaml:"indexes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ColumnDef defines a table column.
|
||||||
|
type ColumnDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"`
|
||||||
|
Nullable bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
|
||||||
|
PrimaryKey bool `json:"primaryKey,omitempty" yaml:"primaryKey,omitempty"`
|
||||||
|
Unique bool `json:"unique,omitempty" yaml:"unique,omitempty"`
|
||||||
|
Default string `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectionDef defines a NoSQL collection.
|
||||||
|
type CollectionDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Schema json.RawMessage `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexDef defines a database index.
|
||||||
|
type IndexDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Table string `json:"table" yaml:"table"`
|
||||||
|
Columns []string `json:"columns" yaml:"columns"`
|
||||||
|
Unique bool `json:"unique,omitempty" yaml:"unique,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceDef defines a user or system interface.
|
||||||
|
type InterfaceDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"` // web, cli, api, gui
|
||||||
|
Endpoints []EndpointDef `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
|
||||||
|
Screens []ScreenDef `json:"screens,omitempty" yaml:"screens,omitempty"`
|
||||||
|
Components []ComponentDef `json:"components,omitempty" yaml:"components,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointDef defines an API endpoint.
|
||||||
|
type EndpointDef struct {
|
||||||
|
Path string `json:"path" yaml:"path"`
|
||||||
|
Method string `json:"method" yaml:"method"`
|
||||||
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
|
Inputs []ParameterDef `json:"inputs,omitempty" yaml:"inputs,omitempty"`
|
||||||
|
Outputs []ParameterDef `json:"outputs,omitempty" yaml:"outputs,omitempty"`
|
||||||
|
Auth []string `json:"auth,omitempty" yaml:"auth,omitempty"`
|
||||||
|
RateLimit *RateLimitDef `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScreenDef defines a UI screen.
|
||||||
|
type ScreenDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Route string `json:"route,omitempty" yaml:"route,omitempty"`
|
||||||
|
Components []ComponentDef `json:"components,omitempty" yaml:"components,omitempty"`
|
||||||
|
Actions []ActionDef `json:"actions,omitempty" yaml:"actions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComponentDef defines a UI component.
|
||||||
|
type ComponentDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"`
|
||||||
|
Properties map[string]string `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionDef defines a UI action.
|
||||||
|
type ActionDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Trigger string `json:"trigger" yaml:"trigger"`
|
||||||
|
Handler string `json:"handler" yaml:"handler"`
|
||||||
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelConfig defines a communication channel.
|
||||||
|
type ChannelConfig struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"` // telegram, discord, slack, webhook, etc.
|
||||||
|
Config map[string]string `json:"config" yaml:"config"`
|
||||||
|
Enabled bool `json:"enabled" yaml:"enabled"`
|
||||||
|
Commands []CommandDef `json:"commands,omitempty" yaml:"commands,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandDef defines a channel command.
|
||||||
|
type CommandDef struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Handler string `json:"handler" yaml:"handler"`
|
||||||
|
Permissions []string `json:"permissions,omitempty" yaml:"permissions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitDef defines rate limiting configuration.
|
||||||
|
type RateLimitDef struct {
|
||||||
|
Requests int `json:"requests" yaml:"requests"`
|
||||||
|
Window time.Duration `json:"window" yaml:"window"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyDefinition defines an OPA policy.
|
||||||
|
type PolicyDefinition struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Package string `json:"package" yaml:"package"`
|
||||||
|
Rules []PolicyRule `json:"rules,omitempty" yaml:"rules,omitempty"`
|
||||||
|
Rego string `json:"rego" yaml:"rego"`
|
||||||
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyRule defines a single policy rule.
|
||||||
|
type PolicyRule struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Condition string `json:"condition" yaml:"condition"`
|
||||||
|
Effect string `json:"effect" yaml:"effect"` // allow, deny
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkillDefinition defines a skill to be generated.
|
||||||
|
type SkillDefinition struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Code string `json:"code" yaml:"code"`
|
||||||
|
Dependencies []string `json:"dependencies,omitempty" yaml:"dependencies,omitempty"`
|
||||||
|
Triggers []string `json:"triggers,omitempty" yaml:"triggers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToolDefinition defines a tool to be generated.
|
||||||
|
type ToolDefinition struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Type string `json:"type" yaml:"type"` // shell, api, mcp, custom
|
||||||
|
Config map[string]string `json:"config,omitempty" yaml:"config,omitempty"`
|
||||||
|
Code string `json:"code,omitempty" yaml:"code,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MCPConfiguration defines MCP server configuration.
|
||||||
|
type MCPConfiguration struct {
|
||||||
|
Servers []MCPServerConfig `json:"servers" yaml:"servers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MCPServerConfig defines a single MCP server.
|
||||||
|
type MCPServerConfig struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Type string `json:"type" yaml:"type"` // stdio, sse, websocket
|
||||||
|
Command string `json:"command,omitempty" yaml:"command,omitempty"`
|
||||||
|
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
|
||||||
|
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||||
|
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationReport contains validation results.
|
||||||
|
type ValidationReport struct {
|
||||||
|
Valid bool `json:"valid" yaml:"valid"`
|
||||||
|
Errors []ValidationError `json:"errors,omitempty" yaml:"errors,omitempty"`
|
||||||
|
Warnings []ValidationWarning `json:"warnings,omitempty" yaml:"warnings,omitempty"`
|
||||||
|
Suggestions []string `json:"suggestions,omitempty" yaml:"suggestions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationError represents a validation error.
|
||||||
|
type ValidationError struct {
|
||||||
|
Field string `json:"field" yaml:"field"`
|
||||||
|
Message string `json:"message" yaml:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidationWarning represents a validation warning.
|
||||||
|
type ValidationWarning struct {
|
||||||
|
Field string `json:"field" yaml:"field"`
|
||||||
|
Message string `json:"message" yaml:"message"`
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue