Why you should let the LLM call Hive, not call it yourself.
Most teams reach for the imperative version first, because it looks simpler:
# Imperative: your code decides, always, on every turn.
context = hive.search(user_question) # always fires, relevant or not
answer = llm(prompt=user_question, context=context)
return answer
The tool-calling version hands that judgement to the model:
# Tool-based: the model decides whether to search, what to search for,
# and whether what came back was worth a follow-up query.
tools = [
{
"name": "search_team_knowledge",
"description": (
"Search the team's knowledge base for prior decisions, standards "
"and meeting outcomes. Use whenever the answer might depend on "
"something the team already decided. Prefer this over guessing."
),
"input_schema": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "A natural-language question, not keywords.",
}
},
"required": ["question"],
},
},
]
# ... on a tool_use block, call aiqbee_search and return the result.
aiqbee_search is semantic. Handed the user's raw words it does
reasonably; handed a well-formed question it does considerably better. The
model has the conversation in front of it — it can resolve "did we decide
against that?" into "what was the decision on rate limiting the public API?"
Your code cannot, because it does not know what "that" was.
"Thanks, that worked" needs no retrieval. An unconditional search burns latency and pushes irrelevant context into the prompt, which measurably degrades answers. Letting the model abstain is a feature, not laziness.
Real questions need a first search, a look at what came back, and a second, narrower search. That loop is native to tool calling and awkward to express in fixed control flow.
This is the part most integrations skip, and it is the whole point. Give the model a write tool as well, and knowledge accumulates as a side effect of normal work instead of requiring someone to remember to file it.
{
"name": "record_team_knowledge",
"description": (
"Record a durable decision, standard or outcome so it is available "
"to the team later. Use when something is settled that a colleague "
"would need in six months. Do NOT use for chit-chat, transient "
"status, or anything you were not told."
),
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"content": {"type": "string",
"description": "Include who, what and WHY. Self-contained."},
"type_id": {"type": "string",
"description": "A real id from aiqbee_get_neuron_types."},
},
"required": ["title", "content", "type_id"],
},
}
The description is your actual integration surface — it is the prompt that decides whether the tool gets used correctly. A few things that consistently matter:
Handing the model judgement over when to call is not the same as handing it unrestricted authority. Keep these on your side:
There is a legitimate case for calling directly: deterministic, non-conversational work. Bulk-importing documents, a nightly sync, a provisioning script, a migration. No judgement is required, so there is nothing to delegate — use the REST API or call the MCP tools directly and keep it simple.