Skip to content

Commit b2917bb

Browse files
Merge pull request #159 from vamplabAI/157-tools-docs
Tunes of documentation
2 parents 81655f1 + fd08f62 commit b2917bb

File tree

25 files changed

+1132
-522
lines changed

25 files changed

+1132
-522
lines changed

.cursor/rules/architecture.mdc

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ The project uses **modular architecture with clear separation of concerns**. The
2626

2727
### Layer 3: Factory and Services
2828
- `agent_factory.py` - AgentFactory for creating agents
29-
- `services/mcp_service.py` - MCP2ToolConverter
29+
- `services/mcp_service.py` - MCP2ToolConverter for MCP tool integration
3030
- `services/tavily_search.py` - Tavily search service
31-
- `next_step_tool.py` - NextStepToolsBuilder, NextStepToolStub
31+
- `next_step_tool.py` - NextStepToolsBuilder, NextStepToolStub, DiscriminantToolMixin
3232

3333
### Layer 4: Agent Implementations
3434
- `agents/sgr_agent.py` - SGRAgent (Structured Output)
@@ -54,43 +54,59 @@ The project uses **modular architecture with clear separation of concerns**. The
5454

5555
## Agent Execution Cycle
5656

57-
All agents follow a two-phase cycle:
57+
All agents follow a three-phase cycle implemented in `BaseAgent._execution_step()`:
5858

5959
```python
60-
while agent.state not in FINISH_STATES:
60+
while agent._context.state not in FINISH_STATES:
6161
reasoning = await agent._reasoning_phase()
6262
action_tool = await agent._select_action_phase(reasoning)
6363
await agent._action_phase(action_tool)
6464
```
6565

66-
### Phase 1: Reasoning Phase
67-
- Agent analyzes current context
68-
- Decides on next action
69-
- Implementation varies by agent type (SO, FC, or Hybrid)
66+
The main `execute()` method runs this cycle until agent reaches a finish state.
7067

71-
### Phase 2: Select Action Phase
72-
- Selects appropriate tool based on reasoning
73-
- Returns tool instance ready for execution
68+
### Phase 1: Reasoning Phase (`_reasoning_phase()`)
69+
- Agent analyzes current context via `_prepare_context()`
70+
- Decides on next action using LLM
71+
- Implementation varies by agent type:
72+
- **SGRAgent**: Uses structured output with `NextStepToolStub` (reasoning + tool selection)
73+
- **ToolCallingAgent**: Returns `None` (no explicit reasoning)
74+
- **SGRToolCallingAgent**: Uses Function Calling to get `ReasoningTool` result, then executes it
75+
- Returns `ReasoningTool` or `NextStepToolStub` (or `None` for ToolCallingAgent)
7476

75-
### Phase 3: Action Phase
76-
- Executes selected tool
77-
- Updates conversation history
78-
- Updates agent context
77+
### Phase 2: Select Action Phase (`_select_action_phase()`)
78+
- Selects appropriate tool based on reasoning
79+
- Implementation varies by agent type:
80+
- **SGRAgent**: Extracts tool from `reasoning.function` field
81+
- **ToolCallingAgent**: Uses Function Calling with `tool_choice="required"`
82+
- **SGRToolCallingAgent**: Uses Function Calling with `tool_choice="required"` (handles text response edge case)
83+
- Returns `BaseTool` instance ready for execution
84+
85+
### Phase 3: Action Phase (`_action_phase()`)
86+
- Executes selected tool: `result = await tool(context, config)`
87+
- Updates conversation history with tool call and result
88+
- Updates agent context (state, iteration, sources, etc.)
89+
- Logs tool execution
90+
- Handles special cases (e.g., `ClarificationTool` pauses execution)
7991

8092
## Module Rules
8193

8294
### Agents (`sgr_agent_core/agents/`)
8395
- Must inherit from `BaseAgent`
8496
- Must implement `_reasoning_phase()`, `_select_action_phase()`, `_action_phase()`
85-
- Automatically registered in `AgentRegistry` via `AgentRegistryMixin`
97+
- Can override `_execution_step()` to customize execution cycle
8698
- Can override `_prepare_context()` and `_prepare_tools()` for customization
99+
- Automatically registered in `AgentRegistry` via `AgentRegistryMixin`
100+
- Must set `name` class variable (used for registration)
87101

