botbook/src/chapter-06-gbdialog/script-execution-flow.md

484 lines
13 KiB
Markdown
Raw Normal View History

2025-12-03 19:56:35 -03:00
# Script Execution Flow & Entry Points
Understanding how General Bots BASIC scripts are loaded, compiled, and executed is essential for building effective automation. This document covers the complete execution lifecycle.
## Execution Entry Points
Scripts in General Bots can be triggered through several entry points:
![Script Execution Flow](../assets/script-execution-flow.svg)
*BASIC scripts compile to Rhai AST and execute with registered keyword handlers. Scripts in .gbdialog/ are hot-reloaded on change.*
### 1. Bot Startup (`start.bas`)
The primary entry point. Executed when a bot initializes or a conversation begins.
```basic
' start.bas - Primary entry point
' NO MAIN function needed - execution starts at line 1
' 1. Register tools for LLM to use
ADD TOOL "create-order"
ADD TOOL "track-shipment"
ADD TOOL "customer-lookup"
' 2. Load knowledge bases
USE KB "products"
USE KB "policies"
' 3. Set AI context/personality
BEGIN SYSTEM PROMPT
You are a helpful e-commerce assistant for AcmeStore.
You can help customers with orders, tracking, and product questions.
Always be friendly and professional.
END SYSTEM PROMPT
' 4. Setup UI suggestions
CLEAR SUGGESTIONS
ADD SUGGESTION "New Order"
ADD SUGGESTION "Track Package"
ADD SUGGESTION "Contact Support"
' 5. Welcome message
BEGIN TALK
**Welcome to AcmeStore!** 🛒
I can help you:
• Browse and order products
• Track your shipments
• Answer questions
What would you like to do?
END TALK
```
### 2. Scheduled Execution (`SET SCHEDULE`)
Scripts can run on a cron schedule without user interaction.
```basic
' sync-data.bas - Runs automatically on schedule
SET SCHEDULE "0 0 */4 * * *" ' Every 4 hours
' Variables from config.csv are available
' param-host -> host, param-limit -> limit, param-pages -> pages
SEND EMAIL admin1, "Data sync started..."
page = 1
DO WHILE page > 0 AND page < pages
res = GET host + "/products?page=" + page + "&limit=" + limit
IF res.data THEN
MERGE "products" WITH res.data BY "id"
page = page + 1
ELSE
page = 0
END IF
WAIT 0.5 ' Rate limiting
LOOP
SEND EMAIL admin1, "Sync complete! " + REPORT
RESET REPORT
```
**Cron Format:** `second minute hour day month weekday`
| Pattern | Description |
|---------|-------------|
| `0 0 8 * * *` | Daily at 8:00 AM |
| `0 30 22 * * *` | Daily at 10:30 PM |
| `0 0 0 */2 * *` | Every 2 days at midnight |
| `0 0 * * * *` | Every hour |
| `0 */15 * * * *` | Every 15 minutes |
### 3. Webhook Entry (`WEBHOOK`)
Scripts exposed as HTTP endpoints for external integrations.
```basic
' order-webhook.bas - HTTP endpoint
WEBHOOK "order-received"
' Creates: POST /api/bot/{botname}/order-received
' Parameters become variables automatically
' Access webhook parameters
orderId = GET TOOL PARAM "orderId"
customerEmail = GET TOOL PARAM "email"
amount = GET TOOL PARAM "amount"
' Validate (optional)
IF orderId = "" THEN
RETURN #{ status: 400, error: "Missing orderId" }
END IF
' Process the webhook
order = NEW OBJECT
order.id = orderId
order.email = customerEmail
order.amount = amount
order.status = "received"
order.timestamp = NOW
SAVE "orders", order
' Notify
TALK TO customerEmail, "Order " + orderId + " received! Total: $" + amount
RETURN #{ status: 200, orderId: orderId, message: "Order processed" }
```
### 4. LLM Tool Invocation
When registered with `ADD TOOL`, scripts become callable by the LLM during conversation.
```basic
' create-order.bas - Called by LLM when user wants to order
PARAM productId AS STRING LIKE "PROD-001" REQUIRED
PARAM quantity AS NUMBER LIKE 1 REQUIRED
PARAM customerEmail AS STRING LIKE "john@example.com" REQUIRED
DESCRIPTION "Creates a new order for a product"
' This script is invoked by the LLM, not directly by user
' The LLM collects all parameters through natural conversation
product = FIND "products", "id=" + productId
IF ISEMPTY(product) THEN
RETURN "Product not found: " + productId
END IF
IF product.stock < quantity THEN
RETURN "Only " + product.stock + " available"
END IF
' Create the order
order = NEW OBJECT
order.id = "ORD-" + FORMAT(NOW, "yyyyMMddHHmmss")
order.productId = productId
order.quantity = quantity
order.total = product.price * quantity
order.customerEmail = customerEmail
order.status = "pending"
SAVE "orders", order
' Update inventory
UPDATE "products", productId, "stock=" + (product.stock - quantity)
RETURN "Order " + order.id + " created! Total: $" + order.total
```
### 5. Event Handlers (`ON`)
React to system events.
```basic
' events.bas - Event handlers
ON "message" CALL HandleMessage
ON "user_joined" CALL WelcomeUser
ON "error" CALL LogError
SUB HandleMessage(message)
' Process incoming message
LOG_INFO "Received: " + message.text
END SUB
SUB WelcomeUser(user)
TALK TO user.email, "Welcome to our service!"
END SUB
SUB LogError(error)
LOG_ERROR "Error occurred: " + error.message
SEND EMAIL admin1, "Bot Error: " + error.message
END SUB
```
---
## Variable Injection from config.csv
Variables defined with `param-` prefix in config.csv are automatically injected into script scope.
### config.csv
```csv
name,value
bot-name,Bling Integration
bot-description,ERP synchronization bot
param-host,https://api.bling.com.br/Api/v3
param-limit,100
param-pages,50
param-admin1,admin@company.com
param-admin2,backup@company.com
param-blingClientID,your-client-id
param-blingClientSecret,your-secret
```
### Script Usage
```basic
' Variables are available without param- prefix
' All normalized to lowercase for case-insensitivity
result = GET host + "/products?limit=" + limit
DO WHILE page < pages
' Use injected variables directly
data = GET host + "/items?page=" + page
LOOP
' Admin emails for notifications
SEND EMAIL admin1, "Sync complete!"
SEND EMAIL admin2, "Backup notification"
```
### Type Conversion
Values are automatically converted:
- Numbers: `param-limit,100``limit` as integer `100`
- Floats: `param-rate,0.15``rate` as float `0.15`
- Booleans: `param-enabled,true``enabled` as boolean `true`
- Strings: Everything else remains as string
---
## Case Insensitivity
**All variables in General Bots BASIC are case-insensitive.**
```basic
' These all refer to the same variable
host = "https://api.example.com"
result = GET Host + "/endpoint"
TALK HOST
```
The preprocessor normalizes all variable names to lowercase while preserving:
- Keywords (remain UPPERCASE for clarity)
- String literals (exact content preserved)
- Comments (skipped entirely)
---
## Script Compilation Flow
```
┌─────────────────────────────────────────────────────────────────┐
│ COMPILATION PIPELINE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. LOAD SOURCE │
│ └─→ Read .bas file from .gbdialog folder │
│ │
│ 2. LOAD CONFIG │
│ └─→ Read config.csv, extract param-* entries │
│ └─→ Inject into execution scope │
│ │
│ 3. PREPROCESS │
│ ├─→ Strip comments (REM, ', //) │
│ ├─→ Process SWITCH/CASE blocks │
│ ├─→ Normalize variables to lowercase │
│ ├─→ Transform multi-word keywords │
│ └─→ Handle FOR EACH blocks │
│ │
│ 4. COMPILE │
│ └─→ Parse to Rhai AST │
│ │
│ 5. EXECUTE │
│ └─→ Run AST with injected scope │
│ └─→ Keywords call registered handlers │
│ │
└─────────────────────────────────────────────────────────────────┘
```
---
## Functions vs Entry Points
### NO MAIN Function
Unlike traditional programming, BASIC scripts do NOT use a `MAIN` function. Execution starts at line 1.
```basic
' ❌ WRONG - Don't do this
SUB MAIN()
TALK "Hello"
END SUB
' ✅ CORRECT - Start directly
TALK "Hello"
```
### SUB and FUNCTION for Reuse
Use `SUB` and `FUNCTION` for reusable code within tools, not as entry points.
```basic
' sync-products.bas - A complex tool with helper functions
FUNCTION CalculateDiscount(price, percentage)
RETURN price * (1 - percentage / 100)
END FUNCTION
SUB NotifyAdmin(message)
SEND EMAIL admin1, message
LOG_INFO message
END SUB
SUB ProcessProduct(product)
IF product.discount > 0 THEN
product.finalPrice = CalculateDiscount(product.price, product.discount)
ELSE
product.finalPrice = product.price
END IF
SAVE "products", product
END SUB
' Main execution starts here (not in a MAIN sub)
products = GET host + "/products"
FOR EACH product IN products.data
CALL ProcessProduct(product)
NEXT product
CALL NotifyAdmin("Processed " + COUNT(products.data) + " products")
```
---
## Tool Chain Pattern
Register tools in `start.bas`, implement in separate files:
### start.bas
```basic
' Register tools - LLM can call these
ADD TOOL "create-customer"
ADD TOOL "update-customer"
ADD TOOL "delete-customer"
' Or clear and re-register
CLEAR TOOLS
ADD TOOL "order-management"
ADD TOOL "inventory-check"
```
### create-customer.bas
```basic
PARAM name AS STRING LIKE "John Doe" REQUIRED
PARAM email AS STRING LIKE "john@example.com" REQUIRED
PARAM phone AS STRING LIKE "+1-555-0123"
DESCRIPTION "Creates a new customer record in the CRM"
' Tool implementation
customer = NEW OBJECT
customer.id = "CUS-" + FORMAT(NOW, "yyyyMMddHHmmss")
customer.name = name
customer.email = email
customer.phone = phone
customer.createdAt = NOW
SAVE "customers", customer
RETURN #{
success: true,
customerId: customer.id,
message: "Customer created successfully"
}
```
---
## Best Practices
### 1. Organize by Purpose
```
mybot.gbai/
├── mybot.gbdialog/
│ ├── start.bas ' Entry point, tool registration
│ ├── tables.bas ' Database schema (TABLE definitions)
│ │
│ ├── create-order.bas ' Tool: order creation
│ ├── track-order.bas ' Tool: order tracking
│ ├── cancel-order.bas ' Tool: order cancellation
│ │
│ ├── sync-products.bas ' Scheduled: product sync
│ ├── sync-inventory.bas ' Scheduled: inventory sync
│ │
│ └── order-webhook.bas ' Webhook: external orders
├── mybot.gbot/
│ └── config.csv ' Configuration & param-* variables
└── mybot.gbkb/ ' Knowledge base files
```
### 2. Use param-* for Configuration
Keep credentials and settings in config.csv, not hardcoded:
```basic
' ❌ WRONG
host = "https://api.bling.com.br/Api/v3"
apiKey = "hardcoded-key"
' ✅ CORRECT - From config.csv
' param-host and param-apiKey in config.csv
result = GET host + "/endpoint"
SET HEADER "Authorization", "Bearer " + apikey
```
### 3. Error Handling in Tools
```basic
PARAM orderId AS STRING REQUIRED
DESCRIPTION "Cancels an order"
order = FIND "orders", "id=" + orderId
IF ISEMPTY(order) THEN
RETURN #{ success: false, error: "Order not found" }
END IF
IF order.status = "shipped" THEN
RETURN #{ success: false, error: "Cannot cancel shipped orders" }
END IF
UPDATE "orders", orderId, "status=cancelled"
RETURN #{ success: true, message: "Order cancelled" }
```
### 4. Logging for Scheduled Jobs
```basic
SET SCHEDULE "0 0 6 * * *"
LOG_INFO "Daily sync started"
' ... sync logic ...
IF errorCount > 0 THEN
LOG_WARN "Sync completed with " + errorCount + " errors"
SEND EMAIL admin1, "Sync Warning", REPORT
ELSE
LOG_INFO "Sync completed successfully"
END IF
RESET REPORT
```
---
## See Also
- [Keyword Reference](./keywords.md) - Complete keyword documentation
- [SET SCHEDULE](./keyword-set-schedule.md) - Scheduling details
- [WEBHOOK](./keyword-webhook.md) - Webhook configuration
- [Tools System](./keyword-use-tool.md) - Tool registration
- [BEGIN SYSTEM PROMPT](./prompt-blocks.md) - AI context configuration