# Search Agent 问题诊断报告 ## Pod 信息 - **Pod 名称**: `search-agent-bd115e1f-fc89c0` - **命名空间**: `agent-search-agent-bd115e1f-fc89c0` - **状态**: Running --- ## 一、发现的问题 ### 问题 0:为什么 Agent 还能返回内容?(即使 LiteLLM 返回 401) #### 现象 虽然 LiteLLM 返回 401 错误,但 Agent 仍然返回了内容,例如: ```json { "query": "什么是人工智能", "answer": "关于「什么是人工智能」,以下是搜索到的相关信息:\n### 来源 [1]: 人工智能 (AI)\n..." } ``` #### 原因:降级策略(Fallback Mechanism) Agent 有一个**降级策略**,当 LLM 调用失败时,会直接从搜索结果中提取内容: 1. **正常流程**(LLM 成功时): ``` 搜索 → 提取内容 → 重排序 → LLM 生成答案 → 返回 ``` 2. **降级流程**(LLM 失败时): ``` 搜索 → 提取内容 → 重排序 → LLM 失败 → 使用 _fallback_answer() → 返回 ``` 3. **降级答案生成逻辑**(`answer_generator.py:125-150`): ```python def _fallback_answer(self, query: str, documents: List[RankedDocument]) -> Answer: """后备答案生成(LLM失败时)""" # 简单汇总文档内容 content_parts = [f"关于「{query}」,以下是搜索到的相关信息:\n"] for i, doc in enumerate(documents[:5], 1): content_parts.append(f"### 来源 [{i}]: {doc.title}\n") content_parts.append(f"{doc.content[:500]}...\n\n") return Answer( content="".join(content_parts), sources=sources, confidence="low" # 注意:置信度是 low ) ``` 4. **日志证据**: ``` 2026-01-16 12:37:00.009 | ERROR | modules.answer_generator:generate:114 - 答案生成失败: LLM API请求失败: 401 2026-01-16 12:37:00.009 | INFO | agent.search_agent:search:131 - 答案生成完成: confidence=low ``` 注意:虽然 LLM 失败了,但答案生成"完成"了,只是 `confidence=low`。 #### 这意味着什么? - ✅ **搜索功能正常**:Serper 搜索和 Jina 内容提取都成功了 - ✅ **内容提取正常**:从网页提取了内容并进行了重排序 - ❌ **LLM 生成失败**:无法使用 LLM 生成高质量答案 - ⚠️ **返回降级答案**:直接返回搜索结果的简单汇总,质量较低 **所以 Agent 返回的内容是降级答案,不是 LLM 生成的,质量会明显下降。** --- ### 问题 1:API Key 未正确使用(导致 LiteLLM 回调失败) #### 现象 日志显示 LiteLLM 收到的 API Key 是 `placeholder`,而不是用户传入的真实 API Key: ``` LLM API错误: 401 - {"error":{"message":"Authentication Error, LiteLLM Virtual Key expected. Received=placeholder, expected to start with 'sk-'.","type":"auth_error"}} ``` #### 原因分析 1. **代码逻辑**:`search_agent_main.py` 第 276-282 行有更新 API Key 的逻辑: ```python if config and request.llm_api_key: logger.info(f"使用请求中的 LLM API Key: {request.llm_api_key[:10]}...") config.llm_api_key = request.llm_api_key search_agent = SearchAgent(config) # 重新初始化 ``` 2. **问题**:日志中**没有看到** "使用请求中的 LLM API Key" 这条日志,说明: - 要么 `request.llm_api_key` 为空/None - 要么请求中的字段名不是 `llm_api_key` 3. **SearchAgent 初始化时机**: - 各个模块(QueryAnalyzer, AnswerGenerator 等)在 `__init__` 时创建了 `LLMClient` - 即使重新初始化 `SearchAgent`,如果模块内部已经缓存了旧的 `LLMClient`,仍会使用 placeholder #### 验证方法 检查最近的请求日志,看是否有 "使用请求中的 LLM API Key" 这条日志: ```bash kubectl logs -n agent-search-agent-bd115e1f-fc89c0 search-agent-bd115e1f-fc89c0 --tail=1000 | grep "使用请求中的" ``` **如果没有这条日志,说明 API Key 没有被正确传入或处理。** --- ### 问题 2:Agent 回调 URL 无法解析(Agent Manager 回调失败) #### 现象 日志显示回调请求失败,无法解析 `mcp-server` 主机名: ``` Failed to send callback: HTTPConnectionPool(host='mcp-server', port=8002): Max retries exceeded with url: /api/v1/billing/agent-callback (Caused by NameResolutionError("HTTPConnection(host='mcp-server', port=8002): Failed to resolve 'mcp-server' ([Errno -2] Name or service not known)")) ``` #### 回调 URL 信息 - **当前配置的 URL**: `http://mcp-server:8002/api/v1/billing/agent-callback` - **来源**: `agent_callback_utils.py` 第 33-35 行 ```python self.callback_url = callback_url or os.getenv( "AGENT_CALLBACK_URL", "http://mcp-server:8002/api/v1/billing/agent-callback" ) ``` #### 原因分析 1. **服务发现问题**: - `mcp-server` 服务在 `taiji-ai` 命名空间中 - 当前 Pod 在 `agent-search-agent-bd115e1f-fc89c0` 命名空间中 - 跨命名空间访问需要使用完整的服务名:`mcp-server.taiji-ai.svc.cluster.local` 2. **端口问题**: - mcp-server 服务实际端口是 **8000**(不是 8002) - 代码中默认使用的是 8002 端口 3. **正确的回调 URL 应该是**: ``` http://mcp-server.taiji-ai.svc.cluster.local:8000/api/v1/billing/agent-callback ``` 或者(如果 mcp-server 在同一个集群中): ``` http://mcp-server.taiji-ai:8000/api/v1/billing/agent-callback ``` **注意**:端口是 8000,不是 8002! #### 回调请求详情 从日志中可以看到回调请求的 payload: ```json { "agentName": "search-agent-bd115e1f-fc89c0", "userId": "bd115e1f-e2de-4bd2-a641-1ed74ac1a34a", "podRunningTimeSeconds": 22, "toolsUsed": ["web_search", "content_reader"], "startTime": "2026-01-16T12:02:39.355978+00:00", "endTime": "2026-01-16T12:03:01.701388+00:00", "requestId": "search-1768564959" } ``` --- ### 问题 3:LiteLLM 回调失败(但直接测试密钥正常) #### 现象 - 用户使用 `sk-rxegkFOciNmQLhOHr3qP3A` 直接测试模型回调正常 - 但通过 search_agent 调用时,LiteLLM 回调报错 #### 原因分析 1. **API Key 传递问题**: - Agent 使用的是 `placeholder`,不是真实的 `sk-rxegkFOciNmQLhOHr3qP3A` - LiteLLM 无法识别 `placeholder`,导致鉴权失败 - 鉴权失败后,LiteLLM 的回调也会失败(因为无法识别租户) 2. **为什么直接测试正常**: - 直接测试时使用的是真实的 API Key `sk-rxegkFOciNmQLhOHr3qP3A` - 该 API Key 在 LiteLLM 中正确配置了租户信息 - 所以回调正常 3. **为什么 Agent 调用失败**: - Agent 实际使用的是 `placeholder` - LiteLLM 无法识别 `placeholder`,返回 401 错误 - 回调时也无法识别租户,导致回调失败 --- ## 二、问题根源总结 ### 核心问题链 ``` 用户传入 llm_api_key: "sk-rxegkFOciNmQLhOHr3qP3A" ↓ 代码应该更新 config.llm_api_key 并重新初始化 SearchAgent ↓ ❌ 但日志显示没有执行更新逻辑(没有 "使用请求中的 LLM API Key" 日志) ↓ SearchAgent 继续使用 placeholder ↓ LLMClient 使用 placeholder 调用 LiteLLM ↓ LiteLLM 返回 401: "Received=placeholder, expected to start with 'sk-'" ↓ LiteLLM 回调失败(无法识别租户) ``` --- ## 三、验证步骤 ### 1. 验证 API Key 是否被传入 检查最近的请求日志: ```bash kubectl logs -n agent-search-agent-bd115e1f-fc89c0 search-agent-bd115e1f-fc89c0 --tail=1000 | grep -E "使用请求中的|llm_api_key|POST /search" ``` ### 2. 验证回调 URL 配置 检查 Pod 的环境变量: ```bash kubectl exec -n agent-search-agent-bd115e1f-fc89c0 search-agent-bd115e1f-fc89c0 -- env | grep -i "CALLBACK\|MCP" ``` ### 3. 验证 mcp-server 服务 确认 mcp-server 服务的完整地址: ```bash kubectl get svc -n taiji-ai mcp-server kubectl get endpoints -n taiji-ai mcp-server ``` ### 4. 测试回调 URL 连通性 从 Pod 内部测试回调 URL: ```bash # 测试健康检查接口(端口 8000) kubectl exec -n agent-search-agent-bd115e1f-fc89c0 search-agent-bd115e1f-fc89c0 -- curl -v http://mcp-server.taiji-ai.svc.cluster.local:8000/api/v1/billing/agent-callback/health # 测试 DNS 解析 kubectl exec -n agent-search-agent-bd115e1f-fc89c0 search-agent-bd115e1f-fc89c0 -- nslookup mcp-server.taiji-ai.svc.cluster.local ``` --- ## 四、修复建议(不改代码) ### 修复 1:配置正确的回调 URL 通过环境变量或 ConfigMap 设置正确的回调 URL: ```yaml env: - name: AGENT_CALLBACK_URL value: "http://mcp-server.taiji-ai.svc.cluster.local:8000/api/v1/billing/agent-callback" ``` 或者如果 mcp-server 在同一个集群中: ```yaml env: - name: AGENT_CALLBACK_URL value: "http://mcp-server.taiji-ai:8000/api/v1/billing/agent-callback" ``` **重要**: - 服务名:`mcp-server.taiji-ai.svc.cluster.local`(跨命名空间访问) - 端口:**8000**(不是 8002) - 路径:`/api/v1/billing/agent-callback` ### 修复 2:确认 API Key 传递方式 检查请求格式是否正确: ```json { "query": "什么是人工智能", "llm_api_key": "sk-rxegkFOciNmQLhOHr3qP3A", "user_id": "bd115e1f-e2de-4bd2-a641-1ed74ac1a34a" } ``` **注意**:字段名必须是 `llm_api_key`(不是 `LLM_API_KEY` 或其他)。 ### 修复 3:检查 LiteLLM 配置 确认 LiteLLM Proxy 中 `sk-rxegkFOciNmQLhOHr3qP3A` 的配置: - API Key 是否正确注册 - 是否关联了 `team_id` 或 `tenant_id` - 是否有权限访问 `taiji/gpt-4o-mini` 模型 --- ## 五、关键日志位置 ### Agent 回调日志 ``` Sending callback: {'agentName': '...', 'userId': '...', ...} Failed to send callback: HTTPConnectionPool(host='mcp-server', port=8002): ... ``` ### LiteLLM 调用日志 ``` LLM API错误: 401 - {"error":{"message":"Authentication Error, LiteLLM Virtual Key expected. Received=placeholder, ..."}} ``` ### API Key 更新日志(应该出现但没出现) ``` 使用请求中的 LLM API Key: sk-rxegkFO... SearchAgent 已使用新的 API Key 重新初始化 ``` --- ## 六、Agent 执行流程说明 ### 完整执行流程 ``` 1. 接收请求(POST /search) ↓ 2. 查询分析(QueryAnalyzer)→ ❌ LLM 失败,使用默认 intent ↓ 3. 搜索规划(SearchPlanner)→ ✅ 成功 ↓ 4. 执行搜索(SearchExecutor + Serper)→ ✅ 成功,获取 10 条结果 ↓ 5. 提取内容(ContentExtractor + Jina Reader)→ ✅ 成功,提取 10 个文档 ↓ 6. 结果处理(ResultProcessor + Jina Reranker)→ ✅ 成功,排序后返回 5 个 ↓ 7. 生成答案(AnswerGenerator + LLM)→ ❌ LLM 失败(401) ↓ 8. 降级处理(_fallback_answer)→ ✅ 从文档中提取内容,生成简单答案 ↓ 9. 质量评估(Reflector + LLM)→ ❌ LLM 失败(401),跳过评估 ↓ 10. 返回结果 → ✅ 返回降级答案(confidence=low) ``` ### 为什么还能返回内容? - **搜索和内容提取不依赖 LLM**:使用 Serper API 和 Jina Reader API,这些服务都有独立的 API Key - **降级策略**:当 LLM 失败时,直接从搜索结果中提取内容并格式化返回 - **质量下降**:降级答案只是简单汇总,没有 LLM 的智能整合和结构化 ### 如何判断返回的是降级答案? 1. **检查 confidence**:降级答案的 `confidence` 是 `"low"` 2. **检查答案格式**:降级答案通常以 "关于「xxx」,以下是搜索到的相关信息:" 开头 3. **检查日志**:日志中会有 "答案生成失败" 和 "confidence=low" 的记录 --- ## 七、下一步行动 1. **立即检查**:确认请求中是否真的传入了 `llm_api_key` 字段 2. **修复回调 URL**:通过环境变量设置正确的 `AGENT_CALLBACK_URL` 3. **验证修复**:重新发送请求,检查日志中是否出现 "使用请求中的 LLM API Key" 4. **监控回调**:确认 Agent 回调和 LiteLLM 回调都成功 5. **验证答案质量**:修复后,答案的 `confidence` 应该是 `"high"` 或 `"medium"`,而不是 `"low"`