88102
### Tools (`sgr_agent_core/tools/`)
89103
- Must inherit from `BaseTool` or `MCPBaseTool`
90-
- Must be Pydantic models
91-
- Must implement `__call__()` method
104+
- Must be Pydantic models (inherit from `BaseModel`)
105+
- Must implement `__call__(context: AgentContext, config: AgentConfig, **kwargs) -> str`
106+
- Must set `tool_name` and `description` class variables (or use defaults)
92107
- Automatically registered in `ToolRegistry` via `ToolRegistryMixin`
93108
- Return string or JSON string from `__call__()`
109+
- Can be used in `NextStepToolsBuilder` for structured output
94110

95111
### Services (`sgr_agent_core/services/`)
96112
- Stateless utility classes
@@ -99,8 +115,11 @@ while agent.state not in FINISH_STATES:
99115

100116
### Configuration (`agent_config.py`, `agent_definition.py`)
101117
- Hierarchical configuration: GlobalConfig → AgentDefinition → AgentConfig
102-
- Supports YAML loading
118+
- Supports YAML loading via `GlobalConfig.from_yaml()` and `definitions_from_yaml()`
119+
- Supports environment variables via `pydantic-settings` (prefix `SGR__`)
103120
- Automatic inheritance and override of settings
121+
- `AgentDefinition` inherits from `AgentConfig` (has all config fields + name, base_class, tools)
122+
- Config classes use `extra="allow"` to support custom fields
104123

105124
## Design Principles
106125

.cursor/rules/core-modules.mdc

Lines changed: 140 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,95 +10,165 @@ alwaysApply: true
1010

1111
### BaseAgent (`sgr_agent_core/base_agent.py`)
1212
- Parent class for all agents
13-
- Implements two-phase execution cycle: Reasoning → Action
13+
- Implements three-phase execution cycle: Reasoning → Select Action → Action
1414
- Manages agent context, conversation history, and streaming
1515
- Must be subclassed to implement `_reasoning_phase()`, `_select_action_phase()`, `_action_phase()`
1616
- Automatically registered in `AgentRegistry` via `AgentRegistryMixin`
17+
- Key attributes:
18+
- `id`: Unique agent identifier (format: `{def_name or name}_{uuid}`)
19+
- `name`: Agent class name
20+
- `task_messages`: Initial task messages in OpenAI format
21+
- `config`: AgentConfig instance with all settings
22+
- `openai_client`: AsyncOpenAI client for LLM API
23+
- `toolkit`: List of tool classes available to agent
24+
- `_context`: AgentContext instance with execution state
25+
- `conversation`: List of messages in OpenAI format for LLM context
26+
- `streaming_generator`: OpenAIStreamingGenerator for streaming responses
27+
- `logger`: Logger instance for agent logging
28+
- `log`: List of execution logs
29+
- `creation_time`: Datetime when agent was created
30+
- Key methods:
31+
- `execute()`: Main execution loop (called externally)
32+
- `_execution_step()`: Single step of execution cycle (can be overridden)
33+
- `_prepare_context()`: Prepare conversation context (can be overridden)
34+
- `_prepare_tools()`: Prepare available tools (can be overridden)
35+
- `provide_clarification()`: Receive clarification from external source
36+
- `_log_reasoning()`: Log reasoning phase results
37+
- `_log_tool_execution()`: Log tool execution results
38+
- `_save_agent_log()`: Save execution log to file
1739

1840
### BaseTool (`sgr_agent_core/base_tool.py`)
19-
- Parent class for all tools
20-
- Must be a Pydantic model
21-
- Must implement `__call__(context, config)` method
22-
- Returns string or JSON string
41+
- Parent class for all tools (Pydantic `BaseModel`)
42+
- Class variables: `tool_name` (ClassVar[str]), `description` (ClassVar[str])
43+
- Must implement `__call__(context: AgentContext, config: AgentConfig, **kwargs) -> str`
44+
- Returns string or JSON string from `__call__()`
2345
- Automatically registered in `ToolRegistry` via `ToolRegistryMixin`
46+
- `tool_name` defaults to class name (lowercase) if not set
47+
- `description` defaults to class docstring if not set
2448

