sa-assistant 0.1.1__tar.gz
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.
- sa_assistant-0.1.1/PKG-INFO +77 -0
- sa_assistant-0.1.1/README.md +40 -0
- sa_assistant-0.1.1/pyproject.toml +82 -0
- sa_assistant-0.1.1/setup.cfg +4 -0
- sa_assistant-0.1.1/src/agents/__init__.py +32 -0
- sa_assistant-0.1.1/src/agents/orchestrator.py +283 -0
- sa_assistant-0.1.1/src/agents/specialists.py +230 -0
- sa_assistant-0.1.1/src/agentskills/__init__.py +38 -0
- sa_assistant-0.1.1/src/agentskills/discovery.py +107 -0
- sa_assistant-0.1.1/src/agentskills/errors.py +38 -0
- sa_assistant-0.1.1/src/agentskills/models.py +48 -0
- sa_assistant-0.1.1/src/agentskills/parser.py +183 -0
- sa_assistant-0.1.1/src/agentskills/prompt.py +70 -0
- sa_assistant-0.1.1/src/agentskills/tool.py +138 -0
- sa_assistant-0.1.1/src/cli/__init__.py +51 -0
- sa_assistant-0.1.1/src/cli/callback.py +145 -0
- sa_assistant-0.1.1/src/cli/components.py +346 -0
- sa_assistant-0.1.1/src/cli/console.py +106 -0
- sa_assistant-0.1.1/src/cli/mdstream.py +236 -0
- sa_assistant-0.1.1/src/mcp_client/client.py +12 -0
- sa_assistant-0.1.1/src/model/__init__.py +20 -0
- sa_assistant-0.1.1/src/model/load.py +87 -0
- sa_assistant-0.1.1/src/prompts/__init__.py +65 -0
- sa_assistant-0.1.1/src/prompts/system_prompts.py +464 -0
- sa_assistant-0.1.1/src/sa_assistant.egg-info/PKG-INFO +77 -0
- sa_assistant-0.1.1/src/sa_assistant.egg-info/SOURCES.txt +29 -0
- sa_assistant-0.1.1/src/sa_assistant.egg-info/dependency_links.txt +1 -0
- sa_assistant-0.1.1/src/sa_assistant.egg-info/entry_points.txt +2 -0
- sa_assistant-0.1.1/src/sa_assistant.egg-info/requires.txt +15 -0
- sa_assistant-0.1.1/src/sa_assistant.egg-info/top_level.txt +7 -0
- sa_assistant-0.1.1/test/test_main.py +42 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sa-assistant
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: SA Assistant - AWS Solutions Architect Professional Agent with Multi-Agent Architecture
|
|
5
|
+
Author-email: onesuit <wltks2155@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/onesuit/SaAssistant
|
|
8
|
+
Project-URL: Repository, https://github.com/onesuit/SaAssistant
|
|
9
|
+
Project-URL: Issues, https://github.com/onesuit/SaAssistant/issues
|
|
10
|
+
Keywords: aws,solutions-architect,ai-agent,llm,bedrock,claude,multi-agent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: aws-opentelemetry-distro>=0.10.0
|
|
24
|
+
Requires-Dist: bedrock-agentcore>=1.0.3
|
|
25
|
+
Requires-Dist: botocore[crt]
|
|
26
|
+
Requires-Dist: mcp>=1.19.0
|
|
27
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
28
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Requires-Dist: rich>=13.0.0
|
|
31
|
+
Requires-Dist: strands-agents>=1.13.0
|
|
32
|
+
Requires-Dist: strands-agents-tools>=0.2.16
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
37
|
+
|
|
38
|
+
This is a project generated by the agentcore create --template basic CLI tool!
|
|
39
|
+
|
|
40
|
+
# Layout
|
|
41
|
+
|
|
42
|
+
There are two directories generated, `src/` and `test/`. At the root, there is a `.gitignore` file, a `.bedrock_agentcore.yaml` file
|
|
43
|
+
which is used for other `agentcore` commands like `deploy`, `dev`, and `invoke`.
|
|
44
|
+
|
|
45
|
+
## src/
|
|
46
|
+
|
|
47
|
+
The main entrypoint to your app is defined in `src/main.py`. Using the AgentCore SDK `@app.entrypoint` decorator, this file defines a Starlette ASGI app with the chosen Agent framework SDK
|
|
48
|
+
running within.
|
|
49
|
+
|
|
50
|
+
`src/mcp_client/client.py` implements an MCP client using the library from your chosen Agent framework SDK.
|
|
51
|
+
|
|
52
|
+
`src/model/load.py` instantiates your chosen model provider.
|
|
53
|
+
|
|
54
|
+
## test/
|
|
55
|
+
|
|
56
|
+
Tests are not defined by default. Add your own pytest definitions here.
|
|
57
|
+
|
|
58
|
+
# Developing locally
|
|
59
|
+
|
|
60
|
+
If installation was successful, a virtual environment is already created with dependencies installed.
|
|
61
|
+
|
|
62
|
+
Run `source .venv/bin/activate` before developing.
|
|
63
|
+
|
|
64
|
+
`agentcore dev` will start a local server on 0.0.0.0:8080.
|
|
65
|
+
|
|
66
|
+
In a new terminal, you can invoke that server with:
|
|
67
|
+
|
|
68
|
+
`agentcore invoke --dev "What can you do"`
|
|
69
|
+
|
|
70
|
+
# Deployment
|
|
71
|
+
|
|
72
|
+
If you want to customize your project, you can first run `agentcore configure` before deploying. Otherwise, the default project settings
|
|
73
|
+
will work out of the box.
|
|
74
|
+
|
|
75
|
+
After providing credentials, `agentcore deploy` will deploy your project into Amazon Bedrock AgentCore.
|
|
76
|
+
|
|
77
|
+
Use `agentcore invoke` to invoke your deployed agent.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
This is a project generated by the agentcore create --template basic CLI tool!
|
|
2
|
+
|
|
3
|
+
# Layout
|
|
4
|
+
|
|
5
|
+
There are two directories generated, `src/` and `test/`. At the root, there is a `.gitignore` file, a `.bedrock_agentcore.yaml` file
|
|
6
|
+
which is used for other `agentcore` commands like `deploy`, `dev`, and `invoke`.
|
|
7
|
+
|
|
8
|
+
## src/
|
|
9
|
+
|
|
10
|
+
The main entrypoint to your app is defined in `src/main.py`. Using the AgentCore SDK `@app.entrypoint` decorator, this file defines a Starlette ASGI app with the chosen Agent framework SDK
|
|
11
|
+
running within.
|
|
12
|
+
|
|
13
|
+
`src/mcp_client/client.py` implements an MCP client using the library from your chosen Agent framework SDK.
|
|
14
|
+
|
|
15
|
+
`src/model/load.py` instantiates your chosen model provider.
|
|
16
|
+
|
|
17
|
+
## test/
|
|
18
|
+
|
|
19
|
+
Tests are not defined by default. Add your own pytest definitions here.
|
|
20
|
+
|
|
21
|
+
# Developing locally
|
|
22
|
+
|
|
23
|
+
If installation was successful, a virtual environment is already created with dependencies installed.
|
|
24
|
+
|
|
25
|
+
Run `source .venv/bin/activate` before developing.
|
|
26
|
+
|
|
27
|
+
`agentcore dev` will start a local server on 0.0.0.0:8080.
|
|
28
|
+
|
|
29
|
+
In a new terminal, you can invoke that server with:
|
|
30
|
+
|
|
31
|
+
`agentcore invoke --dev "What can you do"`
|
|
32
|
+
|
|
33
|
+
# Deployment
|
|
34
|
+
|
|
35
|
+
If you want to customize your project, you can first run `agentcore configure` before deploying. Otherwise, the default project settings
|
|
36
|
+
will work out of the box.
|
|
37
|
+
|
|
38
|
+
After providing credentials, `agentcore deploy` will deploy your project into Amazon Bedrock AgentCore.
|
|
39
|
+
|
|
40
|
+
Use `agentcore invoke` to invoke your deployed agent.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sa-assistant"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
description = "SA Assistant - AWS Solutions Architect Professional Agent with Multi-Agent Architecture"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "onesuit", email = "wltks2155@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"aws",
|
|
17
|
+
"solutions-architect",
|
|
18
|
+
"ai-agent",
|
|
19
|
+
"llm",
|
|
20
|
+
"bedrock",
|
|
21
|
+
"claude",
|
|
22
|
+
"multi-agent"
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
34
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
dependencies = [
|
|
38
|
+
"aws-opentelemetry-distro >= 0.10.0",
|
|
39
|
+
"bedrock-agentcore >= 1.0.3",
|
|
40
|
+
"botocore[crt]",
|
|
41
|
+
"mcp >= 1.19.0",
|
|
42
|
+
"prompt-toolkit >= 3.0.0",
|
|
43
|
+
"python-dotenv >= 1.2.1",
|
|
44
|
+
"pyyaml >= 6.0",
|
|
45
|
+
"rich >= 13.0.0",
|
|
46
|
+
"strands-agents >= 1.13.0",
|
|
47
|
+
"strands-agents-tools >= 0.2.16"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.optional-dependencies]
|
|
51
|
+
dev = [
|
|
52
|
+
"pytest >= 7.0.0",
|
|
53
|
+
"pytest-asyncio >= 0.21.0",
|
|
54
|
+
"ruff >= 0.1.0",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[project.urls]
|
|
58
|
+
Homepage = "https://github.com/onesuit/SaAssistant"
|
|
59
|
+
Repository = "https://github.com/onesuit/SaAssistant"
|
|
60
|
+
Issues = "https://github.com/onesuit/SaAssistant/issues"
|
|
61
|
+
|
|
62
|
+
[project.scripts]
|
|
63
|
+
sa-assistant = "main:run_cli"
|
|
64
|
+
|
|
65
|
+
[tool.setuptools]
|
|
66
|
+
include-package-data = true
|
|
67
|
+
package-dir = {"" = "src"}
|
|
68
|
+
|
|
69
|
+
[tool.setuptools.packages.find]
|
|
70
|
+
where = ["src"]
|
|
71
|
+
include = ["*"]
|
|
72
|
+
|
|
73
|
+
[tool.setuptools.package-data]
|
|
74
|
+
"*" = ["skills/**/*.md"]
|
|
75
|
+
|
|
76
|
+
[tool.ruff]
|
|
77
|
+
line-length = 100
|
|
78
|
+
target-version = "py310"
|
|
79
|
+
|
|
80
|
+
[tool.ruff.lint]
|
|
81
|
+
select = ["E", "F", "I", "W"]
|
|
82
|
+
ignore = ["E501"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SA 에이전트 모듈
|
|
2
|
+
# Orchestrator + Guru Sub-agents + Specialist Sub-agents 아키텍처
|
|
3
|
+
|
|
4
|
+
from .orchestrator import (
|
|
5
|
+
create_orchestrator,
|
|
6
|
+
consult_guru,
|
|
7
|
+
list_gurus,
|
|
8
|
+
get_available_gurus,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from .specialists import (
|
|
12
|
+
consult_specialist,
|
|
13
|
+
list_specialists,
|
|
14
|
+
get_available_specialists,
|
|
15
|
+
parallel_research,
|
|
16
|
+
run_specialists_parallel,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
# Orchestrator
|
|
21
|
+
"create_orchestrator",
|
|
22
|
+
# Guru
|
|
23
|
+
"consult_guru",
|
|
24
|
+
"list_gurus",
|
|
25
|
+
"get_available_gurus",
|
|
26
|
+
# Specialist
|
|
27
|
+
"consult_specialist",
|
|
28
|
+
"list_specialists",
|
|
29
|
+
"get_available_specialists",
|
|
30
|
+
"parallel_research",
|
|
31
|
+
"run_specialists_parallel",
|
|
32
|
+
]
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# SA Orchestrator - AWS Solutions Architect Professional Agent
|
|
2
|
+
# Guru Sub-agents를 활용한 전문가 자문 시스템
|
|
3
|
+
# ThreadPoolExecutor를 사용하여 Sub-agent를 별도 스레드에서 실행 (OpenTelemetry 컨텍스트 격리)
|
|
4
|
+
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
6
|
+
from typing import List, Any
|
|
7
|
+
from strands import Agent, tool
|
|
8
|
+
|
|
9
|
+
# 로컬 모듈 임포트
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
13
|
+
|
|
14
|
+
from model.load import load_opus, load_sonnet
|
|
15
|
+
from prompts.system_prompts import (
|
|
16
|
+
ORCHESTRATOR_PROMPT,
|
|
17
|
+
GURU_PROMPTS,
|
|
18
|
+
get_orchestrator_prompt,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# =============================================================================
|
|
23
|
+
# Guru Sub-agent 도구 (ThreadPoolExecutor로 별도 스레드에서 실행)
|
|
24
|
+
# =============================================================================
|
|
25
|
+
|
|
26
|
+
def get_available_gurus() -> List[str]:
|
|
27
|
+
"""사용 가능한 Guru 목록을 반환합니다."""
|
|
28
|
+
return list(GURU_PROMPTS.keys())
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _run_guru_in_thread(guru_name: str, question: str) -> str:
|
|
32
|
+
"""
|
|
33
|
+
별도 스레드에서 Guru Agent 실행 (OpenTelemetry 컨텍스트 격리)
|
|
34
|
+
|
|
35
|
+
ThreadPoolExecutor에서 호출되어 독립적인 스레드에서 실행됩니다.
|
|
36
|
+
OpenTelemetry 컨텍스트를 명시적으로 분리하여
|
|
37
|
+
"Failed to detach context" 오류를 방지합니다.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
guru_name: Guru 이름 (소문자)
|
|
41
|
+
question: 자문을 구할 질문
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
str: Guru의 응답 텍스트
|
|
45
|
+
"""
|
|
46
|
+
# OpenTelemetry 컨텍스트 명시적 분리
|
|
47
|
+
try:
|
|
48
|
+
from opentelemetry import context as otel_context
|
|
49
|
+
# 새로운 빈 컨텍스트에서 실행
|
|
50
|
+
token = otel_context.attach(otel_context.Context())
|
|
51
|
+
except ImportError:
|
|
52
|
+
token = None
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
guru_agent = Agent(
|
|
56
|
+
model=load_sonnet(enable_thinking=False),
|
|
57
|
+
system_prompt=GURU_PROMPTS[guru_name],
|
|
58
|
+
)
|
|
59
|
+
result = guru_agent(question)
|
|
60
|
+
return str(result)
|
|
61
|
+
finally:
|
|
62
|
+
# 컨텍스트 복원
|
|
63
|
+
if token is not None:
|
|
64
|
+
try:
|
|
65
|
+
otel_context.detach(token)
|
|
66
|
+
except Exception:
|
|
67
|
+
pass # detach 실패 무시
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@tool
|
|
71
|
+
def consult_guru(guru_name: str, question: str) -> str:
|
|
72
|
+
"""
|
|
73
|
+
전문가 Guru에게 자문을 구합니다.
|
|
74
|
+
|
|
75
|
+
복잡한 아키텍처 결정, 비즈니스 전략, 학습 방법 등에 대해
|
|
76
|
+
저명한 전문가들의 사고방식을 기반으로 조언을 받습니다.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
guru_name: Guru 이름
|
|
80
|
+
- "bezos": Jeff Bezos - 고객 중심 사고, 장기적 비전, Day 1 문화
|
|
81
|
+
- "vogels": Werner Vogels - 분산 시스템, 확장성, 운영 우수성
|
|
82
|
+
- "naval": Naval Ravikant - 레버리지, 비대칭적 결과, 시스템적 사고
|
|
83
|
+
- "feynman": Richard Feynman - 복잡한 개념 단순화, 학습 최적화
|
|
84
|
+
question: 자문을 구할 질문
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
str: Guru의 조언
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
consult_guru("bezos", "이 아키텍처가 고객에게 어떤 가치를 제공하나요?")
|
|
91
|
+
consult_guru("vogels", "이 시스템의 확장성을 어떻게 개선할 수 있을까요?")
|
|
92
|
+
"""
|
|
93
|
+
guru_name_lower = guru_name.lower()
|
|
94
|
+
|
|
95
|
+
if guru_name_lower not in GURU_PROMPTS:
|
|
96
|
+
available = ", ".join(get_available_gurus())
|
|
97
|
+
return f"알 수 없는 Guru입니다: {guru_name}. 사용 가능한 Guru: {available}"
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
# ThreadPoolExecutor로 별도 스레드에서 Guru Agent 실행
|
|
101
|
+
# OpenTelemetry 컨텍스트가 스레드별로 격리되어 충돌 방지
|
|
102
|
+
with ThreadPoolExecutor(max_workers=1) as executor:
|
|
103
|
+
future = executor.submit(_run_guru_in_thread, guru_name_lower, question)
|
|
104
|
+
response_text = future.result()
|
|
105
|
+
|
|
106
|
+
return f"[{guru_name.upper()} GURU 조언]\n\n{response_text}"
|
|
107
|
+
except Exception as e:
|
|
108
|
+
return f"Guru 자문 중 오류 발생: {str(e)}"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@tool
|
|
112
|
+
def list_gurus() -> str:
|
|
113
|
+
"""
|
|
114
|
+
사용 가능한 Guru 목록과 전문 분야를 반환합니다.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
str: Guru 목록 및 설명
|
|
118
|
+
"""
|
|
119
|
+
guru_descriptions = {
|
|
120
|
+
"bezos": "Jeff Bezos - 고객 중심 사고, 장기적 비전, Day 1 문화, 역방향 사고",
|
|
121
|
+
"vogels": "Werner Vogels - 분산 시스템, 확장성, 운영 우수성, 장애 대응",
|
|
122
|
+
"naval": "Naval Ravikant - 레버리지, 비대칭적 결과, 시스템적 사고, 복리 효과",
|
|
123
|
+
"feynman": "Richard Feynman - 복잡한 개념 단순화, 학습 최적화, First Principles"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
result = "## 사용 가능한 Guru 전문가\n\n"
|
|
127
|
+
for name, desc in guru_descriptions.items():
|
|
128
|
+
result += f"- **{name}**: {desc}\n"
|
|
129
|
+
|
|
130
|
+
result += "\n`consult_guru(guru_name, question)` 도구로 자문을 구할 수 있습니다."
|
|
131
|
+
return result
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# =============================================================================
|
|
135
|
+
# Orchestrator 생성
|
|
136
|
+
# =============================================================================
|
|
137
|
+
|
|
138
|
+
def create_orchestrator(
|
|
139
|
+
tools: List[Any] = None,
|
|
140
|
+
system_prompt: str = None,
|
|
141
|
+
enable_thinking: bool = True
|
|
142
|
+
) -> Agent:
|
|
143
|
+
"""
|
|
144
|
+
SA Orchestrator 에이전트를 생성합니다.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
tools: 추가할 도구 목록 (MCP 도구 등)
|
|
148
|
+
system_prompt: 커스텀 시스템 프롬프트 (None이면 기본 프롬프트 사용)
|
|
149
|
+
enable_thinking: Extended Thinking 활성화 여부
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Agent: SA Orchestrator 에이전트
|
|
153
|
+
"""
|
|
154
|
+
# 시스템 프롬프트 구성
|
|
155
|
+
if system_prompt is None:
|
|
156
|
+
system_prompt = get_orchestrator_prompt()
|
|
157
|
+
|
|
158
|
+
# 기본 도구 목록
|
|
159
|
+
default_tools = [
|
|
160
|
+
consult_guru,
|
|
161
|
+
list_gurus,
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
# 추가 도구 병합
|
|
165
|
+
all_tools = default_tools + (tools or [])
|
|
166
|
+
|
|
167
|
+
# Conversation Manager 설정
|
|
168
|
+
conversation_manager = SlidingWindowConversationManager(
|
|
169
|
+
window_size=20 # 최근 20개 메시지 유지
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Orchestrator 에이전트 생성
|
|
173
|
+
orchestrator = Agent(
|
|
174
|
+
model=load_opus(enable_thinking=enable_thinking),
|
|
175
|
+
system_prompt=system_prompt,
|
|
176
|
+
tools=all_tools,
|
|
177
|
+
conversation_manager=conversation_manager
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return orchestrator
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# =============================================================================
|
|
184
|
+
# CLI 실행 지원
|
|
185
|
+
# =============================================================================
|
|
186
|
+
|
|
187
|
+
# ANSI 색상 코드
|
|
188
|
+
class Colors:
|
|
189
|
+
"""터미널 색상 코드"""
|
|
190
|
+
CYAN = '\033[96m'
|
|
191
|
+
GREEN = '\033[92m'
|
|
192
|
+
YELLOW = '\033[93m'
|
|
193
|
+
RED = '\033[91m'
|
|
194
|
+
BLUE = '\033[94m'
|
|
195
|
+
MAGENTA = '\033[95m'
|
|
196
|
+
WHITE = '\033[97m'
|
|
197
|
+
RESET = '\033[0m'
|
|
198
|
+
BOLD = '\033[1m'
|
|
199
|
+
DIM = '\033[2m'
|
|
200
|
+
ORANGE = '\033[38;5;208m' # AWS Orange
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def print_banner():
|
|
204
|
+
"""SA Assistant 배너를 출력합니다."""
|
|
205
|
+
banner = f"""
|
|
206
|
+
{Colors.ORANGE}╔══════════════════════════════════════════════════════════════╗
|
|
207
|
+
║ ║
|
|
208
|
+
║ {Colors.WHITE}AWS SA Assistant{Colors.ORANGE} ║
|
|
209
|
+
║ {Colors.DIM}Solutions Architect Professional Agent{Colors.ORANGE} ║
|
|
210
|
+
║ ║
|
|
211
|
+
║ {Colors.CYAN}Powered by Claude Opus 4.5 with Extended Thinking{Colors.ORANGE} ║
|
|
212
|
+
║ ║
|
|
213
|
+
╚══════════════════════════════════════════════════════════════╝{Colors.RESET}
|
|
214
|
+
"""
|
|
215
|
+
print(banner)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def tool_callback_handler(**kwargs):
|
|
219
|
+
"""도구 호출 콜백 핸들러"""
|
|
220
|
+
if "current_tool_use" in kwargs:
|
|
221
|
+
tool_use = kwargs["current_tool_use"]
|
|
222
|
+
if isinstance(tool_use, dict):
|
|
223
|
+
tool_name = tool_use.get("name", "")
|
|
224
|
+
if tool_name:
|
|
225
|
+
if tool_name == "consult_guru":
|
|
226
|
+
guru = tool_use.get("input", {}).get("guru_name", "")
|
|
227
|
+
print(f"\n{Colors.MAGENTA}🧙 Consulting {guru.upper()} Guru...{Colors.RESET}", flush=True)
|
|
228
|
+
elif tool_name == "list_gurus":
|
|
229
|
+
print(f"\n{Colors.BLUE}📋 Listing available Gurus...{Colors.RESET}", flush=True)
|
|
230
|
+
else:
|
|
231
|
+
print(f"\n{Colors.CYAN}🔧 [{tool_name}]{Colors.RESET}", flush=True)
|
|
232
|
+
|
|
233
|
+
if "data" in kwargs:
|
|
234
|
+
print(kwargs["data"], end="", flush=True)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def run_cli(tools: List[Any] = None):
|
|
238
|
+
"""CLI 모드로 실행합니다."""
|
|
239
|
+
print_banner()
|
|
240
|
+
|
|
241
|
+
print(f"{Colors.WHITE}안녕하세요! AWS Solutions Architect Assistant입니다.{Colors.RESET}")
|
|
242
|
+
print(f"{Colors.DIM}아키텍처 설계, 비용 분석, 보안 검토 등을 도와드립니다.{Colors.RESET}")
|
|
243
|
+
print(f"\n{Colors.YELLOW}💡 Guru 전문가 자문: 'list_gurus'로 사용 가능한 전문가를 확인하세요.{Colors.RESET}")
|
|
244
|
+
print(f"{Colors.DIM}종료하려면 'exit' 또는 'quit'을 입력하세요.{Colors.RESET}\n")
|
|
245
|
+
|
|
246
|
+
# Orchestrator 생성 (Progressive Disclosure: 스킬은 main.py에서 처리)
|
|
247
|
+
orchestrator = create_orchestrator(
|
|
248
|
+
tools=tools,
|
|
249
|
+
enable_thinking=True
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# 콜백 핸들러 설정
|
|
253
|
+
orchestrator.callback_handler = tool_callback_handler
|
|
254
|
+
|
|
255
|
+
while True:
|
|
256
|
+
try:
|
|
257
|
+
user_input = input(f"\n{Colors.GREEN}You: {Colors.RESET}").strip()
|
|
258
|
+
|
|
259
|
+
if not user_input:
|
|
260
|
+
continue
|
|
261
|
+
|
|
262
|
+
if user_input.lower() in ["exit", "quit", "종료"]:
|
|
263
|
+
print(f"\n{Colors.ORANGE}감사합니다. 좋은 하루 되세요! 👋{Colors.RESET}")
|
|
264
|
+
break
|
|
265
|
+
|
|
266
|
+
print(f"\n{Colors.ORANGE}{Colors.BOLD}SA Assistant:{Colors.RESET} ", end="", flush=True)
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
response = orchestrator(user_input)
|
|
270
|
+
print() # 줄바꿈
|
|
271
|
+
except Exception as e:
|
|
272
|
+
print(f"\n{Colors.RED}오류 발생: {e}{Colors.RESET}")
|
|
273
|
+
continue
|
|
274
|
+
|
|
275
|
+
except KeyboardInterrupt:
|
|
276
|
+
print(f"\n\n{Colors.YELLOW}중단되었습니다.{Colors.RESET}")
|
|
277
|
+
break
|
|
278
|
+
except Exception as e:
|
|
279
|
+
print(f"\n{Colors.RED}오류: {e}{Colors.RESET}")
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
if __name__ == "__main__":
|
|
283
|
+
run_cli()
|