This commit is contained in:
1 parent
321421e37f
commit
120ab192f1
7 files changed
+558
-79
No files matched your search
+106
-12
@@ -65,8 +65,8 @@ class TaskWorker:
|
||||
model=model,
|
||||
messages=messages,
|
||||
tools=tools or None,
|
||||
max_tokens=500,
|
||||
temperature=0.7,
|
||||
max_tokens=700,
|
||||
temperature=0.3,
|
||||
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -102,18 +102,72 @@ class TaskWorker:
|
||||
if not text:
|
||||
reg.fail(self.task_id, "LLM returned empty response")
|
||||
return
|
||||
if self._is_deferred_answer(text):
|
||||
reg.add_step(
|
||||
self.task_id,
|
||||
"thinking",
|
||||
"Draft deferred the work; continuing automatically.",
|
||||
)
|
||||
if iteration == max_iterations - 1:
|
||||
reg.fail(self.task_id, "Could not produce a complete answer")
|
||||
return
|
||||
messages.extend(
|
||||
[
|
||||
{"role": "assistant", "content": text},
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
"Do not ask me a follow-up or offer to continue later. "
|
||||
"Continue the task now, run any more targeted tool calls "
|
||||
"you need, make reasonable assumptions, and only finish "
|
||||
"when you have directly answered the original request."
|
||||
),
|
||||
},
|
||||
]
|
||||
)
|
||||
continue
|
||||
reg.add_step(self.task_id, "text", text)
|
||||
reg.complete(self.task_id, text)
|
||||
return
|
||||
|
||||
reg.fail(self.task_id, f"Reached max iterations ({max_iterations})")
|
||||
|
||||
@staticmethod
|
||||
def _is_deferred_answer(text: str) -> bool:
|
||||
"""Reject drafts that ask permission or defer the requested work."""
|
||||
lowered = text.lower()
|
||||
deferral_phrases = (
|
||||
"would you like me",
|
||||
"do you want me",
|
||||
"should i ",
|
||||
"shall i ",
|
||||
"if you'd like",
|
||||
"if you would like",
|
||||
"let me know if",
|
||||
"i need to perform",
|
||||
"i need more information",
|
||||
"i can refine",
|
||||
"i can search",
|
||||
"i can provide",
|
||||
"need a more targeted search",
|
||||
)
|
||||
return "?" in text or any(phrase in lowered for phrase in deferral_phrases)
|
||||
|
||||
def _system_prompt(self) -> str:
|
||||
today = datetime.now(timezone.utc).strftime("%B %d, %Y")
|
||||
return (
|
||||
"You are Hope's research assistant working on a background task. "
|
||||
"Use your tools to gather information thoroughly. When you have "
|
||||
"enough, provide a final answer in 1-3 sentences suitable for "
|
||||
"speaking aloud. Plain text only, no markdown or formatting."
|
||||
"You are Hope's autonomous research assistant working on a background task. "
|
||||
f"Today is {today}. Use your tools until you have directly completed the request. "
|
||||
"Never ask the user a follow-up question, ask permission to search again, or offer "
|
||||
"to finish later; make reasonable assumptions and continue autonomously. If a web "
|
||||
"search is broad or inconclusive, immediately run a more targeted query and scrape "
|
||||
"the most promising result pages, including today's date and specific reputable "
|
||||
"sources when freshness matters. For headline "
|
||||
"requests, return concrete named events rather than links to news homepages, and "
|
||||
"respect any requested item count exactly. Only "
|
||||
"produce a final answer once it fulfills the original request. The final answer must "
|
||||
"be 1-3 concise sentences suitable for speaking aloud, with plain text only and no "
|
||||
"markdown or formatting."
|
||||
)
|
||||
|
||||
def _load_tools(self) -> list[dict]:
|
||||
@@ -157,7 +211,7 @@ class TaskWorker:
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "web_search",
|
||||
"description": "Search the web for current information using Firecrawl.",
|
||||
"description": "Search the web for current information. Returns titles, descriptions, and URLs.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"query": {"type": "string"}},
|
||||
@@ -165,6 +219,18 @@ class TaskWorker:
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "web_scrape",
|
||||
"description": "Read the full content of a promising URL returned by web_search.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"url": {"type": "string"}},
|
||||
"required": ["url"],
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
async def _execute_tool(self, name: str, args: dict) -> str:
|
||||
@@ -227,18 +293,46 @@ class TaskWorker:
|
||||
async with httpx.AsyncClient(timeout=30) as client:
|
||||
resp = await client.post(
|
||||
f"{firecrawl_base}/v1/search",
|
||||
json={"query": query, "limit": 3},
|
||||
json={"query": query, "limit": 5},
|
||||
)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
items = data.get("data", [])
|
||||
lines = [
|
||||
f"{r.get('title', '')}: {r.get('description', '')}"
|
||||
for r in items[:3]
|
||||
]
|
||||
lines = []
|
||||
for item in items[:5]:
|
||||
title = (item.get("title") or "Untitled").strip()
|
||||
description = (item.get("description") or "").strip()
|
||||
url = (item.get("url") or "").strip()
|
||||
lines.append(f"{title}: {description}")
|
||||
if url:
|
||||
lines.append(f"URL: {url}")
|
||||
return "\n".join(lines) or "No results found."
|
||||
return f"Search failed (HTTP {resp.status_code})."
|
||||
|
||||
elif name == "web_scrape":
|
||||
url = args.get("url", "")
|
||||
if not url:
|
||||
return "A URL is required."
|
||||
firecrawl_base = os.environ.get("FIRECRAWL_BASE", "")
|
||||
if not firecrawl_base:
|
||||
return "Web scraping is not configured."
|
||||
async with httpx.AsyncClient(timeout=90) as client:
|
||||
resp = await client.post(
|
||||
f"{firecrawl_base}/v1/scrape",
|
||||
json={"url": url, "formats": ["markdown"], "timeout": 60000},
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
return f"Scrape failed (HTTP {resp.status_code})."
|
||||
data = resp.json()
|
||||
if not data.get("success"):
|
||||
return f"Scrape failed: {data.get('error', 'unknown error')}"
|
||||
result = data.get("data", {})
|
||||
markdown = (result.get("markdown") or "").strip()
|
||||
if not markdown:
|
||||
return "No page content was extracted."
|
||||
title = (result.get("metadata", {}).get("title") or "").strip()
|
||||
return f"Page: {title}\n\n{markdown[:8000]}"
|
||||
|
||||
else:
|
||||
return f"Unknown tool: {name}"
|
||||
|
||||
|
||||
Reference in new issue
Block a user