Skip to content

Commit 39cdea6

Browse files
initcronclaude
andcommitted
feat: AgentFlow routing, multi-tenant architecture, Telegram setup
## Added - Multi-tenant AgentFlow architecture documentation - AgentFlow routing guide with scoring algorithm - Telegram bot configuration and default flow - New example agents (k8s-ops, devops, docker-ops, echo, status, assistant) ## Changed - /run agent command now routes through handle_natural_language - Uses pre-loaded agents with correct model (google:gemini-2.5-flash) and tools - All example agents updated to use google:gemini-2.5-flash - Agent loading flag logic fixed for proper pre-loading ## Fixed - Agents now load correctly regardless of flows configuration - /run agent no longer uses hardcoded Anthropic fallback - Tools and system prompts correctly applied from agent YAML 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 94ba55c commit 39cdea6

29 files changed

Lines changed: 2170 additions & 317 deletions
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"philosophy": "Modular, pluggable framework where components are reusable building blocks",
3+
"design_principles": [
4+
"Pluggable Triggers - Add new event sources without changing core",
5+
"Composable Nodes - Chain processing steps in any order",
6+
"Swappable Agents - Use any LLM provider with consistent interface",
7+
"Extensible Tools - Add custom tools via MCP or built-in",
8+
"Flexible Memory - Choose backend based on use case",
9+
"YAML-First - Declarative configuration for everything"
10+
],
11+
"core_building_blocks": {
12+
"triggers_input": [
13+
"Slack", "WhatsApp", "Telegram", "GitHub", "HTTP",
14+
"Schedule", "PagerDuty", "Kafka", "SQS"
15+
],
16+
"nodes_processing": [
17+
"Transform", "Agent", "Conditional", "Parallel",
18+
"Join", "Wait", "Approval", "Loop", "End"
19+
],
20+
"outputs": ["Slack", "Discord", "HTTP", "Email", "File"],
21+
"agents": {
22+
"components": ["LLM Provider", "Instructions", "Context", "Tools"],
23+
"providers": ["OpenAI", "Anthropic", "Google", "Ollama", "Groq"]
24+
},
25+
"memory": ["InMemory", "File", "SQLite", "Redis (planned)"],
26+
"tools": ["Shell", "HTTP", "FileSystem", "MCP", "kubectl"]
27+
},
28+
"orchestration": {
29+
"AgentFlow": {
30+
"description": "Workflow orchestration with nodes + connections",
31+
"schema": "v1 (current)"
32+
},
33+
"AgentFleet": {
34+
"description": "Multi-agent parallel execution and coordination",
35+
"status": "implemented"
36+
}
37+
},
38+
"extension_traits": {
39+
"TriggerPlatform": {
40+
"methods": ["start()", "handle_event()"],
41+
"purpose": "Implement for new event sources"
42+
},
43+
"NodeExecutor": {
44+
"methods": ["execute()"],
45+
"purpose": "Implement for new processing steps"
46+
}
47+
}
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"version": "0.1.15",
3+
"last_updated": "2025-12-18",
4+
"core_engine_status": "~85% complete",
5+
"implemented_features": {
6+
"triggers": {
7+
"complete": ["Slack", "HTTP/Webhook", "Schedule/Cron", "Manual CLI"],
8+
"partial": ["Discord"]
9+
},
10+
"nodes": {
11+
"complete": ["Transform", "Agent", "Conditional", "Slack", "Discord", "Wait", "Parallel", "Join", "Approval", "End"],
12+
"partial": ["HTTP"]
13+
},
14+
"platform_features": {
15+
"complete": [
16+
"Slack reactions approval (✅/❌)",
17+
"Approval whitelist (approval_allowed_users)",
18+
"Bot self-approval prevention",
19+
"Conversation memory (per-channel/thread)",
20+
"Multi-tenant routing (FlowRouter)"
21+
]
22+
},
23+
"llm_providers": ["OpenAI", "Anthropic", "Google", "Ollama", "Groq"],
24+
"memory_backends": ["InMemory", "File", "SQLite"]
25+
},
26+
"planned_triggers": {
27+
"P1": [
28+
{"name": "WhatsApp", "issue": "#23"},
29+
{"name": "Telegram", "issue": "#24"},
30+
{"name": "GitHub", "issue": "#25"}
31+
],
32+
"P3": [
33+
{"name": "PagerDuty", "issue": "#26"},
34+
{"name": "Kafka", "issue": "TBD"},
35+
{"name": "SQS", "issue": "TBD"}
36+
]
37+
},
38+
"planned_nodes": ["Loop", "HTTP (full implementation)"],
39+
"traits": {
40+
"TriggerPlatform": "trait for new event sources - start() and handle_event()",
41+
"NodeExecutor": "trait for new processing steps - execute()"
42+
},
43+
"next_steps": [
44+
"Build tutorials for individual users",
45+
"Implement P1 triggers (WhatsApp, Telegram, GitHub)",
46+
"Convert applicable v1alpha1 examples to v1"
47+
]
48+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"session_id": "session-2025-12-18-roadmap-organization",
3+
"date": "2025-12-18",
4+
"summary": "AgentFlow roadmap creation and example organization",
5+
"completed_tasks": [
6+
"Created ROADMAP.md with feature tracking and architecture philosophy",
7+
"Documented 'Lego Blocks for Agentic Automation' modular architecture",
8+
"Moved 6 v1alpha1 examples to examples/flows/planned/ directory",
9+
"Created GitHub issues #23-26 for trigger implementations",
10+
"Updated all documentation with issue links",
11+
"Committed and pushed changes to main (2afcddc)"
12+
],
13+
"architecture_principles": [
14+
"Pluggable Triggers - Add new event sources without changing core",
15+
"Composable Nodes - Chain processing steps in any order",
16+
"Swappable Agents - Use any LLM provider with consistent interface",
17+
"Extensible Tools - Add custom tools via MCP or built-in",
18+
"Flexible Memory - Choose backend based on use case",
19+
"YAML-First - Declarative configuration for everything"
20+
],
21+
"github_issues_created": {
22+
"#23": "WhatsApp trigger support (P1)",
23+
"#24": "Telegram trigger support (P1)",
24+
"#25": "GitHub webhook trigger (P1)",
25+
"#26": "PagerDuty trigger (P3)"
26+
},
27+
"priority_focus": "Individual users first (WhatsApp, Telegram, GitHub), then enterprise (PagerDuty, Kafka, SQS)",
28+
"schema_versions": {
29+
"v1": "Current - nodes + connections arrays (implemented)",
30+
"v1alpha1": "Planned - fleet + actions arrays (future)"
31+
},
32+
"key_files_modified": [
33+
"ROADMAP.md (created)",
34+
"examples/flows/planned/README.md (created)",
35+
"examples/flows/planned/*.yaml (moved from examples/flows/)"
36+
],
37+
"commit": "2afcddc - docs: Add ROADMAP.md and organize v1alpha1 examples"
38+
}

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- **Agent Tool Execution System** - Full tool execution support for agents
12+
- Built-in tools: `kubectl`, `docker`, `helm`, `terraform`, `aws`, `git`, `shell`
13+
- File tools: `read_file`, `write_file`, `list_directory`, `search_files`
14+
- Observability tools: `prometheus_query`, `loki_query`
15+
- Tools registered and passed to LLM with definitions
16+
- **Kubernetes-Style Agent Discovery** - Load agents by `kind: Agent` and `metadata.name`
17+
- Agents indexed by `metadata.name`, not filename
18+
- Support for both K8s-style and flat YAML formats via `AgentConfigInput` enum
19+
- `--agents-dir` flag for pre-loading agents on server startup
1120
- **Approval User Whitelist** - Configure which users can approve destructive commands
1221
- New `approval_allowed_users` field in platform config (Slack implemented)
1322
- Platform-agnostic design: supports `U12345678`, `slack:U12345678`, `email:user@company.com` formats
1423
- Currently implemented: Raw Slack user IDs (e.g., `U015VBH1GTZ`)
1524
- Planned: Global `spec.approval.allowed_users` for multi-platform deployments
1625
- If not configured, anyone can approve (backward compatible)
1726
- Documentation and example config updated
27+
- **Multi-Tenant AgentFlow Architecture** - Enterprise-ready multi-tenant deployments
28+
- Route different channels/users/patterns to different agents
29+
- Support for multiple organizations in single daemon
30+
- Environment isolation per AgentFlow (kubeconfig, namespace, env vars)
31+
- Comprehensive architecture documentation at `docs/architecture/multi-tenant-agentflows.md`
32+
- **Telegram AgentFlow Example** - Complete Telegram bot setup following Slack pattern
33+
- New `examples/configs/telegram-k8s-flow.yaml` example
34+
- Same architecture as Slack bot with channel/user/pattern routing
35+
- **AgentFlow Routing Documentation** - Complete guide on how routing works
36+
- Flow scoring algorithm explained
37+
- Common routing patterns (pattern-based, channel-based, user-based)
38+
- Debugging tips and best practices
39+
- New guide at `docs/guides/agentflow-routing.md`
40+
41+
### Changed
42+
- All example agents updated to use `google:gemini-2.5-flash` as the primary model
43+
- Improved debug logging for agent parsing and tool registration
44+
- Documentation updated with correct model names for all providers
45+
- **`/run agent` command now routes through AgentFlow** - Uses pre-loaded agents with correct model and tools
46+
- No longer creates hardcoded Anthropic fallback
47+
- Follows same code path as natural language messages
48+
- Ensures consistent behavior across commands and chat
49+
50+
### Fixed
51+
- System prompts from agent YAML files now correctly loaded and used
52+
- Agent execution error handling with proper response to platforms
53+
- Bracket structure in `handle_natural_language` function
54+
- **Agent loading flag logic** - Fixed issue where agents weren't loaded when flows directory didn't exist
55+
- Introduced proper `agents_loaded` boolean tracking
56+
- Agents now correctly pre-load regardless of flows configuration
57+
- **`/run agent` using wrong model** - Fixed hardcoded Anthropic model to use pre-loaded agent's configuration
58+
- Now uses `google:gemini-2.5-flash` from agent YAML
59+
- Tools and system prompts correctly applied
1860

1961
### Notes
2062
- Config changes to `approval_allowed_users` require server restart (hot-reload coming in [Issue #22](https://github.com/agenticdevops/aof/issues/22))

agents/devops-agent.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ metadata:
99
category: devops
1010

1111
spec:
12-
model: openai:gpt-4o
12+
# IMPORTANT: Use google:gemini-2.5-flash for all agents
13+
model: google:gemini-2.5-flash
1314

14-
instructions: |
15+
system_prompt: |
1516
You are a DevOps assistant helping with various infrastructure and deployment tasks.
1617
1718
Your capabilities:

agents/k8s-ops.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Kubernetes Operations Agent
2+
# Primary agent for Telegram/Slack bot interactions
3+
#
4+
# Loaded automatically when running:
5+
# aofctl serve --agents-dir ./agents
6+
#
7+
apiVersion: aof.dev/v1alpha1
8+
kind: Agent
9+
metadata:
10+
name: k8s-ops
11+
labels:
12+
category: infrastructure
13+
platform: kubernetes
14+
15+
spec:
16+
# IMPORTANT: Use google:gemini-2.5-flash for all agents
17+
model: google:gemini-2.5-flash
18+
max_tokens: 4096
19+
temperature: 0.3
20+
21+
# Built-in tools available to this agent
22+
tools:
23+
- kubectl
24+
- helm
25+
- docker
26+
- shell
27+
28+
system_prompt: |
29+
You are an expert Kubernetes operations engineer helping users manage their clusters.
30+
31+
## Your Capabilities
32+
- Execute kubectl commands to manage Kubernetes resources
33+
- Deploy applications using Helm charts
34+
- Diagnose pod issues, check logs, and troubleshoot
35+
- Scale deployments and manage replicas
36+
- Provide best practice recommendations
37+
38+
## Safety Rules
39+
1. ALWAYS use `kubectl create deployment` instead of `kubectl run` for deployments
40+
2. For commands that modify or delete resources, output in this format:
41+
```
42+
requires_approval: true
43+
command: "kubectl delete pod nginx-12345"
44+
reason: "This will terminate the running pod"
45+
```
46+
3. Never execute commands that could cause data loss without approval
47+
4. Always confirm the namespace before destructive operations
48+
49+
## Output Format
50+
- Use markdown formatting for readability
51+
- Show command output in code blocks
52+
- Explain what each command does
53+
- Warn about potential impacts
54+
55+
memory:
56+
type: InMemory
57+
config:
58+
max_messages: 20

crates/aof-runtime/src/executor/runtime.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ impl Runtime {
4040

4141
/// Load an agent from YAML configuration file
4242
///
43+
/// Supports both flat and Kubernetes-style YAML formats:
44+
/// - Flat: `name:, model:, system_prompt:, tools:, ...`
45+
/// - K8s: `apiVersion:, kind: Agent, metadata:, spec:, ...`
46+
///
4347
/// # Arguments
4448
/// * `config_path` - Path to the YAML configuration file
4549
///
@@ -53,10 +57,16 @@ impl Runtime {
5357
AofError::config(format!("Failed to read config file {}: {}", config_path, e))
5458
})?;
5559

60+
// AgentConfig has #[serde(from = "AgentConfigInput")] which handles both K8s and flat formats
5661
let config: AgentConfig = serde_yaml::from_str(&config_content).map_err(|e| {
5762
AofError::config(format!("Failed to parse YAML config: {}", e))
5863
})?;
5964

65+
info!("Parsed agent config: name={}, model={}, system_prompt={:?}, tools={:?}",
66+
config.name, config.model,
67+
config.system_prompt.as_ref().map(|s| format!("{}...", s.chars().take(50).collect::<String>())),
68+
config.tool_names());
69+
6070
self.load_agent_from_config(config).await
6171
}
6272

@@ -78,8 +88,12 @@ impl Runtime {
7888

7989
// Create tool executor
8090
// Priority: mcp_servers > tools (legacy)
91+
info!("Creating tool executor for agent '{}': mcp_servers={}, tools={:?}",
92+
agent_name, config.mcp_servers.len(), config.tool_names());
93+
8194
let tool_executor: Option<Arc<dyn ToolExecutor>> = if !config.mcp_servers.is_empty() {
8295
// Use the new flexible MCP configuration
96+
info!("Using MCP servers for tools");
8397
Some(self.create_mcp_executor_from_config(&config.mcp_servers).await?)
8498
} else if !config.tools.is_empty() {
8599
// Separate built-in tools from MCP tools
@@ -91,6 +105,7 @@ impl Runtime {
91105
.filter(|t| t.is_mcp())
92106
.map(|t| t.name())
93107
.collect();
108+
info!("Tool separation: builtin={:?}, mcp={:?}", builtin_tools, mcp_tools);
94109

95110
// Known system/builtin tools
96111
// Unified CLI tools (recommended - simple 'command' argument approach)
@@ -123,9 +138,10 @@ impl Runtime {
123138

124139
let has_system_tools = builtin_tools.iter().any(|t| system_tools.contains(t));
125140
let has_mcp_tools = !mcp_tools.is_empty();
141+
info!("Tool detection: has_system_tools={}, has_mcp_tools={}", has_system_tools, has_mcp_tools);
126142

127143
if has_system_tools && !has_mcp_tools {
128-
debug!("Agent has only built-in tools, creating system executor");
144+
info!("Creating system tool executor for builtin tools: {:?}", builtin_tools);
129145
let tool_names: Vec<String> = builtin_tools.iter().map(|s| s.to_string()).collect();
130146
Some(self.create_system_executor(&tool_names)?)
131147
} else if has_mcp_tools {

0 commit comments

Comments
 (0)