feat: enhance agent adapters with structured output support

- Introduced BaseConverterAdapter as an abstract class for structured output handling.
- Implemented LangGraphConverterAdapter and OpenAIConverterAdapter to manage structured output in their respective agents.
- Updated BaseAgentAdapter to accept an agent configuration dictionary during initialization.
- Enhanced LangGraphAgentAdapter to utilize the new converter and improved tool handling.
- Added methods for configuring structured output and enhancing system prompts in converter adapters.
This commit is contained in:
lorenzejay
2025-04-11 12:00:52 -07:00
parent 8afbd2b071
commit 31d910e9c2
5 changed files with 60 additions and 12 deletions

View File

@@ -1,7 +1,7 @@
from abc import ABC, abstractmethod
from typing import Any, List, Optional
from typing import Any, Dict, List, Optional
from pydantic import BaseModel
from pydantic import PrivateAttr
from crewai.agent import BaseAgent
from crewai.tools import BaseTool
@@ -16,11 +16,13 @@ class BaseAgentAdapter(BaseAgent, ABC):
"""
adapted_structured_output: bool = False
_agent_config: Optional[Dict[str, Any]] = PrivateAttr(default=None)
model_config = {"arbitrary_types_allowed": True}
def __init__(self, **kwargs: Any):
def __init__(self, agent_config: Optional[Dict[str, Any]] = None, **kwargs: Any):
super().__init__(**kwargs)
self._agent_config = agent_config
@abstractmethod
def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None:

View File

@@ -0,0 +1,24 @@
from abc import ABC, abstractmethod
class BaseConverterAdapter(ABC):
def __init__(self, agent_adapter):
self.agent_adapter = agent_adapter
@abstractmethod
def configure_structured_output(self, task) -> None:
"""Configure agents to return structured output.
Must support json and pydantic output.
"""
pass
@abstractmethod
def enhance_system_prompt(self, base_prompt: str) -> str:
"""Enhance the system prompt with structured output instructions."""
pass
@abstractmethod
def post_process_result(self, result: str) -> str:
"""Post-process the result to ensure it matches the expected format."""
# Transform the string result to the expected format.
pass

View File

@@ -45,9 +45,10 @@ class LangGraphAgentAdapter(BaseAgentAdapter):
role: str,
goal: str,
backstory: str,
tools: Optional[List[BaseTool]] = None,
tools: Optional[List[BaseTool]] = [],
llm: Any = None,
max_iterations: int = 10,
agent_config: Optional[Dict[str, Any]] = None,
**kwargs,
):
"""Initialize the LangGraph agent adapter."""
@@ -57,9 +58,10 @@ class LangGraphAgentAdapter(BaseAgentAdapter):
backstory=backstory,
tools=tools,
llm=llm or self.model,
agent_config=agent_config,
**kwargs,
)
self._tool_adapter = LangGraphToolAdapter(tools=tools)
self._tool_adapter = LangGraphToolAdapter(tools=tools or [])
self._converter_adapter = LangGraphConverterAdapter(self)
self._max_iterations = max_iterations
self._setup_graph()
@@ -70,15 +72,13 @@ class LangGraphAgentAdapter(BaseAgentAdapter):
# Initialize memory for the agent
self._memory = MemorySaver()
# Convert CrewAI tools to LangGraph/LangChain compatible tools
converted_tools = self._tool_adapter.converted_tools
print("langgraph converted_tools", converted_tools)
# Create the agent graph with ReAct pattern
self._graph = create_react_agent(
model=self.llm,
tools=converted_tools,
checkpointer=self._memory,
debug=self.verbose,
)
except ImportError as e:

View File

@@ -1,9 +1,10 @@
import json
from crewai.agents.agent_adapters.base_converter_adapter import BaseConverterAdapter
from crewai.utilities.converter import generate_model_description
class LangGraphConverterAdapter:
class LangGraphConverterAdapter(BaseConverterAdapter):
"""Adapter for handling structured output conversion in LangGraph agents"""
def __init__(self, agent_adapter):

View File

@@ -1,10 +1,11 @@
import json
import re
from crewai.agents.agent_adapters.base_converter_adapter import BaseConverterAdapter
from crewai.utilities.converter import generate_model_description
class OpenAIConverterAdapter:
class OpenAIConverterAdapter(BaseConverterAdapter):
"""
Adapter for handling structured output conversion in OpenAI agents.
@@ -53,6 +54,28 @@ class OpenAIConverterAdapter:
self._output_model = task.output_pydantic
self.agent_adapter._openai_agent.output_type = task.output_pydantic
def enhance_system_prompt(self, base_prompt: str) -> str:
"""
Enhance the base system prompt with structured output requirements if needed.
Args:
base_prompt: The original system prompt
Returns:
Enhanced system prompt with output format instructions if needed
"""
if not self._output_format:
return base_prompt
output_instructions = f"""
Your response MUST conform to the following {self._output_format.upper()} schema:
{self._schema}
Ensure your final response is properly formatted according to this schema.
"""
return f"{base_prompt}\n\n{output_instructions}"
def post_process_result(self, result: str) -> str:
"""
Post-process the result to ensure it matches the expected format.
@@ -65,10 +88,8 @@ class OpenAIConverterAdapter:
Returns:
Processed result conforming to the expected output format
"""
print("result", result)
if not self._output_format:
return result
print("self._output_format", self._output_format)
# Try to extract valid JSON if it's wrapped in code blocks or other text
if isinstance(result, str) and self._output_format in ["json", "pydantic"]:
# First, try to parse as is