Spaces:
Runtime error
Runtime error
Phase 3: LangGraph Workflow Orchestration (#2)
Browse files* Add LangGraph workflow state and orchestration
Implemented core workflow infrastructure:
- State schema with TypedDict for all workflow phases
- StateGraph with research, analysis, writing, human review nodes
- Conditional routing between agents
- SQLite checkpointing for state persistence
- Cost tracking integration across all nodes
- Error handling and recovery logic
- Budget enforcement
* Add SQLite checkpointing for production persistenceAdded langgraph-checkpoint-sqlite for production-grade state management:- Workflows persist across restarts and crashes- Enables audit trails for compliance- Supports long-running multi-day analyses - Critical for human-in-the-loop approval workflows
- requirements.txt +5 -0
- src/workflows/intelligence.py +288 -0
- src/workflows/state.py +45 -0
- test_workflow.py +85 -0
- workflow_test_report.md +156 -0
requirements.txt
CHANGED
|
@@ -39,3 +39,8 @@ mypy==1.18.2
|
|
| 39 |
python-dotenv==1.2.1
|
| 40 |
tenacity==9.1.2
|
| 41 |
httpx==0.28.1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
python-dotenv==1.2.1
|
| 40 |
tenacity==9.1.2
|
| 41 |
httpx==0.28.1
|
| 42 |
+
langgraph==1.0.4
|
| 43 |
+
langgraph-checkpoint==3.0.1
|
| 44 |
+
langgraph-checkpoint-sqlite==3.0.0
|
| 45 |
+
langgraph-prebuilt==1.0.5
|
| 46 |
+
langgraph-sdk==0.2.10
|
src/workflows/intelligence.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Main LangGraph workflow for market intelligence."""
|
| 2 |
+
|
| 3 |
+
from langgraph.graph import StateGraph, END
|
| 4 |
+
from langgraph.checkpoint.sqlite import SqliteSaver
|
| 5 |
+
|
| 6 |
+
from src.workflows.state import IntelligenceState
|
| 7 |
+
from src.agents.researcher import ResearchAgent
|
| 8 |
+
from src.agents.analyst import AnalysisAgent
|
| 9 |
+
from src.agents.writer import WriterAgent
|
| 10 |
+
from src.utils.cost_tracker import CostTracker, BudgetExceededError
|
| 11 |
+
from src.utils.logging import setup_logger
|
| 12 |
+
|
| 13 |
+
logger = setup_logger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class MarketIntelligenceWorkflow:
|
| 17 |
+
"""
|
| 18 |
+
LangGraph workflow orchestrating research, analysis, and writing agents.
|
| 19 |
+
|
| 20 |
+
Features:
|
| 21 |
+
- Multi-agent coordination
|
| 22 |
+
- State persistence with checkpointing
|
| 23 |
+
- Cost tracking and budget enforcement
|
| 24 |
+
- Human-in-the-loop approval
|
| 25 |
+
- Error recovery
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
def __init__(
|
| 29 |
+
self, checkpoint_path: str = "./checkpoints.db", max_budget: float = 2.0
|
| 30 |
+
):
|
| 31 |
+
"""
|
| 32 |
+
Initialize workflow.
|
| 33 |
+
|
| 34 |
+
Args:
|
| 35 |
+
checkpoint_path: Path to SQLite checkpoint database
|
| 36 |
+
max_budget: Maximum cost per run in USD
|
| 37 |
+
"""
|
| 38 |
+
self.max_budget = max_budget
|
| 39 |
+
self.cost_tracker = CostTracker()
|
| 40 |
+
self.checkpoint_path = checkpoint_path
|
| 41 |
+
|
| 42 |
+
# Initialize agents (shared cost tracker)
|
| 43 |
+
self.research_agent = ResearchAgent(cost_tracker=self.cost_tracker)
|
| 44 |
+
self.analysis_agent = AnalysisAgent(cost_tracker=self.cost_tracker)
|
| 45 |
+
self.writer_agent = WriterAgent(cost_tracker=self.cost_tracker)
|
| 46 |
+
|
| 47 |
+
# Build workflow graph
|
| 48 |
+
self.workflow = self._build_graph()
|
| 49 |
+
|
| 50 |
+
logger.info("Market Intelligence Workflow initialized")
|
| 51 |
+
|
| 52 |
+
def _build_graph(self) -> StateGraph:
|
| 53 |
+
"""Build LangGraph workflow."""
|
| 54 |
+
# Initialize graph
|
| 55 |
+
graph = StateGraph(IntelligenceState)
|
| 56 |
+
|
| 57 |
+
# Add nodes (agent wrappers)
|
| 58 |
+
graph.add_node("research", self._research_node)
|
| 59 |
+
graph.add_node("analysis", self._analysis_node)
|
| 60 |
+
graph.add_node("writing", self._writing_node)
|
| 61 |
+
graph.add_node("human_review", self._human_review_node)
|
| 62 |
+
|
| 63 |
+
# Set entry point
|
| 64 |
+
graph.set_entry_point("research")
|
| 65 |
+
|
| 66 |
+
# Add edges
|
| 67 |
+
graph.add_conditional_edges(
|
| 68 |
+
"research",
|
| 69 |
+
self._should_continue_to_analysis,
|
| 70 |
+
{"analysis": "analysis", "end": END},
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
graph.add_edge("analysis", "writing")
|
| 74 |
+
graph.add_edge("writing", "human_review")
|
| 75 |
+
|
| 76 |
+
graph.add_conditional_edges(
|
| 77 |
+
"human_review",
|
| 78 |
+
self._check_approval,
|
| 79 |
+
{"approved": END, "revise": "research", "max_revisions": END},
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
# Compile with SQLite checkpointing for production persistence
|
| 83 |
+
checkpointer = SqliteSaver.from_conn_string(self.checkpoint_path)
|
| 84 |
+
return graph.compile(checkpointer=checkpointer)
|
| 85 |
+
|
| 86 |
+
async def _research_node(self, state: IntelligenceState) -> dict:
|
| 87 |
+
"""Research agent node."""
|
| 88 |
+
logger.info(f"Research node: {state['company_name']}")
|
| 89 |
+
|
| 90 |
+
try:
|
| 91 |
+
# Run research agent
|
| 92 |
+
research_results = await self.research_agent.run(
|
| 93 |
+
company_name=state["company_name"],
|
| 94 |
+
industry=state.get("industry"),
|
| 95 |
+
research_depth="comprehensive",
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
# Update state
|
| 99 |
+
return {
|
| 100 |
+
"current_agent": "research",
|
| 101 |
+
"research_data": research_results,
|
| 102 |
+
"competitors": research_results.get("competitors", []),
|
| 103 |
+
"market_trends": research_results.get("market_trends", {}),
|
| 104 |
+
"raw_sources": research_results.get("raw_sources", []),
|
| 105 |
+
"iteration": state.get("iteration", 0) + 1,
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
except Exception as e:
|
| 109 |
+
logger.error(f"Research node failed: {e}")
|
| 110 |
+
return {
|
| 111 |
+
"errors": [f"Research failed: {str(e)}"],
|
| 112 |
+
"current_agent": "research",
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
async def _analysis_node(self, state: IntelligenceState) -> dict:
|
| 116 |
+
"""Analysis agent node."""
|
| 117 |
+
logger.info(f"Analysis node: {state['company_name']}")
|
| 118 |
+
|
| 119 |
+
try:
|
| 120 |
+
# Check budget before expensive analysis
|
| 121 |
+
self.cost_tracker.check_budget(self.max_budget)
|
| 122 |
+
|
| 123 |
+
# Run analysis agent
|
| 124 |
+
analysis_results = await self.analysis_agent.run(
|
| 125 |
+
research_data=state["research_data"]
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
# Update state
|
| 129 |
+
return {
|
| 130 |
+
"current_agent": "analysis",
|
| 131 |
+
"swot": analysis_results.get("swot", {}),
|
| 132 |
+
"competitive_matrix": analysis_results.get("competitive_matrix", {}),
|
| 133 |
+
"positioning": analysis_results.get("positioning", {}),
|
| 134 |
+
"strategic_recommendations": analysis_results.get(
|
| 135 |
+
"strategic_recommendations", {}
|
| 136 |
+
),
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
except BudgetExceededError as e:
|
| 140 |
+
logger.error(f"Budget exceeded: {e}")
|
| 141 |
+
return {
|
| 142 |
+
"errors": [f"Budget exceeded: {str(e)}"],
|
| 143 |
+
"current_agent": "analysis",
|
| 144 |
+
}
|
| 145 |
+
except Exception as e:
|
| 146 |
+
logger.error(f"Analysis node failed: {e}")
|
| 147 |
+
return {
|
| 148 |
+
"errors": [f"Analysis failed: {str(e)}"],
|
| 149 |
+
"current_agent": "analysis",
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
async def _writing_node(self, state: IntelligenceState) -> dict:
|
| 153 |
+
"""Writer agent node."""
|
| 154 |
+
logger.info(f"Writing node: {state['company_name']}")
|
| 155 |
+
|
| 156 |
+
try:
|
| 157 |
+
# Run writer agent
|
| 158 |
+
report_results = await self.writer_agent.run(
|
| 159 |
+
research_data=state["research_data"],
|
| 160 |
+
analysis_data={
|
| 161 |
+
"swot": state.get("swot", {}),
|
| 162 |
+
"competitive_matrix": state.get("competitive_matrix", {}),
|
| 163 |
+
"positioning": state.get("positioning", {}),
|
| 164 |
+
"strategic_recommendations": state.get(
|
| 165 |
+
"strategic_recommendations", {}
|
| 166 |
+
),
|
| 167 |
+
},
|
| 168 |
+
)
|
| 169 |
+
|
| 170 |
+
# Get cost summary
|
| 171 |
+
cost_summary = self.cost_tracker.get_summary()
|
| 172 |
+
|
| 173 |
+
# Update state
|
| 174 |
+
return {
|
| 175 |
+
"current_agent": "writing",
|
| 176 |
+
"executive_summary": report_results.get("executive_summary", ""),
|
| 177 |
+
"full_report": report_results.get("full_report", ""),
|
| 178 |
+
"report_metadata": report_results.get("metadata", {}),
|
| 179 |
+
"total_cost": cost_summary["total_cost"],
|
| 180 |
+
"total_tokens": cost_summary["total_tokens"],
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
except Exception as e:
|
| 184 |
+
logger.error(f"Writing node failed: {e}")
|
| 185 |
+
return {
|
| 186 |
+
"errors": [f"Writing failed: {str(e)}"],
|
| 187 |
+
"current_agent": "writing",
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
async def _human_review_node(self, state: IntelligenceState) -> dict:
|
| 191 |
+
"""Human review node (placeholder for now)."""
|
| 192 |
+
logger.info(f"Human review node: {state['company_name']}")
|
| 193 |
+
|
| 194 |
+
# For now, auto-approve
|
| 195 |
+
# In Phase 5, this will connect to the Gradio UI
|
| 196 |
+
return {
|
| 197 |
+
"current_agent": "human_review",
|
| 198 |
+
"approved": True, # Auto-approve for testing
|
| 199 |
+
"human_feedback": None,
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
def _should_continue_to_analysis(self, state: IntelligenceState) -> str:
|
| 203 |
+
"""Decide whether to continue to analysis or end."""
|
| 204 |
+
# Check if research was successful
|
| 205 |
+
if state.get("errors") and state["errors"]:
|
| 206 |
+
logger.warning("Research had errors, ending workflow")
|
| 207 |
+
return "end"
|
| 208 |
+
|
| 209 |
+
if not state.get("research_data"):
|
| 210 |
+
logger.warning("No research data, ending workflow")
|
| 211 |
+
return "end"
|
| 212 |
+
|
| 213 |
+
return "analysis"
|
| 214 |
+
|
| 215 |
+
def _check_approval(self, state: IntelligenceState) -> str:
|
| 216 |
+
"""Check if report is approved or needs revision."""
|
| 217 |
+
# Check max revisions
|
| 218 |
+
revision_count = state.get("revision_count", 0)
|
| 219 |
+
if revision_count >= 2:
|
| 220 |
+
logger.warning("Max revisions reached")
|
| 221 |
+
return "max_revisions"
|
| 222 |
+
|
| 223 |
+
# Check approval
|
| 224 |
+
if state.get("approved"):
|
| 225 |
+
return "approved"
|
| 226 |
+
|
| 227 |
+
# Revision requested
|
| 228 |
+
if state.get("human_feedback"):
|
| 229 |
+
return "revise"
|
| 230 |
+
|
| 231 |
+
# Default to approved
|
| 232 |
+
return "approved"
|
| 233 |
+
|
| 234 |
+
async def run(
|
| 235 |
+
self,
|
| 236 |
+
company_name: str,
|
| 237 |
+
industry: str | None = None,
|
| 238 |
+
thread_id: str | None = None,
|
| 239 |
+
) -> dict:
|
| 240 |
+
"""
|
| 241 |
+
Run the complete workflow.
|
| 242 |
+
|
| 243 |
+
Args:
|
| 244 |
+
company_name: Target company name
|
| 245 |
+
industry: Optional industry context
|
| 246 |
+
thread_id: Optional thread ID for checkpointing
|
| 247 |
+
|
| 248 |
+
Returns:
|
| 249 |
+
Final state dictionary
|
| 250 |
+
"""
|
| 251 |
+
logger.info(f"Starting workflow for: {company_name}")
|
| 252 |
+
|
| 253 |
+
# Initial state
|
| 254 |
+
initial_state = {
|
| 255 |
+
"company_name": company_name,
|
| 256 |
+
"industry": industry,
|
| 257 |
+
"research_data": {},
|
| 258 |
+
"competitors": [],
|
| 259 |
+
"market_trends": {},
|
| 260 |
+
"raw_sources": [],
|
| 261 |
+
"swot": {},
|
| 262 |
+
"competitive_matrix": {},
|
| 263 |
+
"positioning": {},
|
| 264 |
+
"strategic_recommendations": {},
|
| 265 |
+
"executive_summary": "",
|
| 266 |
+
"full_report": "",
|
| 267 |
+
"report_metadata": {},
|
| 268 |
+
"current_agent": "research",
|
| 269 |
+
"iteration": 0,
|
| 270 |
+
"total_cost": 0.0,
|
| 271 |
+
"total_tokens": 0,
|
| 272 |
+
"errors": [],
|
| 273 |
+
"human_feedback": None,
|
| 274 |
+
"approved": False,
|
| 275 |
+
"revision_count": 0,
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
# Run workflow
|
| 279 |
+
config = {"configurable": {"thread_id": thread_id or "default"}}
|
| 280 |
+
|
| 281 |
+
try:
|
| 282 |
+
final_state = await self.workflow.ainvoke(initial_state, config)
|
| 283 |
+
logger.info(f"Workflow complete. Cost: ${final_state['total_cost']:.4f}")
|
| 284 |
+
return final_state
|
| 285 |
+
|
| 286 |
+
except Exception as e:
|
| 287 |
+
logger.error(f"Workflow failed: {e}")
|
| 288 |
+
raise
|
src/workflows/state.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""State definitions for LangGraph workflow."""
|
| 2 |
+
|
| 3 |
+
from typing import Annotated, Literal, TypedDict
|
| 4 |
+
import operator
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class IntelligenceState(TypedDict):
|
| 8 |
+
"""
|
| 9 |
+
State for market intelligence workflow.
|
| 10 |
+
|
| 11 |
+
This state is passed between agents and persisted across checkpoints.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
# Input
|
| 15 |
+
company_name: str
|
| 16 |
+
industry: str | None
|
| 17 |
+
|
| 18 |
+
# Research phase outputs
|
| 19 |
+
research_data: dict
|
| 20 |
+
competitors: list[dict]
|
| 21 |
+
market_trends: dict
|
| 22 |
+
raw_sources: list[dict]
|
| 23 |
+
|
| 24 |
+
# Analysis phase outputs
|
| 25 |
+
swot: dict
|
| 26 |
+
competitive_matrix: dict
|
| 27 |
+
positioning: dict
|
| 28 |
+
strategic_recommendations: dict
|
| 29 |
+
|
| 30 |
+
# Writing phase outputs
|
| 31 |
+
executive_summary: str
|
| 32 |
+
full_report: str
|
| 33 |
+
report_metadata: dict
|
| 34 |
+
|
| 35 |
+
# Workflow metadata
|
| 36 |
+
current_agent: Literal["research", "analysis", "writing", "human_review", "done"]
|
| 37 |
+
iteration: int
|
| 38 |
+
total_cost: float
|
| 39 |
+
total_tokens: int
|
| 40 |
+
errors: Annotated[list, operator.add] # Accumulate errors across nodes
|
| 41 |
+
|
| 42 |
+
# Human-in-the-loop
|
| 43 |
+
human_feedback: str | None
|
| 44 |
+
approved: bool
|
| 45 |
+
revision_count: int
|
test_workflow.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test the complete LangGraph workflow.
|
| 3 |
+
|
| 4 |
+
Replaces test_agents.py with proper workflow orchestration.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import asyncio
|
| 8 |
+
|
| 9 |
+
from src.workflows.intelligence import MarketIntelligenceWorkflow
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
async def test_workflow():
|
| 13 |
+
"""Test complete workflow with LangGraph orchestration."""
|
| 14 |
+
|
| 15 |
+
print("=" * 80)
|
| 16 |
+
print("TESTING LANGGRAPH WORKFLOW")
|
| 17 |
+
print("=" * 80)
|
| 18 |
+
print()
|
| 19 |
+
|
| 20 |
+
# Initialize workflow
|
| 21 |
+
workflow = MarketIntelligenceWorkflow(
|
| 22 |
+
checkpoint_path="./test_checkpoints.db", max_budget=2.0
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
# Run on Instagram viral query
|
| 26 |
+
company_name = "going viral on Instagram using AI without showing face"
|
| 27 |
+
industry = "Social Media Marketing"
|
| 28 |
+
|
| 29 |
+
print(f"Query: {company_name}")
|
| 30 |
+
print(f"Industry: {industry}")
|
| 31 |
+
print()
|
| 32 |
+
print("Running workflow...")
|
| 33 |
+
print("-" * 80)
|
| 34 |
+
|
| 35 |
+
try:
|
| 36 |
+
final_state = await workflow.run(
|
| 37 |
+
company_name=company_name, industry=industry, thread_id="test-run-1"
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
print("\nWorkflow Complete!")
|
| 41 |
+
print("=" * 80)
|
| 42 |
+
|
| 43 |
+
# Display results
|
| 44 |
+
print(f"\nCompany: {final_state['company_name']}")
|
| 45 |
+
print(f"Final Agent: {final_state['current_agent']}")
|
| 46 |
+
print(f"Iterations: {final_state['iteration']}")
|
| 47 |
+
print(f"Approved: {final_state['approved']}")
|
| 48 |
+
print(f"Errors: {len(final_state['errors'])}")
|
| 49 |
+
|
| 50 |
+
print(f"\nCost: ${final_state['total_cost']:.4f}")
|
| 51 |
+
print(f"Tokens: {final_state['total_tokens']:,}")
|
| 52 |
+
print(f"Sources: {len(final_state.get('raw_sources', []))}")
|
| 53 |
+
|
| 54 |
+
print("\n" + "=" * 80)
|
| 55 |
+
print("EXECUTIVE SUMMARY")
|
| 56 |
+
print("=" * 80)
|
| 57 |
+
print(final_state["executive_summary"])
|
| 58 |
+
|
| 59 |
+
print("\n" + "=" * 80)
|
| 60 |
+
print("FULL REPORT (first 1000 chars)")
|
| 61 |
+
print("=" * 80)
|
| 62 |
+
print(final_state["full_report"][:1000] + "...")
|
| 63 |
+
|
| 64 |
+
# Save report
|
| 65 |
+
with open("workflow_test_report.md", "w") as f:
|
| 66 |
+
f.write(f"# Market Intelligence Report\n\n")
|
| 67 |
+
f.write(f"Generated via LangGraph Workflow\n\n")
|
| 68 |
+
f.write(f"---\n\n")
|
| 69 |
+
f.write(final_state["full_report"])
|
| 70 |
+
f.write(f"\n\n---\n\n")
|
| 71 |
+
f.write(f"**Cost:** ${final_state['total_cost']:.4f}\n")
|
| 72 |
+
f.write(f"**Tokens:** {final_state['total_tokens']:,}\n")
|
| 73 |
+
|
| 74 |
+
print(f"\n\nReport saved to: workflow_test_report.md")
|
| 75 |
+
print("\nTest PASSED!")
|
| 76 |
+
|
| 77 |
+
except Exception as e:
|
| 78 |
+
print(f"\nTest FAILED: {e}")
|
| 79 |
+
import traceback
|
| 80 |
+
|
| 81 |
+
traceback.print_exc()
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
if __name__ == "__main__":
|
| 85 |
+
asyncio.run(test_workflow())
|
workflow_test_report.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Market Intelligence Report
|
| 2 |
+
|
| 3 |
+
Generated via LangGraph Workflow
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
# Market Intelligence Report: Going Viral on Instagram Using AI Without Showing Face
|
| 8 |
+
|
| 9 |
+
## Executive Summary
|
| 10 |
+
**Executive Summary: Leveraging AI for Faceless Viral Growth on Instagram**
|
| 11 |
+
|
| 12 |
+
The faceless Instagram virality market is led by niche AI platforms including Invideo AI (text-to-Reel generation), Virlo (visuals and shop setup), Zebracat (viral video tools), and TailorTalk (automation), with limited corporate details available (e.g., no founding dates or revenues) [1-3,5]. These SaaS/freemium tools enable rapid content creation, tying into user monetization via affiliates, shops, and digital products, mirroring successful AI influencers like Lil Miquela (3M+ followers) [4,9].
|
| 13 |
+
|
| 14 |
+
**Market Position**: Positioned as efficiency leaders in a $32.55B influencer market (57.1% Instagram share), these tools deliver 3-5% engagement rates vs. 2% average, driven by scalable, anonymous Reels in niches like AI art and pets [3,9]. Amid 93% marketer video spend growth to $140B by 2025, they exploit short-form trends but face saturation from competitors like BigMotion AI [3,4].
|
| 15 |
+
|
| 16 |
+
**Key Findings**:
|
| 17 |
+
- **Strengths**: 20-minute Reel production, 24/7 scalability, privacy advantages [1,3].
|
| 18 |
+
- **Weaknesses**: Tool dependencies, quality limits, transparency gaps [1-3].
|
| 19 |
+
- **Opportunities**: Social commerce ($2.9T by 2026), AI personalization [2,4].
|
| 20 |
+
- **Threats**: Algorithm volatility, authenticity fatigue, bot crackdowns [1,4].
|
| 21 |
+
|
| 22 |
+
**Main Recommendations** (Prioritized for 2-3x growth in 30 days):
|
| 23 |
+
- Post 5-7 daily Reels in proven niches using Invideo/Zebracat; integrate voiceovers/visuals for UGC-style hooks [1,3].
|
| 24 |
+
- Automate via TailorTalk/Madgicx; launch Shops with 20% commerce content, cross-posting to TikTok [3,5].
|
| 25 |
+
- Diversify tools and build Discord communities for retention [3,7].
|
| 26 |
+
|
| 27 |
+
Actionable path: Deploy high-priority tactics immediately to capture 10-20% conversion uplift, mitigating risks for sustained 20% YoY growth. Deeper diligence (e.g., Crunchbase) advised [1-10].
|
| 28 |
+
|
| 29 |
+
(248 words)
|
| 30 |
+
|
| 31 |
+
## 1. Company Overview
|
| 32 |
+
The "Going Viral on Instagram Using AI Without Showing Face" represents an emerging strategy and ecosystem in the social media marketing industry, focused on AI-powered, faceless content creation for Instagram Reels, posts, and automation. It aggregates specialized SaaS tools rather than a single corporate entity, with limited foundational details available (e.g., no founding dates, headquarters, or employee counts specified in public data). Key platforms driving this strategy include:
|
| 33 |
+
|
| 34 |
+
- **Invideo AI**: AI video creation platform generating faceless Instagram Reels from text prompts in ~20 minutes; supports monetization via digital products and affiliates [1].
|
| 35 |
+
- **Virlo (virlo.ai)**: AI tool for faceless Instagram content ideas, visuals (via Leonardo.ai integration), voice-overs (Eleven Labs), and video transformation (Flarecut); enables Instagram Shop setup for merchandise [2].
|
| 36 |
+
- **Zebracat (zebracat.ai)**: AI video generator targeting viral faceless content in niches like pet videos and AI art, achieving up to 5% engagement rates (vs. 2% average) [3].
|
| 37 |
+
- **TailorTalk (tailortalk.ai)**: AI platform for Instagram business automation, including DMs, comments, leads, and product recommendations via natural language processing [5].
|
| 38 |
+
- **Supporting Tools**: Madgicx (AI for trend forecasting and automated funnels) [6]; Juicer (AI for content ideas, captions, and social listening) [7]; ancillary tools like Copy.ai (copywriting) and Descript (video editing) [8].
|
| 39 |
+
|
| 40 |
+
**Business Model**: Primarily SaaS/subscription-based with freemium access, enabling user monetization through affiliates, digital products (e.g., spreadsheets, courses), sponsored posts, Instagram Shops, and brand deals. AI influencers (e.g., Aitana Lopez) exemplify scalability, generating revenue via 24/7 operations and 3% higher engagement [4,9].
|
| 41 |
+
|
| 42 |
+
**Key Metrics**: No direct revenue or employee data; indirect indicators include 5% engagement for AI art accounts and virtual influencers with 3M+ followers (e.g., Lil Miquela) [3,9]. Growth is implied through viral potential and rapid workflows.
|
| 43 |
+
|
| 44 |
+
This ecosystem emphasizes time-saving AI for anonymous, scalable growth, with data gaps suggesting need for sources like Crunchbase [1-10].
|
| 45 |
+
|
| 46 |
+
## 2. Competitive Landscape
|
| 47 |
+
The competitive landscape for faceless AI-driven Instagram virality is emerging and fragmented, dominated by specialized tools for short-form video generation, automation, and monetization. Leaders focus on niches like pets and AI art, with automation challengers gaining traction.
|
| 48 |
+
|
| 49 |
+
### Main Competitors
|
| 50 |
+
- **Zebracat**: AI video generator for faceless niches (pets, AI art); high engagement (5% vs. 2% average); manual creation with virality tips (e.g., 509K-follower accounts) [1].
|
| 51 |
+
- **BigMotion AI**: Free faceless AI video generator with auto-posting to Instagram Reels, TikTok, and YouTube Shorts; <5-minute production, real-time algorithm adaptation [3].
|
| 52 |
+
- **Faceless.video**: Automates faceless video ads and cross-platform posting; e-commerce focus [4].
|
| 53 |
+
- **Crayo AI**: Short-form video generator for Reels; backed by faceless YouTube experts (millions in revenue) [6].
|
| 54 |
+
|
| 55 |
+
### Competitive Matrix
|
| 56 |
+
|
| 57 |
+
| Dimension | Going Viral AI (Faceless IG Strategy) | Zebracat | BigMotion AI | Faceless.video | Crayo AI |
|
| 58 |
+
|------------------------|---------------------------------------|-----------------------------------|-----------------------------------|-----------------------------------|-----------------------------------|
|
| 59 |
+
| **Market Share/Size** | Low (Emerging; no metrics) | **High** (Leader; 509K examples, 5% engagement) | Medium-High (Free entrant, multi-platform) | Low (Ad-focused niche) | Medium (Expert-backed) |
|
| 60 |
+
| **Product Range** | Low (IG Reels-specific) | Medium (Niche videos from text) | **High** (Short videos + auto-post) | Low (Video ads only) | Medium (Short-form Reels/Shorts) |
|
| 61 |
+
| **Pricing Strategy** | N/A (Assumed subscription) | N/A (Assumed paid) | **Free tier** (Aggressive) | N/A (Assumed paid) | N/A (Assumed paid) |
|
| 62 |
+
| **Technology/Innovation** | Medium (IG viral focus) | Medium (Text-to-video + tips) | **High** (Algo adaptation, auto-publish) | Medium (Ad auto-posting) | **High** (Ideation by experts) |
|
| 63 |
+
| **Customer Segments** | IG creators (viral growth) | Niche creators (pets/AI art) | Passive income seekers | E-com/marketers | Viral short-form creators |
|
| 64 |
+
|
| 65 |
+
**Strategic Insights**: Zebracat leads creatively; BigMotion excels in automation. Opportunity for "Going Viral AI" to integrate both for IG-specific dominance [1,3,4,6].
|
| 66 |
+
|
| 67 |
+
## 3. SWOT Analysis
|
| 68 |
+
|
| 69 |
+
#### Strengths
|
| 70 |
+
- High engagement (5% in niches vs. 2% average); 3% uplift for virtual influencers [3,9].
|
| 71 |
+
- Rapid scalability (Reels in 20 mins); 24/7 automation [1,3].
|
| 72 |
+
- Privacy/low risk; diverse monetization (affiliates, shops) [1,2,4].
|
| 73 |
+
- Niche expertise (e.g., 509K-follower proofs); efficiency via integrations [2,3,5].
|
| 74 |
+
|
| 75 |
+
#### Weaknesses
|
| 76 |
+
- Limited transparency (no HQ/revenue data) [1-3,5].
|
| 77 |
+
- Third-party dependencies; content limits (<90s videos) [2,3,8].
|
| 78 |
+
- Engagement automation gaps; skill barriers [3,5,6].
|
| 79 |
+
- Freemium churn risks [1,2].
|
| 80 |
+
|
| 81 |
+
#### Opportunities
|
| 82 |
+
- Short-form video boom ($140B market); AI personalization [3,4].
|
| 83 |
+
- Social commerce ($2.9T by 2026); influencer surge ($32.55B) [2,4].
|
| 84 |
+
- Niche/authenticity demand; multi-platform expansion [1,3,7].
|
| 85 |
+
|
| 86 |
+
#### Threats
|
| 87 |
+
- Competition (e.g., BigMotion free tier) [1,3,4,6].
|
| 88 |
+
- Consumer fatigue (50% limiting use); algorithm volatility [1,2,4].
|
| 89 |
+
- Authenticity pressure; platform risks [1,4,7].
|
| 90 |
+
|
| 91 |
+
## 4. Market Positioning
|
| 92 |
+
Faceless AI Instagram virality is positioned as a **privacy-first, scalable viral enabler** in short-form video AI, delivering 3-5% engagement via anonymous Reels [3,9].
|
| 93 |
+
|
| 94 |
+
**Value Proposition**:
|
| 95 |
+
| Core Element | Description | Evidence [Source] |
|
| 96 |
+
|--------------------|--------------------------------------|-------------------------------|
|
| 97 |
+
| Speed & Scalability| Text-to-video <20 mins; 24/7 output | Invideo/BigMotion [1,3] |
|
| 98 |
+
| Privacy | Faceless niches (pets/AI art) | 5% engagement [3] |
|
| 99 |
+
| Monetization | Affiliates/shops/brands | Aitana Lopez model [2,9] |
|
| 100 |
+
| Engagement Edge | Algorithm-optimized | PLS-SEM study [3,10] |
|
| 101 |
+
|
| 102 |
+
**Target Segments**: Aspiring creators (primary); e-com brands (secondary) [1-5].
|
| 103 |
+
|
| 104 |
+
**Differentiation**: Bridges creative (Zebracat) and automation (BigMotion) gaps with IG-specific hooks [1,3].
|
| 105 |
+
|
| 106 |
+
**Opportunities**: Freemium MVP; niche expansion (e.g., virtual influencers) [2,9].
|
| 107 |
+
|
| 108 |
+
## 5. Market Trends & Insights
|
| 109 |
+
**Key Trends**:
|
| 110 |
+
- Short-form video dominance (93% marketers increasing spend; $140B market) [3,4].
|
| 111 |
+
- AI personalization (50% leaders maximizing tools) [2,4,5].
|
| 112 |
+
- Authenticity/UGC shift; social commerce boom [1,3,7].
|
| 113 |
+
|
| 114 |
+
**Growth Drivers**:
|
| 115 |
+
- Ad/influencer spend ($276.7B social ads; $32.55B influencers) [2,4].
|
| 116 |
+
- Video efficacy (99% product understanding) [4].
|
| 117 |
+
|
| 118 |
+
**Challenges**:
|
| 119 |
+
- Fatigue (50% limiting use); algorithm volatility [1,2].
|
| 120 |
+
|
| 121 |
+
**Outlook**: $2.9T social commerce by 2026; AI + authenticity key to 20% YoY growth [2,4].
|
| 122 |
+
|
| 123 |
+
## 6. Strategic Recommendations
|
| 124 |
+
|
| 125 |
+
#### High Priority (Weeks 1-4)
|
| 126 |
+
- **Post 5-7 daily Reels in niches (AI art/pets)**: Use Invideo/BigMotion for hooks; 2-3x growth expected [1,3].
|
| 127 |
+
- **Integrate voiceovers/visuals + auto-scheduling**: 20-30% higher shares [2,3].
|
| 128 |
+
|
| 129 |
+
#### Medium Priority (6-12 Months)
|
| 130 |
+
- **Automate interactions (TailorTalk)**: Double replies; 15-25% retention [3,5].
|
| 131 |
+
- **Launch Shops + cross-post**: 10-20% conversions; $5K+ monthly [2,3].
|
| 132 |
+
|
| 133 |
+
#### Long-Term (12+ Months)
|
| 134 |
+
- **Diversify tools**: 30-50% downtime reduction [3,8].
|
| 135 |
+
- **Build communities (Discord)**: 20-40% LTV uplift [3,7].
|
| 136 |
+
|
| 137 |
+
## 7. Sources
|
| 138 |
+
- [1] Invideo AI platform and faceless Reels data.
|
| 139 |
+
- [2] Virlo.ai tool features and Instagram Shop integration.
|
| 140 |
+
- [3] Zebracat.ai; BigMotion AI; engagement metrics (5% vs. 2%).
|
| 141 |
+
- [4] Faceless.video; AI influencers (Aitana Lopez); market projections.
|
| 142 |
+
- [5] TailorTalk.ai automation details.
|
| 143 |
+
- [6] Madgicx; Crayo AI expert backing.
|
| 144 |
+
- [7] Juicer AI tools; social listening trends.
|
| 145 |
+
- [8] Copy.ai; Descript; ancillary workflows.
|
| 146 |
+
- [9] Virtual influencers (Lil Miquela, Shudu Gram); 3% engagement edge.
|
| 147 |
+
- [10] PLS-SEM study (n=391) on AI influencer attributes.
|
| 148 |
+
|
| 149 |
+
---
|
| 150 |
+
|
| 151 |
+
Report generated: November 28, 2025
|
| 152 |
+
|
| 153 |
+
---
|
| 154 |
+
|
| 155 |
+
**Cost:** $0.0000
|
| 156 |
+
**Tokens:** 0
|