Skip to content

Ask a human expert

When the knowledge base doesn’t have an answer, ask a person. Halyard routes the question to the right expert in Slack and returns their reply as new evidence for the agent. The agent then searches for relevant organizational knowledge and writes one normal, cited answer rather than forwarding the human reply as the final response. Ask only after a real search miss — not as a first move.

This page is a rule you write for your agent. For how routing picks a person, see Experts & routing.

Ask a human when:

  • A focused search_knowledge (with the retry-with-fewer-filters habit) genuinely returned nothing.
  • The decision is judgment, preference, or undocumented context a human holds.

Don’t ask when:

  • The answer is in the codebase, the docs, or a knowledge entry you haven’t searched for yet.
  • You’re asking to confirm something you can verify yourself.
  1. See who’s available. list_team shows experts, their roles, and skills so you can target the question.

    list_team(role?, skill?, available_only?)
    {
    "members": [
    {
    "name": "David Kim",
    "roles": ["engineer"],
    "skills": ["React", "TypeScript", "Accessibility"],
    "availability": "ONLINE"
    },
    {
    "name": "James Okonkwo",
    "roles": ["architect", "engineer"],
    "skills": ["System Design", "Cloud Architecture", "Security"],
    "availability": "ONLINE"
    }
    ]
    }

    Roles are stored lowercase (engineer, pm, architect); skills are Title-Case domains (React, System Design). Filter by role to ask a category, skill to ask a specialist.

  2. Ask. ask_expert sends the question. Target it with role or skill so it reaches the right person; let Halyard pick the individual.

    ask_expert(prompt, role?, skill?, force_human?, options?, conversation_id?)
    ask_expert(
    prompt: "We're choosing between per-user OAuth and domain-wide sync for calendar import. Which did we land on, and why?",
    skill: "System Design"
    )
  3. Handle the return. ask_expert returns a discriminated union — branch on it:

    • Resolved from the knowledge base. Halyard checked the KB first and the question had an answer. You get the answer back immediately and no human was interrupted. Use it; you’re done. (Pass force_human: true only when you specifically need a person despite a KB match.)
    • Pending a human. No KB answer, so it routed to Slack. You get a conversation_id and a pending status. Move to step 4.
  4. Get the reply. Use check_response with the conversation_id. Pass wait: true to block for the reply instead of polling.

    check_response(conversation_id, wait?)
    check_response(conversation_id: "…", wait: true)
  5. Synthesize the answer. Treat the returned human reply as evidence, not ready-to-forward copy. Search the knowledge base for relevant supporting or conflicting information, then answer in the normal assistant voice. Attribute the human reply by expert name as one source, cite relevant knowledge entries, and surface disagreements rather than hiding them. Slack-originated asks perform this resumed agent turn automatically; MCP hosts resume when the tool returns and must follow the same rule.

  6. Follow up if needed. Send more context or a clarifying question into the same thread with reply_to_expert.

    reply_to_expert(conversation_id, message)

    The wait begins after the follow-up message, so an older expert reply cannot satisfy it. If a new reply arrives, treat it as evidence and repeat the search-and-synthesize step above.

  7. Capture the answer. When the reply changes what you do, file it with summarize_conversation so it becomes searchable knowledge. This step is not optional — see Capture what you learn.

    summarize_conversation(conversation_id, question, answer, source_url?, source_provider?)
Ask a human only after a genuine search_knowledge miss.
1. list_team(role|skill) to see who can answer; target the question.
2. ask_expert(prompt, role|skill). Branch on the return:
- Resolved from the knowledge base -> use the answer, you're done.
- Pending a human -> you get a conversation_id; continue.
3. check_response(conversation_id, wait: true). The wait returns in <=55s; if still
pending, do other work and poll again later. Never tight-loop.
4. On a human reply, search_knowledge for relevant context. Synthesize one normal
answer that attributes the expert response, cites relevant entries, and surfaces conflicts.
5. reply_to_expert(conversation_id, message) to add context or clarify.
6. When the answer changes what you do, summarize_conversation(conversation_id,
question, answer) so the next agent finds it. Always capture.