Remove duplicate a2a-ai-search-agent from search_agent directory

This commit is contained in:
zhanggangyong
2026-01-15 15:19:00 +00:00
parent a946e7d288
commit 225c5d6af7
12 changed files with 0 additions and 1336 deletions
@@ -1,24 +0,0 @@
# Python
__pycache__/
*.py[cod]
*.class
*.so
.Python
venv/
.venv/
ENV/
env/
# IDE
.vscode/
.idea/
*.swp
*.swo
# 环境变量
.env
*.log
# 系统文件
.DS_Store
Thumbs.db
@@ -1,24 +0,0 @@
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY config.py agent.py a2a_server.py ./
RUN useradd -m -u 1000 agent && chown -R agent:agent /app
USER agent
EXPOSE 9000
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl -f http://localhost:9000/health || exit 1
CMD ["uvicorn", "a2a_server:app", "--host", "0.0.0.0", "--port", "9000"]
-181
View File
@@ -1,181 +0,0 @@
# A2A AI Search Agent
基于 LiteLLM 和 A2A (Agent2Agent) 协议的智能搜索 Agent API 服务。
## 🚀 功能特性
- ✅ **A2A 协议兼容** - 完全支持 Google Agent2Agent 协议
- ✅ **多轮对话** - 维护对话上下文,支持连续对话
- ✅ **流式响应** - 支持 SSE 流式输出
- ✅ **LiteLLM 集成** - 统一的 LLM 调用接口
- ✅ **生产就绪** - 适用于 AKS (Azure Kubernetes Service) 部署
## 📁 项目结构
```
a2a-ai-search-agent/
├── config.py # 配置管理
├── agent.py # Agent 核心逻辑
├── a2a_server.py # A2A 协议 API 服务
├── requirements.txt # Python 依赖
├── Dockerfile # Docker 镜像
├── deploy.sh # 部署脚本
└── k8s/ # Kubernetes 配置
├── deployment.yaml
├── service.yaml
├── configmap.yaml
└── secret.yaml
```
## 🔧 配置
**环境变量:**
- `LITELLM_API_KEY` - LiteLLM API 密钥(必需)
- `LITELLM_MODEL` - 模型名称(如: taiji/gpt-4o-mini)
**LiteLLM 服务地址:**
```
https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io
```
## 🐳 Docker 部署
### 构建镜像
```bash
docker build -t a2a-ai-search-agent:latest .
```
### 运行容器
```bash
docker run -d \
-p 9000:9000 \
-e LITELLM_API_KEY="your-api-key" \
-e LITELLM_MODEL="taiji/gpt-4o-mini" \
a2a-ai-search-agent:latest
```
## ☸️ Kubernetes 部署
### 推送到 ACR
```bash
# 登录 ACR
docker login agnettaiji.azurecr.io -u agnettaiji -p 'YOUR_PASSWORD'
# 打标签
docker tag a2a-ai-search-agent:latest agnettaiji.azurecr.io/xiaohei-agent:latest
# 推送
docker push agnettaiji.azurecr.io/xiaohei-agent:latest
```
### 部署到 AKS
```bash
# 方式1: 使用脚本
./deploy.sh
# 方式2: 手动部署
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
```
### 查看部署状态
```bash
# 查看 Pod
kubectl get pods -l app=xiaohei-agent
# 查看 Service 和外网 IP
kubectl get svc xiaohei-agent-service
# 实时监控外网 IP 分配
kubectl get svc xiaohei-agent-service -w
```
## 📡 API 端点
### 健康检查
```bash
GET /health
```
### Agent Card (A2A 发现)
```bash
GET /.well-known/agent.json
```
**响应示例:**
```json
{
"name": "xiaohei-agent",
"description": "一个基于LiteLLM的智能Agent,支持A2A协议",
"version": "1.0.0",
"capabilities": {
"text": true,
"streaming": true,
"push_notifications": false
},
"skills": [
{
"id": "general-assistant",
"name": "通用助手",
"description": "回答问题、提供建议、协助完成各种任务"
}
]
}
```
### 发送消息 (非流式)
```bash
POST /message/send
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "request-1",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "你好"}],
"messageId": "msg-1"
}
}
}
```
### 发送消息 (流式)
```bash
POST /message/stream
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "request-1",
"method": "message/stream",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "你好"}],
"messageId": "msg-1"
}
}
}
```
## 🔗 相关资源
- [A2A 协议规范](https://github.com/a2aproject/A2A)
- [LiteLLM 文档](https://docs.litellm.ai/)
- [Google Agent2Agent 介绍](https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability)
## 📄 许可证
MIT License
@@ -1,487 +0,0 @@
"""
A2A协议兼容的Agent服务
实现Google Agent2Agent协议规范
"""
import asyncio
import json
import uuid
from typing import Optional, Dict, Any, AsyncGenerator
from datetime import datetime
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.responses import StreamingResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
import structlog
from agent import LiteLLMAgent
from config import get_config, AgentConfig, A2AConfig
# 配置日志
logger = structlog.get_logger()
# ============== A2A 协议数据模型 ==============
class A2APart(BaseModel):
"""A2A消息部分"""
kind: str = "text"
text: Optional[str] = None
data: Optional[Dict[str, Any]] = None
mime_type: Optional[str] = None
class A2AMessage(BaseModel):
"""A2A消息"""
role: str
parts: list[A2APart]
messageId: str = Field(default_factory=lambda: uuid.uuid4().hex)
class A2AMessageSendParams(BaseModel):
"""A2A发送消息参数"""
message: A2AMessage
configuration: Optional[Dict[str, Any]] = None
class A2ARequest(BaseModel):
"""A2A JSON-RPC请求"""
jsonrpc: str = "2.0"
id: str
method: str
params: Optional[Dict[str, Any]] = None
class A2AArtifact(BaseModel):
"""A2A响应工件"""
artifactId: str = Field(default_factory=lambda: uuid.uuid4().hex)
name: str = "response"
parts: list[A2APart]
class A2ATaskStatus(BaseModel):
"""A2A任务状态"""
state: str # submitted, working, input-required, completed, failed, canceled
timestamp: str = Field(default_factory=lambda: datetime.utcnow().isoformat() + "Z")
message: Optional[str] = None
class A2ATask(BaseModel):
"""A2A任务"""
kind: str = "task"
id: str = Field(default_factory=lambda: uuid.uuid4().hex)
contextId: str = Field(default_factory=lambda: uuid.uuid4().hex)
status: A2ATaskStatus
artifacts: Optional[list[A2AArtifact]] = None
class A2AResponse(BaseModel):
"""A2A JSON-RPC响应"""
jsonrpc: str = "2.0"
id: str
result: Optional[A2ATask] = None
error: Optional[Dict[str, Any]] = None
class A2AStreamEvent(BaseModel):
"""A2A流式事件"""
kind: str
taskId: str
contextId: str
data: Optional[Dict[str, Any]] = None
# ============== Agent Card ==============
class AgentSkill(BaseModel):
"""Agent技能"""
id: str
name: str
description: str
inputSchema: Optional[Dict[str, Any]] = None
outputSchema: Optional[Dict[str, Any]] = None
class AgentCapabilities(BaseModel):
"""Agent能力"""
text: bool = True
streaming: bool = True
push_notifications: bool = False
forms: bool = False
files: bool = False
class AgentCard(BaseModel):
"""A2A Agent Card - 描述Agent能力"""
name: str
description: str
version: str
url: str
capabilities: AgentCapabilities
skills: list[AgentSkill]
authentication: Optional[Dict[str, Any]] = None
# ============== A2A Server ==============
class A2AAgentServer:
"""A2A协议Agent服务器"""
def __init__(
self,
api_key: Optional[str] = None,
model: Optional[str] = None
):
"""
初始化A2A Agent服务器
Args:
api_key: LiteLLM API密钥
model: 模型名称
"""
# 获取配置
self.llm_config, self.agent_config, self.a2a_config = get_config(api_key, model)
# 创建Agent
self.agent = LiteLLMAgent(
litellm_config=self.llm_config,
agent_config=self.agent_config
)
# 任务存储
self.tasks: Dict[str, A2ATask] = {}
# 创建FastAPI应用
self.app = self._create_app()
def _create_app(self) -> FastAPI:
"""创建FastAPI应用"""
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("A2A Agent服务启动", agent_name=self.agent_config.name)
yield
await self.agent.close()
logger.info("A2A Agent服务关闭")
app = FastAPI(
title=f"{self.agent_config.name} - A2A Agent",
description=self.agent_config.description,
version=self.agent_config.version,
lifespan=lifespan
)
# CORS中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
self._register_routes(app)
return app
def _register_routes(self, app: FastAPI):
"""注册A2A协议路由"""
@app.get("/")
async def root():
"""服务根路径"""
return {
"name": self.agent_config.name,
"version": self.agent_config.version,
"protocol": "A2A",
"status": "running"
}
@app.get("/.well-known/agent.json")
async def get_agent_card(request: Request):
"""获取Agent Card (A2A发现协议)"""
base_url = str(request.base_url).rstrip("/")
card = AgentCard(
name=self.agent_config.name,
description=self.agent_config.description,
version=self.agent_config.version,
url=base_url,
capabilities=AgentCapabilities(
text=True,
streaming=self.agent_config.enable_streaming,
push_notifications=False
),
skills=[
AgentSkill(
id="general-assistant",
name="通用助手",
description="回答问题、提供建议、协助完成各种任务"
),
AgentSkill(
id="code-helper",
name="代码助手",
description="编写、解释和调试代码"
)
]
)
return card.model_dump()
@app.post("/message/send")
async def send_message(request: Request):
"""A2A message/send 端点"""
body = await request.json()
# 解析JSON-RPC请求
try:
rpc_request = A2ARequest(**body)
except Exception as e:
return JSONResponse({
"jsonrpc": "2.0",
"id": body.get("id", "unknown"),
"error": {
"code": -32600,
"message": f"Invalid Request: {str(e)}"
}
})
# 处理 message/send 方法
if rpc_request.method == "message/send":
return await self._handle_message_send(rpc_request)
elif rpc_request.method == "message/stream":
return await self._handle_message_stream(rpc_request)
else:
return JSONResponse({
"jsonrpc": "2.0",
"id": rpc_request.id,
"error": {
"code": -32601,
"message": f"Method not found: {rpc_request.method}"
}
})
@app.post("/message/stream")
async def stream_message(request: Request):
"""A2A message/stream 端点 (SSE流式响应)"""
body = await request.json()
try:
rpc_request = A2ARequest(**body)
except Exception as e:
return JSONResponse({
"jsonrpc": "2.0",
"id": body.get("id", "unknown"),
"error": {
"code": -32600,
"message": f"Invalid Request: {str(e)}"
}
})
return await self._handle_message_stream(rpc_request)
@app.get("/tasks/{task_id}")
async def get_task(task_id: str):
"""获取任务状态"""
if task_id not in self.tasks:
raise HTTPException(status_code=404, detail="Task not found")
return self.tasks[task_id].model_dump()
@app.get("/health")
async def health_check():
"""健康检查"""
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
async def _handle_message_send(self, request: A2ARequest) -> JSONResponse:
"""处理 message/send 请求"""
params = request.params or {}
message_data = params.get("message", {})
# 提取用户消息文本
user_text = ""
parts = message_data.get("parts", [])
for part in parts:
if part.get("kind") == "text":
user_text += part.get("text", "")
if not user_text:
return JSONResponse({
"jsonrpc": "2.0",
"id": request.id,
"error": {
"code": -32602,
"message": "Invalid params: no text content found"
}
})
# 创建任务
task_id = uuid.uuid4().hex
context_id = params.get("contextId", uuid.uuid4().hex)
task = A2ATask(
id=task_id,
contextId=context_id,
status=A2ATaskStatus(state="working")
)
self.tasks[task_id] = task
try:
# 调用Agent获取响应
logger.info("处理消息", task_id=task_id, message_preview=user_text[:50])
response_text = await self.agent.chat(
message=user_text,
conversation_id=context_id
)
# 更新任务状态
task.status = A2ATaskStatus(state="completed")
task.artifacts = [
A2AArtifact(
name="response",
parts=[A2APart(kind="text", text=response_text)]
)
]
self.tasks[task_id] = task
return JSONResponse({
"jsonrpc": "2.0",
"id": request.id,
"result": task.model_dump()
})
except Exception as e:
logger.error("处理消息失败", error=str(e))
task.status = A2ATaskStatus(state="failed", message=str(e))
self.tasks[task_id] = task
return JSONResponse({
"jsonrpc": "2.0",
"id": request.id,
"error": {
"code": -32000,
"message": f"Agent error: {str(e)}"
}
})
async def _handle_message_stream(self, request: A2ARequest) -> StreamingResponse:
"""处理 message/stream 请求 (SSE)"""
params = request.params or {}
message_data = params.get("message", {})
# 提取用户消息
user_text = ""
parts = message_data.get("parts", [])
for part in parts:
if part.get("kind") == "text":
user_text += part.get("text", "")
task_id = uuid.uuid4().hex
context_id = params.get("contextId", uuid.uuid4().hex)
async def event_generator() -> AsyncGenerator[str, None]:
"""生成SSE事件流"""
# 发送任务开始事件
start_event = {
"kind": "task-start",
"taskId": task_id,
"contextId": context_id
}
yield f"data: {json.dumps(start_event)}\n\n"
try:
# 获取流式响应
stream = await self.agent.chat(
message=user_text,
conversation_id=context_id,
stream=True
)
full_response = ""
async for chunk in stream:
full_response += chunk
# 发送文本增量事件
delta_event = {
"kind": "artifact-delta",
"taskId": task_id,
"contextId": context_id,
"data": {
"kind": "text",
"text": chunk
}
}
yield f"data: {json.dumps(delta_event)}\n\n"
# 发送完成事件
complete_event = {
"kind": "task-complete",
"taskId": task_id,
"contextId": context_id,
"data": {
"status": "completed",
"artifacts": [{
"name": "response",
"parts": [{"kind": "text", "text": full_response}]
}]
}
}
yield f"data: {json.dumps(complete_event)}\n\n"
except Exception as e:
# 发送错误事件
error_event = {
"kind": "task-error",
"taskId": task_id,
"contextId": context_id,
"data": {
"error": str(e)
}
}
yield f"data: {json.dumps(error_event)}\n\n"
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no"
}
)
def run(self, host: Optional[str] = None, port: Optional[int] = None):
"""运行服务器"""
import uvicorn
host = host or self.agent_config.host
port = port or self.agent_config.port
logger.info(f"启动A2A Agent服务", host=host, port=port)
uvicorn.run(self.app, host=host, port=port)
def create_app(api_key: Optional[str] = None, model: Optional[str] = None) -> FastAPI:
"""
创建FastAPI应用(用于uvicorn启动)
使用方式:
uvicorn a2a_server:app --host 0.0.0.0 --port 8080
或设置环境变量后:
export LITELLM_API_KEY="your-key"
export LITELLM_MODEL="your-model"
uvicorn a2a_server:app --host 0.0.0.0 --port 8080
"""
import os
api_key = api_key or os.getenv("LITELLM_API_KEY")
model = model or os.getenv("LITELLM_MODEL")
server = A2AAgentServer(api_key=api_key, model=model)
return server.app
# uvicorn 启动入口
# 环境变量: LITELLM_API_KEY, LITELLM_MODEL
app = create_app()
-333
View File
@@ -1,333 +0,0 @@
"""
LiteLLM Agent 核心模块
基于LiteLLM框架的Agent实现,支持A2A协议
"""
import asyncio
import json
import uuid
from typing import AsyncGenerator, Optional, Dict, Any, List
from dataclasses import dataclass, field
from datetime import datetime
import httpx
import structlog
from config import LiteLLMConfig, AgentConfig, get_config
# 配置日志
logger = structlog.get_logger()
@dataclass
class Message:
"""消息数据结构"""
role: str # user, assistant, system
content: str
message_id: str = field(default_factory=lambda: uuid.uuid4().hex)
timestamp: datetime = field(default_factory=datetime.now)
metadata: Dict[str, Any] = field(default_factory=dict)
@dataclass
class Conversation:
"""对话上下文"""
conversation_id: str = field(default_factory=lambda: uuid.uuid4().hex)
messages: List[Message] = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.now)
def add_message(self, role: str, content: str) -> Message:
"""添加消息到对话"""
msg = Message(role=role, content=content)
self.messages.append(msg)
return msg
def to_openai_format(self) -> List[Dict[str, str]]:
"""转换为OpenAI格式的消息列表"""
return [{"role": m.role, "content": m.content} for m in self.messages]
class LiteLLMAgent:
"""
基于LiteLLM的Agent实现
支持功能:
- 多轮对话
- 流式响应
- 工具调用
- A2A协议兼容
"""
def __init__(
self,
api_key: Optional[str] = None,
model: Optional[str] = None,
litellm_config: Optional[LiteLLMConfig] = None,
agent_config: Optional[AgentConfig] = None
):
"""
初始化Agent
Args:
api_key: LiteLLM API密钥
model: 模型名称
litellm_config: LiteLLM配置对象
agent_config: Agent配置对象
"""
if litellm_config:
self.llm_config = litellm_config
else:
self.llm_config = LiteLLMConfig(api_key=api_key, model=model)
if agent_config:
self.agent_config = agent_config
else:
self.agent_config = AgentConfig()
# 验证配置
self.llm_config.validate()
# HTTP客户端
self._client: Optional[httpx.AsyncClient] = None
# 对话管理
self.conversations: Dict[str, Conversation] = {}
# 工具注册
self.tools: Dict[str, callable] = {}
logger.info(
"Agent初始化完成",
agent_name=self.agent_config.name,
model=self.llm_config.model,
base_url=self.llm_config.base_url
)
async def _get_client(self) -> httpx.AsyncClient:
"""获取或创建HTTP客户端"""
if self._client is None or self._client.is_closed:
self._client = httpx.AsyncClient(
timeout=httpx.Timeout(self.llm_config.timeout),
headers={
"Authorization": f"Bearer {self.llm_config.api_key}",
"Content-Type": "application/json"
}
)
return self._client
async def close(self):
"""关闭资源"""
if self._client and not self._client.is_closed:
await self._client.aclose()
def register_tool(self, name: str, func: callable, description: str = ""):
"""注册工具函数"""
self.tools[name] = {
"function": func,
"description": description
}
logger.info(f"注册工具: {name}")
def get_or_create_conversation(self, conversation_id: Optional[str] = None) -> Conversation:
"""获取或创建对话"""
if conversation_id and conversation_id in self.conversations:
return self.conversations[conversation_id]
conv = Conversation(conversation_id=conversation_id or uuid.uuid4().hex)
# 添加系统提示
conv.add_message("system", self.agent_config.system_prompt)
self.conversations[conv.conversation_id] = conv
return conv
async def chat(
self,
message: str,
conversation_id: Optional[str] = None,
stream: bool = False
) -> str | AsyncGenerator[str, None]:
"""
发送消息并获取回复
Args:
message: 用户消息
conversation_id: 对话ID(用于多轮对话)
stream: 是否流式响应
Returns:
如果stream=False,返回完整回复字符串
如果stream=True,返回异步生成器
"""
# 获取对话上下文
conversation = self.get_or_create_conversation(conversation_id)
conversation.add_message("user", message)
if stream:
return self._stream_chat(conversation)
else:
return await self._simple_chat(conversation)
async def _simple_chat(self, conversation: Conversation) -> str:
"""非流式对话"""
client = await self._get_client()
request_body = {
"model": self.llm_config.model,
"messages": conversation.to_openai_format(),
"temperature": self.llm_config.temperature,
"max_tokens": self.llm_config.max_tokens
}
logger.debug("发送请求", endpoint=self.llm_config.chat_endpoint)
try:
response = await client.post(
self.llm_config.chat_endpoint,
json=request_body
)
response.raise_for_status()
result = response.json()
assistant_message = result["choices"][0]["message"]["content"]
# 保存助手回复到对话
conversation.add_message("assistant", assistant_message)
logger.info("收到回复", length=len(assistant_message))
return assistant_message
except httpx.HTTPStatusError as e:
logger.error("HTTP错误", status_code=e.response.status_code, detail=e.response.text)
raise
except Exception as e:
logger.error("请求失败", error=str(e))
raise
async def _stream_chat(self, conversation: Conversation) -> AsyncGenerator[str, None]:
"""流式对话"""
client = await self._get_client()
request_body = {
"model": self.llm_config.model,
"messages": conversation.to_openai_format(),
"temperature": self.llm_config.temperature,
"max_tokens": self.llm_config.max_tokens,
"stream": True
}
full_response = ""
try:
async with client.stream(
"POST",
self.llm_config.chat_endpoint,
json=request_body
) as response:
response.raise_for_status()
async for line in response.aiter_lines():
if line.startswith("data: "):
data = line[6:]
if data == "[DONE]":
break
try:
chunk = json.loads(data)
delta = chunk.get("choices", [{}])[0].get("delta", {})
content = delta.get("content", "")
if content:
full_response += content
yield content
except json.JSONDecodeError:
continue
# 保存完整回复到对话
conversation.add_message("assistant", full_response)
except Exception as e:
logger.error("流式请求失败", error=str(e))
raise
async def invoke_tool(self, tool_name: str, **kwargs) -> Any:
"""调用注册的工具"""
if tool_name not in self.tools:
raise ValueError(f"未找到工具: {tool_name}")
tool = self.tools[tool_name]
func = tool["function"]
logger.info(f"调用工具: {tool_name}", kwargs=kwargs)
if asyncio.iscoroutinefunction(func):
return await func(**kwargs)
else:
return func(**kwargs)
# 示例工具函数
def tool_get_current_time() -> str:
"""获取当前时间"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def tool_calculate(expression: str) -> str:
"""计算数学表达式"""
try:
# 安全的数学计算
allowed_chars = set("0123456789+-*/.() ")
if not all(c in allowed_chars for c in expression):
return "错误: 不支持的字符"
result = eval(expression)
return str(result)
except Exception as e:
return f"计算错误: {str(e)}"
async def main():
"""示例用法"""
import os
# 从环境变量或命令行获取配置
api_key = os.getenv("LITELLM_API_KEY")
model = os.getenv("LITELLM_MODEL", "gpt-4")
if not api_key:
print("请设置 LITELLM_API_KEY 环境变量")
print("示例: export LITELLM_API_KEY='your-api-key'")
return
# 创建Agent
agent = LiteLLMAgent(api_key=api_key, model=model)
# 注册工具
agent.register_tool("get_time", tool_get_current_time, "获取当前时间")
agent.register_tool("calculate", tool_calculate, "计算数学表达式")
try:
# 简单对话示例
print("\n=== 简单对话 ===")
response = await agent.chat("你好,请介绍一下你自己")
print(f"Agent: {response}")
# 多轮对话示例
print("\n=== 多轮对话 ===")
conv_id = "test-conversation"
response1 = await agent.chat("我的名字叫张三", conversation_id=conv_id)
print(f"Agent: {response1}")
response2 = await agent.chat("你还记得我的名字吗?", conversation_id=conv_id)
print(f"Agent: {response2}")
# 流式响应示例
print("\n=== 流式响应 ===")
print("Agent: ", end="", flush=True)
stream = await agent.chat("请用3句话描述Python编程语言", stream=True)
async for chunk in stream:
print(chunk, end="", flush=True)
print()
finally:
await agent.close()
if __name__ == "__main__":
asyncio.run(main())
-141
View File
@@ -1,141 +0,0 @@
"""
LiteLLM Agent 配置模块
支持用户传入密钥和模型名称
"""
import os
from dataclasses import dataclass, field
from typing import Optional
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
@dataclass
class LiteLLMConfig:
"""LiteLLM 配置"""
# 基础URL - 用户提供的LiteLLM服务地址
base_url: str = "https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io"
# 完整的chat completions端点
chat_endpoint: str = field(init=False)
# API密钥 - 需要用户传入
api_key: Optional[str] = None
# 模型名称 - 需要用户传入
model: Optional[str] = None
# 请求超时时间(秒)
timeout: int = 120
# 最大重试次数
max_retries: int = 3
# 温度参数
temperature: float = 0.7
# 最大token数
max_tokens: int = 4096
def __post_init__(self):
self.chat_endpoint = f"{self.base_url}/chat/completions"
# 从环境变量读取(如果未直接提供)
if self.api_key is None:
self.api_key = os.getenv("LITELLM_API_KEY")
if self.model is None:
self.model = os.getenv("LITELLM_MODEL", "gpt-4")
def validate(self) -> bool:
"""验证配置是否完整"""
if not self.api_key:
raise ValueError("API密钥未设置! 请设置 LITELLM_API_KEY 环境变量或直接传入 api_key")
if not self.model:
raise ValueError("模型名称未设置! 请设置 LITELLM_MODEL 环境变量或直接传入 model")
return True
@dataclass
class AgentConfig:
"""Agent 配置"""
# Agent名称
name: str = "xiaohei-agent"
# Agent描述
description: str = "一个基于LiteLLM的智能Agent,支持A2A协议"
# Agent版本
version: str = "1.0.0"
# 服务端口
port: int = 8080
# 服务主机
host: str = "0.0.0.0"
# 是否启用流式响应
enable_streaming: bool = True
# 系统提示词
system_prompt: str = """你是小黑Agent,一个智能助手。
你可以帮助用户完成各种任务,包括:
- 回答问题
- 代码编写和解释
- 文档分析
- 任务规划
请用中文回答用户的问题,保持友好和专业。"""
@dataclass
class A2AConfig:
"""A2A协议配置"""
# A2A协议版本
protocol_version: str = "1.0"
# Agent Card配置
agent_card: dict = field(default_factory=lambda: {
"name": "xiaohei-agent",
"description": "基于LiteLLM的智能Agent,支持A2A协议通信",
"version": "1.0.0",
"capabilities": {
"text": True,
"streaming": True,
"push_notifications": False
},
"skills": [
{
"id": "general-assistant",
"name": "通用助手",
"description": "回答问题、提供建议、协助任务"
},
{
"id": "code-helper",
"name": "代码助手",
"description": "编写、解释和调试代码"
}
]
})
def get_config(
api_key: Optional[str] = None,
model: Optional[str] = None
) -> tuple[LiteLLMConfig, AgentConfig, A2AConfig]:
"""
获取完整配置
Args:
api_key: LiteLLM API密钥(可选,也可通过环境变量设置)
model: 模型名称(可选,也可通过环境变量设置)
Returns:
(LiteLLMConfig, AgentConfig, A2AConfig) 配置元组
"""
litellm_config = LiteLLMConfig(api_key=api_key, model=model)
agent_config = AgentConfig()
a2a_config = A2AConfig()
return litellm_config, agent_config, a2a_config
@@ -1,27 +0,0 @@
#!/bin/bash
# 部署小黑Agent到AKS
echo "开始部署小黑Agent到AKS..."
# 应用ConfigMap
kubectl apply -f k8s/configmap.yaml
# 应用Secret
kubectl apply -f k8s/secret.yaml
# 应用Deployment
kubectl apply -f k8s/deployment.yaml
# 应用Service
kubectl apply -f k8s/service.yaml
echo "部署完成!"
echo ""
echo "查看Pod状态:"
echo " kubectl get pods -l app=xiaohei-agent"
echo ""
echo "查看Service状态:"
echo " kubectl get svc xiaohei-agent-service"
echo ""
echo "查看外网IP (可能需要几分钟):"
echo " kubectl get svc xiaohei-agent-service -w"
@@ -1,11 +0,0 @@
# 小黑Agent ConfigMap
# 存储非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
name: xiaohei-config
labels:
app: xiaohei-agent
data:
litellm-model: "taiji/gpt-4o-mini"
@@ -1,70 +0,0 @@
# 小黑Agent Kubernetes 部署配置
# 适用于 AKS (Azure Kubernetes Service)
apiVersion: apps/v1
kind: Deployment
metadata:
name: xiaohei-agent
labels:
app: xiaohei-agent
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: xiaohei-agent
template:
metadata:
labels:
app: xiaohei-agent
version: v1
spec:
containers:
- name: xiaohei-agent
image: agnettaiji.azurecr.io/xiaohei-agent:latest
imagePullPolicy: Always
ports:
- containerPort: 9000
protocol: TCP
env:
- name: LITELLM_API_KEY
valueFrom:
secretKeyRef:
name: xiaohei-secrets
key: litellm-api-key
- name: LITELLM_MODEL
valueFrom:
configMapKeyRef:
name: xiaohei-config
key: litellm-model
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# 存活探针
livenessProbe:
httpGet:
path: /health
port: 9000
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
# 就绪探针
readinessProbe:
httpGet:
path: /health
port: 9000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
# 安全上下文
securityContext:
runAsNonRoot: true
runAsUser: 1000
# 优雅终止
terminationGracePeriodSeconds: 30
@@ -1,13 +0,0 @@
# 小黑Agent Secret
# 存储敏感配置(API密钥等)
# 注意: 实际部署时请使用 Azure Key Vault 或 kubectl create secret
apiVersion: v1
kind: Secret
metadata:
name: xiaohei-secrets
labels:
app: xiaohei-agent
type: Opaque
stringData:
litellm-api-key: "sk-7tvnni908QJzuuUWedvx5w"
@@ -1,18 +0,0 @@
# 小黑Agent Kubernetes Service
# 暴露服务供外部访问
apiVersion: v1
kind: Service
metadata:
name: xiaohei-agent-service
labels:
app: xiaohei-agent
spec:
type: LoadBalancer # 使用Azure LoadBalancer暴露外网
selector:
app: xiaohei-agent
ports:
- name: http
port: 80
targetPort: 9000
protocol: TCP
@@ -1,7 +0,0 @@
# LiteLLM Agent API服务依赖
httpx>=0.27.0
fastapi>=0.115.0
uvicorn[standard]>=0.32.0
pydantic>=2.0.0
python-dotenv>=1.0.0
structlog>=24.0.0