Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Agent support #727

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open

Agent support #727

wants to merge 1 commit into from

Conversation

wassemgtk
Copy link
Collaborator

Key Features:

Base Agent Infrastructure

Introduces a flexible BaseAgent class that all agents inherit from Implements tool management and conversation history tracking Provides abstract methods for custom agent implementations

Built-in Agents:

a) ReAct Agent

Implements the Reasoning + Acting pattern
Follows a structured process: Reason → Act → Observe → Respond Excels at complex tasks requiring tool use and step-by-step thinking Maximum 5 steps to prevent infinite loops

b) Chain of Thought (CoT) Agent

Specializes in breaking down complex problems into logical steps Shows explicit reasoning process for each step
Focuses on transparent decision-making
Ideal for math, logic, and analysis tasks

c) Tool-Using Agent

Optimized for scenarios requiring multiple tool interactions Analyzes available tools and plans usage sequence
Executes tools and processes results
Built-in error handling for missing tools

Custom Agent Framework

Allows creation of specialized agents with custom behaviors Configurable system prompts and extraction patterns Adjustable maximum steps and tool usage
Flexible pattern matching for different response formats

Shared Components:

Tool class for defining and executing custom tools AgentOutput class for standardized response formatting Conversation history tracking
Consistent error handling

from writer.agents import ReActAgent, Tool

# Create a tool
calculator = Tool(
    name="calculator",
    description="Performs basic math operations",
    func=lambda x, y, op: eval(f"{x}{op}{y}")
)

# Initialize agent
agent = ReActAgent(tools=[calculator])

# Run agent
result = agent.run("What is 25 * 48?")

# Access structured output
print(result.response)      # Final answer
print(result.reasoning)     # List of reasoning steps
print(result.actions)       # List of tools used

Key Features:

Base Agent Infrastructure


Introduces a flexible BaseAgent class that all agents inherit from
Implements tool management and conversation history tracking
Provides abstract methods for custom agent implementations


Built-in Agents:

a) ReAct Agent

Implements the Reasoning + Acting pattern
Follows a structured process: Reason → Act → Observe → Respond
Excels at complex tasks requiring tool use and step-by-step thinking
Maximum 5 steps to prevent infinite loops

b) Chain of Thought (CoT) Agent

Specializes in breaking down complex problems into logical steps
Shows explicit reasoning process for each step
Focuses on transparent decision-making
Ideal for math, logic, and analysis tasks

c) Tool-Using Agent

Optimized for scenarios requiring multiple tool interactions
Analyzes available tools and plans usage sequence
Executes tools and processes results
Built-in error handling for missing tools


Custom Agent Framework


Allows creation of specialized agents with custom behaviors
Configurable system prompts and extraction patterns
Adjustable maximum steps and tool usage
Flexible pattern matching for different response formats


Shared Components:


Tool class for defining and executing custom tools
AgentOutput class for standardized response formatting
Conversation history tracking
Consistent error handling

```
from writer.agents import ReActAgent, Tool

# Create a tool
calculator = Tool(
    name="calculator",
    description="Performs basic math operations",
    func=lambda x, y, op: eval(f"{x}{op}{y}")
)

# Initialize agent
agent = ReActAgent(tools=[calculator])

# Run agent
result = agent.run("What is 25 * 48?")

# Access structured output
print(result.response)      # Final answer
print(result.reasoning)     # List of reasoning steps
print(result.actions)       # List of tools used
```
@ramedina86
Copy link
Collaborator

@mmikita95 Mikita, can you please take a look and see how this relates to AI module? I'm seeing clear duplication in things like Tool

@ramedina86
Copy link
Collaborator

@raaymax

from abc import ABC, abstractmethod
import re
from typing import Any, Dict, List, Optional, Union
from enum import Enum

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Output of ruff check:

agents.py:3:47: F401 [*] `typing.Union` imported but unused
  |
1 | from abc import ABC, abstractmethod
2 | import re
3 | from typing import Any, Dict, List, Optional, Union
  |                                               ^^^^^ F401
4 | from enum import Enum
5 | import json
  |
  = help: Remove unused import: `typing.Union`

agents.py:4:18: F401 [*] `enum.Enum` imported but unused
  |
2 | import re
3 | from typing import Any, Dict, List, Optional, Union
4 | from enum import Enum
  |                  ^^^^ F401
5 | import json
  |
  = help: Remove unused import: `enum.Enum`

])

response = None
max_steps = 5 # Prevent infinite loops

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both system prompt and max_steps should not be hardcoded.
The prompt needs to be refined, I see next regex failing a lot:

match = re.search(r"Reason:(.+?)(?=Action:|Response:|$)", text, re.DOTALL)

I did not guess from the prompt what is the expected format, the model will struggle with this too.


for _ in range(max_steps):
# Get next action from AI
action_response = self._get_llm_response(messages)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No error handling.

# Execute tool if specified
if action.get("tool"):
tool_result = self._execute_tool(action["tool"], action.get("args", {}))
messages.append({"role": "system", "content": f"Observation: {tool_result}"})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cannot be a "system" message. Our models don't support multiple system messages shuffled in.
We can make it into an "assistant" message and merge all assistant messages at the end.

return f"Tool '{tool_name}' not found"
return tool.run(**args)

class ChainOfThoughtAgent(BaseAgent):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ChainOfThoughtAgent does not use tools, but it extends BaseAgent class with has tools and tools executions. That is confusing.

self.reasoning = reasoning
self.actions = actions

class Tool:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class Tool should have a serialisation method.
The standard format looks like this:

tools = [
    { 
        "type": "function",
        "function": {
            "name": "calculate_mean", 
            "description": "Calculate the mean (average) of a list of numbers.", 
            "parameters": { 
                "type": "object", 
                "properties": { 
                    "numbers": { 
                        "type": "array", 
                        "items": {"type": "number"}, 
                        "description": "List of numbers"
                    } 
                }, 
                "required": ["numbers"] 
            } 
        }
    }
]

def __init__(self, name: str, description: str, func: callable):
self.name = name
self.description = description
self.func = func
Copy link

@melisa-writer melisa-writer Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing properties: self.parameters and self.type

"""Run the agent with the given prompt"""
pass

def add_tool(self, tool: Tool):
Copy link

@melisa-writer melisa-writer Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like BaseAgent should not have tools. We have ToolUsingAgent for this.
ChainIfThoughAgent extends BaseAgents and it does not use tools.

3. Observation: Consider the results
4. Response: Provide final answer based on observations

{self._format_tool_descriptions()}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think tool description is already handled in Writer sdk tool calling:

response = client.chat.chat(
    model="palmyra-x-004", 
    messages=messages, 
    tools=tools, 
    tool_choice="auto", 
    stream=False # Set to True if you want to use streaming
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants