443 lines
14 KiB
Python
443 lines
14 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
多 AI 模型调用脚本 (Universal Architect Engine v4.0)
|
||
|
||
支持双中转站 API:
|
||
- 中转站 GPT: gpt-5.2-codex 等
|
||
- 中转站 Gemini: gemini-2.5-pro, gemini-2.5-flash 等
|
||
|
||
环境变量:
|
||
AI_API_KEY - 中转站 API 密钥 (GPT 模型)
|
||
AI_BASE_URL - 中转站地址 (默认: https://api.gbro.site)
|
||
GEMINI_API_KEY - 中转站 API 密钥 (Gemini 模型,可与 AI_API_KEY 相同)
|
||
GEMINI_BASE_URL - Gemini 中转站地址 (默认: 与 AI_BASE_URL 相同)
|
||
|
||
使用方法:
|
||
python multi_ai.py --model gpt-5.2-codex --task audit --file code.py
|
||
python multi_ai.py --model gemini-2.5-pro --task frontend --prompt "生成登录组件"
|
||
python multi_ai.py --list-models
|
||
python multi_ai.py --test
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import argparse
|
||
import requests
|
||
import json
|
||
|
||
# ============================================
|
||
# 配置
|
||
# ============================================
|
||
|
||
# 默认中转站地址
|
||
DEFAULT_BASE_URL = "https://api.gbro.site"
|
||
|
||
# 模型到 API 源的映射
|
||
MODEL_API_SOURCE = {
|
||
# GPT 模型 → GPT 中转站
|
||
"gpt-5.2-codex": "gpt",
|
||
"gpt-5.2": "gpt",
|
||
"gpt-5.1": "gpt",
|
||
"gpt-5.1-codex": "gpt",
|
||
"gpt-5.1-codex-mini": "gpt",
|
||
"gpt-5.1-codex-max": "gpt",
|
||
|
||
# Gemini 模型 → Gemini 中转站
|
||
"gemini-2.5-pro": "gemini",
|
||
"gemini-2.5-flash": "gemini",
|
||
"gemini-3-pro-preview": "gemini",
|
||
"gemini-3-pro-preview-thinking": "gemini",
|
||
"gemini-2.5-flash-image": "gemini",
|
||
"gemini-2.5-flash-image-preview": "gemini",
|
||
|
||
# Claude 模型 → GPT 中转站
|
||
"claude-sonnet-4-5-20250929": "gpt",
|
||
"claude-opus-4-5": "gpt",
|
||
"claude-haiku-4-5-20251001": "gpt",
|
||
}
|
||
|
||
# 模型别名映射
|
||
MODELS = {
|
||
# ===== OpenAI/GPT 系列 =====
|
||
"gpt-5.2-codex": "gpt-5.2-codex",
|
||
"gpt-5.2": "gpt-5.2",
|
||
"gpt-5.1": "gpt-5.1",
|
||
"codex": "gpt-5.2-codex",
|
||
"openai": "gpt-5.2-codex",
|
||
"gpt": "gpt-5.2-codex",
|
||
|
||
# ===== Gemini 系列 =====
|
||
"gemini-2.5-pro": "gemini-2.5-pro",
|
||
"gemini-2.5-flash": "gemini-2.5-flash",
|
||
"gemini-3-pro": "gemini-3-pro-preview",
|
||
"gemini-3-pro-preview": "gemini-3-pro-preview",
|
||
"gemini": "gemini-2.5-flash",
|
||
"flash": "gemini-2.5-flash",
|
||
|
||
# ===== Claude 系列 =====
|
||
"claude-sonnet": "claude-sonnet-4-5-20250929",
|
||
"claude-opus": "claude-opus-4-5",
|
||
"claude-haiku": "claude-haiku-4-5-20251001",
|
||
"claude": "claude-sonnet-4-5-20250929",
|
||
|
||
# ===== 任务别名 =====
|
||
"audit": "gpt-5.2-codex", # 代码审计用 GPT
|
||
"frontend": "gemini-2.5-flash", # 前端生成用 Gemini
|
||
"design": "gemini-2.5-flash", # 设计系统用 Gemini
|
||
"backend": "gpt-5.2-codex", # 后端逻辑用 GPT
|
||
"fast": "gemini-2.5-flash", # 快速任务用 Gemini Flash
|
||
}
|
||
|
||
# 任务提示词模板
|
||
TASK_PROMPTS = {
|
||
"audit": """你是一位首席架构师,请对以下代码进行深度审计。
|
||
|
||
审计维度:
|
||
1. 🔐 Security: 注入预防、鉴权逻辑、敏感数据泄露
|
||
2. ⚡ Performance: N+1 查询、冗余渲染、索引建议
|
||
3. 🔧 Maintainability: 代码耦合度、模块化清晰度
|
||
|
||
请输出结构化报告,区分:
|
||
- 🚫 BLOCKER (必须修改)
|
||
- 💡 ADVICE (改进建议)
|
||
|
||
代码:
|
||
```
|
||
{content}
|
||
```
|
||
|
||
请用中文回复。""",
|
||
|
||
"frontend": """你是一位高级前端架构师,请根据以下需求生成现代化前端代码。
|
||
|
||
要求:
|
||
1. 使用 React + TypeScript + Tailwind CSS
|
||
2. 组件命名具备清晰的业务语义
|
||
3. 包含完整的类型定义
|
||
4. 预留 OpenTelemetry 埋点
|
||
|
||
需求:
|
||
{content}
|
||
|
||
请用中文注释。""",
|
||
|
||
"design-system": """你是一位 UI/UX 设计系统专家,请根据以下项目信息生成 Design Tokens。
|
||
|
||
项目信息:
|
||
{content}
|
||
|
||
请输出 JSON 格式的 Design Tokens,包括:
|
||
1. colors (primary, secondary, semantic)
|
||
2. spacing (unit, scale)
|
||
3. typography (fontFamily, fontSize)
|
||
4. borderRadius
|
||
5. shadows
|
||
|
||
同时提供关键 UI 组件的设计规范描述。""",
|
||
|
||
"backend": """你是一位后端架构专家,请根据以下需求设计后端架构。
|
||
|
||
要求:
|
||
1. 遵循 SOLID 原则和 Clean Architecture
|
||
2. 考虑高并发和幂等性设计
|
||
3. 预留分布式追踪埋点
|
||
4. 包含异常处理框架
|
||
|
||
需求:
|
||
{content}
|
||
|
||
请用中文注释。""",
|
||
|
||
"contract": """你是一位全栈架构师,请根据以下 API 设计生成前后端握手契约。
|
||
|
||
要求:
|
||
1. TypeScript Interfaces 或 Protobuf 格式
|
||
2. 包含 DTO (Data Transfer Object) 定义
|
||
3. 包含错误码枚举
|
||
4. 确保前端组件与后端 100% 类型对齐
|
||
|
||
API 设计:
|
||
{content}
|
||
|
||
请输出完整的 Handshake Contract。""",
|
||
|
||
"general": """{content}"""
|
||
}
|
||
|
||
|
||
def get_config():
|
||
"""获取配置"""
|
||
# GPT 中转站配置
|
||
gpt_key = os.environ.get("AI_API_KEY")
|
||
gpt_url = os.environ.get("AI_BASE_URL") or DEFAULT_BASE_URL
|
||
|
||
# Gemini 中转站配置(可单独设置,也可复用 GPT 配置)
|
||
gemini_key = os.environ.get("GEMINI_API_KEY") or gpt_key
|
||
gemini_url = os.environ.get("GEMINI_BASE_URL") or gpt_url
|
||
|
||
return {
|
||
"gpt_key": gpt_key,
|
||
"gpt_url": gpt_url,
|
||
"gemini_key": gemini_key,
|
||
"gemini_url": gemini_url
|
||
}
|
||
|
||
|
||
def call_model(model: str, prompt: str, temperature: float = 0.3) -> str:
|
||
"""调用模型"""
|
||
config = get_config()
|
||
|
||
# 解析模型名称
|
||
actual_model = MODELS.get(model, model)
|
||
|
||
# 确定使用哪个 API 源
|
||
api_source = MODEL_API_SOURCE.get(actual_model, "gpt")
|
||
|
||
if api_source == "gemini":
|
||
return call_proxy_api(
|
||
actual_model, prompt,
|
||
config["gemini_key"], config["gemini_url"],
|
||
temperature
|
||
)
|
||
else:
|
||
return call_proxy_api(
|
||
actual_model, prompt,
|
||
config["gpt_key"], config["gpt_url"],
|
||
temperature
|
||
)
|
||
|
||
|
||
def call_proxy_api(model: str, prompt: str, api_key: str, base_url: str, temperature: float) -> str:
|
||
"""调用中转站 API (OpenAI 兼容格式)"""
|
||
if not api_key:
|
||
print("❌ 未设置 API Key 环境变量")
|
||
sys.exit(1)
|
||
|
||
url = f"{base_url}/v1/chat/completions"
|
||
headers = {
|
||
"Content-Type": "application/json",
|
||
"Authorization": f"Bearer {api_key}"
|
||
}
|
||
data = {
|
||
"model": model,
|
||
"messages": [{"role": "user", "content": prompt}],
|
||
"temperature": temperature
|
||
}
|
||
|
||
response = requests.post(url, headers=headers, json=data, timeout=180)
|
||
response.raise_for_status()
|
||
|
||
result = response.json()
|
||
return result["choices"][0]["message"]["content"]
|
||
|
||
|
||
def list_models():
|
||
"""列出可用模型"""
|
||
config = get_config()
|
||
|
||
print("=" * 60)
|
||
print("📋 可用模型列表")
|
||
print("=" * 60)
|
||
|
||
# 显示 GPT 中转站模型
|
||
print("\n🔶 GPT 中转站模型:")
|
||
print(f" 地址: {config['gpt_url']}")
|
||
if config["gpt_key"]:
|
||
url = f"{config['gpt_url']}/v1/models"
|
||
headers = {"Authorization": f"Bearer {config['gpt_key']}"}
|
||
|
||
try:
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
response.raise_for_status()
|
||
|
||
models = response.json().get("data", [])
|
||
gpt_models = [m["id"] for m in models if "gpt" in m["id"].lower() or "codex" in m["id"].lower()]
|
||
|
||
for m in sorted(gpt_models)[:10]:
|
||
print(f" - {m}")
|
||
if len(gpt_models) > 10:
|
||
print(f" ... 还有 {len(gpt_models) - 10} 个")
|
||
|
||
except Exception as e:
|
||
print(f" ❌ 获取失败: {e}")
|
||
else:
|
||
print(" ⚠️ 未设置 AI_API_KEY")
|
||
|
||
# 显示 Gemini 中转站模型
|
||
print("\n🔷 Gemini 中转站模型:")
|
||
print(f" 地址: {config['gemini_url']}")
|
||
if config["gemini_key"]:
|
||
url = f"{config['gemini_url']}/v1/models"
|
||
headers = {"Authorization": f"Bearer {config['gemini_key']}"}
|
||
|
||
try:
|
||
response = requests.get(url, headers=headers, timeout=30)
|
||
response.raise_for_status()
|
||
|
||
models = response.json().get("data", [])
|
||
gemini_models = [m["id"] for m in models if "gemini" in m["id"].lower()]
|
||
|
||
for m in sorted(gemini_models)[:10]:
|
||
print(f" - {m}")
|
||
if len(gemini_models) > 10:
|
||
print(f" ... 还有 {len(gemini_models) - 10} 个")
|
||
|
||
except Exception as e:
|
||
print(f" ❌ 获取失败: {e}")
|
||
else:
|
||
print(" ⚠️ 未设置 GEMINI_API_KEY")
|
||
|
||
print("\n" + "=" * 60)
|
||
print("环境变量配置:")
|
||
print(f" AI_API_KEY: {'✅ 已设置' if config['gpt_key'] else '❌ 未设置'} (GPT)")
|
||
print(f" GEMINI_API_KEY: {'✅ 已设置' if config['gemini_key'] else '❌ 未设置'} (Gemini)")
|
||
print("=" * 60)
|
||
|
||
|
||
def test_models():
|
||
"""测试主要模型是否可用"""
|
||
config = get_config()
|
||
|
||
print("=" * 60)
|
||
print("🔍 测试模型可用性")
|
||
print("=" * 60)
|
||
|
||
# 测试 GPT 中转站
|
||
print("\n--- GPT 中转站 ---")
|
||
if config["gpt_key"]:
|
||
for model, name in [("gpt-5.2-codex", "GPT-5.2 Codex")]:
|
||
print(f"\n📝 测试 {name}...")
|
||
try:
|
||
result = call_proxy_api(model, "说你好", config["gpt_key"], config["gpt_url"], 0.1)
|
||
print(f" ✅ 可用: {result[:30]}...")
|
||
except requests.exceptions.HTTPError as e:
|
||
error_msg = str(e)
|
||
if hasattr(e, 'response'):
|
||
error_msg = e.response.text[:100]
|
||
print(f" ❌ 失败: {error_msg}")
|
||
except Exception as e:
|
||
print(f" ❌ 失败: {e}")
|
||
else:
|
||
print(" ⚠️ 未设置 AI_API_KEY")
|
||
|
||
# 测试 Gemini 中转站
|
||
print("\n--- Gemini 中转站 ---")
|
||
if config["gemini_key"]:
|
||
for model, name in [
|
||
("gemini-2.5-flash", "Gemini 2.5 Flash"),
|
||
("gemini-2.5-pro", "Gemini 2.5 Pro"),
|
||
]:
|
||
print(f"\n📝 测试 {name}...")
|
||
try:
|
||
result = call_proxy_api(model, "说你好", config["gemini_key"], config["gemini_url"], 0.1)
|
||
print(f" ✅ 可用: {result[:30]}...")
|
||
except requests.exceptions.HTTPError as e:
|
||
error_msg = str(e)
|
||
if hasattr(e, 'response'):
|
||
try:
|
||
err_json = e.response.json()
|
||
error_msg = err_json.get("error", {}).get("message", str(e))[:80]
|
||
except:
|
||
error_msg = e.response.text[:80]
|
||
print(f" ❌ 失败: {error_msg}")
|
||
except Exception as e:
|
||
print(f" ❌ 失败: {e}")
|
||
else:
|
||
print(" ⚠️ 未设置 GEMINI_API_KEY")
|
||
|
||
print("\n" + "=" * 60)
|
||
print("测试完成")
|
||
print("=" * 60)
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description='多 AI 模型调用工具 (Universal Architect Engine v4.0)',
|
||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||
)
|
||
|
||
parser.add_argument('--model', '-m', default='gpt-5.2-codex',
|
||
help='模型名称 (gpt-5.2-codex, gemini-2.5-pro, etc.)')
|
||
parser.add_argument('--task', '-t', default='general',
|
||
choices=['audit', 'frontend', 'design-system', 'backend', 'contract', 'general'],
|
||
help='任务类型')
|
||
parser.add_argument('--file', '-f', help='输入文件')
|
||
parser.add_argument('--prompt', '-p', help='直接输入提示词')
|
||
parser.add_argument('--output', '-o', help='输出文件')
|
||
parser.add_argument('--temperature', type=float, default=0.3, help='温度参数')
|
||
parser.add_argument('--list-models', action='store_true', help='列出可用模型')
|
||
parser.add_argument('--test', action='store_true', help='测试主要模型可用性')
|
||
parser.add_argument('--json', '-j', action='store_true', help='JSON 格式输出')
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 列出模型
|
||
if args.list_models:
|
||
list_models()
|
||
return
|
||
|
||
# 测试模型
|
||
if args.test:
|
||
test_models()
|
||
return
|
||
|
||
# 获取输入内容
|
||
if args.file:
|
||
with open(args.file, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
elif args.prompt:
|
||
content = args.prompt
|
||
elif not sys.stdin.isatty():
|
||
content = sys.stdin.read()
|
||
else:
|
||
parser.print_help()
|
||
return
|
||
|
||
# 构建提示词
|
||
task_template = TASK_PROMPTS.get(args.task, TASK_PROMPTS["general"])
|
||
full_prompt = task_template.format(content=content)
|
||
|
||
# 输出状态
|
||
actual_model = MODELS.get(args.model, args.model)
|
||
print(f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", file=sys.stderr)
|
||
print(f"[Status: Executing {args.task} via {actual_model}]", file=sys.stderr)
|
||
print(f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", file=sys.stderr)
|
||
|
||
try:
|
||
result = call_model(args.model, full_prompt, args.temperature)
|
||
|
||
if args.json:
|
||
output = json.dumps({
|
||
"model": actual_model,
|
||
"task": args.task,
|
||
"result": result
|
||
}, ensure_ascii=False, indent=2)
|
||
else:
|
||
output = result
|
||
|
||
# 输出结果
|
||
if args.output:
|
||
with open(args.output, 'w', encoding='utf-8') as f:
|
||
f.write(output)
|
||
print(f"✅ 结果已保存到: {args.output}", file=sys.stderr)
|
||
else:
|
||
print()
|
||
print(output)
|
||
|
||
print(f"\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", file=sys.stderr)
|
||
print(f"[Task Complete]", file=sys.stderr)
|
||
print(f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", file=sys.stderr)
|
||
|
||
except requests.exceptions.HTTPError as e:
|
||
print(f"❌ API 错误: {e}")
|
||
if hasattr(e, 'response'):
|
||
print(f" 响应: {e.response.text}")
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
print(f"❌ 失败: {e}")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|