Planning Phase 17 - huqianghui/AI-Coach-vibe-coding GitHub Wiki
Auto-generated from
.planning/phases/17-agent-knowledge-base-foundry-iq
Last synced: 2026-04-28
HCP Agent 知识库管理,对齐 Azure AI Foundry Knowledge 配置体验。Admin 可在 HCP 编辑器中连接 Azure AI Search 资源的 Knowledge Base (Foundry IQ),将知识库绑定到 HCP Agent。Agent 在对话中自动使用 RAG 检索知识。
Decision: 使用 MCPTool(MCP 协议)连接 AI Search Knowledge Base,对齐 AI Foundry portal 的 "Connect to Foundry IQ" 流程。
Why: 我们的 HCP Agent 就是 Foundry Agent(通过 agent_sync_service 同步),应保持与 portal 一致的行为。MCPTool 是 Foundry 内部实现 "Connect to Foundry IQ" 的方式。
Rejected alternatives:
-
AzureAISearchTool(GA 但不是 portal 默认方式) -
FileSearchTool(自动向量化但灵活性低)
SDK pattern:
from azure.ai.projects.models import MCPTool
mcp_tool = MCPTool(
server_label="knowledge-base-{kb_name}",
server_url=f"{search_endpoint}/knowledgebases/{kb_name}/mcp?api-version=2025-11-01-preview",
require_approval="never",
allowed_tools=["knowledge_base_retrieve"],
)Decision: 每个 HCP Agent 可连接多个 Knowledge Base,每个作为单独的 MCPTool 加入 Agent 的 tools 列表。
Why: HCP 可能需要产品知识库 + 医学文献知识库等多个来源。
Impact: Agent sync 时需合并已有 tools(如 Voice Live metadata)和新增的 KB tools。
Decision: 平台只做"列出 + 选择 + 连接"。Knowledge Base 的创建和文档上传在 AI Foundry portal 中完成。
Why: 这属于 Agent 能力配置范畴。KB 的创建和维护是其他模块(知识管理模块)的职责,不在 Agent 配置的范围内。Agent 配置只负责"选择并连接已有 KB"。
Impact: Admin 需要先在 AI Foundry portal 或知识管理模块中创建好 KB,然后在 HCP Agent 配置中选择连接。
Decision: 平台通过 client.connections.list() 列出 AI Search Connections,通过 AI Search API 列出 Knowledge Bases,admin 选择后绑定到 Agent。不做创建/删除 KB。
Why: 职责分离 — Agent 配置模块定义"Agent 用哪些知识库",知识库本身的创建/维护/文档上传是知识管理模块的职责。Connections 是 project 级资源,SDK 只支持读取。
Decision: 在 HCP 编辑器中恢复 Knowledge tab(Phase 14 添加后 Phase 15 移除的 tab),但这次填充真实功能而非占位符。
Layout: 参照 AI Foundry portal Knowledge 面板:
- "Add" 按钮下拉:Connect to Foundry IQ
- 已连接 KB 列表(名称、connection、状态)
- 解绑按钮
Decision: 扩展 agent_sync_service.sync_agent_for_profile() 在 PromptAgentDefinition 中加入 tools 参数,传入 KB 的 MCPTool 列表。
Current state: create_version() 只传 model + instructions + metadata。需要新增 tools 参数。
Merge strategy: Agent 可能同时有 Voice Live metadata + Knowledge MCPTools,tools 列表需要合并来自不同来源的 tools。
- Phase 11: Agent sync service (
agent_sync_service.py) 完整的 CRUD + version 管理 - Phase 14: Knowledge/Tools tab 曾存在于 HCP editor,后在 Phase 15 移除
- Phase 16: Agent sync 包含 Voice Live metadata,需要与 KB tools 共存
- Phase 05: Training Material 管理已存在但不在本 phase scope(MVP 不做自动索引)
| Asset | Location | Reuse |
|---|---|---|
| AIProjectClient factory | agent_sync_service._get_project_client() |
复用 client.connections API |
| Agent sync flow | sync_agent_for_profile() |
扩展 tools 参数 |
| i18n KB keys |
admin.json (hcp.tabKnowledge etc.) |
已存在,扩展 |
| HCP Editor VALID_TABS | hcp-profile-editor.tsx |
添加 "knowledge" |
| HCP Profile model | hcp_profile.py |
新增 KB 关联字段 |
-
可能不需要
azure-search-documents— 如果client.connections+client.indexes已足够列出 KB -
需要验证:
AIProjectClient是否有列出 Knowledge Bases 的 API(不是 indexes,是 KB) - 如果没有,可能需要 REST API 调用 AI Search 的
/knowledgebasesendpoint
In scope:
- HCP Editor Knowledge tab UI(列出/连接/解绑 KB)
- Backend API:list connections, list KBs, bind/unbind KB to HCP
- Agent sync 扩展(tools 参数含 MCPTool)
- DB migration(HCP profile KB 关联表)
- 测试 + i18n
Out of scope(其他模块职责):
- KB 创建/维护/文档上传 — 知识管理模块职责(可在 AI Foundry portal 或后续知识管理模块完成)
- Phase 05 材料自动索引到 AI Search — 知识管理模块职责
- Azure Blob Storage 集成 — 知识管理模块职责
- Tools tab(Function Call 配置)— 后续 Agent 工具配置 phase
-
AIProjectClient.indexes.list()是否返回 Foundry IQ Knowledge Bases?还是需要直接调用 AI Search REST API/knowledgebases? - MCPTool 的
server_url构建需要 AI Search endpoint — 这个从 connection.target 获取还是另外配置? - 现有
create_version()调用加入tools参数后是否需要同时保留metadata(Voice Live config)? - HCP Profile 和 KB 的关联关系:多对多(一个 KB 可被多个 HCP 使用)还是一对多?
| # | Plan File | Status |
|---|---|---|
| 17-01 | 17-01-PLAN.md | Complete |
| 17-02 | 17-02-PLAN.md | Complete |
| 17-03 | 17-03-PLAN.md | Complete |
Click to expand research notes
Research date: 2026-04-10 SDK version:
azure-ai-projects==2.0.1All outputs verified against the installed SDK inbackend/.venv
AIProjectClient attributes: ['close', 'get_openai_client', 'send_request']
The client does NOT expose .connections or .indexes as direct properties.
Access pattern is through operations — need to verify runtime access (e.g., client.connections.list()
may still work via dynamic attribute resolution at instance level).
from azure.ai.projects.operations import ConnectionsOperations
# Methods: ['get', 'get_default', 'list']Signatures:
ConnectionsOperations.list(
self,
*,
connection_type: Union[str, ConnectionType, None] = None,
default_connection: Optional[bool] = None,
**kwargs
) -> ItemPaged['Connection']
ConnectionsOperations.get(
self,
name: str,
*,
include_credentials: Optional[bool] = False,
**kwargs
) -> ConnectionConnection model attributes:
| Attribute | Description |
|---|---|
id |
Connection resource ID |
name |
Connection name |
type |
ConnectionType enum value |
target |
Endpoint URL for the connected resource |
is_default |
Whether it is the default connection for its type |
credentials |
Credential info (key, token, etc.) |
metadata |
Dict of metadata |
Usage to list AI Search connections:
connections = client.connections.list(
connection_type=ConnectionType.AZURE_AI_SEARCH
)
for conn in connections:
print(conn.name, conn.target) # name and search endpointConnectionType values:
AZURE_OPEN_AI = 'AzureOpenAI'
AZURE_BLOB_STORAGE = 'AzureBlob'
AZURE_STORAGE_ACCOUNT = 'AzureStorageAccount'
AZURE_AI_SEARCH = 'CognitiveSearch' # <-- THIS ONE
COSMOS_DB = 'CosmosDB'
API_KEY = 'ApiKey'
APPLICATION_CONFIGURATION = 'AppConfig'
APPLICATION_INSIGHTS = 'AppInsights'
CUSTOM = 'CustomKeys'
REMOTE_TOOL = 'RemoteTool_Preview'from azure.ai.projects.operations import IndexesOperations
# Methods: ['create_or_update', 'delete', 'get', 'list', 'list_versions']Signatures:
IndexesOperations.list(self, **kwargs) -> ItemPaged['Index']
IndexesOperations.get(self, name: str, version: str, **kwargs) -> Index
IndexesOperations.list_versions(self, name: str, **kwargs) -> ItemPaged['Index']Index model attributes:
| Attribute | Description |
|---|---|
id |
Index resource ID |
name |
Index name |
version |
Version string |
type |
Index type |
description |
Description |
tags |
Tags dict |
Usage to list knowledge base indexes:
indexes = client.indexes.list()
for idx in indexes:
print(idx.name, idx.version, idx.type)| Class | Purpose |
|---|---|
AzureAISearchTool |
Attach Azure AI Search index as grounding tool |
MCPTool |
Attach an MCP server (Foundry IQ, external tools) |
FileSearchTool |
File-based search |
CodeInterpreterTool |
Code interpreter |
FunctionTool |
Custom function calling |
BingGroundingTool |
Bing web grounding |
OpenApiTool |
OpenAPI spec-based tool |
WebSearchTool |
Web search |
from azure.ai.projects.models import (
AzureAISearchTool,
AzureAISearchToolResource,
AISearchIndexResource,
AzureAISearchQueryType,
)
tool = AzureAISearchTool(
azure_ai_search=AzureAISearchToolResource(
indexes=[
AISearchIndexResource(
project_connection_id="<connection-name-or-id>",
index_name="<index-name>",
query_type=AzureAISearchQueryType.VECTOR_SEMANTIC_HYBRID,
top_k=5,
filter=None,
index_asset_id=None,
)
]
)
)AISearchIndexResource attributes:
| Attribute | Type | Description |
|---|---|---|
project_connection_id |
str | Connection name or ID for the AI Search resource |
index_name |
str | Name of the search index |
query_type |
AzureAISearchQueryType | Query strategy |
top_k |
int | Number of documents to retrieve |
filter |
str | OData filter expression |
index_asset_id |
str | Optional asset ID |
AzureAISearchQueryType enum:
SIMPLE = 'simple'
SEMANTIC = 'semantic'
VECTOR = 'vector'
VECTOR_SIMPLE_HYBRID = 'vector_simple_hybrid'
VECTOR_SEMANTIC_HYBRID = 'vector_semantic_hybrid'
Constraint: Maximum 1 index resource per agent (per SDK docs).
from azure.ai.projects.models import MCPTool
tool = MCPTool(
server_label="foundry-iq", # Required: label for tool calls
server_url="https://<endpoint>/mcp", # One of server_url or connector_id
connector_id=None, # Pre-built connector (Dropbox, Gmail, etc.)
authorization="Bearer <token>", # OAuth token if needed
server_description="Foundry IQ knowledge base",
headers={"x-custom": "value"}, # Optional headers
allowed_tools=["search", "retrieve"], # Filter which tools to expose
require_approval="never", # "always" | "never" | MCPToolRequireApproval
project_connection_id="<connection-id>", # Connection storing auth details
)MCPTool attributes:
| Attribute | Required | Description |
|---|---|---|
server_label |
Yes | Label identifying the MCP server in tool calls |
server_url |
One of url/connector | MCP server URL |
connector_id |
One of url/connector | Pre-built service connector ID |
authorization |
No | OAuth access token |
server_description |
No | Description for context |
headers |
No | Custom HTTP headers |
allowed_tools |
No | Filter to specific tools (list or MCPToolFilter) |
require_approval |
No | Approval mode for tool use |
project_connection_id |
No | Connection ID storing auth details |
from azure.ai.projects.models import PromptAgentDefinitionConfirmed attributes:
| Attribute | Description |
|---|---|
instructions |
Agent system prompt |
model |
Model deployment name |
tools |
List of Tool objects (AzureAISearchTool, MCPTool, etc.) |
tool_choice |
Tool selection strategy |
temperature |
Sampling temperature |
top_p |
Top-p sampling |
reasoning |
Reasoning configuration |
structured_inputs |
Structured input config |
rai_config |
Responsible AI config |
text |
Text output config |
kind |
Definition kind |
Usage with tools:
definition = PromptAgentDefinition(
model="gpt-4o",
instructions="You are an HCP...",
tools=[
AzureAISearchTool(
azure_ai_search=AzureAISearchToolResource(
indexes=[
AISearchIndexResource(
project_connection_id="my-search-connection",
index_name="product-knowledge-base",
query_type=AzureAISearchQueryType.VECTOR_SEMANTIC_HYBRID,
top_k=5,
)
]
)
)
],
)File: backend/app/services/agent_sync_service.py
async def create_agent(
db: AsyncSession,
name: str,
instructions: str,
model: str | None = None,
*,
metadata: dict[str, str] | None = None,
endpoint_override: str = "",
key_override: str = "",
) -> dict:
definition = PromptAgentDefinition(model=model, instructions=instructions)
result = await asyncio.to_thread(
client.agents.create_version,
agent_name=agent_name,
definition=definition,
description=f"HCP Agent: {name}",
metadata=metadata,
)Same pattern — creates PromptAgentDefinition(model=model, instructions=instructions) with NO tools parameter.
Both create_agent() and update_agent() must accept an optional tools parameter:
async def create_agent(
db, name, instructions, model=None, *,
metadata=None,
tools: list | None = None, # NEW
endpoint_override="", key_override="",
) -> dict:
definition = PromptAgentDefinition(
model=model,
instructions=instructions,
tools=tools or [], # NEW — pass tools list
)File: backend/app/models/hcp_profile.py
No knowledge base fields exist. Current fields relevant to agent sync:
| Field | Type | Description |
|---|---|---|
agent_id |
String(100) | Foundry Agent name/ID |
agent_version |
String(50) | Current agent version |
agent_sync_status |
String(20) | none/pending/synced/failed |
agent_sync_error |
Text | Error message |
Option A: New columns on hcp_profiles (Recommended for MVP)
Add to hcp_profiles table:
# Knowledge base configuration
kb_connection_id: Mapped[str] = mapped_column(String(255), default="")
kb_index_name: Mapped[str] = mapped_column(String(255), default="")
kb_query_type: Mapped[str] = mapped_column(String(50), default="vector_semantic_hybrid")
kb_top_k: Mapped[int] = mapped_column(default=5)
kb_enabled: Mapped[bool] = mapped_column(Boolean, default=False)Option B: Separate hcp_knowledge_configs table (Better for multi-index future)
class HcpKnowledgeConfig(Base, TimestampMixin):
__tablename__ = "hcp_knowledge_configs"
hcp_profile_id: FK -> hcp_profiles.id (unique, 1:1)
connection_id: String(255) # AI Search connection name
index_name: String(255) # Index name
query_type: String(50) # AzureAISearchQueryType value
top_k: int # default 5
filter_expression: Text # OData filter (optional)
is_enabled: bool # default FalseRecommendation: Option A (columns on hcp_profiles) for simplicity. The SDK limits agents to 1 index anyway. A separate table adds join complexity without current benefit.
Latest migration: o18a_rename_vl_model_instruction.py
New migration needed: p19a_add_kb_fields_to_hcp_profiles.py
GET /api/v1/knowledge-base/connections
-> List AI Search connections from Foundry
-> Returns: [{ name, target, is_default }]
GET /api/v1/knowledge-base/indexes
-> List indexes from Foundry project
-> Returns: [{ name, version, type, description }]
GET /api/v1/knowledge-base/indexes/{connection_name}
-> List indexes within a specific AI Search connection
-> (May require direct Azure Search REST call, not SDK)
PUT /api/v1/hcp-profiles/{id}/knowledge-base
-> Configure KB for an HCP profile
-> Body: { connection_id, index_name, query_type, top_k, enabled }
-> Triggers agent re-sync with tools parameter
GET /api/v1/hcp-profiles/{id}/knowledge-base
-> Get current KB config for an HCP profile
async def list_search_connections(db: AsyncSession) -> list[dict]:
"""List Azure AI Search connections from Foundry project."""
endpoint, key = await get_project_endpoint(db)
client = _get_project_client(endpoint, key)
connections = await asyncio.to_thread(
client.connections.list,
connection_type=ConnectionType.AZURE_AI_SEARCH,
)
return [{"name": c.name, "target": c.target, "is_default": c.is_default} for c in connections]
async def list_indexes(db: AsyncSession) -> list[dict]:
"""List all indexes in the Foundry project."""
endpoint, key = await get_project_endpoint(db)
client = _get_project_client(endpoint, key)
indexes = await asyncio.to_thread(client.indexes.list)
return [{"name": i.name, "version": i.version, "type": i.type} for i in indexes]
def build_search_tool(connection_id: str, index_name: str, query_type: str, top_k: int):
"""Build AzureAISearchTool from config."""
return AzureAISearchTool(
azure_ai_search=AzureAISearchToolResource(
indexes=[AISearchIndexResource(
project_connection_id=connection_id,
index_name=index_name,
query_type=query_type,
top_k=top_k,
)]
)
)Location: frontend/src/components/coach/HcpKnowledgeTab.tsx
Structure:
KnowledgeTab
+-- EnableToggle (kb_enabled on/off)
+-- ConnectionSelector (dropdown, fetched from GET /knowledge-base/connections)
+-- IndexSelector (dropdown, fetched from GET /knowledge-base/indexes)
+-- QueryTypeSelector (dropdown: simple, semantic, vector, hybrid variants)
+-- TopKSlider (1-20, default 5)
+-- SaveButton (PUT /hcp-profiles/{id}/knowledge-base)
+-- SyncStatusBadge (shows if agent re-sync succeeded)
// frontend/src/hooks/useKnowledgeBase.ts
useSearchConnections() // GET /knowledge-base/connections
useIndexes() // GET /knowledge-base/indexes
useHcpKnowledgeConfig(id) // GET /hcp-profiles/{id}/knowledge-base
useUpdateKnowledgeConfig() // PUT /hcp-profiles/{id}/knowledge-base (mutation)// frontend/src/types/knowledgeBase.ts
interface SearchConnection {
name: string;
target: string;
is_default: boolean;
}
interface SearchIndex {
name: string;
version: string;
type: string;
description: string;
}
interface KnowledgeBaseConfig {
connection_id: string;
index_name: string;
query_type: 'simple' | 'semantic' | 'vector' | 'vector_simple_hybrid' | 'vector_semantic_hybrid';
top_k: number;
enabled: boolean;
}| Criterion | AzureAISearchTool | MCPTool |
|---|---|---|
| Simplicity | Simpler, purpose-built | More flexible, more config |
| SDK support | First-class, well-documented | Newer, server_url construction unclear |
| Index limit | 1 per agent | Depends on MCP server |
| Auth | Via connection_id | Needs OAuth or headers |
| Foundry IQ alignment | Native integration | Would need Foundry IQ MCP endpoint URL |
Recommendation: Use AzureAISearchTool for Phase 17. It is the natural fit for connecting agents to Azure AI Search indexes. MCPTool is better suited for external MCP servers (Foundry IQ's MCP endpoint is not yet well-documented for direct construction).
-
client.connectionsruntime access —AIProjectClientdoesn't showconnectionsindir(). Need to verify at runtime with a real endpoint thatclient.connections.list()works. (It should via dynamic binding, but must test.) -
client.indexesruntime access — Same concern.IndexesOperationsclass exists but client attribute is not statically visible. -
Index listing within a specific AI Search connection — The SDK
IndexesOperations.list()lists project-level indexes, NOT indexes within a specific AI Search service. To list indexes within a specific Azure AI Search connection, a direct REST call to the Search service endpoint (fromconnection.target) may be needed:GET https://<search-endpoint>/indexes?api-version=2024-07-01. -
Agent re-sync on KB change — Changing KB config must trigger a full agent version bump via
create_version. This is already the pattern for updates, but the tools parameter is new. -
1 index limit — SDK doc says max 1 index per
AzureAISearchToolResource.indexes. This is fine for MVP but constrains multi-index scenarios.
-
DB migration — Add KB columns to
hcp_profiles -
Pydantic schemas — Add
KnowledgeBaseConfigrequest/response schemas - knowledge_base_service.py — List connections, list indexes, build_search_tool()
-
Modify agent_sync_service.py — Add
toolsparam to create/update_agent, build tools from KB config -
API router —
/knowledge-base/connections,/knowledge-base/indexes, HCP KB endpoints - Frontend types + hooks — TypeScript types, TanStack Query hooks
- Frontend KnowledgeTab component — UI for configuring KB per HCP
- Integration into HCP editor — Add Knowledge tab to existing HCP profile editor
- Tests — Unit tests for service, API integration tests, E2E for UI flow