2549
### MCPBaseTool (`sgr_agent_core/base_tool.py`)
26-
- Base class for MCP-integrated tools
27-
- Handles MCP client calls
28-
- Converts MCP responses to tool format
50+
- Base class for MCP-integrated tools (inherits from `BaseTool`)
51+
- Class variable: `_client` (ClassVar[Client | None]) - MCP client instance
52+
- `__call__()`: Calls MCP tool via `fastmcp.Client.call_tool()`
53+
- Converts MCP responses to JSON string
54+
- Respects `mcp_context_limit` from `ExecutionConfig`
55+
- Handles errors gracefully (returns error message as string)
2956

3057
## Configuration Modules
3158

3259
### GlobalConfig (`sgr_agent_core/agent_config.py`)
3360
- Singleton pattern for global configuration
34-
- Loads from YAML files (`config.yaml`, `agents.yaml`)
35-
- Provides default values for all agent settings
61+
- All calls to `GlobalConfig()` return the same instance
62+
- Loads from YAML files via `from_yaml()` method
63+
- Loads from environment variables via `pydantic-settings` (prefix `SGR__`)
64+
- Contains: `llm`, `search`, `execution`, `prompts`, `mcp`, `agents`, `tools`
65+
- `agents`: Dictionary of `AgentDefinition` instances by name
66+
- `tools`: Dictionary of tool definitions by name
67+
- `definitions_from_yaml()`: Loads agent definitions from YAML (merges with existing)
3668

3769
### AgentDefinition (`sgr_agent_core/agent_definition.py`)
3870
- Definition template for creating agents
39-
- Contains: name, base_class, tools, llm, prompts, execution, search, mcp configs
40-
- Supports YAML loading
41-
- Validates required fields
71+
- Inherits from `AgentConfig` (has all config fields)
72+
- Additional fields: `name`, `base_class`, `tools`
73+
- `base_class`: Can be class, ImportString (e.g., `"sgr_agent_core.agents.SGRAgent"`), or registry name
74+
- `tools`: List of tool names (strings) or tool classes
75+
- Supports YAML loading via `GlobalConfig.definitions_from_yaml()`
76+
- Validates import strings point to existing files
77+
- Automatically merges with `GlobalConfig` defaults
4278

4379
### AgentConfig (`sgr_agent_core/agent_definition.py`)
4480
- Runtime configuration for agent instance
45-
- Combines: LLMConfig, SearchConfig, ExecutionConfig, PromptsConfig, MCPConfig
46-
- Supports hierarchical inheritance from GlobalConfig
81+
- Combines: `LLMConfig`, `SearchConfig`, `ExecutionConfig`, `PromptsConfig`, `MCPConfig`
82+
- Supports hierarchical inheritance from `GlobalConfig`
83+
- Uses `extra="allow"` to support custom fields for agent-specific parameters
4784

4885
## Factory and Services
4986

5087
### AgentFactory (`sgr_agent_core/agent_factory.py`)
51-
- Creates agent instances from AgentDefinition
52-
- Resolves agent classes from AgentRegistry
53-
- Resolves tools from ToolRegistry
54-
- Builds MCP tools via MCP2ToolConverter
55-
- Creates OpenAI client with proxy support
88+
- Creates agent instances from `AgentDefinition`
89+
- Resolves agent classes from `AgentRegistry` (by name or ImportString)
90+
- Resolves tools from `ToolRegistry` or `config.tools` section
91+
- Tool resolution order:
92+
1. Tools defined in `config.tools` section
93+
2. Tools in `ToolRegistry` by name (snake_case or PascalCase)
94+
3. Auto-conversion snake_case → PascalCase for backward compatibility
95+
- Builds MCP tools via `MCP2ToolConverter`
96+
- Creates OpenAI client with proxy support via `httpx.AsyncClient`
97+
- `get_definitions_list()`: Returns all agent definitions from `GlobalConfig`
5698

