feat: add message-search fallback in dialectic tools for short sessions (#362)
This commit is contained in:
parent
c9dee51fb9
commit
44577e048c
|
|
@ -65,7 +65,7 @@ Honcho has four storage primitives that work together:
|
|||
- **Workspaces** - Top-level containers that isolate different applications or environments
|
||||
- **Peers** - Any entity that persists but changes over time (users, agents, objects, and more)
|
||||
- **Sessions** - Interaction threads between peers with temporal boundaries
|
||||
- **Messages** - Units of data that trigger reasoning (conversations, events, activity, documents, and more)
|
||||
- **Messages** - Units of data that trigger reasoning (conversations, events, activity, documents, and more)
|
||||
|
||||
When you write messages to Honcho, they're stored and processed in the background. Custom reasoning models perform formal logical [_reasoning_](/v3/documentation/core-concepts/reasoning) to generate conclusions about each peer. These conclusions are stored as [_representations_](/v3/documentation/core-concepts/representation) that you can query to provide rich context for your agents.
|
||||
|
||||
|
|
@ -100,4 +100,4 @@ Welcome to Honcho. We're excited to have you at the frontier of AI with us 🫡.
|
|||
<Card title="Reasoning" icon="gears" href="/v3/documentation/core-concepts/reasoning">
|
||||
Learn how Honcho reasons about data to build memory
|
||||
</Card>
|
||||
</CardGroup>
|
||||
</CardGroup>
|
||||
|
|
|
|||
|
|
@ -1129,7 +1129,34 @@ async def _handle_search_memory(ctx: ToolContext, tool_input: dict[str, Any]) ->
|
|||
mem = Representation.from_documents(documents)
|
||||
total_count = mem.len()
|
||||
if total_count == 0:
|
||||
return f"No observations found for query '{tool_input['query']}'"
|
||||
# fallback behavior: if the memory is *empty*, that means we're quite
|
||||
# early in a workspace/peer/session -- in order to give good answers in
|
||||
# this stage, and be efficient with tool calls, and make sure the model
|
||||
# doesn't short-circuit and think there's nothing here, we
|
||||
# automatically search the message history for relevant information.
|
||||
query = tool_input["query"]
|
||||
if ctx.agent_type == "dialectic":
|
||||
limit = min(tool_input.get("top_k", 20), 20)
|
||||
snippets = await crud.search_messages(
|
||||
ctx.db,
|
||||
workspace_name=ctx.workspace_name,
|
||||
session_name=ctx.session_name,
|
||||
query=query,
|
||||
limit=limit,
|
||||
context_window=0,
|
||||
)
|
||||
if snippets:
|
||||
message_output = _format_message_snippets(
|
||||
snippets, f"for query '{query}'"
|
||||
)
|
||||
return (
|
||||
f"No observations yet. Message search results:\n\n{message_output}"
|
||||
)
|
||||
return (
|
||||
f"No observations found for query '{query}', and no messages found in "
|
||||
"history. Try a different phrasing or use grep_messages for exact text."
|
||||
)
|
||||
return f"No observations found for query '{query}'"
|
||||
mem_str = mem.str_with_ids() if ctx.include_observation_ids else str(mem)
|
||||
return f"Found {total_count} observations for query '{tool_input['query']}':\n\n{mem_str}"
|
||||
|
||||
|
|
@ -1696,12 +1723,14 @@ async def create_tool_executor(
|
|||
Returns:
|
||||
String result describing what was done
|
||||
"""
|
||||
logger.info(f"[tool call] {tool_name}")
|
||||
logger.info(f"[tool call] {tool_name} {tool_input}")
|
||||
|
||||
try:
|
||||
handler = _TOOL_HANDLERS.get(tool_name)
|
||||
if handler:
|
||||
return await handler(ctx, tool_input)
|
||||
result = await handler(ctx, tool_input)
|
||||
logger.info(f"[tool result] {tool_name} {result}")
|
||||
return result
|
||||
return f"Unknown tool: {tool_name}"
|
||||
|
||||
except ValueError as e:
|
||||
|
|
|
|||
|
|
@ -706,6 +706,7 @@ async def _execute_tool_loop(
|
|||
total_output_tokens = 0
|
||||
total_cache_creation_tokens = 0
|
||||
total_cache_read_tokens = 0
|
||||
empty_response_retries = 0
|
||||
# Track effective tool_choice - switches from "required" to "auto" after first iteration
|
||||
effective_tool_choice = tool_choice
|
||||
|
||||
|
|
@ -778,6 +779,25 @@ async def _execute_tool_loop(
|
|||
# No tool calls, return final response
|
||||
logger.debug("No tool calls in response, finishing")
|
||||
|
||||
if (
|
||||
isinstance(response.content, str)
|
||||
and not response.content.strip()
|
||||
and empty_response_retries < 1
|
||||
and iteration < max_tool_iterations - 1
|
||||
):
|
||||
empty_response_retries += 1
|
||||
conversation_messages.append(
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
"Your last response was empty. Provide a concise answer "
|
||||
"to the original query using the available context."
|
||||
),
|
||||
}
|
||||
)
|
||||
iteration += 1
|
||||
continue
|
||||
|
||||
if stream_final:
|
||||
# Stream the final response with metadata from tool execution
|
||||
stream = _stream_final_response(
|
||||
|
|
|
|||
Loading…
Reference in New Issue