mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
124 lines
3.9 KiB
Python
124 lines
3.9 KiB
Python
"""
|
|
Market Research Agent — uses Gemini for source-dense research.
|
|
وكيل بحث السوق — يستخدم Gemini للبحث المعتمد على المصادر.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from core.agents.base import BaseAgent
|
|
from core.config.models import Task
|
|
from core.llm.base import Message
|
|
from core.utils import generate_id, utcnow
|
|
|
|
|
|
@dataclass
|
|
class ResearchFinding:
|
|
id: str
|
|
question: str
|
|
summary: str
|
|
bullet_points: list[str] = field(default_factory=list)
|
|
locale: str = "en"
|
|
created_at: datetime = field(default_factory=utcnow)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"id": self.id,
|
|
"question": self.question,
|
|
"summary": self.summary,
|
|
"bullet_points": self.bullet_points,
|
|
"locale": self.locale,
|
|
"created_at": self.created_at.isoformat(),
|
|
}
|
|
|
|
|
|
class MarketResearchAgent(BaseAgent):
|
|
"""Runs open-ended research questions through Gemini."""
|
|
|
|
name = "market_research"
|
|
|
|
async def run(
|
|
self,
|
|
*,
|
|
question: str,
|
|
locale: str = "en",
|
|
depth: str = "standard", # quick | standard | deep
|
|
**_: Any,
|
|
) -> ResearchFinding:
|
|
"""Answer a research question with bullet-pointed summary."""
|
|
depth_tokens = {"quick": 800, "standard": 1500, "deep": 3000}.get(depth, 1500)
|
|
|
|
system = (
|
|
"You are a market research analyst specializing in the Saudi and GCC markets. "
|
|
"Prefer concrete facts, numbers, and named entities. "
|
|
"If uncertain, say so explicitly. Output structured markdown."
|
|
)
|
|
user_prompt = (
|
|
f"Research question (answer in {'Arabic' if locale == 'ar' else 'English'}):\n"
|
|
f"{question}\n\n"
|
|
"Structure the answer as:\n"
|
|
"## Summary (3-4 sentences)\n"
|
|
"## Key Points (5-8 bullets)\n"
|
|
"## Caveats / Unknowns\n"
|
|
)
|
|
|
|
response = await self.router.run(
|
|
task=Task.RESEARCH,
|
|
messages=[Message(role="user", content=user_prompt)],
|
|
system=system,
|
|
max_tokens=depth_tokens,
|
|
temperature=0.3,
|
|
)
|
|
|
|
summary, bullets = self._parse_markdown(response.content)
|
|
|
|
finding = ResearchFinding(
|
|
id=generate_id("rsrch"),
|
|
question=question,
|
|
summary=summary,
|
|
bullet_points=bullets,
|
|
locale=locale,
|
|
)
|
|
|
|
self.log.info(
|
|
"research_done",
|
|
id=finding.id,
|
|
depth=depth,
|
|
n_bullets=len(bullets),
|
|
)
|
|
return finding
|
|
|
|
@staticmethod
|
|
def _parse_markdown(md: str) -> tuple[str, list[str]]:
|
|
"""Extract summary + bullets sections loosely."""
|
|
lines = md.splitlines()
|
|
section: str | None = None
|
|
summary_parts: list[str] = []
|
|
bullets: list[str] = []
|
|
|
|
for line in lines:
|
|
stripped = line.strip()
|
|
lower = stripped.lower()
|
|
if lower.startswith("## ") and ("summary" in lower or "ملخص" in lower):
|
|
section = "summary"
|
|
continue
|
|
if lower.startswith("## ") and (
|
|
"key points" in lower or "النقاط" in lower or "bullet" in lower
|
|
):
|
|
section = "bullets"
|
|
continue
|
|
if lower.startswith("## "):
|
|
section = "other"
|
|
continue
|
|
|
|
if section == "summary" and stripped and not stripped.startswith("#"):
|
|
summary_parts.append(stripped)
|
|
elif section == "bullets" and (stripped.startswith("-") or stripped.startswith("*")):
|
|
bullets.append(stripped.lstrip("-* ").strip())
|
|
|
|
summary = " ".join(summary_parts)
|
|
return summary or md[:300], bullets
|