File size: 8,124 Bytes
741c3da
 
 
 
 
df075f4
 
741c3da
 
df075f4
49f76f6
 
954371b
49f76f6
741c3da
 
 
 
49f76f6
741c3da
49f76f6
741c3da
49f76f6
741c3da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49f76f6
741c3da
49f76f6
 
 
df075f4
741c3da
49f76f6
741c3da
49f76f6
741c3da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49f76f6
741c3da
49f76f6
741c3da
49f76f6
df075f4
741c3da
49f76f6
741c3da
49f76f6
741c3da
 
 
 
 
 
49f76f6
741c3da
49f76f6
741c3da
49f76f6
 
 
 
 
 
 
 
 
 
 
 
741c3da
 
 
5e4f9b3
49f76f6
 
 
 
 
 
df075f4
741c3da
 
 
 
 
 
 
 
 
 
 
 
 
49f76f6
 
 
 
 
 
 
 
 
 
 
 
741c3da
49f76f6
 
741c3da
49f76f6
 
 
 
741c3da
49f76f6
741c3da
49f76f6
741c3da
49f76f6
 
 
741c3da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49f76f6
 
954371b
49f76f6
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from tools.simple_tools import (
    search_web_tool, record_notes_tool, write_report_tool, review_report_tool,
    get_workflow_state, reset_workflow_state
)
from dotenv import load_dotenv
import os
from llama_index.core.agent.workflow import AgentWorkflow, ReActAgent
from llama_index.core.workflow import Context

load_dotenv(os.path.join(os.path.dirname(__file__), 'env.local'))

