feat: 更新 agent 模板支持 MODEL_NAME 环境变量
- search_agent: 支持 MODEL_NAME 环境变量配置模型名称 - a2a_litellm_agent: 支持 MODEL_NAME 或 LITELLM_MODEL 环境变量 - mysql_agent/postgresql_agent: 新增数据库查询 agent - echo_agent: 新增回显测试 agent - jina_search_agent: 新增 Jina 搜索 agent - 更新 callback_utils 默认回调 URL - 优化 k8s_manager 模板端口和镜像映射 - 清理冗余文件和备份文件
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
# Search Agent LiteLLM 问题分析报告
|
||||
|
||||
## 一、Search Agent 模型和密钥部署方式分析
|
||||
|
||||
### 1.1 配置来源
|
||||
|
||||
Search Agent 的模型和密钥配置通过以下方式部署:
|
||||
|
||||
#### 环境变量配置(主要方式)
|
||||
|
||||
从 `agent_templates/agents/search_agent/search_agent/config.py` 和 `search_agent_main.py` 可以看到:
|
||||
|
||||
```python
|
||||
# 必须的环境变量
|
||||
LLM_BASE_URL: str # LLM API基础URL(如:https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io/)
|
||||
LLM_API_KEY: str # LLM API密钥(可选,可在请求中传入)
|
||||
LLM_MODEL: str # 模型名称(默认:gpt-4o-mini,实际使用:taiji/gpt-4o-mini)
|
||||
SERPER_API_KEY: str # Serper 搜索 API 密钥
|
||||
JINA_API_KEY: str # Jina Reader API 密钥
|
||||
```
|
||||
|
||||
#### 部署配置示例
|
||||
|
||||
从 `k8s-test-deployment.yaml` 可以看到实际部署配置:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
- name: LLM_BASE_URL
|
||||
value: "https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io/"
|
||||
- name: LLM_MODEL
|
||||
value: "taiji/gpt-4o-mini"
|
||||
- name: LLM_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: search-agent-secrets
|
||||
key: LLM_API_KEY
|
||||
```
|
||||
|
||||
### 1.2 密钥使用流程
|
||||
|
||||
1. **初始化阶段**:
|
||||
- Agent 启动时从环境变量读取 `LLM_BASE_URL` 和 `LLM_MODEL`
|
||||
- `LLM_API_KEY` 可以为空(使用占位符),等待请求时传入
|
||||
|
||||
2. **请求处理阶段**(`search_agent_main.py:233-282`):
|
||||
```python
|
||||
# 临时更新API key - 重要:在搜索前设置
|
||||
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
|
||||
# 重新初始化整个 SearchAgent 以使用新的 API key
|
||||
search_agent = SearchAgent(config)
|
||||
```
|
||||
|
||||
3. **LLM 调用**(`llm_client.py:49-89`):
|
||||
```python
|
||||
# Azure OpenAI 风格的URL
|
||||
url = f"{self.base_url}/chat/completions?api-version={self.API_VERSION}"
|
||||
|
||||
# Azure OpenAI 使用 api-key 头
|
||||
headers = {
|
||||
"api-key": self.api_key, # 使用传入的 API Key
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 关键发现
|
||||
|
||||
1. **模型名称**:使用的是 `taiji/gpt-4o-mini`,这是一个**模型别名**(model alias),不是原始模型名
|
||||
2. **API Key 传递方式**:
|
||||
- 通过 HTTP Header `api-key` 传递(Azure OpenAI 风格)
|
||||
- 支持在请求中动态传入,覆盖环境变量
|
||||
3. **LiteLLM Proxy 地址**:`https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io/`
|
||||
|
||||
---
|
||||
|
||||
## 二、LiteLLM 回调报错原因分析
|
||||
|
||||
### 2.1 错误现象
|
||||
|
||||
根据提供的日志信息:
|
||||
|
||||
```
|
||||
"call_type": "/chat/completions",
|
||||
"model": "taiji/gpt-4o-mini",
|
||||
"status": "failure",
|
||||
"response_time": 0.00039 # 极短的响应时间,说明在鉴权阶段就被拒绝
|
||||
|
||||
异常位置:
|
||||
ProxyException
|
||||
File ".../auth_checks.py", line 1756
|
||||
can_team_access_model(model=_model, team_model_aliases=...)
|
||||
```
|
||||
|
||||
### 2.2 根本原因
|
||||
|
||||
**LiteLLM Proxy 在鉴权阶段无法将当前请求使用的 API Key 映射到任何租户(tenant/team),因此直接拒绝了模型调用。**
|
||||
|
||||
#### 问题链路:
|
||||
|
||||
1. **请求到达 LiteLLM Proxy**
|
||||
- Search Agent 发送请求到 `https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io/chat/completions`
|
||||
- Header: `api-key: <用户提供的API Key>`
|
||||
- Model: `taiji/gpt-4o-mini`
|
||||
|
||||
2. **LiteLLM 鉴权流程**
|
||||
```
|
||||
LiteLLM Proxy 收到请求
|
||||
↓
|
||||
查找 API Key 对应的 team/tenant
|
||||
↓
|
||||
检查该 team/tenant 是否有权限访问 "taiji/gpt-4o-mini"
|
||||
↓
|
||||
❌ 失败:无法找到 API Key 对应的租户,或租户没有该模型的访问权限
|
||||
↓
|
||||
抛出 ProxyException,拒绝请求
|
||||
```
|
||||
|
||||
3. **为什么 Agent 还有回复?**
|
||||
- 可能的原因:
|
||||
- Agent 使用了**备用 API Key**(环境变量中的默认值)
|
||||
- 或者 LiteLLM Proxy 配置了**降级策略**(fallback)
|
||||
- 或者请求被**重试**,使用了不同的 API Key
|
||||
|
||||
### 2.3 具体问题点
|
||||
|
||||
#### 问题 1:API Key 未正确映射到租户
|
||||
|
||||
LiteLLM Proxy 需要知道:
|
||||
- 哪个 API Key 属于哪个 team/tenant
|
||||
- 该 team/tenant 可以访问哪些模型
|
||||
|
||||
**可能的原因**:
|
||||
- API Key 未在 LiteLLM 的数据库中注册
|
||||
- API Key 没有关联 `team_id` 或 `tenant_id`
|
||||
- API Key 的 `auth_metadata` 中缺少租户信息
|
||||
|
||||
#### 问题 2:模型别名权限配置缺失
|
||||
|
||||
模型 `taiji/gpt-4o-mini` 是一个别名,需要:
|
||||
- 在 LiteLLM 中配置该别名映射到实际模型
|
||||
- 配置哪些 team/tenant 可以访问该别名
|
||||
|
||||
**可能的原因**:
|
||||
- 模型别名 `taiji/gpt-4o-mini` 未在 LiteLLM 中配置
|
||||
- 或者配置了,但当前租户没有访问权限
|
||||
|
||||
#### 问题 3:回调时缺少租户信息
|
||||
|
||||
从回调文档可以看到,LiteLLM 回调需要包含租户信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"user_api_key_auth_metadata": {
|
||||
"tenant_id": "tenant-123",
|
||||
"channel_id": "channel-456"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**如果回调中缺少这些信息,计费系统无法识别租户,导致计费失败。**
|
||||
|
||||
---
|
||||
|
||||
## 三、修复方案
|
||||
|
||||
### 3.1 立即修复(LiteLLM Proxy 配置)
|
||||
|
||||
#### 方案 A:在 LiteLLM 中正确配置 API Key 和租户映射
|
||||
|
||||
1. **检查 LiteLLM 数据库中的 API Key 配置**
|
||||
|
||||
确保每个 API Key 都有:
|
||||
```yaml
|
||||
# LiteLLM config.yaml 或数据库记录
|
||||
api_keys:
|
||||
- sk-xxx:
|
||||
team_id: "team-123"
|
||||
metadata:
|
||||
tenant_id: "tenant-123"
|
||||
channel_id: "channel-456"
|
||||
```
|
||||
|
||||
2. **配置模型别名和团队访问权限**
|
||||
|
||||
```yaml
|
||||
# LiteLLM config.yaml
|
||||
model_list:
|
||||
- model_name: taiji/gpt-4o-mini
|
||||
litellm_params:
|
||||
model: gpt-4o-mini
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
# 团队模型访问配置
|
||||
team_settings:
|
||||
team-123:
|
||||
team_model_aliases:
|
||||
- taiji/gpt-4o-mini
|
||||
```
|
||||
|
||||
#### 方案 B:在请求中添加租户信息(如果 LiteLLM 支持)
|
||||
|
||||
如果 LiteLLM Proxy 支持通过 Header 传递租户信息:
|
||||
|
||||
```python
|
||||
# 在 llm_client.py 中修改
|
||||
headers = {
|
||||
"api-key": self.api_key,
|
||||
"Content-Type": "application/json",
|
||||
"x-tenant-id": tenant_id, # 如果 LiteLLM 支持
|
||||
"x-team-id": team_id # 如果 LiteLLM 支持
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 代码层面修复(Search Agent)
|
||||
|
||||
#### 修复 1:在 LLM 调用时传递租户信息
|
||||
|
||||
修改 `search_agent/utils/llm_client.py`,在请求中包含租户信息:
|
||||
|
||||
```python
|
||||
class LLMClient:
|
||||
def __init__(
|
||||
self,
|
||||
base_url: str,
|
||||
api_key: str,
|
||||
model: str = "xchat52",
|
||||
timeout: int = 60,
|
||||
tenant_id: Optional[str] = None, # 新增
|
||||
team_id: Optional[str] = None # 新增
|
||||
):
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.api_key = api_key
|
||||
self.model = model
|
||||
self.timeout = timeout
|
||||
self.tenant_id = tenant_id
|
||||
self.team_id = team_id
|
||||
|
||||
async def chat(self, ...):
|
||||
headers = {
|
||||
"api-key": self.api_key,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# 如果 LiteLLM 支持通过 Header 传递租户信息
|
||||
if self.tenant_id:
|
||||
headers["x-tenant-id"] = self.tenant_id
|
||||
if self.team_id:
|
||||
headers["x-team-id"] = self.team_id
|
||||
|
||||
# ... 其余代码
|
||||
```
|
||||
|
||||
#### 修复 2:从请求中提取并传递租户信息
|
||||
|
||||
修改 `search_agent_main.py`,在搜索请求中包含租户信息:
|
||||
|
||||
```python
|
||||
class SearchRequest(BaseModel):
|
||||
query: str
|
||||
llm_api_key: str
|
||||
user_id: Optional[str] = None
|
||||
tenant_id: Optional[str] = None # 新增
|
||||
team_id: Optional[str] = None # 新增
|
||||
|
||||
# 在初始化 SearchAgent 时传递租户信息
|
||||
config = Config(
|
||||
...
|
||||
tenant_id=request.tenant_id, # 传递租户信息
|
||||
team_id=request.team_id
|
||||
)
|
||||
```
|
||||
|
||||
### 3.3 LiteLLM Proxy 配置检查清单
|
||||
|
||||
1. ✅ **API Key 配置**
|
||||
- [ ] 所有使用的 API Key 都在 LiteLLM 数据库中
|
||||
- [ ] 每个 API Key 都关联了 `team_id` 或 `tenant_id`
|
||||
- [ ] API Key 的 `auth_metadata` 包含租户信息
|
||||
|
||||
2. ✅ **模型别名配置**
|
||||
- [ ] `taiji/gpt-4o-mini` 在 `model_list` 中定义
|
||||
- [ ] 模型别名正确映射到实际模型
|
||||
|
||||
3. ✅ **团队/租户访问权限**
|
||||
- [ ] 每个 team/tenant 的 `team_model_aliases` 包含 `taiji/gpt-4o-mini`
|
||||
- [ ] 或者使用通配符允许所有模型
|
||||
|
||||
4. ✅ **回调配置**
|
||||
- [ ] LiteLLM 的 `webhook_url` 指向正确的回调地址
|
||||
- [ ] 回调中包含 `metadata.user_api_key_auth_metadata` 信息
|
||||
|
||||
### 3.4 验证步骤
|
||||
|
||||
1. **测试 API Key 映射**
|
||||
```bash
|
||||
# 使用 curl 测试
|
||||
curl -X POST https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io/chat/completions \
|
||||
-H "api-key: <your-api-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "taiji/gpt-4o-mini",
|
||||
"messages": [{"role": "user", "content": "test"}]
|
||||
}'
|
||||
```
|
||||
|
||||
2. **检查 LiteLLM 日志**
|
||||
- 查看 LiteLLM Proxy 的日志,确认 API Key 是否被正确识别
|
||||
- 检查是否有 `can_team_access_model` 相关的错误
|
||||
|
||||
3. **验证回调数据**
|
||||
- 检查回调请求中的 `metadata` 字段
|
||||
- 确认包含 `user_api_key_auth_metadata.tenant_id`
|
||||
|
||||
---
|
||||
|
||||
## 四、总结
|
||||
|
||||
### 核心问题
|
||||
|
||||
**LiteLLM Proxy 的多租户鉴权机制无法将 API Key 映射到租户,导致模型调用被拒绝。**
|
||||
|
||||
### 修复优先级
|
||||
|
||||
1. **高优先级**:修复 LiteLLM Proxy 配置
|
||||
- 确保 API Key 正确映射到租户
|
||||
- 配置模型别名和访问权限
|
||||
|
||||
2. **中优先级**:代码层面改进
|
||||
- 在请求中传递租户信息(如果 LiteLLM 支持)
|
||||
- 改进错误处理和日志记录
|
||||
|
||||
3. **低优先级**:长期优化
|
||||
- 统一租户信息管理
|
||||
- 添加更详细的监控和告警
|
||||
|
||||
### 为什么 Agent 还有回复?
|
||||
|
||||
可能的原因:
|
||||
1. Agent 使用了环境变量中的备用 API Key(有权限的)
|
||||
2. LiteLLM Proxy 配置了降级策略
|
||||
3. 请求被重试,使用了不同的 API Key
|
||||
|
||||
**建议**:检查 LiteLLM Proxy 的日志,确认实际使用的 API Key 和租户信息。
|
||||
|
||||
Reference in New Issue
Block a user