feat(ml): multi-agent context framework + v4 orchestrator prompt
Adds ml/agents/ — five specialised sub-agents (overdue_task, momentum, time_of_day, recent_patterns, focus_area) each producing a prompt snippet from user signals. A registry wires them up; the orchestrator prompt in ml/serving/prompts.py synthesises their outputs into one tip via LiteLLM. Also wires /api/agents route in the API and updates the Dockerfile to copy the full ml/ tree with PYTHONPATH=/app so agent imports resolve correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,52 @@ PROMPTS: dict[str, Prompt] = {
|
||||
}
|
||||
|
||||
|
||||
# ── v4-orchestrator ────────────────────────────────────────────────────────
|
||||
# Not a Prompt entry — takes pre-computed agent snippets, not a _Ctx.
|
||||
|
||||
_SYS_V4_ORCHESTRATOR = (
|
||||
"You are a personal advisor generating a single, perfectly-timed tip. "
|
||||
"Multiple specialized agents have analyzed the user's current context and provided "
|
||||
"their insights below. Synthesize their combined perspective to generate exactly ONE "
|
||||
"tip that is specific, actionable, and relevant right now. "
|
||||
"Respond ONLY with a JSON object with keys: "
|
||||
'"id" (short slug), "content" (the tip, ≤2 sentences), '
|
||||
'"rationale" (why now, ≤1 sentence). '
|
||||
"No markdown, no prose outside the JSON object."
|
||||
)
|
||||
|
||||
|
||||
def build_orchestrator_messages(
|
||||
agent_outputs: list[dict],
|
||||
tasks: list[dict],
|
||||
hour_of_day: int,
|
||||
day_of_week: int,
|
||||
) -> list[dict]:
|
||||
"""Build the [system, user] message list for the orchestrator LLM call.
|
||||
|
||||
agent_outputs: list of {agent_id, prompt_text} dicts.
|
||||
Falls back to raw task summary when agent_outputs is empty.
|
||||
"""
|
||||
lines = [f"Current time: {hour_of_day:02d}:00, day_of_week={day_of_week}", ""]
|
||||
if agent_outputs:
|
||||
lines.append("Context from analysis agents:")
|
||||
for s in agent_outputs:
|
||||
lines.append(f"[{s['agent_id']}] {s['prompt_text']}")
|
||||
else:
|
||||
overdue = [t for t in tasks if t.get("is_overdue")]
|
||||
lines.append(
|
||||
f"No pre-computed agent context available. "
|
||||
f"Tasks: {len(tasks)} total, {len(overdue)} overdue."
|
||||
)
|
||||
for t in tasks[:3]:
|
||||
lines.append(f" - {t.get('content', '?')}")
|
||||
lines.append("\nGenerate one tip as a JSON object.")
|
||||
return [
|
||||
{"role": "system", "content": _SYS_V4_ORCHESTRATOR},
|
||||
{"role": "user", "content": "\n".join(lines)},
|
||||
]
|
||||
|
||||
|
||||
def default_version() -> str:
|
||||
return os.getenv("DEFAULT_PROMPT_VERSION", "v1")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user