prism-agent 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prism/__init__.py +5 -0
- prism/acp/__init__.py +5 -0
- prism/acp/client.py +63 -0
- prism/agent.py +144 -0
- prism/cli.py +419 -0
- prism/config.py +150 -0
- prism/gateway/__init__.py +91 -0
- prism/gateway/base.py +39 -0
- prism/gateway/discord.py +149 -0
- prism/gateway/feishu.py +242 -0
- prism/gateway/telegram.py +200 -0
- prism/mcp/__init__.py +202 -0
- prism/mcp/config_loader.py +96 -0
- prism/mcp/http_client.py +124 -0
- prism/providers/__init__.py +13 -0
- prism/providers/fallback.py +25 -0
- prism/providers/manager.py +158 -0
- prism/providers/pool.py +28 -0
- prism/skills/__init__.py +233 -0
- prism/tools/__init__.py +1 -0
- prism/tools/browser.py +389 -0
- prism/tools/browser_bridge.py +34 -0
- prism/tools/browser_echo.py +36 -0
- prism/tools/code_executor.py +160 -0
- prism/tools/registry.py +272 -0
- prism-desktop/prism_desktop/__init__.py +5 -0
- prism-desktop/prism_desktop/main.py +187 -0
- prism-desktop/prism_desktop/version.py +1 -0
- prism_agent-0.2.1.dist-info/METADATA +223 -0
- prism_agent-0.2.1.dist-info/RECORD +34 -0
- prism_agent-0.2.1.dist-info/WHEEL +5 -0
- prism_agent-0.2.1.dist-info/entry_points.txt +2 -0
- prism_agent-0.2.1.dist-info/licenses/LICENSE +21 -0
- prism_agent-0.2.1.dist-info/top_level.txt +2 -0
prism/__init__.py
ADDED
prism/acp/__init__.py
ADDED
prism/acp/client.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PRISM Agent - ACP (Agent Communication Protocol) 客户端
|
|
3
|
+
提供与 ACP 兼容 agent 通信的能力
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import subprocess
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ACPClient:
|
|
12
|
+
"""
|
|
13
|
+
ACP 客户端
|
|
14
|
+
通过 stdio 与 ACP 兼容 agent 通信
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, command: str, args: Optional[list] = None):
|
|
18
|
+
self.command = command
|
|
19
|
+
self.args = args or []
|
|
20
|
+
self.process: Optional[subprocess.Popen] = None
|
|
21
|
+
|
|
22
|
+
def start(self) -> Dict[str, Any]:
|
|
23
|
+
try:
|
|
24
|
+
self.process = subprocess.Popen(
|
|
25
|
+
[self.command] + self.args,
|
|
26
|
+
stdin=subprocess.PIPE,
|
|
27
|
+
stdout=subprocess.PIPE,
|
|
28
|
+
stderr=subprocess.PIPE,
|
|
29
|
+
text=True,
|
|
30
|
+
bufsize=1,
|
|
31
|
+
)
|
|
32
|
+
return {'success': True}
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return {'success': False, 'error': str(e)}
|
|
35
|
+
|
|
36
|
+
def send(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
37
|
+
if not self.process:
|
|
38
|
+
return {'success': False, 'error': 'ACP client not started'}
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
request = json.dumps({'jsonrpc': '2.0', 'id': 1, **payload}) + '\n'
|
|
42
|
+
self.process.stdin.write(request)
|
|
43
|
+
self.process.stdin.flush()
|
|
44
|
+
response_line = self.process.stdout.readline()
|
|
45
|
+
if not response_line:
|
|
46
|
+
return {'success': False, 'error': 'No response from ACP agent'}
|
|
47
|
+
response = json.loads(response_line)
|
|
48
|
+
if 'result' in response:
|
|
49
|
+
return {'success': True, 'result': response['result']}
|
|
50
|
+
if 'error' in response:
|
|
51
|
+
return {'success': False, 'error': response['error'].get('message', 'Unknown error')}
|
|
52
|
+
return {'success': False, 'error': 'Invalid ACP response'}
|
|
53
|
+
except Exception as e:
|
|
54
|
+
return {'success': False, 'error': str(e)}
|
|
55
|
+
|
|
56
|
+
def close(self):
|
|
57
|
+
if self.process:
|
|
58
|
+
try:
|
|
59
|
+
self.process.terminate()
|
|
60
|
+
self.process.wait(timeout=5)
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
self.process = None
|
prism/agent.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PRISM Agent - 核心Agent循环
|
|
3
|
+
整合 Hermes 的上下文压缩 + Codex 的工具调用 + OpenClaw 的浏览器控制
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import logging
|
|
9
|
+
from typing import List, Dict, Any, Optional, Callable
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from prism.providers.manager import provider_pool
|
|
14
|
+
from prism.tools.registry import registry
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("prism.agent")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Message:
|
|
21
|
+
"""消息结构"""
|
|
22
|
+
role: str # system | user | assistant | tool
|
|
23
|
+
content: str
|
|
24
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
25
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ToolCall:
|
|
30
|
+
"""工具调用"""
|
|
31
|
+
id: str
|
|
32
|
+
name: str
|
|
33
|
+
arguments: Dict[str, Any]
|
|
34
|
+
result: Optional[Dict[str, Any]] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Agent:
|
|
38
|
+
"""
|
|
39
|
+
PRISM Agent 核心
|
|
40
|
+
整合:
|
|
41
|
+
- Hermes 的 Skills 系统
|
|
42
|
+
- Codex 的代码执行能力
|
|
43
|
+
- OpenClaw 的浏览器自动化
|
|
44
|
+
- 统一的模型调用接口
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, system_prompt: Optional[str] = None):
|
|
48
|
+
self.messages: List[Message] = []
|
|
49
|
+
self.tool_calls: List[ToolCall] = []
|
|
50
|
+
self.system_prompt = system_prompt or self._default_system_prompt()
|
|
51
|
+
self.max_turns = 150
|
|
52
|
+
self.tools_enabled = True
|
|
53
|
+
|
|
54
|
+
# 初始化系统消息
|
|
55
|
+
self.messages.append(Message(
|
|
56
|
+
role="system",
|
|
57
|
+
content=self.system_prompt,
|
|
58
|
+
))
|
|
59
|
+
|
|
60
|
+
def _default_system_prompt(self) -> str:
|
|
61
|
+
"""默认系统提示词"""
|
|
62
|
+
return """你是 PRISM Agent,一个强大的 AI 助手。
|
|
63
|
+
|
|
64
|
+
你可以:
|
|
65
|
+
1. 读写文件、执行命令
|
|
66
|
+
2. 搜索网页、控制浏览器
|
|
67
|
+
3. 执行 Python 代码
|
|
68
|
+
4. 管理定时任务
|
|
69
|
+
5. 发送消息到 Telegram/Discord/飞书
|
|
70
|
+
|
|
71
|
+
原则:
|
|
72
|
+
- 先理解用户需求,再执行
|
|
73
|
+
- 复杂任务拆成步骤
|
|
74
|
+
- 遇到问题主动报告,不要隐瞒
|
|
75
|
+
- 安全第一,危险操作先确认
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def chat(self, user_message: str) -> str:
|
|
79
|
+
"""
|
|
80
|
+
发送消息并获取回复
|
|
81
|
+
整合了:
|
|
82
|
+
- Hermes 的上下文管理
|
|
83
|
+
- Codex 的函数调用
|
|
84
|
+
- OpenClaw 的工具执行
|
|
85
|
+
"""
|
|
86
|
+
# 添加用户消息
|
|
87
|
+
self.messages.append(Message(role="user", content=user_message))
|
|
88
|
+
|
|
89
|
+
# 构建 API 消息格式
|
|
90
|
+
api_messages = [
|
|
91
|
+
{"role": m.role, "content": m.content}
|
|
92
|
+
for m in self.messages
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
# 调用模型
|
|
96
|
+
result = provider_pool.chat(api_messages)
|
|
97
|
+
|
|
98
|
+
if not result.get('success'):
|
|
99
|
+
logger.warning("chat failed: %s", result.get('error'))
|
|
100
|
+
return f"Error: {result.get('error', 'Unknown error')}"
|
|
101
|
+
|
|
102
|
+
assistant_content = result.get('content', '')
|
|
103
|
+
logger.info("chat success model=%s", result.get('model'))
|
|
104
|
+
|
|
105
|
+
# 添加助手回复
|
|
106
|
+
self.messages.append(Message(role="assistant", content=assistant_content))
|
|
107
|
+
|
|
108
|
+
return assistant_content
|
|
109
|
+
|
|
110
|
+
def execute_tool(self, tool_name: str, **kwargs) -> Dict[str, Any]:
|
|
111
|
+
"""
|
|
112
|
+
执行工具
|
|
113
|
+
整合了 Codex 的终端执行 + OpenClaw 的浏览器控制
|
|
114
|
+
"""
|
|
115
|
+
if not self.tools_enabled:
|
|
116
|
+
return {'success': False, 'error': 'Tools disabled'}
|
|
117
|
+
|
|
118
|
+
logger.info("execute tool=%s args=%s", tool_name, kwargs)
|
|
119
|
+
result = registry.execute(tool_name, **kwargs)
|
|
120
|
+
logger.info("execute tool=%s result=%s", tool_name, result.get('success'))
|
|
121
|
+
|
|
122
|
+
# 记录工具调用
|
|
123
|
+
self.tool_calls.append(ToolCall(
|
|
124
|
+
id=f"call_{len(self.tool_calls)}",
|
|
125
|
+
name=tool_name,
|
|
126
|
+
arguments=kwargs,
|
|
127
|
+
result=result,
|
|
128
|
+
))
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
def list_tools(self) -> List[Dict[str, str]]:
|
|
133
|
+
"""列出可用工具"""
|
|
134
|
+
return registry.list_tools()
|
|
135
|
+
|
|
136
|
+
def clear_history(self):
|
|
137
|
+
"""清空对话历史"""
|
|
138
|
+
self.messages = [self.messages[0]] # 保留系统消息
|
|
139
|
+
self.tool_calls = []
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def create_agent(system_prompt: Optional[str] = None) -> Agent:
|
|
143
|
+
"""创建 Agent 实例"""
|
|
144
|
+
return Agent(system_prompt=system_prompt)
|
prism/cli.py
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PRISM Agent - CLI 入口
|
|
3
|
+
整合 Hermes/Codex/OpenClaw 的命令行体验
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
# 添加项目根目录到路径
|
|
11
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
12
|
+
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
import click
|
|
16
|
+
import logging
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
from rich.markdown import Markdown
|
|
20
|
+
from rich.syntax import Syntax
|
|
21
|
+
from rich.prompt import Prompt
|
|
22
|
+
|
|
23
|
+
from prism.config import config as prism_config
|
|
24
|
+
from prism.config import ConfigError
|
|
25
|
+
from prism.agent import create_agent
|
|
26
|
+
|
|
27
|
+
console = Console()
|
|
28
|
+
|
|
29
|
+
# 统一日志配置
|
|
30
|
+
LOG_DIR = Path.home() / ".prism" / "logs"
|
|
31
|
+
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
LOG_FILE = LOG_DIR / "prism.log"
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger("prism")
|
|
35
|
+
logger.setLevel(logging.DEBUG)
|
|
36
|
+
formatter = logging.Formatter(
|
|
37
|
+
"%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
38
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# 文件日志,带轮转
|
|
42
|
+
from logging.handlers import RotatingFileHandler
|
|
43
|
+
fh = RotatingFileHandler(LOG_FILE, encoding="utf-8", maxBytes=5 * 1024 * 1024, backupCount=5)
|
|
44
|
+
fh.setLevel(logging.DEBUG)
|
|
45
|
+
fh.setFormatter(formatter)
|
|
46
|
+
logger.addHandler(fh)
|
|
47
|
+
|
|
48
|
+
# 控制台日志只显示 WARNING+
|
|
49
|
+
ch = logging.StreamHandler()
|
|
50
|
+
ch.setLevel(logging.WARNING)
|
|
51
|
+
ch.setFormatter(formatter)
|
|
52
|
+
logger.addHandler(ch)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@click.group()
|
|
56
|
+
@click.version_option(version="0.2.1", prog_name="PRISM")
|
|
57
|
+
def cli():
|
|
58
|
+
"""
|
|
59
|
+
PRISM Agent - 统一 AI Agent CLI
|
|
60
|
+
|
|
61
|
+
整合 Hermes/Codex/OpenClaw 优势的新一代 AI Agent
|
|
62
|
+
"""
|
|
63
|
+
# 配置校验延后到实际需要时执行,避免影响 help / version 等命令
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@cli.group()
|
|
67
|
+
def gateway():
|
|
68
|
+
"""Gateway 控制命令"""
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@gateway.command()
|
|
73
|
+
@click.option('--platform', '-p', help='平台名称')
|
|
74
|
+
@click.option('--token', '-t', help='Bot Token')
|
|
75
|
+
@click.option('--app-id', help='飞书 App ID')
|
|
76
|
+
@click.option('--app-secret', help='飞书 App Secret')
|
|
77
|
+
@click.option('--encrypt-key', help='飞书 Encrypt Key')
|
|
78
|
+
@click.option('--verification-token', help='飞书 Verification Token')
|
|
79
|
+
def start(platform: Optional[str], token: Optional[str], app_id: Optional[str],
|
|
80
|
+
app_secret: Optional[str], encrypt_key: Optional[str], verification_token: Optional[str]):
|
|
81
|
+
"""启动 Gateway 服务"""
|
|
82
|
+
from prism.gateway import gateway as gw
|
|
83
|
+
|
|
84
|
+
if not platform:
|
|
85
|
+
platforms = gw.list_platforms()
|
|
86
|
+
if platforms:
|
|
87
|
+
click.echo(f"已配置平台: {', '.join(platforms)}")
|
|
88
|
+
else:
|
|
89
|
+
click.echo("未配置任何平台,请用 --platform 指定")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
# 保存配置到 config
|
|
93
|
+
config_path = Path.home() / ".prism" / "config.yaml"
|
|
94
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
|
|
96
|
+
import yaml
|
|
97
|
+
if config_path.exists():
|
|
98
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
99
|
+
cfg = yaml.safe_load(f) or {}
|
|
100
|
+
else:
|
|
101
|
+
cfg = {}
|
|
102
|
+
|
|
103
|
+
if 'gateway' not in cfg:
|
|
104
|
+
cfg['gateway'] = {}
|
|
105
|
+
cfg['gateway'][platform] = {
|
|
106
|
+
'token': token or '',
|
|
107
|
+
'app_id': app_id or '',
|
|
108
|
+
'app_secret': app_secret or '',
|
|
109
|
+
'encrypt_key': encrypt_key or '',
|
|
110
|
+
'verification_token': verification_token or '',
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
with open(config_path, 'w', encoding='utf-8') as f:
|
|
114
|
+
yaml.dump(cfg, f, allow_unicode=True, default_flow_style=False)
|
|
115
|
+
|
|
116
|
+
click.echo(f"Gateway 配置已保存到 {config_path}")
|
|
117
|
+
click.echo(f"平台: {platform}")
|
|
118
|
+
|
|
119
|
+
# 显示启动说明
|
|
120
|
+
if platform == 'feishu':
|
|
121
|
+
click.echo("\n飞书 Gateway 启动说明:")
|
|
122
|
+
click.echo("1. 在飞书开放平台创建应用")
|
|
123
|
+
click.echo("2. 开启机器人能力")
|
|
124
|
+
click.echo("3. 配置事件订阅 URL")
|
|
125
|
+
click.echo("4. 使用 prism gateway start --platform feishu 启动")
|
|
126
|
+
elif platform == 'telegram':
|
|
127
|
+
click.echo("\nTelegram Gateway 启动说明:")
|
|
128
|
+
click.echo("1. 与 @BotFather 对话创建 Bot")
|
|
129
|
+
click.echo("2. 获取 Bot Token")
|
|
130
|
+
click.echo("3. 使用 prism gateway start --platform telegram --token <TOKEN> 启动")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@gateway.command()
|
|
134
|
+
@click.argument('platform')
|
|
135
|
+
def stop(platform: str):
|
|
136
|
+
"""停止 Gateway 服务"""
|
|
137
|
+
click.echo(f"停止 {platform} Gateway...")
|
|
138
|
+
from prism.gateway import gateway as gw
|
|
139
|
+
click.echo("Gateway 已停止")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@gateway.command()
|
|
143
|
+
def status():
|
|
144
|
+
"""查看 Gateway 状态"""
|
|
145
|
+
from prism.gateway import gateway as gw
|
|
146
|
+
platforms = gw.list_platforms()
|
|
147
|
+
if platforms:
|
|
148
|
+
click.echo(f"运行中平台: {', '.join(platforms)}")
|
|
149
|
+
else:
|
|
150
|
+
click.echo("未运行任何 Gateway")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@cli.group()
|
|
154
|
+
def skill():
|
|
155
|
+
"""Skills 管理命令"""
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@skill.command()
|
|
160
|
+
def list():
|
|
161
|
+
"""列出所有已安装的 Skills"""
|
|
162
|
+
from prism.skills import skills
|
|
163
|
+
skill_list = skills.list_skills()
|
|
164
|
+
|
|
165
|
+
console = Console()
|
|
166
|
+
console.print("\n[bold cyan]已安装 Skills:[/bold cyan]")
|
|
167
|
+
for s in skill_list:
|
|
168
|
+
status = "[green]✓[/green]" if s.get('enabled', True) else "[red]✗[/red]"
|
|
169
|
+
console.print(f" {status} [green]{s['name']}[/green]: {s['description']}")
|
|
170
|
+
console.print()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@skill.command()
|
|
174
|
+
@click.argument('name')
|
|
175
|
+
def install(name: str):
|
|
176
|
+
"""安装一个 Skill"""
|
|
177
|
+
from prism.skills import skills
|
|
178
|
+
result = skills.install_skill(name)
|
|
179
|
+
if result.get('success'):
|
|
180
|
+
console.print(f"[green]✓[/green] 已安装 skill: {name}")
|
|
181
|
+
else:
|
|
182
|
+
console.print(f"[red]✗[/red] 安装失败: {result.get('error', '未知错误')}")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@skill.command()
|
|
186
|
+
@click.argument('name')
|
|
187
|
+
def remove(name: str):
|
|
188
|
+
"""移除一个 Skill"""
|
|
189
|
+
from prism.skills import skills
|
|
190
|
+
result = skills.uninstall_skill(name)
|
|
191
|
+
if result.get('success'):
|
|
192
|
+
console.print(f"[green]✓[/green] 已移除 skill: {name}")
|
|
193
|
+
else:
|
|
194
|
+
console.print(f"[red]✗[/red] 移除失败: {result.get('error', '未知错误')}")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@cli.group()
|
|
198
|
+
def browser():
|
|
199
|
+
"""浏览器控制命令"""
|
|
200
|
+
pass
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@browser.command()
|
|
204
|
+
@click.argument('url')
|
|
205
|
+
@click.option('--headless/--no-headless', default=True, help='无头模式')
|
|
206
|
+
def open(url: str, headless: bool):
|
|
207
|
+
"""打开网页"""
|
|
208
|
+
from prism.tools.browser import browser as browser_api
|
|
209
|
+
result = browser_api.navigate(url, headless=headless)
|
|
210
|
+
if result.get('success'):
|
|
211
|
+
console.print(f"[green]✓[/green] 已打开: {url}")
|
|
212
|
+
console.print(f"标题: {result.get('title', 'N/A')}")
|
|
213
|
+
console.print(f"状态码: {result.get('status', 'N/A')}")
|
|
214
|
+
else:
|
|
215
|
+
console.print(f"[red]✗[/red] 打开失败: {result.get('error')}")
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@browser.command()
|
|
219
|
+
def close():
|
|
220
|
+
"""关闭浏览器"""
|
|
221
|
+
from prism.tools.browser import browser as browser_api
|
|
222
|
+
result = browser_api.disconnect()
|
|
223
|
+
if result.get('success'):
|
|
224
|
+
console.print(f"[green]✓[/green] {result.get('message')}")
|
|
225
|
+
else:
|
|
226
|
+
console.print(f"[red]✗[/red] {result.get('error')}")
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@cli.group()
|
|
230
|
+
def config():
|
|
231
|
+
"""配置管理命令"""
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@config.command()
|
|
236
|
+
@click.argument('key')
|
|
237
|
+
@click.argument('value')
|
|
238
|
+
def set(key: str, value: str):
|
|
239
|
+
"""设置配置项"""
|
|
240
|
+
prism_config.set(key, value)
|
|
241
|
+
console.print(f"[green]✓[/green] 已设置 {key} = {value}")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@config.command()
|
|
245
|
+
@click.argument('key', required=False)
|
|
246
|
+
def get(key: Optional[str]):
|
|
247
|
+
"""查看配置项"""
|
|
248
|
+
if key:
|
|
249
|
+
value = prism_config.get(key)
|
|
250
|
+
console.print(f"{key} = {value}")
|
|
251
|
+
else:
|
|
252
|
+
all_config = prism_config.show()
|
|
253
|
+
console.print_json(data=all_config)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@cli.command()
|
|
257
|
+
@click.option('--model', '-m', help='模型名称')
|
|
258
|
+
@click.option('--provider', '-p', help='提供商')
|
|
259
|
+
def chat(model: Optional[str], provider: Optional[str]):
|
|
260
|
+
|
|
261
|
+
# 启动前校验配置
|
|
262
|
+
try:
|
|
263
|
+
prism_config.validate()
|
|
264
|
+
except ConfigError as e:
|
|
265
|
+
console.print(f"[red]配置错误:{e}[/red]")
|
|
266
|
+
return
|
|
267
|
+
console.print(Panel.fit(
|
|
268
|
+
"[bold cyan]PRISM Agent[/bold cyan] [dim]v0.1.0[/dim]\n"
|
|
269
|
+
"整合 Hermes + Codex + OpenClaw 能力\n"
|
|
270
|
+
"输入 /help 查看命令,/exit 退出",
|
|
271
|
+
border_style="cyan"
|
|
272
|
+
))
|
|
273
|
+
|
|
274
|
+
# 创建 Agent
|
|
275
|
+
agent = create_agent()
|
|
276
|
+
|
|
277
|
+
# 显示当前模型
|
|
278
|
+
current_model = model or config.get('model.default', 'gpt-4o')
|
|
279
|
+
console.print(f"[dim]当前模型: {current_model}[/dim]\n")
|
|
280
|
+
|
|
281
|
+
# 对话循环
|
|
282
|
+
while True:
|
|
283
|
+
try:
|
|
284
|
+
# 获取用户输入
|
|
285
|
+
user_input = Prompt.ask("[bold green]你[/bold green]")
|
|
286
|
+
|
|
287
|
+
if not user_input.strip():
|
|
288
|
+
continue
|
|
289
|
+
|
|
290
|
+
# 处理特殊命令
|
|
291
|
+
if user_input.startswith('/'):
|
|
292
|
+
cmd = user_input.lower()
|
|
293
|
+
if cmd in ['/exit', '/quit']:
|
|
294
|
+
console.print("[yellow]再见![/yellow]")
|
|
295
|
+
break
|
|
296
|
+
elif cmd == '/help':
|
|
297
|
+
show_help()
|
|
298
|
+
continue
|
|
299
|
+
elif cmd == '/tools':
|
|
300
|
+
show_tools(agent)
|
|
301
|
+
continue
|
|
302
|
+
elif cmd == '/model':
|
|
303
|
+
show_model()
|
|
304
|
+
continue
|
|
305
|
+
elif cmd == '/clear':
|
|
306
|
+
agent.clear_history()
|
|
307
|
+
console.print("[green]历史已清空[/green]")
|
|
308
|
+
continue
|
|
309
|
+
else:
|
|
310
|
+
console.print(f"[red]未知命令: {cmd}[/red]")
|
|
311
|
+
continue
|
|
312
|
+
|
|
313
|
+
# 发送消息给 Agent
|
|
314
|
+
with console.status("[bold cyan]思考中...", spinner="dots"):
|
|
315
|
+
response = agent.chat(user_input)
|
|
316
|
+
|
|
317
|
+
# 显示回复
|
|
318
|
+
console.print(Markdown(response))
|
|
319
|
+
console.print()
|
|
320
|
+
|
|
321
|
+
except KeyboardInterrupt:
|
|
322
|
+
console.print("\n[yellow]再见![/yellow]")
|
|
323
|
+
break
|
|
324
|
+
except Exception as e:
|
|
325
|
+
console.print(f"[red]错误: {e}[/red]")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@cli.command()
|
|
329
|
+
@click.argument('message')
|
|
330
|
+
@click.option('--model', '-m', help='模型名称')
|
|
331
|
+
def ask(message: str, model: Optional[str]):
|
|
332
|
+
"""单次提问"""
|
|
333
|
+
try:
|
|
334
|
+
prism_config.validate()
|
|
335
|
+
except ConfigError as e:
|
|
336
|
+
console.print(f"[red]配置错误:{e}[/red]")
|
|
337
|
+
return
|
|
338
|
+
agent = create_agent()
|
|
339
|
+
response = agent.chat(message)
|
|
340
|
+
console.print(Markdown(response))
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
@cli.command()
|
|
344
|
+
def tools():
|
|
345
|
+
"""列出所有可用工具"""
|
|
346
|
+
try:
|
|
347
|
+
prism_config.validate()
|
|
348
|
+
except ConfigError as e:
|
|
349
|
+
console.print(f"[red]配置错误:{e}[/red]")
|
|
350
|
+
return
|
|
351
|
+
agent = create_agent()
|
|
352
|
+
show_tools(agent)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@cli.command()
|
|
356
|
+
def model():
|
|
357
|
+
"""显示当前模型配置"""
|
|
358
|
+
try:
|
|
359
|
+
prism_config.validate()
|
|
360
|
+
except ConfigError as e:
|
|
361
|
+
console.print(f"[red]配置错误:{e}[/red]")
|
|
362
|
+
return
|
|
363
|
+
show_model()
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@cli.command()
|
|
367
|
+
def version():
|
|
368
|
+
"""显示版本信息"""
|
|
369
|
+
console.print("PRISM Agent v0.1.0")
|
|
370
|
+
console.print("整合 Hermes + Codex + OpenClaw 能力")
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def show_help():
|
|
374
|
+
"""显示帮助信息"""
|
|
375
|
+
help_text = """
|
|
376
|
+
**可用命令:**
|
|
377
|
+
- `/help` - 显示帮助
|
|
378
|
+
- `/exit` - 退出
|
|
379
|
+
- `/tools` - 列出所有工具
|
|
380
|
+
- `/model` - 显示当前模型
|
|
381
|
+
- `/clear` - 清空对话历史
|
|
382
|
+
|
|
383
|
+
**示例:**
|
|
384
|
+
- "帮我写一个Python脚本"
|
|
385
|
+
- "读取文件 README.md"
|
|
386
|
+
- "执行命令 ls -la"
|
|
387
|
+
- "搜索网页 Python教程"
|
|
388
|
+
"""
|
|
389
|
+
console.print(Markdown(help_text))
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def show_tools(agent):
|
|
393
|
+
"""显示可用工具"""
|
|
394
|
+
tools = agent.list_tools()
|
|
395
|
+
console.print("\n[bold cyan]可用工具:[/bold cyan]")
|
|
396
|
+
for tool in tools:
|
|
397
|
+
console.print(f" • [green]{tool['name']}[/green]: {tool['description']}")
|
|
398
|
+
console.print()
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def show_model():
|
|
402
|
+
"""显示模型配置"""
|
|
403
|
+
model = config.get('model.default', 'gpt-4o')
|
|
404
|
+
provider = config.get('model.provider', 'openai')
|
|
405
|
+
base_url = config.get('model.base_url', '')
|
|
406
|
+
|
|
407
|
+
console.print(f"\n[bold cyan]当前模型配置:[/bold cyan]")
|
|
408
|
+
console.print(f" 模型: [green]{model}[/green]")
|
|
409
|
+
console.print(f" 提供商: [green]{provider}[/green]")
|
|
410
|
+
console.print(f" API地址: [green]{base_url}[/green]\n")
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def main():
|
|
414
|
+
"""主入口"""
|
|
415
|
+
cli()
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
if __name__ == '__main__':
|
|
419
|
+
main()
|