File size: 8,637 Bytes
5967b99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
from crewai import Agent, Task, Crew, Process
import os
from dotenv import load_dotenv

load_dotenv()

os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY")

MODEL = "groq/llama-3.3-70b-versatile"

# ── AGENT 1: SCOUT ──────────────────────────────────────────
scout = Agent(
    role="Job Scout",
    goal="Extract all important details from a freelance job posting clearly and completely",
    backstory="""You are an expert at reading freelance job postings on platforms 

    like Upwork and Fiverr. You extract key requirements, skills needed, budget, 

    timeline, and client tone with perfect accuracy.""",
    verbose=True,
    allow_delegation=False,
    llm=MODEL
)

# ── AGENT 2: RESEARCHER ─────────────────────────────────────
researcher = Agent(
    role="Client Researcher",
    goal="Research the client and job context to find useful background information",
    backstory="""You are a skilled researcher who finds information about clients, 

    their companies, and their project needs. You help freelancers understand 

    exactly who they are pitching to so they can personalize their proposals.""",
    verbose=True,
    allow_delegation=False,
    llm=MODEL
)

# ── AGENT 3: WRITER ─────────────────────────────────────────
writer = Agent(
    role="Proposal Writer",
    goal="Write a compelling, personalized freelance proposal that wins the job",
    backstory="""You are an expert freelance proposal writer with years of 

    experience winning jobs on Upwork and Fiverr. You write proposals that 

    feel personal, professional, and directly address the client's needs. 

    You never use generic templates.""",
    verbose=True,
    allow_delegation=False,
    llm=MODEL
)

# ── AGENT 4: PRICER ─────────────────────────────────────────
pricer = Agent(
    role="Pricing Strategist",
    goal="Recommend the perfect price for the freelance job based on market rates",
    backstory="""You are a freelance pricing expert who understands market rates 

    for different skills and project types. You suggest competitive prices that 

    are neither too low nor too high, helping freelancers win jobs while earning 

    what they deserve.""",
    verbose=True,
    allow_delegation=False,
    llm=MODEL
)

# ── AGENT 5: REVIEWER ────────────────────────────────────────
reviewer = Agent(
    role="Quality Reviewer",
    goal="Review and improve the proposal to make it perfect before sending",
    backstory="""You are a senior editor who reviews freelance proposals for 

    quality, tone, relevance, and professionalism. You catch any issues and 

    improve the proposal to maximize the chance of winning the job.""",
    verbose=True,
    allow_delegation=False,
    llm=MODEL
)


# ── TASKS ────────────────────────────────────────────────────
def create_tasks(job_posting):

    task1 = Task(
        description=f"""Carefully read this job posting and extract all key information:



        JOB POSTING:

        {job_posting}



        Extract and clearly list:

        1. What exactly the client needs

        2. Required skills and technologies

        3. Budget (if mentioned)

        4. Timeline/deadline (if mentioned)

        5. Client tone (formal/casual/technical)

        6. Any special requirements or preferences

        """,
        expected_output="A clear, structured list of all job requirements and details",
        agent=scout
    )

    task2 = Task(
        description=f"""Based on this job posting, research and identify:



        JOB POSTING:

        {job_posting}



        Find out:

        1. What type of client/company is this likely to be

        2. What do clients like this usually value most

        3. What common mistakes freelancers make with this type of job

        4. What would make a proposal stand out for this specific job

        5. Any industry context that would help personalize the proposal

        """,
        expected_output="Detailed client research and insights to personalize the proposal",
        agent=researcher
    )

    task3 = Task(
       description=f"""Using the job analysis and client research, 

write a complete freelance proposal following this EXACT structure:



JOB POSTING:

{job_posting}



Follow this structure STRICTLY:



LINE 1 - HOOK (1 sentence):

Start with something specific from the job that caught your attention.

NOT "I am excited" or "I am thrilled" β€” be specific and different.

Example: "Optimizing MongoDB indexing for high-traffic apps is exactly 

the kind of challenge I enjoy solving."



LINE 2-3 - CREDIBILITY (2-3 sentences):

Mention your relevant experience briefly.

Use numbers if possible: "I have done this for 5+ clients"

Reference the freelancer profile if provided.



LINE 4-5 - YOUR PLAN (3-4 sentences):

Tell them SPECIFICALLY what you will do for their project.

Reference their exact requirements by name.

Show you read and understood their job posting.



LINE 6 - AVAILABILITY AND TIMELINE (1 sentence):

Confirm you can meet their timeline and are available immediately.



LINE 7 - CALL TO ACTION (1 sentence):

Invite them to chat β€” keep it simple and confident.



SIGN OFF:

Best regards,

[Your Name]



STRICT RULES:

- Total length: 150-200 words MAXIMUM

- Never start with "I am excited", "I am thrilled", "I am happy"

- Never repeat any sentence

- Never write the proposal twice

- Replace [Your Name] literally as [Your Name] β€” do not change it

- Do NOT sign off with any agent name or role name

- Sound like a real human freelancer, not a robot""",
        expected_output="A complete, personalized 150-200 word freelance proposal ready to send",
        agent=writer
    )

    task4 = Task(
        description=f"""Based on this job posting, suggest the ideal pricing:



        JOB POSTING:

        {job_posting}



        Provide:

        1. Recommended price range (minimum and maximum)

        2. What to include in the price

        3. Whether to charge hourly or fixed price

        4. Reasoning for this price recommendation

        5. One negotiation tip for this specific job

        """,
        expected_output="Clear pricing recommendation with reasoning and strategy",
        agent=pricer
    )

    task5 = Task(
       description="""Review the proposal from the Writer agent.



YOUR ONLY JOB:

1. Make sure it follows the hook, credibility, plan, availability, CTA structure

2. Make sure it does NOT start with "I am excited" or "I am thrilled"

3. Make sure the sign off is exactly: Best regards, [Your Name]

4. Make sure it is NOT longer than 200 words

5. Remove ANY repetition

6. Make sure it sounds human and genuine



OUTPUT RULES β€” CRITICAL:

- Output the final proposal text ONLY

- Do NOT add any explanation before or after

- Do NOT write "Here is the proposal" or any introduction

- Do NOT sign with your agent role name

- The ONLY sign off allowed is: Best regards, [Your Name]

- Write it ONCE and STOP""",
        expected_output="The final polished proposal ready to copy and send to the client",
        agent=reviewer
    )

    return [task1, task2, task3, task4, task5]


# ── MAIN CREW FUNCTION ───────────────────────────────────────
def run_pitchpilot(job_posting):
    tasks = create_tasks(job_posting)

    crew = Crew(
        agents=[scout, researcher, writer, pricer, reviewer],
        tasks=tasks,
        process=Process.sequential,
        verbose=True
    )

    result = crew.kickoff()

    # Return only reviewer (task5) output to avoid mixed/duplicated text
    try:
        task_outputs = getattr(result, "tasks_output", None)
        if task_outputs and len(task_outputs) >= 5:
            reviewer_output = task_outputs[4]
            reviewer_text = getattr(reviewer_output, "raw", None) or str(reviewer_output)
            return reviewer_text.strip()
    except Exception:
        pass

    return str(result).strip()