feat: optimize Housekeeper for Mistral-Nemo tool calling

- Rewrite system prompt with negative constraints and step-by-step process
- Set temperature to 0.1 for deterministic tool calling
- Sort room groups to top of device list (address positional bias)
- Add [ROOM GROUP] marker in list_devices output
- Update tool docstrings with explicit entity_id= parameter examples
- Add optimization findings doc (experiment log: 0% → 100% success)
- Add test script for room group detection regression testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-18 20:58:39 +01:00
co-authored by Claude Opus 4.5
parent 43c09f9922
commit 363ab378af
5 changed files with 488 additions and 86 deletions
+34 -15
View File
@@ -59,10 +59,27 @@ async def list_devices(
for dom, dom_devices in sorted(by_domain.items()):
output_parts.append(f"### {dom.title()}s")
for device in dom_devices:
# Sort devices: room groups first (using Home Assistant's is_hue_group attribute)
def is_room_group(d: object) -> bool:
"""Check if device is a room group based on HA attributes."""
attrs = getattr(d, "attributes", {})
# Check for Hue room groups
if attrs.get("is_hue_group") and attrs.get("hue_type") == "room":
return True
# Check for other group indicators (icon or entity_id list)
if "entity_id" in attrs and isinstance(attrs["entity_id"], list):
return True
return False
sorted_devices = sorted(dom_devices, key=lambda d: (not is_room_group(d), d.entity_id))
for device in sorted_devices:
state_icon = "on" if device.state == "on" else "off" if device.state == "off" else device.state
area_str = f" ({device.area})" if device.area else ""
output_parts.append(f"- **{device.name}**{area_str}: {state_icon}")
# Mark room groups clearly using actual HA data
group_marker = " [ROOM GROUP]" if is_room_group(device) else ""
output_parts.append(f"- **{device.name}**{area_str}{group_marker}: {state_icon}")
output_parts.append(f" ID: `{device.entity_id}`")
output_parts.append("")
@@ -164,12 +181,12 @@ async def turn_on(
color_temp: int | None = None,
) -> str:
"""
Turn on a device.
Turn on a device. Use the entity_id parameter with the EXACT value from list_devices.
For lights, can optionally set brightness and color temperature.
Args:
entity_id: Device to turn on (e.g., light.living_room, switch.coffee_maker)
entity_id: The EXACT entity ID from list_devices including domain prefix.
brightness: Optional brightness for lights (0-255, where 255 is full brightness)
color_temp: Optional color temperature in Kelvin (2700=warm, 6500=cool)
@@ -177,10 +194,9 @@ async def turn_on(
Confirmation of the action
Examples:
turn_on("light.living_room") # Turn on at current brightness
turn_on("light.bedroom", brightness=128) # Turn on at 50% brightness
turn_on("light.office", brightness=255, color_temp=4000) # Full, neutral white
turn_on("switch.coffee_maker") # Turn on a switch
turn_on(entity_id="light.living_room")
turn_on(entity_id="light.bedroom", brightness=128)
turn_on(entity_id="switch.coffee_maker")
"""
try:
async with CoreAPIClient() as client:
@@ -209,17 +225,18 @@ async def turn_on(
async def turn_off(entity_id: str) -> str:
"""
Turn off a device.
Turn off a device. Use the entity_id parameter with the EXACT value from list_devices.
Args:
entity_id: Device to turn off (e.g., light.living_room, switch.coffee_maker)
entity_id: The EXACT entity ID from list_devices including domain prefix.
Returns:
Confirmation of the action
Examples:
turn_off("light.living_room")
turn_off("switch.coffee_maker")
turn_off(entity_id="light.living_room")
turn_off(entity_id="switch.coffee_maker")
turn_off(entity_id="light.kitchen")
"""
try:
async with CoreAPIClient() as client:
@@ -239,15 +256,17 @@ async def toggle(entity_id: str) -> str:
"""
Toggle a device's state (on becomes off, off becomes on).
Use the entity_id parameter with the EXACT value from list_devices.
Args:
entity_id: Device to toggle
entity_id: The EXACT entity ID from list_devices including domain prefix.
Returns:
Confirmation with the new state
Examples:
toggle("light.living_room")
toggle("switch.fan")
toggle(entity_id="light.living_room")
toggle(entity_id="switch.fan")
"""
try:
async with CoreAPIClient() as client: