Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions tools/sdk-ai-bots/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
__pycache__/
*.pyc
*.pyo
*.egg-info/
dist/
build/
logs/
.pytest_cache/
.env
**/graphrag_config/input/
**/graphrag_config/output/
**/graphrag_config/cache/
21 changes: 19 additions & 2 deletions tools/sdk-ai-bots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ A quality assurance system that continuously monitors and evaluates the performa

A standalone TypeScript application that processes documentation from various repositories and maintains the knowledge base. It clones repositories, processes markdown files and TypeSpec Spector test files, uploads processed content to Azure Blob Storage, and updates the Azure AI Search index. This service maintains change detection for efficient processing and serves as the primary knowledge management component for the system.

### 7. Knowledge Graph Sync (`azure-sdk-qa-bot-knowledge-graph-sync/`)

A Python application that extends the knowledge sync pipeline with a knowledge graph layer built using [Microsoft GraphRAG](https://github.com/microsoft/graphrag). It performs the same documentation sync as the TypeScript service (ported to Python), then additionally:

- Extracts entities (decorators, patterns, APIs, services, etc.) and relationships from documentation
- Detects communities of related concepts via hierarchical clustering
- Uploads the graph to Azure Cosmos DB for entity-aware retrieval at query time
- Supports **incremental indexing** — only re-processes documents that changed in the current sync run
Comment on lines +53 to +58

## Knowledge Sources

The bot provides intelligent responses by searching through comprehensive knowledge bases including:
Expand All @@ -63,7 +72,7 @@ The bot provides intelligent responses by searching through comprehensive knowle

- **Node.js**: Version 20+
- **Go**: Version 1.23+ (for backend service)
- **Python**: Version 3.10+ (for evaluation framework)
- **Python**: Version 3.11+ (for knowledge graph sync and evaluation framework)
- **Azure Subscription**: For deploying cloud resources
- **Teams Toolkit**: For Teams app development and deployment

Expand Down Expand Up @@ -101,6 +110,14 @@ npm install
npm start
```

#### Knowledge Graph Sync (Python)

```bash
cd azure-sdk-qa-bot-knowledge-graph-sync
pip install -e ".[dev]"
sync-knowledge-graph
```

#### Shared Library

```bash
Expand All @@ -125,7 +142,7 @@ To run evaluations, see: [azure-sdk-qa-bot-evaluation/README.md](./azure-sdk-qa-

### Documentation Sources

Add new documentation sources by updating the knowledge configuration. The Knowledge Sync Service uses `azure-sdk-qa-bot-knowledge-sync/config/knowledge-config.json`. See [Self-Serve Knowledge Sources Guide](docs/SELF_SERVE_ADD_KNOWLEDGE_SOURCES.md) for detailed instructions.
Add new documentation sources by updating the knowledge configuration. Both the Knowledge Sync Service and Knowledge Graph Sync use `config/knowledge-config.json` in their respective directories. See [Self-Serve Knowledge Sources Guide](docs/SELF_SERVE_ADD_KNOWLEDGE_SOURCES.md) for detailed instructions.

### Environment Variables

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import config.app_config as app_config
from config.app_config import get as cfg
from tools.knowledge_tools import KnowledgeTools
from tools.graph_knowledge_tools import GraphKnowledgeTools
from tools.web_tools import WebTools
from tools.ado_mcp_tools import create_ado_mcp_tool
from tools.github_mcp_tools import create_github_mcp_tool
Expand Down Expand Up @@ -90,14 +91,37 @@ async def main() -> None:

# Init Tools (synchronous / instant)
knowledge_tools = KnowledgeTools()
graph_knowledge_tools = GraphKnowledgeTools()
web_tools = WebTools()
pipeline_tools = PipelineTools()
web_search_tool = agent_client.get_web_search_tool(
search_context_size="medium",
)

# KNOWLEDGE_TOOL_MODE selects which knowledge-retrieval tool to expose
# to the agent. Accepted values (case-insensitive):
# "vector" (default) — Azure AI Search-backed `search_knowledge_base`.
# "graph" — GraphRAG DRIFT-search `search_knowledge_graph`.
knowledge_tool_mode = cfg("KNOWLEDGE_TOOL_MODE", "vector").strip().lower()
if knowledge_tool_mode == "graph":
knowledge_tool_choice = graph_knowledge_tools.search_knowledge_graph
else:
if knowledge_tool_mode != "vector":
logger.warning(
"KNOWLEDGE_TOOL_MODE=%r is not recognised "
"(expected 'vector' or 'graph'); falling back to 'vector'.",
knowledge_tool_mode,
)
knowledge_tool_mode = "vector"
knowledge_tool_choice = knowledge_tools.search_knowledge_base
logger.info(
"Knowledge tool registration: mode=%s, tool=%s",
knowledge_tool_mode,
getattr(knowledge_tool_choice, "__name__", repr(knowledge_tool_choice)),
)

tools = [
knowledge_tools.search_knowledge_base,
knowledge_tool_choice,
web_tools.web_fetch,
pipeline_tools.azsdk_analyze_pipeline,
web_search_tool,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# GraphRAG query-side configuration for the Azure SDK QA Bot Agent.
#
# This file mirrors the model + vector-store config of the
# `azure-sdk-qa-bot-knowledge-graph-sync` project so we can query the same
# AI Search indexes and run local/global search via the GraphRAG Python API.
#
# Only the sections needed for QUERY are present (no input / extract_graph /
# community_reports prompts, etc.). Indexing happens in the sync project.
#
# Environment variables resolved by graphrag_common.config._parse_env_variables:
# AOAI_CHAT_COMPLETIONS_ENDPOINT — e.g. https://<resource>.openai.azure.com
# AI_SEARCH_BASE_URL — e.g. https://<service>.search.windows.net
# AI_SEARCH_INDEX_TEXT_UNITS — text units index name
# AI_SEARCH_INDEX_ENTITIES — entity description index name
# AI_SEARCH_INDEX_COMMUNITIES — community full-content index name

completion_models:
default_completion_model:
model_provider: azure
model: gpt-5.4
auth_method: azure_managed_identity
api_base: ${AOAI_CHAT_COMPLETIONS_ENDPOINT}
api_version: "2024-12-01-preview"
cognitive_services_endpoint: https://cognitiveservices.azure.com/.default

embedding_models:
default_embedding_model:
model_provider: azure
model: text-embedding-3-small
auth_method: azure_managed_identity
api_base: ${AOAI_CHAT_COMPLETIONS_ENDPOINT}
api_version: "2024-12-01-preview"
cognitive_services_endpoint: https://cognitiveservices.azure.com/.default
rate_limit:
type: sliding_window
period_in_seconds: 60
requests_per_period: 60
tokens_per_period: 100000
retry:
type: exponential_backoff
max_retries: 5
base_delay: 2.0
max_delay: 30.0
jitter: true

vector_store:
type: azure_ai_search
url: ${AI_SEARCH_BASE_URL}
audience: "https://search.azure.com"
index_schema:
text_unit_text:
index_name: "azuresdkqabot-dev-search-index-text-units"
vector_size: 1536
entity_description:
index_name: "azuresdkqabot-dev-search-index-entities"
vector_size: 1536
community_full_content:
index_name: "azuresdkqabot-dev-search-index-communities"
vector_size: 1536
Comment thread
tadelesh marked this conversation as resolved.

# DRIFT search latency tuning.
# Field reference: https://microsoft.github.io/graphrag/config/yaml/#drift_search
drift_search:
n_depth: 1 # default 3 — drop iterative rounds
drift_k_followups: 5 # default 20 — fewer follow-up local searches
primer_folds: 2 # default 5 — cheaper primer ranking
primer_llm_max_tokens: 4000 # default 12000 — bound primer prompt/response
data_max_tokens: 4000 # default 12000 — bound data prompt size
concurrency: 16 # default 32 — leave headroom on AOAI TPM
local_search_top_k_mapped_entities: 10 # leave at default; expose for tuning
local_search_top_k_relationships: 10 # leave at default; expose for tuning
5 changes: 4 additions & 1 deletion tools/sdk-ai-bots/azure-sdk-qa-bot-agent/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ mcp>=1.0.0
# Backend server
fastapi
uvicorn[standard]
azure-monitor-opentelemetry
azure-monitor-opentelemetry
graphrag>=3.1.0
pandas>=2.0
pyarrow>=15.0
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import (
AgentProtocol,
ContainerConfiguration,
HostedAgentDefinition,
ProtocolVersionRecord,
)
Expand Down Expand Up @@ -274,14 +275,14 @@ def main() -> None:
agent = project.agents.create_version(
agent_name=image_name,
definition=HostedAgentDefinition(
container_protocol_versions=[
protocol_versions=[
ProtocolVersionRecord(
protocol=AgentProtocol.RESPONSES, version="1.0.0"
)
],
cpu="2",
memory="4Gi",
image=image,
container_configuration=ContainerConfiguration(image=image),
environment_variables=env_vars,
),
metadata={"enableVnextExperience": "true"},
Expand Down
69 changes: 68 additions & 1 deletion tools/sdk-ai-bots/azure-sdk-qa-bot-agent/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

load_dotenv(override=False)

from fastapi import FastAPI, Request
from fastapi import FastAPI, Header, HTTPException, Request
from models.chat import ChatRequest, ChatResponse
from models.conversation import ConversationMessage, SaveConversationMessageResponse
from models.feedback import FeedbackRequest, FeedbackResponse
Expand Down Expand Up @@ -233,5 +233,72 @@ async def _update_thread_memory(message: ConversationMessage) -> None:
)


# --------------------------------------------------------------------------- #
# GraphRAG admin endpoints
# --------------------------------------------------------------------------- #
# These let the knowledge-graph-sync project tell the bot "I've just
# published a fresh build — please reload from blob" without restarting
# the pod. See utils/knowledge_graph.py for the atomic swap mechanics.
#
# Auth: a shared secret header (``X-Admin-Token``) is checked against the
# ``GRAPHRAG_ADMIN_TOKEN`` value loaded from App Configuration.
# The sync job reads the same secret and includes it in the POST.


def _verify_admin_token(token: str | None) -> None:
"""Raise 401/503 if the supplied admin token doesn't match the
configured ``GRAPHRAG_ADMIN_TOKEN``.

Treats an empty configured secret as "endpoint disabled" (returns
503) — never authorise unauthenticated reloads even by accident.
"""
expected = app_config.get("GRAPHRAG_ADMIN_TOKEN", "")
if not expected:
raise HTTPException(
status_code=503,
detail="GraphRAG admin endpoint disabled (GRAPHRAG_ADMIN_TOKEN not configured)",
)
if not token or token != expected:
raise HTTPException(status_code=401, detail="invalid admin token")


@app.post("/admin/graphrag/reload")
async def admin_graphrag_reload(
x_admin_token: str | None = Header(default=None, alias="X-Admin-Token"),
):
"""Atomically reload the GraphRAG parquets from the configured blob source.

In-flight DRIFT queries keep their captured DataFrame snapshot and
finish against the old data; subsequent queries see the new data.
On failure the prior build remains active.
"""
_verify_admin_token(x_admin_token)
from utils.knowledge_graph import get_knowledge_graph_service

service = get_knowledge_graph_service()
if not service.enabled:
raise HTTPException(
status_code=409, detail="GraphRAG service is disabled (no source configured)"
)
try:
status = await service.reload()
except Exception as exc:
logger.exception("GraphRAG reload failed")
raise HTTPException(status_code=500, detail=f"reload failed: {exc}") from exc
logger.info("GraphRAG reload succeeded: %s", status.get("version"))
return status


@app.get("/admin/graphrag/status")
async def admin_graphrag_status(
x_admin_token: str | None = Header(default=None, alias="X-Admin-Token"),
):
"""Return the currently-loaded GraphRAG build metadata."""
_verify_admin_token(x_admin_token)
from utils.knowledge_graph import get_knowledge_graph_service

return get_knowledge_graph_service().get_status()


if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8089)
Loading
Loading