发布类型: [feature/bugfix/hotfix/docs]
影响范围: [core/api/ui/config] 测试状态: [passed/failed/pending] 回滚策略: [已准备/无需回滚]
This commit is contained in:
parent
7dd48c5781
commit
09a42d29ea
|
|
@ -0,0 +1,227 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Agent Identity Manager
|
||||
为每个AI agent提供独立的git身份和提交能力
|
||||
|
||||
这个系统让每个agent拥有:
|
||||
- 独立的SSH key对
|
||||
- 独立的GPG签名key
|
||||
- 独立的git配置(name, email)
|
||||
- 可追溯的提交历史
|
||||
|
||||
模拟真实团队协作,而非内部讨论
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
import logging
|
||||
|
||||
class AgentIdentity:
|
||||
"""单个agent的身份信息"""
|
||||
def __init__(self, name: str, email: str, role: str):
|
||||
self.name = name
|
||||
self.email = email
|
||||
self.role = role
|
||||
self.ssh_key_path = None
|
||||
self.gpg_key_id = None
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
return {
|
||||
"name": self.name,
|
||||
"email": self.email,
|
||||
"role": self.role,
|
||||
"ssh_key_path": str(self.ssh_key_path) if self.ssh_key_path else None,
|
||||
"gpg_key_id": self.gpg_key_id
|
||||
}
|
||||
|
||||
class AgentIdentityManager:
|
||||
"""管理所有agent的身份和git操作"""
|
||||
|
||||
def __init__(self, base_dir: str = "/home/ben/github/liurenchaxin"):
|
||||
self.base_dir = Path(base_dir)
|
||||
self.agents_dir = self.base_dir / "agents"
|
||||
self.keys_dir = self.agents_dir / "keys"
|
||||
self.config_file = self.agents_dir / "identities.json"
|
||||
|
||||
# 确保目录存在
|
||||
self.agents_dir.mkdir(exist_ok=True)
|
||||
self.keys_dir.mkdir(exist_ok=True)
|
||||
|
||||
self.identities: Dict[str, AgentIdentity] = {}
|
||||
self.load_identities()
|
||||
|
||||
def load_identities(self):
|
||||
"""从配置文件加载agent身份"""
|
||||
if self.config_file.exists():
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
for name, identity_data in data.items():
|
||||
identity = AgentIdentity(
|
||||
identity_data["name"],
|
||||
identity_data["email"],
|
||||
identity_data["role"]
|
||||
)
|
||||
identity.ssh_key_path = Path(identity_data["ssh_key_path"]) if identity_data["ssh_key_path"] else None
|
||||
identity.gpg_key_id = identity_data["gpg_key_id"]
|
||||
self.identities[name] = identity
|
||||
|
||||
def save_identities(self):
|
||||
"""保存agent身份到配置文件"""
|
||||
data = {name: identity.to_dict() for name, identity in self.identities.items()}
|
||||
with open(self.config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
def create_agent(self, name: str, email: str, role: str) -> AgentIdentity:
|
||||
"""创建新的agent身份"""
|
||||
if name in self.identities:
|
||||
raise ValueError(f"Agent {name} 已存在")
|
||||
|
||||
identity = AgentIdentity(name, email, role)
|
||||
|
||||
# 生成SSH key
|
||||
ssh_key_path = self.keys_dir / f"{name}_rsa"
|
||||
self._generate_ssh_key(name, email, ssh_key_path)
|
||||
identity.ssh_key_path = ssh_key_path
|
||||
|
||||
# 生成GPG key
|
||||
gpg_key_id = self._generate_gpg_key(name, email)
|
||||
identity.gpg_key_id = gpg_key_id
|
||||
|
||||
self.identities[name] = identity
|
||||
self.save_identities()
|
||||
|
||||
logging.info(f"创建agent: {name} ({role})")
|
||||
return identity
|
||||
|
||||
def _generate_ssh_key(self, name: str, email: str, key_path: Path):
|
||||
"""为agent生成SSH key"""
|
||||
cmd = [
|
||||
"ssh-keygen",
|
||||
"-t", "rsa",
|
||||
"-b", "4096",
|
||||
"-C", email,
|
||||
"-f", str(key_path),
|
||||
"-N", "" # 空密码
|
||||
]
|
||||
|
||||
try:
|
||||
subprocess.run(cmd, check=True, capture_output=True)
|
||||
logging.info(f"SSH key已生成: {key_path}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.error(f"生成SSH key失败: {e}")
|
||||
raise
|
||||
|
||||
def _generate_gpg_key(self, name: str, email: str) -> str:
|
||||
"""为agent生成GPG key"""
|
||||
# 这里简化处理,实际应该使用python-gnupg库
|
||||
# 返回模拟的key ID
|
||||
return f"{name.upper()}12345678"
|
||||
|
||||
def switch_to_agent(self, agent_name: str):
|
||||
"""切换到指定agent身份"""
|
||||
if agent_name not in self.identities:
|
||||
raise ValueError(f"Agent {agent_name} 不存在")
|
||||
|
||||
identity = self.identities[agent_name]
|
||||
|
||||
# 设置git配置
|
||||
commands = [
|
||||
["git", "config", "user.name", identity.name],
|
||||
["git", "config", "user.email", identity.email],
|
||||
["git", "config", "user.signingkey", identity.gpg_key_id],
|
||||
["git", "config", "commit.gpgsign", "true"]
|
||||
]
|
||||
|
||||
for cmd in commands:
|
||||
try:
|
||||
subprocess.run(cmd, check=True, cwd=self.base_dir)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.error(f"设置git配置失败: {e}")
|
||||
raise
|
||||
|
||||
# 设置SSH key (通过ssh-agent)
|
||||
if identity.ssh_key_path and identity.ssh_key_path.exists():
|
||||
self._setup_ssh_agent(identity.ssh_key_path)
|
||||
|
||||
logging.info(f"已切换到agent: {agent_name}")
|
||||
|
||||
def _setup_ssh_agent(self, key_path: Path):
|
||||
"""设置SSH agent使用指定key"""
|
||||
# 这里简化处理,实际应该管理ssh-agent
|
||||
os.environ["GIT_SSH_COMMAND"] = f"ssh -i {key_path}"
|
||||
|
||||
def commit_as_agent(self, agent_name: str, message: str, files: List[str] = None):
|
||||
"""以指定agent身份提交代码"""
|
||||
self.switch_to_agent(agent_name)
|
||||
|
||||
# 添加文件
|
||||
if files:
|
||||
subprocess.run(["git", "add"] + files, check=True, cwd=self.base_dir)
|
||||
else:
|
||||
subprocess.run(["git", "add", "."], check=True, cwd=self.base_dir)
|
||||
|
||||
# 提交
|
||||
subprocess.run(["git", "commit", "-S", "-m", message], check=True, cwd=self.base_dir)
|
||||
|
||||
logging.info(f"Agent {agent_name} 提交: {message}")
|
||||
|
||||
def list_agents(self) -> List[Dict]:
|
||||
"""列出所有agent"""
|
||||
return [identity.to_dict() for identity in self.identities.values()]
|
||||
|
||||
def get_agent_stats(self, agent_name: str) -> Dict:
|
||||
"""获取agent的git统计信息"""
|
||||
if agent_name not in self.identities:
|
||||
raise ValueError(f"Agent {agent_name} 不存在")
|
||||
|
||||
identity = self.identities[agent_name]
|
||||
|
||||
# 获取提交统计
|
||||
cmd = [
|
||||
"git", "log", "--author", identity.email,
|
||||
"--pretty=format:%h|%an|%ae|%ad|%s",
|
||||
"--date=short"
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.base_dir)
|
||||
commits = result.stdout.strip().split('\n') if result.stdout.strip() else []
|
||||
|
||||
return {
|
||||
"agent_name": agent_name,
|
||||
"total_commits": len(commits),
|
||||
"commits": commits[:10] # 最近10条
|
||||
}
|
||||
except subprocess.CalledProcessError:
|
||||
return {
|
||||
"agent_name": agent_name,
|
||||
"total_commits": 0,
|
||||
"commits": []
|
||||
}
|
||||
|
||||
# 使用示例和初始化
|
||||
if __name__ == "__main__":
|
||||
manager = AgentIdentityManager()
|
||||
|
||||
# 创建示例agents
|
||||
agents_config = [
|
||||
{"name": "claude-ai", "email": "claude@ai-collaboration.local", "role": "架构师"},
|
||||
{"name": "gemini-dev", "email": "gemini@ai-collaboration.local", "role": "开发者"},
|
||||
{"name": "qwen-ops", "email": "qwen@ai-collaboration.local", "role": "运维"},
|
||||
{"name": "llama-research", "email": "llama@ai-collaboration.local", "role": "研究员"}
|
||||
]
|
||||
|
||||
for agent in agents_config:
|
||||
try:
|
||||
manager.create_agent(agent["name"], agent["email"], agent["role"])
|
||||
print(f"✅ 创建agent: {agent['name']}")
|
||||
except ValueError as e:
|
||||
print(f"⚠️ {e}")
|
||||
|
||||
print("\n📊 当前agent列表:")
|
||||
for agent in manager.list_agents():
|
||||
print(f" - {agent['name']} ({agent['role']}) - {agent['email']}")
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Agent协作演示
|
||||
展示如何让不同AI agent以真实身份协作完成任务
|
||||
|
||||
这个演示模拟以下场景:
|
||||
1. 架构师agent设计系统架构
|
||||
2. 开发者agent实现核心功能
|
||||
3. 运维agent配置部署
|
||||
4. 研究员agent撰写文档
|
||||
|
||||
每个步骤都有真实的git提交记录
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
from agent_identity_manager import AgentIdentityManager
|
||||
|
||||
class AgentCollaborationDemo:
|
||||
def __init__(self):
|
||||
self.manager = AgentIdentityManager()
|
||||
self.base_dir = Path("/home/ben/github/liurenchaxin")
|
||||
|
||||
def create_demo_files(self):
|
||||
"""创建演示用的文件"""
|
||||
demo_dir = self.base_dir / "demo_feature"
|
||||
demo_dir.mkdir(exist_ok=True)
|
||||
|
||||
# 架构师的设计文档
|
||||
architecture_file = demo_dir / "architecture.md"
|
||||
architecture_content = """# 新功能架构设计
|
||||
|
||||
## 概述
|
||||
设计一个智能监控系统,用于跟踪AI agent的工作状态。
|
||||
|
||||
## 组件设计
|
||||
- 状态收集器:收集各agent的运行状态
|
||||
- 分析引擎:分析agent行为模式
|
||||
- 告警系统:异常行为实时通知
|
||||
|
||||
## 技术栈
|
||||
- Python 3.9+
|
||||
- Redis作为消息队列
|
||||
- PostgreSQL存储状态数据
|
||||
- FastAPI提供REST接口
|
||||
"""
|
||||
architecture_file.write_text(architecture_content)
|
||||
|
||||
# 开发者的实现代码
|
||||
core_file = demo_dir / "monitor.py"
|
||||
core_content = """#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any
|
||||
|
||||
class AgentMonitor:
|
||||
def __init__(self):
|
||||
self.agents_status = {}
|
||||
|
||||
async def collect_status(self, agent_name: str) -> Dict[str, Any]:
|
||||
return {
|
||||
"name": agent_name,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"status": "active",
|
||||
"tasks_completed": 0
|
||||
}
|
||||
|
||||
async def run(self):
|
||||
while True:
|
||||
# 模拟状态收集
|
||||
await asyncio.sleep(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
monitor = AgentMonitor()
|
||||
asyncio.run(monitor.run())
|
||||
"""
|
||||
core_file.write_text(core_content)
|
||||
|
||||
# 运维的配置文件
|
||||
config_file = demo_dir / "deploy.yaml"
|
||||
config_content = """version: '3.8'
|
||||
services:
|
||||
agent-monitor:
|
||||
build: .
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- DB_URL=postgresql://user:pass@postgres:5432/agentdb
|
||||
depends_on:
|
||||
- redis
|
||||
- postgres
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
postgres:
|
||||
image: postgres:13
|
||||
environment:
|
||||
POSTGRES_DB: agentdb
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: pass
|
||||
"""
|
||||
config_file.write_text(config_content)
|
||||
|
||||
# 研究员的文档
|
||||
docs_file = demo_dir / "usage_guide.md"
|
||||
docs_content = """# Agent监控系统使用指南
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 启动监控服务
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 2. 查看agent状态
|
||||
```bash
|
||||
curl http://localhost:8000/api/agents
|
||||
```
|
||||
|
||||
### 3. 配置告警
|
||||
编辑 `config/alerts.yaml` 文件设置告警规则。
|
||||
|
||||
## API文档
|
||||
|
||||
### GET /api/agents
|
||||
获取所有agent的当前状态
|
||||
|
||||
### POST /api/agents/{name}/task
|
||||
记录agent完成的任务
|
||||
"""
|
||||
docs_file.write_text(docs_content)
|
||||
|
||||
return [architecture_file, core_file, config_file, docs_file]
|
||||
|
||||
def run_collaboration_demo(self):
|
||||
"""运行协作演示"""
|
||||
print("🎭 开始Agent协作演示")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. 架构师设计
|
||||
print("1️⃣ 架构师agent开始设计...")
|
||||
files = self.create_demo_files()
|
||||
self.manager.commit_as_agent(
|
||||
"claude-ai",
|
||||
"📐 设计智能监控系统架构 - 添加架构设计文档",
|
||||
[str(f) for f in files[:1]]
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
# 2. 开发者实现
|
||||
print("2️⃣ 开发者agent开始编码...")
|
||||
self.manager.commit_as_agent(
|
||||
"gemini-dev",
|
||||
"💻 实现监控系统核心功能 - 添加AgentMonitor类",
|
||||
[str(files[1])]
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
# 3. 运维配置
|
||||
print("3️⃣ 运维agent配置部署...")
|
||||
self.manager.commit_as_agent(
|
||||
"qwen-ops",
|
||||
"⚙️ 添加Docker部署配置 - 支持一键启动",
|
||||
[str(files[2])]
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
# 4. 研究员文档
|
||||
print("4️⃣ 研究员agent撰写文档...")
|
||||
self.manager.commit_as_agent(
|
||||
"llama-research",
|
||||
"📚 完善使用文档 - 添加API说明和快速指南",
|
||||
[str(files[3])]
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
# 5. 架构师review
|
||||
print("5️⃣ 架构师review并优化...")
|
||||
optimize_file = self.base_dir / "demo_feature" / "optimization.md"
|
||||
optimize_content = """# 架构优化建议
|
||||
|
||||
基于实现代码的review,提出以下优化:
|
||||
|
||||
## 性能优化
|
||||
- 使用asyncio.create_task替换直接调用
|
||||
- 添加连接池管理
|
||||
|
||||
## 监控增强
|
||||
- 添加prometheus指标收集
|
||||
- 实现健康检查端点
|
||||
|
||||
## 下一步计划
|
||||
1. 实现告警系统
|
||||
2. 添加Web界面
|
||||
3. 集成日志分析
|
||||
"""
|
||||
optimize_file.write_text(optimize_content)
|
||||
|
||||
self.manager.commit_as_agent(
|
||||
"claude-ai",
|
||||
"🔍 架构review - 提出性能优化和监控增强建议",
|
||||
[str(optimize_file)]
|
||||
)
|
||||
|
||||
print("\n✅ 协作演示完成!")
|
||||
|
||||
def show_git_history(self):
|
||||
"""显示git提交历史"""
|
||||
print("\n📊 Git提交历史(按agent分组):")
|
||||
print("=" * 50)
|
||||
|
||||
for agent_name in ["claude-ai", "gemini-dev", "qwen-ops", "llama-research"]:
|
||||
stats = self.manager.get_agent_stats(agent_name)
|
||||
if stats["commits"]:
|
||||
print(f"\n👤 {agent_name}:")
|
||||
for commit in stats["commits"]:
|
||||
parts = commit.split("|", 4)
|
||||
if len(parts) >= 5:
|
||||
hash_id, name, email, date, message = parts
|
||||
print(f" {hash_id[:8]} {date} {message}")
|
||||
|
||||
def cleanup_demo(self):
|
||||
"""清理演示文件"""
|
||||
demo_dir = self.base_dir / "demo_feature"
|
||||
if demo_dir.exists():
|
||||
# 保留git历史,只移除工作区文件
|
||||
subprocess.run(["git", "rm", "-rf", "demo_feature"],
|
||||
cwd=self.base_dir, capture_output=True)
|
||||
subprocess.run(["git", "commit", "-m", "🧹 清理演示文件 - 保留协作历史"],
|
||||
cwd=self.base_dir, capture_output=True)
|
||||
print("🧹 演示文件已清理,git历史保留")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
demo = AgentCollaborationDemo()
|
||||
|
||||
print("🎭 AI Agent协作演示")
|
||||
print("=" * 50)
|
||||
print("这个演示将展示如何让不同agent以真实身份协作")
|
||||
print("每个agent都有独立的git身份和提交记录")
|
||||
print("")
|
||||
|
||||
# 检查agent是否已创建
|
||||
if not demo.manager.list_agents():
|
||||
print("❌ 请先运行 ./agents/setup_agents.sh 创建agent")
|
||||
return
|
||||
|
||||
# 运行演示
|
||||
demo.run_collaboration_demo()
|
||||
demo.show_git_history()
|
||||
|
||||
print("\n💡 下一步:")
|
||||
print("1. 查看git log --oneline --graph 查看提交历史")
|
||||
print("2. 使用 ./agents/stats.sh 查看agent统计")
|
||||
print("3. 开始你自己的多agent协作项目!")
|
||||
|
||||
# 询问是否清理
|
||||
response = input("\n是否清理演示文件?(y/N): ")
|
||||
if response.lower() == 'y':
|
||||
demo.cleanup_demo()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
"""
|
||||
Git 协作管理系统
|
||||
管理 Agent 之间基于 Git 的真实协作
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple, Any
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from .identity_manager import AgentIdentityManager
|
||||
|
||||
@dataclass
|
||||
class Repository:
|
||||
"""仓库信息"""
|
||||
name: str
|
||||
local_path: str
|
||||
remotes: Dict[str, str] # remote_name -> url
|
||||
current_agent: Optional[str] = None
|
||||
|
||||
class GitCollaborationManager:
|
||||
"""Git 协作管理器"""
|
||||
|
||||
def __init__(self, identity_manager: AgentIdentityManager):
|
||||
self.identity_manager = identity_manager
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.repositories = {}
|
||||
self._load_repositories()
|
||||
|
||||
def _load_repositories(self):
|
||||
"""加载仓库配置"""
|
||||
config_file = Path("config/repositories.json")
|
||||
if config_file.exists():
|
||||
with open(config_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
self.repositories = {
|
||||
name: Repository(**repo_data)
|
||||
for name, repo_data in data.items()
|
||||
}
|
||||
|
||||
def _save_repositories(self):
|
||||
"""保存仓库配置"""
|
||||
config_file = Path("config/repositories.json")
|
||||
config_file.parent.mkdir(exist_ok=True)
|
||||
|
||||
data = {
|
||||
name: {
|
||||
'name': repo.name,
|
||||
'local_path': repo.local_path,
|
||||
'remotes': repo.remotes,
|
||||
'current_agent': repo.current_agent
|
||||
}
|
||||
for name, repo in self.repositories.items()
|
||||
}
|
||||
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
def setup_progressive_deployment(self,
|
||||
repo_name: str,
|
||||
gitea_url: str,
|
||||
bitbucket_url: str,
|
||||
github_url: str,
|
||||
local_path: Optional[str] = None):
|
||||
"""设置渐进发布的三个远程仓库"""
|
||||
|
||||
if not local_path:
|
||||
local_path_str = f"./repos/{repo_name}"
|
||||
else:
|
||||
local_path_str = local_path
|
||||
|
||||
local_path_obj = Path(local_path_str)
|
||||
local_path_obj.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 初始化本地仓库(如果不存在)
|
||||
if not (local_path_obj / ".git").exists():
|
||||
subprocess.run(["git", "init"], cwd=local_path)
|
||||
|
||||
# 设置远程仓库
|
||||
remotes = {
|
||||
"gitea": gitea_url,
|
||||
"bitbucket": bitbucket_url,
|
||||
"github": github_url
|
||||
}
|
||||
|
||||
for remote_name, remote_url in remotes.items():
|
||||
# 检查远程是否已存在
|
||||
result = subprocess.run([
|
||||
"git", "remote", "get-url", remote_name
|
||||
], cwd=local_path, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
# 添加新的远程
|
||||
subprocess.run([
|
||||
"git", "remote", "add", remote_name, remote_url
|
||||
], cwd=local_path)
|
||||
else:
|
||||
# 更新现有远程
|
||||
subprocess.run([
|
||||
"git", "remote", "set-url", remote_name, remote_url
|
||||
], cwd=local_path)
|
||||
|
||||
# 创建仓库记录
|
||||
repository = Repository(
|
||||
name=repo_name,
|
||||
local_path=str(local_path),
|
||||
remotes=remotes
|
||||
)
|
||||
|
||||
self.repositories[repo_name] = repository
|
||||
self._save_repositories()
|
||||
|
||||
self.logger.info(f"设置渐进发布仓库: {repo_name}")
|
||||
return repository
|
||||
|
||||
def switch_agent_context(self, repo_name: str, agent_name: str):
|
||||
"""切换仓库的 Agent 上下文"""
|
||||
if repo_name not in self.repositories:
|
||||
raise ValueError(f"仓库 {repo_name} 不存在")
|
||||
|
||||
repository = self.repositories[repo_name]
|
||||
|
||||
# 设置 Git 配置
|
||||
self.identity_manager.setup_git_config(agent_name, repository.local_path)
|
||||
|
||||
# 设置 SSH 密钥
|
||||
identity = self.identity_manager.get_agent_identity(agent_name)
|
||||
if identity:
|
||||
self._setup_ssh_agent(identity.ssh_key_path)
|
||||
|
||||
repository.current_agent = agent_name
|
||||
self._save_repositories()
|
||||
|
||||
self.logger.info(f"切换仓库 {repo_name} 到 Agent: {agent_name}")
|
||||
|
||||
def _setup_ssh_agent(self, ssh_key_path: str):
|
||||
"""设置 SSH Agent"""
|
||||
try:
|
||||
# 启动 ssh-agent(如果未运行)
|
||||
result = subprocess.run([
|
||||
"ssh-add", "-l"
|
||||
], capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
# 启动 ssh-agent
|
||||
result = subprocess.run([
|
||||
"ssh-agent", "-s"
|
||||
], capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
# 解析环境变量
|
||||
for line in result.stdout.split('\n'):
|
||||
if 'SSH_AUTH_SOCK' in line:
|
||||
sock = line.split('=')[1].split(';')[0]
|
||||
os.environ['SSH_AUTH_SOCK'] = sock
|
||||
elif 'SSH_AGENT_PID' in line:
|
||||
pid = line.split('=')[1].split(';')[0]
|
||||
os.environ['SSH_AGENT_PID'] = pid
|
||||
|
||||
# 添加 SSH 密钥
|
||||
subprocess.run(["ssh-add", ssh_key_path])
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"SSH Agent 设置失败: {e}")
|
||||
|
||||
def commit_as_agent(self,
|
||||
repo_name: str,
|
||||
message: str,
|
||||
files: Optional[List[str]] = None,
|
||||
sign: bool = True) -> bool:
|
||||
"""以当前 Agent 身份提交代码"""
|
||||
|
||||
if repo_name not in self.repositories:
|
||||
raise ValueError(f"仓库 {repo_name} 不存在")
|
||||
|
||||
repository = self.repositories[repo_name]
|
||||
repo_path = Path(repository.local_path)
|
||||
|
||||
try:
|
||||
# 添加文件
|
||||
if files:
|
||||
for file in files:
|
||||
subprocess.run(["git", "add", file], cwd=repo_path)
|
||||
else:
|
||||
subprocess.run(["git", "add", "."], cwd=repo_path)
|
||||
|
||||
# 提交
|
||||
commit_cmd = ["git", "commit", "-m", message]
|
||||
if sign:
|
||||
commit_cmd.append("-S")
|
||||
|
||||
result = subprocess.run(commit_cmd, cwd=repo_path, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
self.logger.info(f"Agent {repository.current_agent} 提交成功: {message}")
|
||||
return True
|
||||
else:
|
||||
self.logger.error(f"提交失败: {result.stderr}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"提交过程出错: {e}")
|
||||
return False
|
||||
|
||||
def progressive_push(self, repo_name: str, branch: str = "main") -> Dict[str, bool]:
|
||||
"""渐进式推送到三个平台"""
|
||||
|
||||
if repo_name not in self.repositories:
|
||||
raise ValueError(f"仓库 {repo_name} 不存在")
|
||||
|
||||
repository = self.repositories[repo_name]
|
||||
repo_path = Path(repository.local_path)
|
||||
|
||||
results = {}
|
||||
|
||||
# 按顺序推送:Gitea -> Bitbucket -> GitHub
|
||||
push_order = ["gitea", "bitbucket", "github"]
|
||||
|
||||
for remote in push_order:
|
||||
if remote in repository.remotes:
|
||||
try:
|
||||
result = subprocess.run([
|
||||
"git", "push", remote, branch
|
||||
], cwd=repo_path, capture_output=True, text=True)
|
||||
|
||||
results[remote] = result.returncode == 0
|
||||
|
||||
if result.returncode == 0:
|
||||
self.logger.info(f"推送到 {remote} 成功")
|
||||
else:
|
||||
self.logger.error(f"推送到 {remote} 失败: {result.stderr}")
|
||||
# 如果某个平台失败,停止后续推送
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"推送到 {remote} 出错: {e}")
|
||||
results[remote] = False
|
||||
break
|
||||
|
||||
return results
|
||||
|
||||
def create_pull_request_workflow(self,
|
||||
repo_name: str,
|
||||
source_agent: str,
|
||||
target_agent: str,
|
||||
feature_branch: str,
|
||||
title: str,
|
||||
description: str = "") -> bool:
|
||||
"""创建 Agent 间的 Pull Request 工作流"""
|
||||
|
||||
repository = self.repositories[repo_name]
|
||||
repo_path = Path(repository.local_path)
|
||||
|
||||
try:
|
||||
# 1. 切换到源 Agent
|
||||
self.switch_agent_context(repo_name, source_agent)
|
||||
|
||||
# 2. 创建功能分支
|
||||
subprocess.run([
|
||||
"git", "checkout", "-b", feature_branch
|
||||
], cwd=repo_path)
|
||||
|
||||
# 3. 推送功能分支
|
||||
subprocess.run([
|
||||
"git", "push", "-u", "gitea", feature_branch
|
||||
], cwd=repo_path)
|
||||
|
||||
# 4. 这里可以集成 API 调用来创建实际的 PR
|
||||
# 具体实现取决于使用的 Git 平台
|
||||
|
||||
self.logger.info(f"创建 PR 工作流: {source_agent} -> {target_agent}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"创建 PR 工作流失败: {e}")
|
||||
return False
|
||||
|
||||
def get_repository_status(self, repo_name: str) -> Dict[str, Any]:
|
||||
"""获取仓库状态"""
|
||||
if repo_name not in self.repositories:
|
||||
raise ValueError(f"仓库 {repo_name} 不存在")
|
||||
|
||||
repository = self.repositories[repo_name]
|
||||
repo_path = Path(repository.local_path)
|
||||
|
||||
status = {
|
||||
"current_agent": repository.current_agent,
|
||||
"branch": None,
|
||||
"uncommitted_changes": False,
|
||||
"remotes": repository.remotes
|
||||
}
|
||||
|
||||
try:
|
||||
# 获取当前分支
|
||||
result = subprocess.run([
|
||||
"git", "branch", "--show-current"
|
||||
], cwd=repo_path, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
status["branch"] = result.stdout.strip()
|
||||
|
||||
# 检查未提交的更改
|
||||
result = subprocess.run([
|
||||
"git", "status", "--porcelain"
|
||||
], cwd=repo_path, capture_output=True, text=True)
|
||||
|
||||
status["uncommitted_changes"] = bool(result.stdout.strip())
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"获取仓库状态失败: {e}")
|
||||
|
||||
return status
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
"""
|
||||
Agent Identity Management System
|
||||
管理多个 AI Agent 的身份信息,包括 SSH/GPG 密钥、Git 配置等
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
from dataclasses import dataclass, asdict
|
||||
import logging
|
||||
|
||||
@dataclass
|
||||
class AgentIdentity:
|
||||
"""Agent 身份信息"""
|
||||
name: str
|
||||
email: str
|
||||
ssh_key_path: str
|
||||
gpg_key_id: Optional[str] = None
|
||||
git_username: str = ""
|
||||
description: str = ""
|
||||
repositories: List[str] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.repositories is None:
|
||||
self.repositories = []
|
||||
if not self.git_username:
|
||||
self.git_username = self.name.lower().replace(" ", "_")
|
||||
|
||||
class AgentIdentityManager:
|
||||
"""Agent 身份管理器"""
|
||||
|
||||
def __init__(self, config_dir: str = "config/agents"):
|
||||
self.config_dir = Path(config_dir)
|
||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.identities_file = self.config_dir / "identities.json"
|
||||
self.ssh_keys_dir = self.config_dir / "ssh_keys"
|
||||
self.gpg_keys_dir = self.config_dir / "gpg_keys"
|
||||
|
||||
# 创建必要的目录
|
||||
self.ssh_keys_dir.mkdir(exist_ok=True)
|
||||
self.gpg_keys_dir.mkdir(exist_ok=True)
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self._load_identities()
|
||||
|
||||
def _load_identities(self):
|
||||
"""加载已有的身份信息"""
|
||||
if self.identities_file.exists():
|
||||
with open(self.identities_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
self.identities = {
|
||||
name: AgentIdentity(**identity_data)
|
||||
for name, identity_data in data.items()
|
||||
}
|
||||
else:
|
||||
self.identities = {}
|
||||
|
||||
def _save_identities(self):
|
||||
"""保存身份信息到文件"""
|
||||
data = {
|
||||
name: asdict(identity)
|
||||
for name, identity in self.identities.items()
|
||||
}
|
||||
with open(self.identities_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
def create_agent_identity(self,
|
||||
name: str,
|
||||
email: str,
|
||||
description: str = "",
|
||||
generate_keys: bool = True) -> AgentIdentity:
|
||||
"""创建新的 Agent 身份"""
|
||||
|
||||
if name in self.identities:
|
||||
raise ValueError(f"Agent {name} 已存在")
|
||||
|
||||
# 生成 SSH 密钥路径
|
||||
ssh_key_path = str(self.ssh_keys_dir / f"{name.lower().replace(' ', '_')}_rsa")
|
||||
|
||||
identity = AgentIdentity(
|
||||
name=name,
|
||||
email=email,
|
||||
ssh_key_path=ssh_key_path,
|
||||
description=description
|
||||
)
|
||||
|
||||
if generate_keys:
|
||||
self._generate_ssh_key(identity)
|
||||
self._generate_gpg_key(identity)
|
||||
|
||||
self.identities[name] = identity
|
||||
self._save_identities()
|
||||
|
||||
self.logger.info(f"创建 Agent 身份: {name}")
|
||||
return identity
|
||||
|
||||
def _generate_ssh_key(self, identity: AgentIdentity):
|
||||
"""为 Agent 生成 SSH 密钥对"""
|
||||
try:
|
||||
cmd = [
|
||||
"ssh-keygen",
|
||||
"-t", "rsa",
|
||||
"-b", "4096",
|
||||
"-C", identity.email,
|
||||
"-f", identity.ssh_key_path,
|
||||
"-N", "" # 无密码
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"SSH 密钥生成失败: {result.stderr}")
|
||||
|
||||
# 设置正确的权限
|
||||
os.chmod(identity.ssh_key_path, 0o600)
|
||||
os.chmod(f"{identity.ssh_key_path}.pub", 0o644)
|
||||
|
||||
self.logger.info(f"为 {identity.name} 生成 SSH 密钥: {identity.ssh_key_path}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"SSH 密钥生成失败: {e}")
|
||||
raise
|
||||
|
||||
def _generate_gpg_key(self, identity: AgentIdentity):
|
||||
"""为 Agent 生成 GPG 密钥"""
|
||||
try:
|
||||
# GPG 密钥生成配置
|
||||
gpg_config = f"""
|
||||
Key-Type: RSA
|
||||
Key-Length: 4096
|
||||
Subkey-Type: RSA
|
||||
Subkey-Length: 4096
|
||||
Name-Real: {identity.name}
|
||||
Name-Email: {identity.email}
|
||||
Expire-Date: 0
|
||||
%no-protection
|
||||
%commit
|
||||
"""
|
||||
|
||||
# 写入临时配置文件
|
||||
config_file = self.gpg_keys_dir / f"{identity.git_username}_gpg_config"
|
||||
with open(config_file, 'w') as f:
|
||||
f.write(gpg_config)
|
||||
|
||||
# 生成 GPG 密钥
|
||||
cmd = ["gpg", "--batch", "--generate-key", str(config_file)]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
self.logger.warning(f"GPG 密钥生成失败: {result.stderr}")
|
||||
return
|
||||
|
||||
# 获取生成的密钥 ID
|
||||
cmd = ["gpg", "--list-secret-keys", "--keyid-format", "LONG", identity.email]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
# 解析密钥 ID
|
||||
lines = result.stdout.split('\n')
|
||||
for line in lines:
|
||||
if 'sec' in line and 'rsa4096/' in line:
|
||||
key_id = line.split('rsa4096/')[1].split(' ')[0]
|
||||
identity.gpg_key_id = key_id
|
||||
break
|
||||
|
||||
# 清理临时文件
|
||||
config_file.unlink()
|
||||
|
||||
self.logger.info(f"为 {identity.name} 生成 GPG 密钥: {identity.gpg_key_id}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"GPG 密钥生成失败: {e}")
|
||||
|
||||
def get_agent_identity(self, name: str) -> Optional[AgentIdentity]:
|
||||
"""获取 Agent 身份信息"""
|
||||
return self.identities.get(name)
|
||||
|
||||
def list_agents(self) -> List[str]:
|
||||
"""列出所有 Agent"""
|
||||
return list(self.identities.keys())
|
||||
|
||||
def setup_git_config(self, agent_name: str, repo_path: str = "."):
|
||||
"""为指定仓库设置 Agent 的 Git 配置"""
|
||||
identity = self.get_agent_identity(agent_name)
|
||||
if not identity:
|
||||
raise ValueError(f"Agent {agent_name} 不存在")
|
||||
|
||||
repo_path = Path(repo_path)
|
||||
|
||||
# 设置 Git 用户信息
|
||||
subprocess.run([
|
||||
"git", "config", "--local", "user.name", identity.name
|
||||
], cwd=repo_path)
|
||||
|
||||
subprocess.run([
|
||||
"git", "config", "--local", "user.email", identity.email
|
||||
], cwd=repo_path)
|
||||
|
||||
# 设置 GPG 签名
|
||||
if identity.gpg_key_id:
|
||||
subprocess.run([
|
||||
"git", "config", "--local", "user.signingkey", identity.gpg_key_id
|
||||
], cwd=repo_path)
|
||||
|
||||
subprocess.run([
|
||||
"git", "config", "--local", "commit.gpgsign", "true"
|
||||
], cwd=repo_path)
|
||||
|
||||
self.logger.info(f"为仓库 {repo_path} 设置 {agent_name} 的 Git 配置")
|
||||
|
||||
def get_ssh_public_key(self, agent_name: str) -> str:
|
||||
"""获取 Agent 的 SSH 公钥"""
|
||||
identity = self.get_agent_identity(agent_name)
|
||||
if not identity:
|
||||
raise ValueError(f"Agent {agent_name} 不存在")
|
||||
|
||||
pub_key_path = f"{identity.ssh_key_path}.pub"
|
||||
if not os.path.exists(pub_key_path):
|
||||
raise FileNotFoundError(f"SSH 公钥文件不存在: {pub_key_path}")
|
||||
|
||||
with open(pub_key_path, 'r') as f:
|
||||
return f.read().strip()
|
||||
|
||||
def export_gpg_public_key(self, agent_name: str) -> str:
|
||||
"""导出 Agent 的 GPG 公钥"""
|
||||
identity = self.get_agent_identity(agent_name)
|
||||
if not identity or not identity.gpg_key_id:
|
||||
raise ValueError(f"Agent {agent_name} 没有 GPG 密钥")
|
||||
|
||||
cmd = ["gpg", "--armor", "--export", identity.gpg_key_id]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"GPG 公钥导出失败: {result.stderr}")
|
||||
|
||||
return result.stdout
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
#!/bin/bash
|
||||
# Agent协作系统设置脚本
|
||||
# 为一人公司创建多agent git协作环境
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 设置AI Agent协作系统..."
|
||||
|
||||
# 创建必要的目录
|
||||
mkdir -p agents/keys
|
||||
mkdir -p agents/logs
|
||||
|
||||
# 设置权限
|
||||
chmod 700 agents/keys
|
||||
|
||||
# 检查依赖
|
||||
check_dependency() {
|
||||
if ! command -v $1 &> /dev/null; then
|
||||
echo "❌ 需要安装: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_dependency "git"
|
||||
check_dependency "ssh-keygen"
|
||||
|
||||
echo "✅ 依赖检查通过"
|
||||
|
||||
# 初始化agent身份管理器
|
||||
echo "🤖 初始化agent身份..."
|
||||
python3 agents/agent_identity_manager.py
|
||||
|
||||
# 创建git hooks模板
|
||||
cat > agents/pre-commit-hook << 'EOF'
|
||||
#!/bin/bash
|
||||
# Agent提交前的钩子
|
||||
|
||||
echo "🔍 检查agent身份..."
|
||||
AGENT_NAME=$(git config user.name)
|
||||
if [[ -z "$AGENT_NAME" ]]; then
|
||||
echo "❌ 未设置agent身份,请先使用agent协作系统"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 当前agent: $AGENT_NAME"
|
||||
EOF
|
||||
|
||||
chmod +x agents/pre-commit-hook
|
||||
|
||||
# 创建快速切换脚本
|
||||
cat > agents/switch_agent.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 快速切换agent身份
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "用法: ./switch_agent.sh <agent名称>"
|
||||
echo "可用agents:"
|
||||
python3 -c "
|
||||
import sys
|
||||
sys.path.append('agents')
|
||||
from agent_identity_manager import AgentIdentityManager
|
||||
manager = AgentIdentityManager()
|
||||
for agent in manager.list_agents():
|
||||
print(f' - {agent[\"name\"]} ({agent[\"role\"]})')
|
||||
"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AGENT_NAME=$1
|
||||
echo "🔄 切换到agent: $AGENT_NAME"
|
||||
python3 -c "
|
||||
import sys
|
||||
sys.path.append('agents')
|
||||
from agent_identity_manager import AgentIdentityManager
|
||||
manager = AgentIdentityManager()
|
||||
try:
|
||||
manager.switch_to_agent('$AGENT_NAME')
|
||||
print('✅ 切换成功')
|
||||
except Exception as e:
|
||||
print(f'❌ 切换失败: {e}')
|
||||
exit(1)
|
||||
"
|
||||
EOF
|
||||
|
||||
chmod +x agents/switch_agent.sh
|
||||
|
||||
# 创建agent提交脚本
|
||||
cat > agents/commit_as_agent.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 以指定agent身份提交
|
||||
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "用法: ./commit_as_agent.sh <agent名称> \"提交信息\" [文件...]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AGENT_NAME=$1
|
||||
MESSAGE=$2
|
||||
shift 2
|
||||
FILES=$@
|
||||
|
||||
echo "📝 Agent $AGENT_NAME 正在提交..."
|
||||
python3 -c "
|
||||
import sys
|
||||
sys.path.append('agents')
|
||||
from agent_identity_manager import AgentIdentityManager
|
||||
manager = AgentIdentityManager()
|
||||
try:
|
||||
manager.commit_as_agent('$AGENT_NAME', '$MESSAGE', '$FILES'.split() if '$FILES' else None)
|
||||
print('✅ 提交成功')
|
||||
except Exception as e:
|
||||
print(f'❌ 提交失败: {e}')
|
||||
exit(1)
|
||||
"
|
||||
EOF
|
||||
|
||||
chmod +x agents/commit_as_agent.sh
|
||||
|
||||
# 创建统计脚本
|
||||
cat > agents/stats.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 查看agent统计信息
|
||||
|
||||
echo "📊 Agent协作统计"
|
||||
echo "=================="
|
||||
|
||||
python3 -c "
|
||||
import sys
|
||||
sys.path.append('agents')
|
||||
from agent_identity_manager import AgentIdentityManager
|
||||
manager = AgentIdentityManager()
|
||||
|
||||
for agent in manager.list_agents():
|
||||
name = agent['name']
|
||||
stats = manager.get_agent_stats(name)
|
||||
print(f'👤 {name} ({agent["role"]})')
|
||||
print(f' 📧 {agent["email"]}')
|
||||
print(f' 📈 提交数: {stats["total_commits"]}')
|
||||
if stats["commits"]:
|
||||
print(f' 📝 最近提交: {stats["commits"][0]}')
|
||||
print()
|
||||
"
|
||||
EOF
|
||||
|
||||
chmod +x agents/stats.sh
|
||||
|
||||
echo "🎉 设置完成!"
|
||||
echo ""
|
||||
echo "📋 使用说明:"
|
||||
echo "1. 查看agent列表: ./agents/stats.sh"
|
||||
echo "2. 切换agent: ./agents/switch_agent.sh <agent名称>"
|
||||
echo "3. agent提交: ./agents/commit_as_agent.sh <agent名称> \"消息\""
|
||||
echo "4. 查看统计: ./agents/stats.sh"
|
||||
echo ""
|
||||
echo "🔑 SSH公钥位置:"
|
||||
for key in agents/keys/*_rsa.pub; do
|
||||
if [[ -f "$key" ]]; then
|
||||
agent_name=$(basename "$key" _rsa.pub)
|
||||
echo " $agent_name: $key"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "💡 下一步:"
|
||||
echo "1. 将SSH公钥添加到GitHub/Gitea/Bitbucket"
|
||||
echo "2. 测试agent切换和提交功能"
|
||||
echo "3. 开始真正的多agent协作开发!"
|
||||
Loading…
Reference in New Issue