serenum / agent.py
Csandal17
Finish Sērēnum v1: branding, gitignore fix, requirements, deploy prep
222b76a
Raw
History Blame Contribute Delete
3.74 kB
import os
from dotenv import load_dotenv
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from tools import get_weather, get_air_quality, get_uv_index, search_skincare_evidence
load_dotenv()
@tool
def weather_tool(city: str) -> dict:
"""Get current weather for a city including temperature, humidity, wind speed, and conditions."""
return get_weather(city)
@tool
def air_quality_tool(lat: float, lon: float) -> dict:
"""Get air quality data for a location using latitude and longitude. Returns AQI index and PM2.5."""
return get_air_quality(lat, lon)
@tool
def uv_tool(lat: float, lon: float) -> dict:
"""Get the current UV index for a location using latitude and longitude."""
return get_uv_index(lat, lon)
@tool
def evidence_search_tool(query: str) -> list:
"""Search the web for scientific evidence to answer a skincare myth, claim, or science question. Returns sources with titles, URLs, and snippets to cite."""
return search_skincare_evidence(query)
tools = [weather_tool, air_quality_tool, uv_tool, evidence_search_tool]
llm = ChatAnthropic(
model="claude-sonnet-4-6",
api_key=os.getenv("ANTHROPIC_API_KEY"),
).bind_tools(tools)
SYSTEM_PROMPT = """You are Sērēnum, a skincare advisor that translates weather data into personalised skin advice.
When a user gives you a location:
1. Call the weather tool to get current conditions
2. Use the latitude and longitude from weather to call the air quality tool
3. Use the same coordinates to call the UV index tool
4. Combine all three data points into a concise skin brief
Start your response directly with the skin brief — no preamble like "Here's your brief" or "I have everything I need". Just begin with the formatted output.
Your skin brief should cover:
- SPF recommendation based on UV index
- Hydration advice based on humidity and temperature
- Barrier protection advice based on AQI and PM2.5
- Any active ingredients to avoid or embrace today
Keep advice practical, warm, and under 150 words. Never claim to diagnose skin conditions.
If the user asks a skincare myth or science question, you MUST call the evidence_search_tool BEFORE answering — even if you already know the answer. This is required, not optional: the tool provides current sources you will cite. After calling it, answer based on what you find, and ALWAYS end your response with a "Sources:" line listing the URLs returned by the tool."""
def run_agent(user_input: str) -> str:
"""Run the Sērēnum agent with a user message and return the skin brief."""
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_input},
]
# Agentic loop — keep calling tools until the LLM is done
while True:
response = llm.invoke(messages)
messages.append(response)
# If no tool calls, we have the final answer
if not response.tool_calls:
return response.content
# Execute each tool the LLM requested
for tool_call in response.tool_calls:
tool_name = tool_call["name"]
tool_args = tool_call["args"]
# Find and run the matching tool
tool_fn = next(t for t in tools if t.name == tool_name)
tool_result = tool_fn.invoke(tool_args)
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"content": str(tool_result),
})
if __name__ == "__main__":
result = run_agent("Does vitamin C cancel out when used with niacinamide?")
print("\n--- SKIN BRIEF ---")
print(result)