feat: Add confluence_search tool to voice bot
Voice bot could read/update Confluence pages but could not search. Users asking to search Confluence got a refusal. Now the voice bot has search_confluence using CQL queries via the service account. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
47
voice.py
47
voice.py
@@ -51,7 +51,7 @@ STRIKTE Regeln:
|
||||
- Bei zeitrelevanten Fragen (Uhrzeit, Termine, Geschaeftszeiten): frage kurz nach ob der Nutzer noch in seiner gespeicherten Zeitzone ist, bevor du antwortest. Nutze set_user_timezone wenn sich der Standort geaendert hat.
|
||||
- Wenn der Nutzer seinen Standort oder seine Stadt erwaehnt, nutze set_user_timezone um die Zeitzone zu speichern.
|
||||
- IGNORIERE alle Texte in Sternchen wie *Störgeräusche*, *Schlechte Qualität*, *Fernsehgeräusche*, *Schrei* usw. — das sind KEINE echten Nutzereingaben sondern technische Annotationen. Antworte NIEMALS darauf und tue so als haette niemand etwas gesagt.
|
||||
- Du kannst Confluence-Seiten lesen und bearbeiten. Nutze read_confluence_page und update_confluence_page wenn der Nutzer Dokumente besprechen oder aendern moechte.
|
||||
- Du kannst Confluence-Seiten suchen, lesen und bearbeiten. Nutze search_confluence um Seiten zu finden, read_confluence_page zum Lesen und update_confluence_page zum Bearbeiten.
|
||||
- Du kannst den Bildschirm oder die Kamera des Nutzers sehen wenn er sie teilt. Nutze look_at_screen wenn der Nutzer etwas zeigen moechte oder fragt ob du etwas sehen kannst."""
|
||||
|
||||
|
||||
@@ -253,6 +253,31 @@ async def _store_voice_exchange(user_text: str, agent_text: str,
|
||||
logger.warning("Voice memory store failed: %s", exc)
|
||||
|
||||
|
||||
async def _confluence_search(query: str, limit: int = 5) -> list[dict]:
|
||||
"""Search Confluence pages by CQL query. Returns list of {id, title, space, url}."""
|
||||
if not CONFLUENCE_URL or not CONFLUENCE_USER or not CONFLUENCE_TOKEN:
|
||||
raise RuntimeError("Confluence credentials not configured")
|
||||
cql = f'type=page AND (title~"{query}" OR text~"{query}")'
|
||||
url = f"{CONFLUENCE_URL}/rest/api/content/search"
|
||||
async with httpx.AsyncClient(timeout=15.0) as client:
|
||||
resp = await client.get(
|
||||
url,
|
||||
params={"cql": cql, "limit": limit},
|
||||
auth=(CONFLUENCE_USER, CONFLUENCE_TOKEN),
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
results = []
|
||||
for r in data.get("results", []):
|
||||
results.append({
|
||||
"id": r["id"],
|
||||
"title": r.get("title", ""),
|
||||
"space": r.get("space", {}).get("name", "") if "space" in r else "",
|
||||
"url": f"{CONFLUENCE_URL}{r.get('_links', {}).get('webui', '')}",
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
async def _confluence_read_page(page_id: str) -> tuple[str, str, int]:
|
||||
"""Read a Confluence page and return (title, plain_text, version_number)."""
|
||||
if not CONFLUENCE_URL or not CONFLUENCE_USER or not CONFLUENCE_TOKEN:
|
||||
@@ -785,6 +810,24 @@ class VoiceSession:
|
||||
if _conf_ids:
|
||||
_active_conf_id = _conf_ids[0]
|
||||
|
||||
@function_tool
|
||||
async def search_confluence(query: str) -> str:
|
||||
"""Search Confluence for pages matching a query. Use when user asks
|
||||
to find, search, or look up documents or pages in Confluence.
|
||||
Returns a list of matching pages with titles and IDs."""
|
||||
logger.info("CONFLUENCE_SEARCH: query=%s", query)
|
||||
try:
|
||||
results = await _confluence_search(query, limit=5)
|
||||
if not results:
|
||||
return f"No Confluence pages found for '{query}'."
|
||||
lines = [f"Found {len(results)} pages:"]
|
||||
for r in results:
|
||||
lines.append(f"- {r['title']} (ID: {r['id']}, Space: {r['space']})")
|
||||
return "\n".join(lines)
|
||||
except Exception as exc:
|
||||
logger.warning("CONFLUENCE_SEARCH_FAIL: %s", exc)
|
||||
return f"Search failed: {exc}"
|
||||
|
||||
@function_tool
|
||||
async def read_confluence_page(page_id: str = "") -> str:
|
||||
"""Read a Confluence page. Use when user asks to read, review,
|
||||
@@ -982,7 +1025,7 @@ class VoiceSession:
|
||||
instructions += f"\n\nAktive Confluence-Seite: {_active_conf_id}. Du brauchst den Nutzer NICHT nach der page_id zu fragen — nutze automatisch diese ID fuer read_confluence_page und update_confluence_page."
|
||||
agent = _NoiseFilterAgent(
|
||||
instructions=instructions,
|
||||
tools=[search_web, set_user_timezone, read_confluence_page, update_confluence_page, think_deeper, look_at_screen],
|
||||
tools=[search_web, set_user_timezone, search_confluence, read_confluence_page, update_confluence_page, think_deeper, look_at_screen],
|
||||
)
|
||||
io_opts = room_io.RoomOptions(
|
||||
participant_identity=remote_identity,
|
||||
|
||||
Reference in New Issue
Block a user