class LlamaIndexReportAgent:
    def __init__(self):
        self.llm = HuggingFaceInferenceAPI(
            model_name="microsoft/Phi-3.5-mini-instruct",
            token=os.getenv("HUGGING_FACE_TOKEN")
        )

        self.research_agent = ReActAgent(
            name="ResearchAgent",
            description="Searches the web and records notes.",
            system_prompt=(
                "You are a Research Agent. Your ONLY job is to research and hand off to WriteAgent.\n"
                "\n"
                "STRICT WORKFLOW:\n"
                "1. Use search_web tool to search for information\n"
                "2. Use record_notes tool to save what you found\n"
                "3. Say: 'Research complete. I have gathered sufficient information. Handing off to WriteAgent.'\n"
                "\n"
                "CRITICAL RULES:\n"
                "- You can ONLY use search_web and record_notes tools\n"
                "- You CANNOT write reports - that's WriteAgent's job\n"
                "- You CANNOT use write_report tool - you don't have access to it\n"
                "- After research, you MUST hand off with the exact message above\n"
                "- Do NOT attempt to write any report content yourself\n"
                "\n"
                "AVAILABLE TOOLS: search_web, record_notes\n"
                "HANDOFF MESSAGE: 'Research complete. I have gathered sufficient information. Handing off to WriteAgent.'"
            ),
            tools=[search_web_tool, record_notes_tool],
            llm=self.llm,
            can_handoff_to=["WriteAgent"],
        )

        self.write_agent = ReActAgent(
            name="WriteAgent",
            description="Writes a structured report based on research notes.",
            system_prompt=(
                "You are a Writing Agent. Your purpose is to create a concise, well-structured report.\n"
                "\n"
                "INSTRUCTIONS:\n"
                "1. Check if there's any feedback from ReviewAgent (not 'Review required.')\n"
                "2. If there's feedback, revise the report accordingly\n"
                "3. If no feedback, create initial report based on research\n"
                "4. MUST call write_report tool with these parameters:\n"
                "   - report_content: Concise markdown report (200-400 words)\n"
                "   - title: Descriptive report title\n"
                "5. Report structure (keep sections brief):\n"
                "   - # Main Title\n"
                "   - ## Introduction (1-2 sentences)\n"
                "   - ## Key Points (2-3 bullet points)\n"
                "   - ## Conclusion (1-2 sentences)\n"
                "6. After calling tool: 'Report written. Handing off to ReviewAgent.'\n"
                "\n"
                "CRITICAL: Keep the report_content CONCISE to avoid truncation!\n"
                "You MUST actually call the write_report tool with proper parameters!"
            ),
            tools=[write_report_tool],
            llm=self.llm,
            can_handoff_to=["ReviewAgent"],
        )

        self.review_agent = ReActAgent(
            name="ReviewAgent",
            description="Reviews the written report.",
            system_prompt=(
                "You are a Reviewing Agent. Your purpose is to review the report quality.\n"
                "1. Check the report content that was written\n"
                "2. Use review_report tool to provide feedback\n"
                "3. If report is good quality, start feedback with 'APPROVED:'\n"
                "4. If needs improvement, provide specific suggestions and hand off to WriteAgent\n"
                "5. Quality criteria: clear structure, sufficient detail, proper formatting"
            ),
            tools=[review_report_tool],
            llm=self.llm,
            can_handoff_to=["WriteAgent"],
        )

        self.agent_workflow = AgentWorkflow(
            agents=[self.research_agent, self.write_agent, self.review_agent],
            root_agent=self.research_agent.name,
            initial_state={
                "research_notes": {},
                "report_content": "Not written yet.",
                "review": "Review required.",
            },
        )

    def get_final_state(self) -> dict:
        """Get the final workflow state from the simple tools."""
        return get_workflow_state()

    async def run_workflow(self, user_msg=None):
        if user_msg is None:
            user_msg = (
                "Write me a report on the history of the internet. "
                "Briefly describe the history of the internet, including the development of the internet, the development of the web, "
                "and the development of the internet in the 21st century."
            )
        
        # Reset state for new workflow
        reset_workflow_state()
        
        # Create context and initialize state
        ctx = Context(self.agent_workflow)
        await ctx.set("state", {
            "research_notes": {},
            "report_content": "Not written yet.",
            "review": "Review required.",
        })
        
        handler = self.agent_workflow.run(user_msg=user_msg, ctx=ctx)

        current_agent = None
        async for event in handler.stream_events():
            if (
                hasattr(event, "current_agent_name")
                and event.current_agent_name != current_agent
            ):
                current_agent = event.current_agent_name
                print(f"\n{'='*50}")
                print(f"🤖 Agent: {current_agent}")
                print(f"{'='*50}\n")

            if hasattr(event, "response") and hasattr(event.response, "content"):
                if event.response.content:
                    print("📤 Output:", event.response.content)
                if hasattr(event, "tool_calls") and event.tool_calls:
                    print(
                        "🛠️  Planning to use tools:",
                        [call.tool_name for call in event.tool_calls],
                    )
            elif hasattr(event, "tool_name") and hasattr(event, "tool_output"):
                print(f"🔧 Tool Result ({event.tool_name}):")
                print(f"  Arguments: {getattr(event, 'tool_kwargs', {})}")
                print(f"  Output: {event.tool_output}")
            elif hasattr(event, "tool_name") and hasattr(event, "tool_kwargs"):
                print(f"🔨 Calling Tool: {event.tool_name}")
                print(f"  With arguments: {event.tool_kwargs}")

        # After the workflow completes, print the final report
        final_state = self.get_final_state()
        print(f"\n📊 Final State:")
        print(f"Research notes: {len(final_state.get('research_notes', {}))}")
        print(f"Report written: {final_state.get('report_content', 'Not written') != 'Not written yet.'}")
        print(f"Review: {final_state.get('review', 'No review')[:100]}...")
        
        if final_state.get("structured_report"):
            print("\n📄 Final Report Generated Successfully!")
            report = final_state["structured_report"]
            print(f"Title: {report['title']}")
            print(f"Word count: {report['word_count']}")
            print(f"Sections: {len(report['sections'])}")
        else:
            print("\n⚠️ No final report was generated by the workflow.")

if __name__ == "__main__":
    import asyncio
    agent = LlamaIndexReportAgent()
    user_msg = input("Enter the topic or instructions for the report (leave blank for default): ").strip()
    if not user_msg:
        user_msg = None
    asyncio.run(agent.run_workflow(user_msg=user_msg))