5799
### AgentRegistry (`sgr_agent_core/services/registry.py`)
58-
- Centralized registry for agent classes
59-
- Automatic registration via `AgentRegistryMixin`
100+
- Registry for agent classes (subclass of `Registry[BaseAgent]`)
101+
- Automatic registration via `AgentRegistryMixin` in `BaseAgent.__init_subclass__()`
102+
- Registers by class name (lowercase) and `name` attribute
60103
- Supports lookup by name (case-insensitive)
61104

62105
### ToolRegistry (`sgr_agent_core/services/registry.py`)
63-
- Centralized registry for tool classes
64-
- Automatic registration via `ToolRegistryMixin`
106+
- Registry for tool classes (subclass of `Registry[BaseTool]`)
107+
- Automatic registration via `ToolRegistryMixin` in `BaseTool.__init_subclass__()`
108+
- Registers by class name (lowercase) and `tool_name` attribute
65109
- Supports lookup by name (case-insensitive)
66110

67111
### PromptLoader (`sgr_agent_core/services/prompt_loader.py`)
68-
- Loads and formats prompts from files or strings
69-
- Generates system prompts with tool descriptions
70-
- Formats initial user requests and clarification responses
112+
- Static class for loading and formatting prompts
113+
- `get_system_prompt()`: Formats system prompt with available tools list
114+
- `get_initial_user_request()`: Formats initial user request with current date
115+
- `get_clarification_template()`: Formats clarification response template
116+
- Uses templates from `PromptsConfig` (files or strings)
117+
- Supports placeholders: `{available_tools}`, `{current_date}`
71118

72119
### MCP2ToolConverter (`sgr_agent_core/services/mcp_service.py`)
73-
- Converts MCP server tools to BaseTool instances
74-
- Handles MCP client initialization
75-
- Builds tools from MCP configuration
120+
- Converts MCP server tools to `BaseTool` instances
121+
- `build_tools_from_mcp()`: Async method to build tools from MCP config
122+
- Uses `fastmcp.Client` to connect to MCP servers
123+
- Uses `jambo.SchemaConverter` to convert JSON schemas to Pydantic models
124+
- Creates dynamic tool classes inheriting from `MCPBaseTool`
125+
- Tool names converted to CamelCase (e.g., `web_search` → `MCPWebSearch`)
76126

77127
## Agent Implementations
78128

79129
### SGRAgent (`sgr_agent_core/agents/sgr_agent.py`)
80-
- Uses Structured Output approach
81-
- Creates dynamic JSON schema for tools
82-
- LLM returns reasoning + tool schema in one call
83-
- Extracts tool directly from reasoning result
130+
- Uses Structured Output approach with `response_format`
131+
- Uses `NextStepToolsBuilder` to create dynamic union tool type
132+
- `_prepare_tools()` returns `Type[NextStepToolStub]` for structured output
133+
- `_reasoning_phase()` uses `response_format` to get `NextStepToolStub` with selected tool
134+
- `_select_action_phase()` extracts tool from `reasoning.function` field
135+
- LLM returns reasoning + tool selection in one structured call
136+
- Best for models with strong structured output support
84137

85138
### ToolCallingAgent (`sgr_agent_core/agents/tool_calling_agent.py`)
86-
- Uses native Function Calling
87-
- No explicit reasoning phase
88-
- Uses `tool_choice="required"` for tool selection
89-
- Best for advanced LLM models
139+
- Uses native Function Calling approach
140+
- `_reasoning_phase()` returns `None` (no explicit reasoning)
141+
- `_select_action_phase()` uses `tool_choice="required"` to force tool selection
142+
- LLM directly selects and calls tool via function calling
143+
- No structured reasoning output
144+
- Best for advanced LLM models with strong function calling support
90145

91146
### SGRToolCallingAgent (`sgr_agent_core/agents/sgr_tool_calling_agent.py`)
92-
- Hybrid approach: SGR + Function Calling
93-
- Uses ReasoningTool for explicit reasoning
94-
- Uses Function Calling for tool selection
95-
- Best balance for most tasks
147+
- Hybrid approach: SGR reasoning + Function Calling for tools
148+
- `_reasoning_phase()` uses Function Calling to get `ReasoningTool` result
149+
- Executes `ReasoningTool` to get structured reasoning output
150+
- `_select_action_phase()` uses Function Calling with `tool_choice="required"` for tool selection
151+
- Handles edge case: if LLM returns text instead of tool call, creates `FinalAnswerTool`
152+
- Best balance for most tasks - combines structured reasoning with flexible tool selection
96153

