process-gpt-agent-sdk 0.3.3__py3-none-any.whl → 0.3.4__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.
Potentially problematic release.
This version of process-gpt-agent-sdk might be problematic. Click here for more details.
- process_gpt_agent_sdk-0.3.4.dist-info/METADATA +177 -0
- {process_gpt_agent_sdk-0.3.3.dist-info → process_gpt_agent_sdk-0.3.4.dist-info}/RECORD +4 -4
- process_gpt_agent_sdk-0.3.3.dist-info/METADATA +0 -665
- {process_gpt_agent_sdk-0.3.3.dist-info → process_gpt_agent_sdk-0.3.4.dist-info}/WHEEL +0 -0
- {process_gpt_agent_sdk-0.3.3.dist-info → process_gpt_agent_sdk-0.3.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: process-gpt-agent-sdk
|
|
3
|
+
Version: 0.3.4
|
|
4
|
+
Summary: Supabase 기반 이벤트/작업 폴링으로 A2A AgentExecutor를 실행하는 SDK
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/your-org/process-gpt-agent-sdk
|
|
7
|
+
Project-URL: Issues, https://github.com/your-org/process-gpt-agent-sdk/issues
|
|
8
|
+
Keywords: agent,a2a,supabase,workflow,sdk,processgpt
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: supabase>=2.0.0
|
|
16
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
17
|
+
Requires-Dist: a2a-sdk==0.3.0
|
|
18
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
19
|
+
Requires-Dist: process-gpt-llm-factory==1.0.2
|
|
20
|
+
Requires-Dist: pydantic>=2.0.0
|
|
21
|
+
Requires-Dist: requests>=2.25.0
|
|
22
|
+
|
|
23
|
+
# 📘 ProcessGPT Agent SDK – README
|
|
24
|
+
|
|
25
|
+
## 1. 이게 뭐하는 건가요?
|
|
26
|
+
이 SDK는 **ProcessGPT 에이전트 서버**를 만들 때 필요한 **공통 기능**을 제공합니다.
|
|
27
|
+
|
|
28
|
+
- DB에서 **작업(todo) 폴링** → 처리할 일감 가져오기
|
|
29
|
+
- **컨텍스트 준비** (사용자 정보, 폼 정의, MCP 설정 등 자동으로 조회)
|
|
30
|
+
- 다양한 **에이전트 오케스트레이션(A2A)** 과 호환
|
|
31
|
+
- **이벤트(Event) 전송 규격 통일화** → 결과를 DB에 안전하게 저장
|
|
32
|
+
|
|
33
|
+
👉 쉽게 말하면: **여러 종류의 AI 에이전트를 같은 규칙으로 실행/저장/호출할 수 있게 해주는 통합 SDK** 입니다.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. 아키텍처 다이어그램
|
|
38
|
+
```mermaid
|
|
39
|
+
flowchart TD
|
|
40
|
+
subgraph DB[Postgres/Supabase]
|
|
41
|
+
T[todolist]:::db
|
|
42
|
+
E[events]:::db
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
subgraph SDK
|
|
46
|
+
P[Polling\n(fetch_pending_task)] --> C[Context 준비\n(fetch_context_bundle 등)]
|
|
47
|
+
C --> X[Executor\n(MinimalExecutor)]
|
|
48
|
+
X -->|TaskStatusUpdateEvent| E
|
|
49
|
+
X -->|TaskArtifactUpdateEvent| T
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
classDef db fill=#f2f2f2,stroke=#333,stroke-width=1px;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **todolist**: 각 작업(Task)의 진행 상태, 결과물 저장
|
|
56
|
+
- **events**: 실행 중간에 발생한 이벤트 로그 저장
|
|
57
|
+
- SDK는 두 테이블을 자동으로 연결해 줍니다.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 3. A2A 타입과 이벤트 종류
|
|
62
|
+
|
|
63
|
+
### A2A 타입 (2가지)
|
|
64
|
+
| A2A 타입 | 설명 | 매칭 테이블 |
|
|
65
|
+
|----------|------|-------------|
|
|
66
|
+
| **TaskStatusUpdateEvent** | 작업 상태 업데이트 | `events` 테이블 |
|
|
67
|
+
| **TaskArtifactUpdateEvent** | 작업 결과물 업데이트 | `todolist` 테이블 |
|
|
68
|
+
|
|
69
|
+
### Event Type (4가지)
|
|
70
|
+
| Event Type | Python 클래스 | 저장 테이블 | 설명 |
|
|
71
|
+
|------------|---------------|-------------|------|
|
|
72
|
+
| **task_started** | `TaskStatusUpdateEvent` | `events` | 작업 시작 상태 |
|
|
73
|
+
| **task_working** | `TaskStatusUpdateEvent` | `events` | 작업 진행 중 상태 |
|
|
74
|
+
| **task_completed** | `TaskArtifactUpdateEvent` | `todolist` | 작업 완료 및 결과물 저장 |
|
|
75
|
+
| **task_error** | `TaskStatusUpdateEvent` | `events` | 작업 오류 발생 |
|
|
76
|
+
|
|
77
|
+
👉 **A2A 타입 2가지**가 핵심이며, 각각 `events`와 `todolist` 테이블에 매칭됩니다. **Event Type 4가지**로 세부 상태를 구분합니다.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 4. 미니멀 예제 (기본 사용법)
|
|
82
|
+
|
|
83
|
+
### minimal_executor.py
|
|
84
|
+
```python
|
|
85
|
+
class MinimalExecutor(AgentExecutor):
|
|
86
|
+
async def execute(self, context: RequestContext, event_queue: EventQueue):
|
|
87
|
+
# 1) 입력 가져오기
|
|
88
|
+
query = context.get_user_input()
|
|
89
|
+
print("User Query:", query)
|
|
90
|
+
|
|
91
|
+
# 2) 상태 이벤트 (events 테이블 저장)
|
|
92
|
+
payload = {"demo": "hello world"}
|
|
93
|
+
event_queue.enqueue_event(
|
|
94
|
+
TaskStatusUpdateEvent(
|
|
95
|
+
status={
|
|
96
|
+
"state": TaskState.working,
|
|
97
|
+
"message": new_agent_text_message(
|
|
98
|
+
json.dumps(payload, ensure_ascii=False), # ⚠️ str() 쓰지말고 반드시 json.dumps!
|
|
99
|
+
context.get_context_data()["row"]["proc_inst_id"],
|
|
100
|
+
context.get_context_data()["row"]["id"],
|
|
101
|
+
),
|
|
102
|
+
},
|
|
103
|
+
contextId=context.get_context_data()["row"]["proc_inst_id"],
|
|
104
|
+
taskId=context.get_context_data()["row"]["id"],
|
|
105
|
+
metadata={"crew_type": "action", "event_type": "task_started"},
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# 3) 최종 아티팩트 이벤트 (todolist 테이블 저장)
|
|
110
|
+
artifact = new_text_artifact(
|
|
111
|
+
name="result",
|
|
112
|
+
description="Demo Result",
|
|
113
|
+
text=json.dumps(payload, ensure_ascii=False), # ⚠️ 여기서도 str() 금지!
|
|
114
|
+
)
|
|
115
|
+
event_queue.enqueue_event(
|
|
116
|
+
TaskArtifactUpdateEvent(
|
|
117
|
+
artifact=artifact,
|
|
118
|
+
lastChunk=True,
|
|
119
|
+
contextId=context.get_context_data()["row"]["proc_inst_id"],
|
|
120
|
+
taskId=context.get_context_data()["row"]["id"],
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### minimal_server.py
|
|
126
|
+
```python
|
|
127
|
+
async def main():
|
|
128
|
+
load_dotenv()
|
|
129
|
+
server = ProcessGPTAgentServer(
|
|
130
|
+
agent_executor=MinimalExecutor(),
|
|
131
|
+
agent_type="crewai-action" # 오케스트레이터 타입
|
|
132
|
+
)
|
|
133
|
+
await server.run()
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
👉 실행하면 SDK가 자동으로:
|
|
137
|
+
1. DB에서 작업 하나 가져오기 (`fetch_pending_task`)
|
|
138
|
+
2. 컨텍스트 준비 (폼/유저/MCP 조회)
|
|
139
|
+
3. Executor 실행 → 이벤트/결과 DB에 저장
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 5. ⚠️ JSON 직렬화 주의 (str() 절대 금지)
|
|
144
|
+
|
|
145
|
+
반드시 `json.dumps()`로 직렬화해야 합니다.
|
|
146
|
+
|
|
147
|
+
- ❌ 이렇게 하면 안됨:
|
|
148
|
+
```python
|
|
149
|
+
text = str({"key": "value"}) # Python dict string → JSON 아님
|
|
150
|
+
```
|
|
151
|
+
DB에 `"'{key: value}'"` 꼴로 문자열 저장됨 → 파싱 실패
|
|
152
|
+
|
|
153
|
+
- ✅ 이렇게 해야 함:
|
|
154
|
+
```python
|
|
155
|
+
text = json.dumps({"key": "value"}, ensure_ascii=False)
|
|
156
|
+
```
|
|
157
|
+
DB에 `{"key": "value"}` JSON 저장됨 → 파싱 성공
|
|
158
|
+
|
|
159
|
+
👉 **SDK는 내부에서 `json.loads`로 재파싱**하기 때문에, 표준 JSON 문자열이 아니면 무조건 문자열로만 남습니다.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 6. 요약
|
|
164
|
+
- 이 SDK는 **ProcessGPT Agent**를 표준 규격으로 실행/저장/호출하는 공통 레이어
|
|
165
|
+
- 작업 → 컨텍스트 준비 → Executor 실행 → 이벤트 저장 전체를 자동화
|
|
166
|
+
- **A2A 타입 2가지**: `TaskStatusUpdateEvent`, `TaskArtifactUpdateEvent`
|
|
167
|
+
- **Event Type 4가지**: `task_started`, `task_working`, `task_completed`, `task_error`
|
|
168
|
+
- **DB 매핑**:
|
|
169
|
+
- `TaskStatusUpdateEvent` → `events` 테이블
|
|
170
|
+
- `TaskArtifactUpdateEvent` → `todolist` 테이블
|
|
171
|
+
- ⚠️ **str() 대신 무조건 `json.dumps` 사용!**
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## 7. 버전업
|
|
176
|
+
- ./release.sh 버전
|
|
177
|
+
- 오류 발생시 : python -m ensurepip --upgrade
|
|
@@ -2,7 +2,7 @@ processgpt_agent_sdk/__init__.py,sha256=4-Wt-FRImZa1tG4CWx2ZtICcnTkKoAiqAfva9vLi
|
|
|
2
2
|
processgpt_agent_sdk/database.py,sha256=4oQxJ3y28pPdUpvcQ80DEcFmcvKH37RszhSSZKf6tJU,12690
|
|
3
3
|
processgpt_agent_sdk/processgpt_agent_framework.py,sha256=xsQnPXTnIjCkE5jYDVg9TZC-QiyLF0DmXHTvu-oMXa0,23080
|
|
4
4
|
processgpt_agent_sdk/utils.py,sha256=pK7plUKGMIHo5lA0e4442qMAmAtCwKrbewjrXdVbFdE,9228
|
|
5
|
-
process_gpt_agent_sdk-0.3.
|
|
6
|
-
process_gpt_agent_sdk-0.3.
|
|
7
|
-
process_gpt_agent_sdk-0.3.
|
|
8
|
-
process_gpt_agent_sdk-0.3.
|
|
5
|
+
process_gpt_agent_sdk-0.3.4.dist-info/METADATA,sha256=NR2O7M7WttHlU9oGJbRgFv3vcdt4pXSkIfWUOkE_ySU,6840
|
|
6
|
+
process_gpt_agent_sdk-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
process_gpt_agent_sdk-0.3.4.dist-info/top_level.txt,sha256=Xe6zrj3_3Vv7d0pl5RRtenVUckwOVBVLQn2P03j5REo,21
|
|
8
|
+
process_gpt_agent_sdk-0.3.4.dist-info/RECORD,,
|
|
@@ -1,665 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: process-gpt-agent-sdk
|
|
3
|
-
Version: 0.3.3
|
|
4
|
-
Summary: Supabase 기반 이벤트/작업 폴링으로 A2A AgentExecutor를 실행하는 SDK
|
|
5
|
-
License: MIT
|
|
6
|
-
Project-URL: Homepage, https://github.com/your-org/process-gpt-agent-sdk
|
|
7
|
-
Project-URL: Issues, https://github.com/your-org/process-gpt-agent-sdk/issues
|
|
8
|
-
Keywords: agent,a2a,supabase,workflow,sdk,processgpt
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Operating System :: OS Independent
|
|
13
|
-
Requires-Python: >=3.9
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
Requires-Dist: supabase>=2.0.0
|
|
16
|
-
Requires-Dist: openai>=1.30.0
|
|
17
|
-
Requires-Dist: python-dotenv>=1.0.0
|
|
18
|
-
Requires-Dist: a2a-sdk==0.3.0
|
|
19
|
-
Requires-Dist: typing-extensions>=4.0.0; python_version < "3.11"
|
|
20
|
-
|
|
21
|
-
# ProcessGPT Agent Framework
|
|
22
|
-
## A2A SDK 연동을 위한 경량 에이전트 서버 프레임워크
|
|
23
|
-
|
|
24
|
-
Supabase 기반의 프로세스 작업(Todolist)을 폴링하고, A2A 규격 이벤트를 통해 작업 상태/결과를 기록하는 **경량 에이전트 서버 프레임워크**입니다.
|
|
25
|
-
|
|
26
|
-
### 📋 요구사항
|
|
27
|
-
- **런타임**: Python 3.9+ (권장: Python 3.11)
|
|
28
|
-
- **데이터베이스**: Supabase (PostgreSQL) + 제공된 RPC/테이블
|
|
29
|
-
- **이벤트 규격**: A2A `TaskStatusUpdateEvent` / `TaskArtifactUpdateEvent`
|
|
30
|
-
|
|
31
|
-
## 📊 이벤트 종류 및 데이터 구조
|
|
32
|
-
|
|
33
|
-
### 🎯 이벤트 타입 (event_type) 종류
|
|
34
|
-
|
|
35
|
-
| event_type | 설명 | 사용 시점 | 자동 설정 여부 |
|
|
36
|
-
|------------|------|-----------|----------------|
|
|
37
|
-
| `task_started` | 작업 시작 | 작업 처리 시작시 | 수동 설정 |
|
|
38
|
-
| `task_completed` | 작업 완료 | 작업 정상 완료시 | 수동 설정 |
|
|
39
|
-
| `tool_usage_started` | 도구 사용 시작 | 외부 도구/API 호출 시작 | 수동 설정 |
|
|
40
|
-
| `tool_usage_finished` | 도구 사용 완료 | 외부 도구/API 호출 완료 | 수동 설정 |
|
|
41
|
-
| `human_asked` | 사용자 입력 요청 | HITL 패턴 사용시 | **자동 설정** |
|
|
42
|
-
| `human_response` | 사용자 응답 | UI에서 사용자 응답시 | UI가 설정 |
|
|
43
|
-
|
|
44
|
-
### 📋 메타데이터 필드 설명
|
|
45
|
-
|
|
46
|
-
#### crew_type (필수)
|
|
47
|
-
- **의미**: 현재 조직의 크루 이름 또는 행위를 나타냄
|
|
48
|
-
- **예시**: `action`, `report`, `slide`, `analysis`, `research` 등
|
|
49
|
-
- **사용법**: 어떤 종류의 작업인지 분류하는 데 사용
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
metadata = {
|
|
53
|
-
"crew_type": "action", # 액션 수행 크루
|
|
54
|
-
"event_type": "task_started"
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
#### job_id (중요)
|
|
59
|
-
- **의미**: 하나의 작업 단위를 식별하는 고유 ID
|
|
60
|
-
- **규칙**: **시작과 끝이 반드시 매칭되어야 함**
|
|
61
|
-
- **형식**: `job-{task_id}` 또는 `job-{timestamp}` 등
|
|
62
|
-
|
|
63
|
-
```python
|
|
64
|
-
# 올바른 사용법 - 동일한 job_id 사용
|
|
65
|
-
job_id = f"job-{task_id}"
|
|
66
|
-
|
|
67
|
-
# 작업 시작
|
|
68
|
-
metadata = {"crew_type": "action", "event_type": "task_started", "job_id": job_id}
|
|
69
|
-
|
|
70
|
-
# HITL 요청
|
|
71
|
-
metadata = {"crew_type": "action", "job_id": job_id} # human_asked 자동 설정
|
|
72
|
-
|
|
73
|
-
# 작업 완료
|
|
74
|
-
metadata = {"crew_type": "action", "event_type": "task_completed", "job_id": job_id}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 🔄 이벤트 저장 방식
|
|
78
|
-
|
|
79
|
-
#### 1. TaskStatusUpdateEvent → `events` 테이블
|
|
80
|
-
```python
|
|
81
|
-
event_queue.enqueue_event(
|
|
82
|
-
TaskStatusUpdateEvent(
|
|
83
|
-
status={
|
|
84
|
-
"state": TaskState.working,
|
|
85
|
-
"message": new_agent_text_message("진행 상황 메시지", context_id, task_id),
|
|
86
|
-
},
|
|
87
|
-
final=False,
|
|
88
|
-
contextId=context_id,
|
|
89
|
-
taskId=task_id,
|
|
90
|
-
metadata={
|
|
91
|
-
"crew_type": "action",
|
|
92
|
-
"event_type": "task_started", # events.event_type에 저장
|
|
93
|
-
"job_id": "job-12345"
|
|
94
|
-
}
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
**저장 결과** (events 테이블):
|
|
100
|
-
- `event_type`: "task_started"
|
|
101
|
-
- `data`: "진행 상황 메시지" (래퍼 제거된 순수 텍스트)
|
|
102
|
-
- `metadata`: 전체 metadata JSON
|
|
103
|
-
|
|
104
|
-
#### 2. TaskArtifactUpdateEvent → `todolist.output` 컬럼
|
|
105
|
-
```python
|
|
106
|
-
artifact = new_text_artifact(
|
|
107
|
-
name="처리결과",
|
|
108
|
-
description="작업 완료 결과",
|
|
109
|
-
text=json.dumps({"result": "완료"}, ensure_ascii=False)
|
|
110
|
-
)
|
|
111
|
-
event_queue.enqueue_event(
|
|
112
|
-
TaskArtifactUpdateEvent(
|
|
113
|
-
artifact=artifact,
|
|
114
|
-
lastChunk=True, # 최종 결과
|
|
115
|
-
contextId=context_id,
|
|
116
|
-
taskId=task_id,
|
|
117
|
-
)
|
|
118
|
-
)
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**저장 결과** (todolist 테이블):
|
|
122
|
-
- `output`: `{"result": "완료"}` (래퍼 제거된 순수 JSON)
|
|
123
|
-
- `p_final`: `true`
|
|
124
|
-
|
|
125
|
-
### ⚠️ 특별 규칙
|
|
126
|
-
|
|
127
|
-
1. **자동 event_type 설정**:
|
|
128
|
-
- `state=input_required` → `event_type=human_asked` (자동)
|
|
129
|
-
- 작업 완료시 → `event_type=crew_completed` (서버가 자동 추가)
|
|
130
|
-
|
|
131
|
-
2. **JSON 문자열 변환 필수**:
|
|
132
|
-
```python
|
|
133
|
-
# 올바른 방법
|
|
134
|
-
text=json.dumps(data, ensure_ascii=False)
|
|
135
|
-
|
|
136
|
-
# 잘못된 방법
|
|
137
|
-
text=data # 딕셔너리 직접 전달 시 래퍼와 함께 저장됨
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## 🔄 전체 데이터 흐름도
|
|
141
|
-
|
|
142
|
-
```mermaid
|
|
143
|
-
graph TD
|
|
144
|
-
A[Supabase todolist 테이블] -->|폴링| B[ProcessGPTAgentServer]
|
|
145
|
-
B -->|작업 발견| C[RequestContext 생성]
|
|
146
|
-
C -->|컨텍스트 전달| D[AgentExecutor.execute]
|
|
147
|
-
|
|
148
|
-
D -->|진행 상태| E[TaskStatusUpdateEvent]
|
|
149
|
-
D -->|최종 결과| F[TaskArtifactUpdateEvent]
|
|
150
|
-
D -->|HITL 요청| G[TaskStatusUpdateEvent<br/>state=input_required]
|
|
151
|
-
|
|
152
|
-
E -->|래퍼 제거| H[events 테이블<br/>data 컬럼]
|
|
153
|
-
F -->|래퍼 제거| I[todolist.output 컬럼<br/>p_final=true]
|
|
154
|
-
G -->|자동 설정| J[events 테이블<br/>event_type=human_asked]
|
|
155
|
-
|
|
156
|
-
K[Operator UI] -->|사용자 응답| L[events 테이블<br/>event_type=human_response]
|
|
157
|
-
|
|
158
|
-
style E fill:#e1f5fe
|
|
159
|
-
style F fill:#f3e5f5
|
|
160
|
-
style G fill:#fff3e0
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### 🎯 job_id 생명주기 흐름
|
|
164
|
-
|
|
165
|
-
```mermaid
|
|
166
|
-
sequenceDiagram
|
|
167
|
-
participant S as Server
|
|
168
|
-
participant E as Executor
|
|
169
|
-
participant DB as Supabase
|
|
170
|
-
participant UI as Operator UI
|
|
171
|
-
|
|
172
|
-
Note over E: job_id = "job-12345" 생성
|
|
173
|
-
|
|
174
|
-
E->>DB: TaskStatusUpdateEvent<br/>event_type=task_started<br/>job_id=job-12345
|
|
175
|
-
|
|
176
|
-
E->>DB: TaskStatusUpdateEvent<br/>event_type=tool_usage_started<br/>job_id=job-12345
|
|
177
|
-
|
|
178
|
-
E->>DB: TaskStatusUpdateEvent<br/>event_type=tool_usage_finished<br/>job_id=job-12345
|
|
179
|
-
|
|
180
|
-
alt HITL 필요시
|
|
181
|
-
E->>DB: TaskStatusUpdateEvent<br/>state=input_required<br/>job_id=job-12345
|
|
182
|
-
Note over DB: 자동으로 event_type=human_asked 설정
|
|
183
|
-
|
|
184
|
-
UI->>DB: INSERT events<br/>event_type=human_response<br/>job_id=job-12345
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
E->>DB: TaskArtifactUpdateEvent<br/>lastChunk=true
|
|
188
|
-
|
|
189
|
-
E->>DB: TaskStatusUpdateEvent<br/>event_type=task_completed<br/>job_id=job-12345
|
|
190
|
-
|
|
191
|
-
S->>DB: TaskStatusUpdateEvent<br/>event_type=crew_completed<br/>job_id=job-12345
|
|
192
|
-
|
|
193
|
-
Note over S,UI: 동일한 job_id로 시작부터 끝까지 추적 가능
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### 💾 데이터 저장 구조
|
|
197
|
-
|
|
198
|
-
#### events 테이블 저장 예시
|
|
199
|
-
```json
|
|
200
|
-
{
|
|
201
|
-
"id": "uuid",
|
|
202
|
-
"event_type": "task_started",
|
|
203
|
-
"data": "작업을 시작합니다", // 래퍼 제거된 순수 메시지
|
|
204
|
-
"metadata": {
|
|
205
|
-
"crew_type": "action",
|
|
206
|
-
"event_type": "task_started",
|
|
207
|
-
"job_id": "job-12345",
|
|
208
|
-
"contextId": "proc-789",
|
|
209
|
-
"taskId": "task-456"
|
|
210
|
-
},
|
|
211
|
-
"created_at": "2024-01-01T00:00:00Z"
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
#### todolist.output 저장 예시
|
|
216
|
-
```json
|
|
217
|
-
{
|
|
218
|
-
"id": "task-456",
|
|
219
|
-
"output": {
|
|
220
|
-
"status": "completed",
|
|
221
|
-
"result": "처리 결과 데이터"
|
|
222
|
-
}, // 래퍼 제거된 순수 아티팩트 데이터
|
|
223
|
-
"p_final": true,
|
|
224
|
-
"updated_at": "2024-01-01T00:05:00Z"
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### 🔧 값 전달 과정
|
|
229
|
-
```python
|
|
230
|
-
# 1. 서버에서 작업 정보 가져오기
|
|
231
|
-
row = context.get_context_data()["row"] # todolist 테이블의 한 행
|
|
232
|
-
context_id = row.get("root_proc_inst_id") or row.get("proc_inst_id") # 프로세스 ID
|
|
233
|
-
task_id = row.get("id") # 작업 ID
|
|
234
|
-
user_input = context.get_user_input() # 사용자가 입력한 내용
|
|
235
|
-
|
|
236
|
-
# 2. job_id 생성 (작업 전체 추적용)
|
|
237
|
-
job_id = f"job-{task_id}" # 또는 timestamp 기반
|
|
238
|
-
|
|
239
|
-
# 3. 메시지/아티팩트 생성시 JSON 문자열로 변환
|
|
240
|
-
payload = {"result": "처리 완료"}
|
|
241
|
-
message_text = json.dumps(payload, ensure_ascii=False) # 중요: JSON 문자열로!
|
|
242
|
-
|
|
243
|
-
# 4. 메타데이터에 crew_type, job_id 포함
|
|
244
|
-
metadata = {
|
|
245
|
-
"crew_type": "action", # 크루 타입
|
|
246
|
-
"event_type": "task_started", # 이벤트 타입
|
|
247
|
-
"job_id": job_id # 작업 추적 ID
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
# 5. 서버가 자동으로 래퍼 제거 후 순수 payload만 저장
|
|
251
|
-
# events.data 또는 todolist.output에 {"result": "처리 완료"}만 저장됨
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## 🚀 빠른 시작 가이드
|
|
255
|
-
|
|
256
|
-
### 1단계: 설치
|
|
257
|
-
```bash
|
|
258
|
-
# 패키지 설치
|
|
259
|
-
pip install -e .
|
|
260
|
-
|
|
261
|
-
# 또는 requirements.txt 사용
|
|
262
|
-
pip install -r requirements.txt
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### 2단계: 환경 설정
|
|
266
|
-
`.env` 파일 생성:
|
|
267
|
-
```env
|
|
268
|
-
SUPABASE_URL=your_supabase_project_url
|
|
269
|
-
SUPABASE_KEY=your_supabase_anon_key
|
|
270
|
-
ENV=dev
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### 3단계: 서버 구현 방법
|
|
274
|
-
서버는 이렇게 만드세요:
|
|
275
|
-
|
|
276
|
-
```python
|
|
277
|
-
# my_server.py
|
|
278
|
-
import asyncio
|
|
279
|
-
from dotenv import load_dotenv
|
|
280
|
-
from processgpt_agent_sdk.processgpt_agent_framework import ProcessGPTAgentServer
|
|
281
|
-
from my_executor import MyExecutor # 아래에서 구현할 익스큐터
|
|
282
|
-
|
|
283
|
-
async def main():
|
|
284
|
-
load_dotenv()
|
|
285
|
-
|
|
286
|
-
server = ProcessGPTAgentServer(
|
|
287
|
-
agent_executor=MyExecutor(), # 여러분이 구현할 익스큐터
|
|
288
|
-
agent_type="my-agent" # Supabase todolist.agent_orch와 매칭되어야 함
|
|
289
|
-
)
|
|
290
|
-
server.polling_interval = 3 # 3초마다 새 작업 확인
|
|
291
|
-
|
|
292
|
-
print("서버 시작!")
|
|
293
|
-
await server.run()
|
|
294
|
-
|
|
295
|
-
if __name__ == "__main__":
|
|
296
|
-
try:
|
|
297
|
-
asyncio.run(main())
|
|
298
|
-
except KeyboardInterrupt:
|
|
299
|
-
print("서버 종료")
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
### 4단계: 익스큐터 구현 방법
|
|
303
|
-
익스큐터는 이렇게 만드세요:
|
|
304
|
-
|
|
305
|
-
```python
|
|
306
|
-
# my_executor.py
|
|
307
|
-
import asyncio
|
|
308
|
-
import json
|
|
309
|
-
from typing_extensions import override
|
|
310
|
-
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
311
|
-
from a2a.server.events import EventQueue
|
|
312
|
-
from a2a.types import TaskStatusUpdateEvent, TaskState, TaskArtifactUpdateEvent
|
|
313
|
-
from a2a.utils import new_agent_text_message, new_text_artifact
|
|
314
|
-
|
|
315
|
-
class MyExecutor(AgentExecutor):
|
|
316
|
-
@override
|
|
317
|
-
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
318
|
-
# 1. 작업 정보 가져오기
|
|
319
|
-
row = context.get_context_data()["row"]
|
|
320
|
-
context_id = row.get("root_proc_inst_id") or row.get("proc_inst_id")
|
|
321
|
-
task_id = row.get("id")
|
|
322
|
-
user_input = context.get_user_input() # 사용자가 입력한 내용
|
|
323
|
-
|
|
324
|
-
# 2. job_id 생성 (작업 전체 추적용)
|
|
325
|
-
job_id = f"job-{task_id}"
|
|
326
|
-
|
|
327
|
-
print(f"처리할 작업: {user_input} (job_id: {job_id})")
|
|
328
|
-
|
|
329
|
-
# 3. 작업 시작 알림 (events 테이블에 저장됨)
|
|
330
|
-
event_queue.enqueue_event(
|
|
331
|
-
TaskStatusUpdateEvent(
|
|
332
|
-
status={
|
|
333
|
-
"state": TaskState.working,
|
|
334
|
-
"message": new_agent_text_message("작업 시작", context_id, task_id),
|
|
335
|
-
},
|
|
336
|
-
final=False,
|
|
337
|
-
contextId=context_id,
|
|
338
|
-
taskId=task_id,
|
|
339
|
-
metadata={
|
|
340
|
-
"crew_type": "action", # 크루 타입
|
|
341
|
-
"event_type": "task_started",
|
|
342
|
-
"job_id": job_id # 작업 추적 ID
|
|
343
|
-
}
|
|
344
|
-
)
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
# 4. 실제 작업 수행 (여기에 여러분의 로직 작성)
|
|
348
|
-
await asyncio.sleep(2)
|
|
349
|
-
result_data = {"status": "완료", "input": user_input, "output": "처리 결과"}
|
|
350
|
-
|
|
351
|
-
# 5. 작업 완료 알림
|
|
352
|
-
event_queue.enqueue_event(
|
|
353
|
-
TaskStatusUpdateEvent(
|
|
354
|
-
status={
|
|
355
|
-
"state": TaskState.working,
|
|
356
|
-
"message": new_agent_text_message("작업 완료", context_id, task_id),
|
|
357
|
-
},
|
|
358
|
-
final=False,
|
|
359
|
-
contextId=context_id,
|
|
360
|
-
taskId=task_id,
|
|
361
|
-
metadata={
|
|
362
|
-
"crew_type": "action",
|
|
363
|
-
"event_type": "task_completed",
|
|
364
|
-
"job_id": job_id # 동일한 job_id 사용
|
|
365
|
-
}
|
|
366
|
-
)
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
# 6. 최종 결과 전송 (todolist.output에 저장됨)
|
|
370
|
-
artifact = new_text_artifact(
|
|
371
|
-
name="처리결과",
|
|
372
|
-
description="작업 완료 결과",
|
|
373
|
-
text=json.dumps(result_data, ensure_ascii=False) # JSON 문자열로!
|
|
374
|
-
)
|
|
375
|
-
event_queue.enqueue_event(
|
|
376
|
-
TaskArtifactUpdateEvent(
|
|
377
|
-
artifact=artifact,
|
|
378
|
-
lastChunk=True, # 중요: 최종 결과면 True
|
|
379
|
-
contextId=context_id,
|
|
380
|
-
taskId=task_id,
|
|
381
|
-
)
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
@override
|
|
385
|
-
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
386
|
-
pass # 취소 로직 (필요시 구현)
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### 5단계: 실행
|
|
390
|
-
```bash
|
|
391
|
-
python my_server.py
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
## 🤝 Human-in-the-Loop (사용자 입력 요청) 패턴
|
|
395
|
-
|
|
396
|
-
사용자 입력이 필요한 완전한 예시:
|
|
397
|
-
|
|
398
|
-
```python
|
|
399
|
-
class HITLExecutor(AgentExecutor):
|
|
400
|
-
@override
|
|
401
|
-
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
402
|
-
row = context.get_context_data()["row"]
|
|
403
|
-
context_id = row.get("root_proc_inst_id") or row.get("proc_inst_id")
|
|
404
|
-
task_id = row.get("id")
|
|
405
|
-
user_input = context.get_user_input()
|
|
406
|
-
job_id = f"job-{task_id}"
|
|
407
|
-
|
|
408
|
-
# 1. 작업 시작
|
|
409
|
-
event_queue.enqueue_event(
|
|
410
|
-
TaskStatusUpdateEvent(
|
|
411
|
-
status={
|
|
412
|
-
"state": TaskState.working,
|
|
413
|
-
"message": new_agent_text_message("분석을 시작합니다", context_id, task_id),
|
|
414
|
-
},
|
|
415
|
-
final=False,
|
|
416
|
-
contextId=context_id,
|
|
417
|
-
taskId=task_id,
|
|
418
|
-
metadata={
|
|
419
|
-
"crew_type": "analysis", # 분석 크루
|
|
420
|
-
"event_type": "task_started",
|
|
421
|
-
"job_id": job_id
|
|
422
|
-
}
|
|
423
|
-
)
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
await asyncio.sleep(1)
|
|
427
|
-
|
|
428
|
-
# 2. 사용자 입력 요청 (HITL)
|
|
429
|
-
question_data = {
|
|
430
|
-
"question": f"'{user_input}' 작업을 어떤 방식으로 처리할까요?",
|
|
431
|
-
"options": ["빠른 처리", "정밀 분석", "단계별 진행"],
|
|
432
|
-
"context": user_input
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
event_queue.enqueue_event(
|
|
436
|
-
TaskStatusUpdateEvent(
|
|
437
|
-
status={
|
|
438
|
-
"state": TaskState.input_required, # 중요: 자동으로 human_asked 설정됨
|
|
439
|
-
"message": new_agent_text_message(
|
|
440
|
-
json.dumps(question_data, ensure_ascii=False),
|
|
441
|
-
context_id, task_id
|
|
442
|
-
),
|
|
443
|
-
},
|
|
444
|
-
final=True,
|
|
445
|
-
contextId=context_id,
|
|
446
|
-
taskId=task_id,
|
|
447
|
-
metadata={
|
|
448
|
-
"crew_type": "analysis",
|
|
449
|
-
"job_id": job_id # 동일한 job_id 유지
|
|
450
|
-
}
|
|
451
|
-
)
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
# 3. 사용자 응답을 기다리는 로직 (실제 구현에서는 필요)
|
|
455
|
-
# 여기서는 시뮬레이션
|
|
456
|
-
await asyncio.sleep(3)
|
|
457
|
-
|
|
458
|
-
# 4. 사용자 응답 후 작업 완료
|
|
459
|
-
result_data = {
|
|
460
|
-
"original_request": user_input,
|
|
461
|
-
"user_choice": "사용자가 선택한 옵션",
|
|
462
|
-
"result": "HITL 방식으로 처리 완료"
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
# 5. 완료 알림
|
|
466
|
-
event_queue.enqueue_event(
|
|
467
|
-
TaskStatusUpdateEvent(
|
|
468
|
-
status={
|
|
469
|
-
"state": TaskState.working,
|
|
470
|
-
"message": new_agent_text_message("HITL 처리 완료", context_id, task_id),
|
|
471
|
-
},
|
|
472
|
-
final=False,
|
|
473
|
-
contextId=context_id,
|
|
474
|
-
taskId=task_id,
|
|
475
|
-
metadata={
|
|
476
|
-
"crew_type": "analysis",
|
|
477
|
-
"event_type": "task_completed",
|
|
478
|
-
"job_id": job_id # 동일한 job_id로 완료
|
|
479
|
-
}
|
|
480
|
-
)
|
|
481
|
-
)
|
|
482
|
-
|
|
483
|
-
# 6. 최종 결과
|
|
484
|
-
artifact = new_text_artifact(
|
|
485
|
-
name="HITL_결과",
|
|
486
|
-
description="Human-in-the-Loop 처리 결과",
|
|
487
|
-
text=json.dumps(result_data, ensure_ascii=False)
|
|
488
|
-
)
|
|
489
|
-
event_queue.enqueue_event(
|
|
490
|
-
TaskArtifactUpdateEvent(
|
|
491
|
-
artifact=artifact,
|
|
492
|
-
lastChunk=True,
|
|
493
|
-
contextId=context_id,
|
|
494
|
-
taskId=task_id,
|
|
495
|
-
)
|
|
496
|
-
)
|
|
497
|
-
|
|
498
|
-
@override
|
|
499
|
-
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
500
|
-
pass
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
## 📋 체크리스트 (실패 없는 통합을 위한)
|
|
504
|
-
|
|
505
|
-
### 필수 설정
|
|
506
|
-
- [ ] `.env`에 `SUPABASE_URL`, `SUPABASE_KEY` 설정
|
|
507
|
-
- [ ] `requirements.txt` 설치 완료
|
|
508
|
-
- [ ] Supabase에서 제공 SQL(`database_schema.sql`, `function.sql`) 적용
|
|
509
|
-
|
|
510
|
-
### 코드 구현
|
|
511
|
-
- [ ] 서버에서 `agent_type`이 Supabase `todolist.agent_orch`와 매칭됨
|
|
512
|
-
- [ ] 익스큐터에서 `contextId`, `taskId`를 올바르게 설정
|
|
513
|
-
- [ ] **job_id 생성 및 일관성 유지** (`job-{task_id}` 형식 권장)
|
|
514
|
-
- [ ] **crew_type 설정** (`action`, `report`, `slide` 등 행위별 분류)
|
|
515
|
-
- [ ] 상태 이벤트는 `new_agent_text_message()`로 생성
|
|
516
|
-
- [ ] 최종 결과는 `new_text_artifact()` + `lastChunk=True`로 전송
|
|
517
|
-
- [ ] HITL 요청시 `TaskState.input_required` 사용
|
|
518
|
-
- [ ] **JSON 문자열 변환** (`json.dumps(data, ensure_ascii=False)`)
|
|
519
|
-
|
|
520
|
-
## 🚨 자주 발생하는 문제
|
|
521
|
-
|
|
522
|
-
### 1. 설치 문제
|
|
523
|
-
**증상**: `ModuleNotFoundError`
|
|
524
|
-
```bash
|
|
525
|
-
# 해결
|
|
526
|
-
pip install -e .
|
|
527
|
-
pip install a2a-sdk==0.3.0 --force-reinstall
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
### 2. 작업이 폴링되지 않음
|
|
531
|
-
**원인**: Supabase 연결 문제
|
|
532
|
-
**해결**:
|
|
533
|
-
- `.env` 파일 위치 확인 (프로젝트 루트)
|
|
534
|
-
- URL/Key 재확인
|
|
535
|
-
- `agent_type`이 todolist.agent_orch와 매칭되는지 확인
|
|
536
|
-
|
|
537
|
-
### 3. 이벤트가 저장되지 않음
|
|
538
|
-
**원인**: 테이블/함수 누락
|
|
539
|
-
**해결**:
|
|
540
|
-
- `database_schema.sql`, `function.sql` 실행 확인
|
|
541
|
-
- Supabase 테이블 권한 확인
|
|
542
|
-
|
|
543
|
-
### 4. 결과가 래퍼와 함께 저장됨
|
|
544
|
-
**원인**: JSON 문자열 변환 누락
|
|
545
|
-
```python
|
|
546
|
-
# 올바른 방법
|
|
547
|
-
text=json.dumps(data, ensure_ascii=False) # JSON 문자열로!
|
|
548
|
-
|
|
549
|
-
# 잘못된 방법
|
|
550
|
-
text=data # 딕셔너리 직접 전달 (X)
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
## 📚 샘플 코드 (간단 버전)
|
|
554
|
-
|
|
555
|
-
### 기본 서버
|
|
556
|
-
```python
|
|
557
|
-
# sample_server/minimal_server.py
|
|
558
|
-
import asyncio
|
|
559
|
-
from dotenv import load_dotenv
|
|
560
|
-
from processgpt_agent_sdk.processgpt_agent_framework import ProcessGPTAgentServer
|
|
561
|
-
from sample_server.minimal_executor import MinimalExecutor
|
|
562
|
-
|
|
563
|
-
async def main():
|
|
564
|
-
load_dotenv()
|
|
565
|
-
server = ProcessGPTAgentServer(
|
|
566
|
-
agent_executor=MinimalExecutor(),
|
|
567
|
-
agent_type="crewai-action"
|
|
568
|
-
)
|
|
569
|
-
server.polling_interval = 3
|
|
570
|
-
await server.run()
|
|
571
|
-
|
|
572
|
-
if __name__ == "__main__":
|
|
573
|
-
try:
|
|
574
|
-
asyncio.run(main())
|
|
575
|
-
except KeyboardInterrupt:
|
|
576
|
-
pass
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
### 기본 익스큐터
|
|
580
|
-
```python
|
|
581
|
-
# sample_server/minimal_executor.py
|
|
582
|
-
import asyncio
|
|
583
|
-
import json
|
|
584
|
-
from typing_extensions import override
|
|
585
|
-
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
586
|
-
from a2a.server.events import EventQueue
|
|
587
|
-
from a2a.types import TaskStatusUpdateEvent, TaskState, TaskArtifactUpdateEvent
|
|
588
|
-
from a2a.utils import new_agent_text_message, new_text_artifact
|
|
589
|
-
|
|
590
|
-
class MinimalExecutor(AgentExecutor):
|
|
591
|
-
@override
|
|
592
|
-
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
593
|
-
row = context.get_context_data()["row"]
|
|
594
|
-
context_id = row.get("root_proc_inst_id") or row.get("proc_inst_id")
|
|
595
|
-
task_id = row.get("id")
|
|
596
|
-
user_input = context.get_user_input()
|
|
597
|
-
|
|
598
|
-
# 진행 상태
|
|
599
|
-
event_queue.enqueue_event(
|
|
600
|
-
TaskStatusUpdateEvent(
|
|
601
|
-
status={
|
|
602
|
-
"state": TaskState.working,
|
|
603
|
-
"message": new_agent_text_message("처리중", context_id, task_id),
|
|
604
|
-
},
|
|
605
|
-
final=False,
|
|
606
|
-
contextId=context_id,
|
|
607
|
-
taskId=task_id,
|
|
608
|
-
metadata={"event_type": "task_started"}
|
|
609
|
-
)
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
await asyncio.sleep(1)
|
|
613
|
-
|
|
614
|
-
# 최종 결과
|
|
615
|
-
result = {"input": user_input, "output": "처리 완료"}
|
|
616
|
-
artifact = new_text_artifact(
|
|
617
|
-
name="결과",
|
|
618
|
-
description="처리 결과",
|
|
619
|
-
text=json.dumps(result, ensure_ascii=False)
|
|
620
|
-
)
|
|
621
|
-
event_queue.enqueue_event(
|
|
622
|
-
TaskArtifactUpdateEvent(
|
|
623
|
-
artifact=artifact,
|
|
624
|
-
lastChunk=True,
|
|
625
|
-
contextId=context_id,
|
|
626
|
-
taskId=task_id,
|
|
627
|
-
)
|
|
628
|
-
)
|
|
629
|
-
|
|
630
|
-
@override
|
|
631
|
-
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
632
|
-
pass
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
## 🔧 실행 방법
|
|
636
|
-
|
|
637
|
-
### 개발 환경에서 실행
|
|
638
|
-
```bash
|
|
639
|
-
python sample_server/minimal_server.py
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
### 실제 사용시
|
|
643
|
-
```bash
|
|
644
|
-
python my_server.py
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
---
|
|
648
|
-
|
|
649
|
-
## 📚 레퍼런스
|
|
650
|
-
|
|
651
|
-
### 주요 함수들
|
|
652
|
-
- `ProcessGPTAgentServer.run()`: 서버 시작
|
|
653
|
-
- `new_agent_text_message(text, context_id, task_id)`: 상태 메시지 생성
|
|
654
|
-
- `new_text_artifact(name, desc, text)`: 결과 아티팩트 생성
|
|
655
|
-
|
|
656
|
-
### 이벤트 저장 규칙
|
|
657
|
-
- **TaskStatusUpdateEvent** → `events` 테이블 (`data` 컬럼)
|
|
658
|
-
- **TaskArtifactUpdateEvent** → `todolist` 테이블 (`output` 컬럼)
|
|
659
|
-
- 래퍼 자동 제거 후 순수 payload만 저장
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
## 버전업
|
|
663
|
-
./release.sh 버전
|
|
664
|
-
|
|
665
|
-
오류 발생시 : python -m ensurepip --upgrade
|
|
File without changes
|
{process_gpt_agent_sdk-0.3.3.dist-info → process_gpt_agent_sdk-0.3.4.dist-info}/top_level.txt
RENAMED
|
File without changes
|