97154
## Tools
98155

156+
### NextStepToolsBuilder (`sgr_agent_core/next_step_tool.py`)
157+
- Builder for creating dynamic union tool types
158+
- `build_NextStepTools()`: Creates `NextStepToolStub` subclass with union of available tools
159+
- Uses discriminated union pattern with `tool_name_discriminator` field
160+
- Enables structured output with dynamic tool selection
161+
- Used by `SGRAgent` for tool selection
162+
163+
### NextStepToolStub (`sgr_agent_core/next_step_tool.py`)
164+
- Stub class for `NextStepTools` created by `NextStepToolsBuilder`
165+
- Inherits from `ReasoningTool` and contains `function` field with union of tools
166+
- Used in structured output to select tool for next step
167+
99168
### ReasoningTool (`sgr_agent_core/tools/reasoning_tool.py`)
100169
- Provides structured reasoning output
101170
- Contains: reasoning_steps, current_situation, plan_status, enough_data, remaining_steps, task_completed
171+
- Base class for `NextStepToolStub`
102172

103173
### ClarificationTool (`sgr_agent_core/tools/clarification_tool.py`)
104174
- Requests clarification from user
@@ -137,21 +207,41 @@ alwaysApply: true
137207

138208
## Server and API
139209

210+
### Server Settings (`sgr_agent_core/server/settings.py`)
211+
- Server configuration (host, port, etc.)
212+
- Used by FastAPI application
213+
214+
### Server Models (`sgr_agent_core/server/models.py`)
215+
- Pydantic models for API requests/responses
216+
- `ChatCompletionRequest`: OpenAI-compatible request model
217+
- `MessagesList`: Root model for message lists with base64 truncation
218+
- `AgentStateResponse`: Agent state response model
219+
- `ClarificationRequest`: Clarification request model
220+
- `HealthResponse`: Health check response
221+
140222
### FastAPI Application (`sgr_agent_core/server/app.py`)
141223
- Main FastAPI application
142-
- Configures CORS, middleware
143-
- Registers endpoints
224+
- Configures CORS, middleware, logging
225+
- Registers API router from `endpoints.py`
226+
- Uses `server/settings.py` for configuration
144227

145228
### API Endpoints (`sgr_agent_core/server/endpoints.py`)
146-
- `/v1/chat/completions` - OpenAI-compatible chat endpoint
147-
- `/v1/agents/{agent_id}/state` - Get agent state
148-
- `/v1/agents/{agent_id}/clarification` - Provide clarification
149-
- `/v1/agents` - List available agents
229+
- `/v1/chat/completions` - OpenAI-compatible chat endpoint (streaming only)
230+
- Creates agent from `AgentDefinition` by model name
231+
- Supports clarification requests via agent ID in model field
232+
- Returns streaming response via `OpenAIStreamingGenerator`
233+
- `/v1/models` - List available agent definitions (OpenAI-compatible)
234+
- `/agents/{agent_id}/state` - Get agent state (GET)
235+
- `/agents/{agent_id}/provide_clarification` - Provide clarification (POST)
236+
- `/agents` - List all active agents (GET)
237+
- `/health` - Health check endpoint
150238

151239
### Streaming (`sgr_agent_core/stream.py`)
152-
- OpenAIStreamingGenerator for streaming responses
153-
- Formats events in OpenAI-compatible format
240+
- `OpenAIStreamingGenerator` for streaming responses
241+
- Formats events in OpenAI-compatible Server-Sent Events (SSE) format
154242
- Handles tool calls and content chunks
243+
- Provides async iterator via `stream()` method
244+
- Methods: `add_chunk()`, `add_tool_call()`, `add_chunk_from_str()`, `finish()`
155245

156246
## General Rules for All Modules
157247

0 commit comments

Comments
 (0)