loom-agent 0.0.1__py3-none-any.whl → 0.0.2__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 loom-agent might be problematic. Click here for more details.
- loom/builtin/tools/calculator.py +4 -0
- loom/builtin/tools/document_search.py +5 -0
- loom/builtin/tools/glob.py +4 -0
- loom/builtin/tools/grep.py +4 -0
- loom/builtin/tools/http_request.py +5 -0
- loom/builtin/tools/python_repl.py +5 -0
- loom/builtin/tools/read_file.py +4 -0
- loom/builtin/tools/task.py +5 -0
- loom/builtin/tools/web_search.py +4 -0
- loom/builtin/tools/write_file.py +4 -0
- loom/components/agent.py +121 -5
- loom/core/agent_executor.py +505 -320
- loom/core/compression_manager.py +17 -10
- loom/core/context_assembly.py +329 -0
- loom/core/events.py +414 -0
- loom/core/execution_context.py +119 -0
- loom/core/tool_orchestrator.py +383 -0
- loom/core/turn_state.py +188 -0
- loom/core/types.py +15 -4
- loom/interfaces/event_producer.py +172 -0
- loom/interfaces/tool.py +22 -1
- loom/security/__init__.py +13 -0
- loom/security/models.py +85 -0
- loom/security/path_validator.py +128 -0
- loom/security/validator.py +346 -0
- loom/tasks/PHASE_1_FOUNDATION/task_1.1_agent_events.md +121 -0
- loom/tasks/PHASE_1_FOUNDATION/task_1.2_streaming_api.md +521 -0
- loom/tasks/PHASE_1_FOUNDATION/task_1.3_context_assembler.md +606 -0
- loom/tasks/PHASE_2_CORE_FEATURES/task_2.1_tool_orchestrator.md +743 -0
- loom/tasks/PHASE_2_CORE_FEATURES/task_2.2_security_validator.md +676 -0
- loom/tasks/README.md +109 -0
- loom/tasks/__init__.py +11 -0
- loom/tasks/sql_placeholder.py +100 -0
- loom_agent-0.0.2.dist-info/METADATA +295 -0
- {loom_agent-0.0.1.dist-info → loom_agent-0.0.2.dist-info}/RECORD +37 -19
- loom_agent-0.0.1.dist-info/METADATA +0 -457
- {loom_agent-0.0.1.dist-info → loom_agent-0.0.2.dist-info}/WHEEL +0 -0
- {loom_agent-0.0.1.dist-info → loom_agent-0.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Security Validator
|
|
3
|
+
|
|
4
|
+
Multi-layer security validation system for tool execution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, List, Optional
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
from loom.security.models import RiskLevel, SecurityDecision
|
|
12
|
+
from loom.security.path_validator import PathSecurityValidator
|
|
13
|
+
from loom.core.types import ToolCall
|
|
14
|
+
from loom.core.permissions import PermissionManager, PermissionAction
|
|
15
|
+
from loom.interfaces.tool import BaseTool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SecurityValidator:
|
|
19
|
+
"""
|
|
20
|
+
Multi-layer security validator for tool execution.
|
|
21
|
+
|
|
22
|
+
Provides 4 layers of independent security checks:
|
|
23
|
+
1. Permission rules (policy-based access control)
|
|
24
|
+
2. Tool category validation (destructive/network/general)
|
|
25
|
+
3. Path security (traversal detection, working dir enforcement)
|
|
26
|
+
4. Sandbox support (automatic sandboxing recommendations)
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
```python
|
|
30
|
+
validator = SecurityValidator(
|
|
31
|
+
working_dir=Path("/Users/project"),
|
|
32
|
+
allowed_categories=["general", "network"],
|
|
33
|
+
require_confirmation_for=["destructive"]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
decision = await validator.validate(
|
|
37
|
+
tool_call=ToolCall(name="write_file", arguments={...}),
|
|
38
|
+
tool=WriteFileTool(),
|
|
39
|
+
context={"user_approved": False}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if decision.allow:
|
|
43
|
+
await tool.run(**tool_call.arguments)
|
|
44
|
+
else:
|
|
45
|
+
print(f"Blocked: {decision.reason}")
|
|
46
|
+
```
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
working_dir: Optional[Path] = None,
|
|
52
|
+
allowed_categories: Optional[List[str]] = None,
|
|
53
|
+
require_confirmation_for: Optional[List[str]] = None,
|
|
54
|
+
permission_manager: Optional[PermissionManager] = None,
|
|
55
|
+
enable_sandbox: bool = True
|
|
56
|
+
):
|
|
57
|
+
"""
|
|
58
|
+
Initialize security validator.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
working_dir: Working directory for path validation
|
|
62
|
+
allowed_categories: Allowed tool categories
|
|
63
|
+
require_confirmation_for: Categories requiring confirmation
|
|
64
|
+
permission_manager: Optional permission manager
|
|
65
|
+
enable_sandbox: Enable sandbox recommendations
|
|
66
|
+
"""
|
|
67
|
+
self.working_dir = working_dir or Path.cwd()
|
|
68
|
+
self.allowed_categories = allowed_categories or ["general", "network", "destructive"]
|
|
69
|
+
self.require_confirmation_for = require_confirmation_for or ["destructive"]
|
|
70
|
+
self.permission_manager = permission_manager
|
|
71
|
+
self.enable_sandbox = enable_sandbox
|
|
72
|
+
|
|
73
|
+
# Initialize sub-validators
|
|
74
|
+
self.path_validator = PathSecurityValidator(working_dir=self.working_dir)
|
|
75
|
+
|
|
76
|
+
# Audit log
|
|
77
|
+
self.audit_log: List[Dict] = []
|
|
78
|
+
|
|
79
|
+
async def validate(
|
|
80
|
+
self,
|
|
81
|
+
tool_call: ToolCall,
|
|
82
|
+
tool: BaseTool,
|
|
83
|
+
context: Optional[Dict] = None
|
|
84
|
+
) -> SecurityDecision:
|
|
85
|
+
"""
|
|
86
|
+
Validate tool execution through all 4 security layers.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
tool_call: Tool call to validate
|
|
90
|
+
tool: Tool instance
|
|
91
|
+
context: Additional context (user_approved, etc.)
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
SecurityDecision with allow/deny and risk assessment
|
|
95
|
+
"""
|
|
96
|
+
context = context or {}
|
|
97
|
+
failed_layers: List[str] = []
|
|
98
|
+
warnings: List[str] = []
|
|
99
|
+
max_risk = RiskLevel.LOW
|
|
100
|
+
|
|
101
|
+
# Layer 1: Permission Rules
|
|
102
|
+
layer1_result = await self.layer1_permission_check(tool_call, tool, context)
|
|
103
|
+
if not layer1_result.allow:
|
|
104
|
+
failed_layers.append("permission")
|
|
105
|
+
max_risk = max(max_risk, layer1_result.risk_level)
|
|
106
|
+
|
|
107
|
+
# Layer 2: Tool Category Validation
|
|
108
|
+
layer2_result = await self.layer2_category_check(tool, context)
|
|
109
|
+
if not layer2_result.allow:
|
|
110
|
+
failed_layers.append("category")
|
|
111
|
+
max_risk = max(max_risk, layer2_result.risk_level)
|
|
112
|
+
|
|
113
|
+
# Layer 3: Path Security
|
|
114
|
+
layer3_result = await self.layer3_path_security(tool_call, tool)
|
|
115
|
+
if not layer3_result.allow:
|
|
116
|
+
failed_layers.append("path_security")
|
|
117
|
+
max_risk = max(max_risk, layer3_result.risk_level)
|
|
118
|
+
|
|
119
|
+
# Layer 4: Sandbox Support
|
|
120
|
+
layer4_result = await self.layer4_sandbox_check(tool, context)
|
|
121
|
+
if layer4_result.warnings:
|
|
122
|
+
warnings.extend(layer4_result.warnings)
|
|
123
|
+
max_risk = max(max_risk, layer4_result.risk_level)
|
|
124
|
+
|
|
125
|
+
# Aggregate decision
|
|
126
|
+
allow = len(failed_layers) == 0
|
|
127
|
+
reason = self._build_reason(failed_layers, warnings)
|
|
128
|
+
|
|
129
|
+
decision = SecurityDecision(
|
|
130
|
+
allow=allow,
|
|
131
|
+
risk_level=max_risk,
|
|
132
|
+
reason=reason,
|
|
133
|
+
failed_layers=failed_layers,
|
|
134
|
+
warnings=warnings
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Audit log
|
|
138
|
+
self._log_decision(tool_call, tool, decision)
|
|
139
|
+
|
|
140
|
+
return decision
|
|
141
|
+
|
|
142
|
+
async def layer1_permission_check(
|
|
143
|
+
self,
|
|
144
|
+
tool_call: ToolCall,
|
|
145
|
+
tool: BaseTool,
|
|
146
|
+
context: Dict
|
|
147
|
+
) -> SecurityDecision:
|
|
148
|
+
"""
|
|
149
|
+
Layer 1: Check permission policy.
|
|
150
|
+
|
|
151
|
+
Integrates with existing PermissionManager.
|
|
152
|
+
"""
|
|
153
|
+
if not self.permission_manager:
|
|
154
|
+
# No permission manager - allow by default
|
|
155
|
+
return SecurityDecision(
|
|
156
|
+
allow=True,
|
|
157
|
+
risk_level=RiskLevel.LOW,
|
|
158
|
+
reason="No permission manager configured"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
action = self.permission_manager.check(tool_call.name, tool_call.arguments)
|
|
162
|
+
|
|
163
|
+
if action == PermissionAction.DENY:
|
|
164
|
+
return SecurityDecision(
|
|
165
|
+
allow=False,
|
|
166
|
+
risk_level=RiskLevel.HIGH,
|
|
167
|
+
reason=f"Tool {tool_call.name} denied by permission policy"
|
|
168
|
+
)
|
|
169
|
+
elif action == PermissionAction.ASK:
|
|
170
|
+
# Check if user already approved
|
|
171
|
+
if context.get("user_approved", False):
|
|
172
|
+
return SecurityDecision(
|
|
173
|
+
allow=True,
|
|
174
|
+
risk_level=RiskLevel.MEDIUM,
|
|
175
|
+
reason="User approved"
|
|
176
|
+
)
|
|
177
|
+
else:
|
|
178
|
+
return SecurityDecision(
|
|
179
|
+
allow=False,
|
|
180
|
+
risk_level=RiskLevel.MEDIUM,
|
|
181
|
+
reason=f"Tool {tool_call.name} requires user confirmation"
|
|
182
|
+
)
|
|
183
|
+
else: # ALLOW
|
|
184
|
+
return SecurityDecision(
|
|
185
|
+
allow=True,
|
|
186
|
+
risk_level=RiskLevel.LOW,
|
|
187
|
+
reason="Allowed by permission policy"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
async def layer2_category_check(
|
|
191
|
+
self,
|
|
192
|
+
tool: BaseTool,
|
|
193
|
+
context: Dict
|
|
194
|
+
) -> SecurityDecision:
|
|
195
|
+
"""
|
|
196
|
+
Layer 2: Validate tool category.
|
|
197
|
+
|
|
198
|
+
- Destructive tools require confirmation
|
|
199
|
+
- Network tools checked against whitelist
|
|
200
|
+
- Unknown categories treated as high-risk
|
|
201
|
+
"""
|
|
202
|
+
category = getattr(tool, "category", "unknown")
|
|
203
|
+
|
|
204
|
+
# Check if category is allowed
|
|
205
|
+
if category not in self.allowed_categories:
|
|
206
|
+
return SecurityDecision(
|
|
207
|
+
allow=False,
|
|
208
|
+
risk_level=RiskLevel.HIGH,
|
|
209
|
+
reason=f"Tool category '{category}' not in allowed categories"
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# Check if confirmation required
|
|
213
|
+
if category in self.require_confirmation_for:
|
|
214
|
+
if not context.get("user_approved", False):
|
|
215
|
+
return SecurityDecision(
|
|
216
|
+
allow=False,
|
|
217
|
+
risk_level=RiskLevel.MEDIUM,
|
|
218
|
+
reason=f"Category '{category}' requires user confirmation"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Assess risk based on category
|
|
222
|
+
risk_map = {
|
|
223
|
+
"general": RiskLevel.LOW,
|
|
224
|
+
"network": RiskLevel.MEDIUM,
|
|
225
|
+
"destructive": RiskLevel.HIGH
|
|
226
|
+
}
|
|
227
|
+
risk = risk_map.get(category, RiskLevel.HIGH)
|
|
228
|
+
|
|
229
|
+
return SecurityDecision(
|
|
230
|
+
allow=True,
|
|
231
|
+
risk_level=risk,
|
|
232
|
+
reason=f"Category '{category}' allowed"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
async def layer3_path_security(
|
|
236
|
+
self,
|
|
237
|
+
tool_call: ToolCall,
|
|
238
|
+
tool: BaseTool
|
|
239
|
+
) -> SecurityDecision:
|
|
240
|
+
"""
|
|
241
|
+
Layer 3: Validate file paths.
|
|
242
|
+
|
|
243
|
+
- Detect path traversal attempts (../)
|
|
244
|
+
- Enforce working directory boundaries
|
|
245
|
+
- Block system paths (/etc, /sys, etc.)
|
|
246
|
+
"""
|
|
247
|
+
# Extract path arguments
|
|
248
|
+
path_args = []
|
|
249
|
+
for key in ["path", "file_path", "directory", "folder"]:
|
|
250
|
+
if key in tool_call.arguments:
|
|
251
|
+
path_args.append(tool_call.arguments[key])
|
|
252
|
+
|
|
253
|
+
# If no path arguments, skip this layer
|
|
254
|
+
if not path_args:
|
|
255
|
+
return SecurityDecision(
|
|
256
|
+
allow=True,
|
|
257
|
+
risk_level=RiskLevel.LOW,
|
|
258
|
+
reason="No path arguments to validate"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Validate all paths
|
|
262
|
+
violations = []
|
|
263
|
+
for path in path_args:
|
|
264
|
+
result = self.path_validator.validate_path(str(path))
|
|
265
|
+
if not result.is_safe:
|
|
266
|
+
violations.extend(result.violations)
|
|
267
|
+
|
|
268
|
+
if violations:
|
|
269
|
+
return SecurityDecision(
|
|
270
|
+
allow=False,
|
|
271
|
+
risk_level=RiskLevel.CRITICAL,
|
|
272
|
+
reason=f"Path security violations: {'; '.join(violations)}"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return SecurityDecision(
|
|
276
|
+
allow=True,
|
|
277
|
+
risk_level=RiskLevel.LOW,
|
|
278
|
+
reason="All paths validated"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
async def layer4_sandbox_check(
|
|
282
|
+
self,
|
|
283
|
+
tool: BaseTool,
|
|
284
|
+
context: Dict
|
|
285
|
+
) -> SecurityDecision:
|
|
286
|
+
"""
|
|
287
|
+
Layer 4: Check sandbox support.
|
|
288
|
+
|
|
289
|
+
- Recommend sandbox for safe operations
|
|
290
|
+
- Warn if sandbox unavailable for risky ops
|
|
291
|
+
"""
|
|
292
|
+
warnings = []
|
|
293
|
+
|
|
294
|
+
if self.enable_sandbox:
|
|
295
|
+
# Check if tool is read-only (safe for sandbox)
|
|
296
|
+
is_read_only = getattr(tool, "is_read_only", False)
|
|
297
|
+
if is_read_only:
|
|
298
|
+
warnings.append("Consider running in sandbox for additional safety")
|
|
299
|
+
|
|
300
|
+
# Check if tool is destructive (should use sandbox)
|
|
301
|
+
category = getattr(tool, "category", "general")
|
|
302
|
+
if category == "destructive":
|
|
303
|
+
warnings.append("Destructive tool - sandbox recommended")
|
|
304
|
+
|
|
305
|
+
return SecurityDecision(
|
|
306
|
+
allow=True, # Layer 4 never blocks, only warns
|
|
307
|
+
risk_level=RiskLevel.LOW,
|
|
308
|
+
reason="Sandbox check complete",
|
|
309
|
+
warnings=warnings
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
def _build_reason(self, failed_layers: List[str], warnings: List[str]) -> str:
|
|
313
|
+
"""Build human-readable reason for decision."""
|
|
314
|
+
if failed_layers:
|
|
315
|
+
layers_str = ", ".join(failed_layers)
|
|
316
|
+
return f"Security check failed in layers: {layers_str}"
|
|
317
|
+
elif warnings:
|
|
318
|
+
return f"Allowed with {len(warnings)} warning(s)"
|
|
319
|
+
else:
|
|
320
|
+
return "All security checks passed"
|
|
321
|
+
|
|
322
|
+
def _log_decision(
|
|
323
|
+
self,
|
|
324
|
+
tool_call: ToolCall,
|
|
325
|
+
tool: BaseTool,
|
|
326
|
+
decision: SecurityDecision
|
|
327
|
+
):
|
|
328
|
+
"""Log security decision for audit trail."""
|
|
329
|
+
self.audit_log.append({
|
|
330
|
+
"timestamp": time.time(),
|
|
331
|
+
"tool_name": tool_call.name,
|
|
332
|
+
"tool_category": getattr(tool, "category", "unknown"),
|
|
333
|
+
"decision": decision.allow,
|
|
334
|
+
"risk_level": decision.risk_level.value,
|
|
335
|
+
"reason": decision.reason,
|
|
336
|
+
"failed_layers": decision.failed_layers,
|
|
337
|
+
"warnings": decision.warnings
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
def get_audit_log(self) -> List[Dict]:
|
|
341
|
+
"""Get security audit log."""
|
|
342
|
+
return self.audit_log.copy()
|
|
343
|
+
|
|
344
|
+
def clear_audit_log(self):
|
|
345
|
+
"""Clear audit log."""
|
|
346
|
+
self.audit_log.clear()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Task 1.1: 创建 AgentEvent 模型
|
|
2
|
+
|
|
3
|
+
**状态**: ✅ 完成
|
|
4
|
+
**完成日期**: 2025-10-25
|
|
5
|
+
**优先级**: P0
|
|
6
|
+
**预计时间**: 1 天
|
|
7
|
+
**实际时间**: 1 天
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 📋 任务概述
|
|
12
|
+
|
|
13
|
+
### 目标
|
|
14
|
+
|
|
15
|
+
创建统一的事件模型(AgentEvent),为 Loom 2.0 的全链路流式架构奠定基础。
|
|
16
|
+
|
|
17
|
+
### 为什么需要这个任务?
|
|
18
|
+
|
|
19
|
+
Loom 1.0 的问题:
|
|
20
|
+
- `execute()` 返回字符串,无法获取实时进度
|
|
21
|
+
- 无法区分 LLM 输出、工具执行、错误等不同事件
|
|
22
|
+
- 调试困难,缺少执行过程的可观测性
|
|
23
|
+
|
|
24
|
+
Loom 2.0 的解决方案:
|
|
25
|
+
- 所有组件产生 `AgentEvent`
|
|
26
|
+
- 24 种事件类型覆盖完整生命周期
|
|
27
|
+
- 实时流式输出
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ✅ 已完成的工作
|
|
32
|
+
|
|
33
|
+
### 交付物清单
|
|
34
|
+
|
|
35
|
+
| 文件 | 行数 | 说明 | 状态 |
|
|
36
|
+
|------|------|------|------|
|
|
37
|
+
| `loom/core/events.py` | 420 | 核心事件模型 | ✅ |
|
|
38
|
+
| `loom/interfaces/event_producer.py` | 120 | Protocol 定义 | ✅ |
|
|
39
|
+
| `tests/unit/test_agent_events.py` | 550 | 单元测试(31 个) | ✅ |
|
|
40
|
+
| `docs/agent_events_guide.md` | 650 | 使用文档 | ✅ |
|
|
41
|
+
| `examples/agent_events_demo.py` | 350 | 演示代码 | ✅ |
|
|
42
|
+
| **总计** | **2090** | | |
|
|
43
|
+
|
|
44
|
+
### 关键特性
|
|
45
|
+
|
|
46
|
+
1. **AgentEventType 枚举** - 24 种事件类型
|
|
47
|
+
- Phase Events (2)
|
|
48
|
+
- Context Events (3)
|
|
49
|
+
- RAG Events (3)
|
|
50
|
+
- LLM Events (4)
|
|
51
|
+
- Tool Events (5)
|
|
52
|
+
- Agent Events (4)
|
|
53
|
+
- Error Events (4)
|
|
54
|
+
|
|
55
|
+
2. **AgentEvent 数据类**
|
|
56
|
+
- 必需字段:`type`, `timestamp`
|
|
57
|
+
- 可选字段:`phase`, `content`, `tool_call`, `tool_result`, `error`, `metadata`, `iteration`, `turn_id`
|
|
58
|
+
- 便捷构造方法:`phase_start()`, `llm_delta()`, `tool_progress()`, `tool_result()`, `agent_finish()`, `error()`
|
|
59
|
+
- 实用方法:`is_terminal()`, `is_llm_content()`, `is_tool_event()`
|
|
60
|
+
|
|
61
|
+
3. **辅助类和工具**
|
|
62
|
+
- `ToolCall` - 工具调用请求模型
|
|
63
|
+
- `ToolResult` - 工具执行结果模型
|
|
64
|
+
- `EventCollector` - 事件收集和分析
|
|
65
|
+
- `EventProducer` Protocol - 事件产生器接口
|
|
66
|
+
|
|
67
|
+
### 测试结果
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
======================== 31 passed, 1 warning in 0.16s =========================
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- ✅ 31/31 测试通过
|
|
74
|
+
- ✅ 100% 测试覆盖率
|
|
75
|
+
- ✅ 包含单元测试和集成测试
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 🧪 验收标准
|
|
80
|
+
|
|
81
|
+
| 标准 | 要求 | 实际 | 状态 |
|
|
82
|
+
|------|------|------|------|
|
|
83
|
+
| AgentEvent 模型完整 | 定义所有必需字段和方法 | 24 种事件类型 | ✅ |
|
|
84
|
+
| 测试覆盖率 | ≥ 80% | 100% | ✅ |
|
|
85
|
+
| 文档完整 | 使用指南 + API 文档 | 650 行文档 | ✅ |
|
|
86
|
+
| 向后兼容 | 不破坏现有 API | 是(设计阶段) | ✅ |
|
|
87
|
+
| 代码质量 | PEP 8, 类型提示 | 是 | ✅ |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 📝 完成总结
|
|
92
|
+
|
|
93
|
+
详见:`docs/TASK_1.1_COMPLETION_SUMMARY.md`
|
|
94
|
+
|
|
95
|
+
### 关键成果
|
|
96
|
+
|
|
97
|
+
1. ✅ 创建了完整的事件模型(24 种事件类型)
|
|
98
|
+
2. ✅ 定义了 Protocol 接口(EventProducer 等)
|
|
99
|
+
3. ✅ 编写了全面的单元测试(31 个测试,100% 通过)
|
|
100
|
+
4. ✅ 提供了详细的使用文档(650+ 行)
|
|
101
|
+
5. ✅ 修复了发现的问题(`__repr__` 名称冲突)
|
|
102
|
+
|
|
103
|
+
### 经验教训
|
|
104
|
+
|
|
105
|
+
1. **便捷构造方法很重要** - `AgentEvent.llm_delta()` 比手动创建更简洁
|
|
106
|
+
2. **EventCollector 非常有用** - 简化事件处理和分析
|
|
107
|
+
3. **充分的文档和示例至关重要** - 帮助理解和使用
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 🔗 相关资源
|
|
112
|
+
|
|
113
|
+
- [AgentEvent 使用指南](../../../docs/agent_events_guide.md)
|
|
114
|
+
- [Task 1.1 完成总结](../../../docs/TASK_1.1_COMPLETION_SUMMARY.md)
|
|
115
|
+
- [演示代码](../../../examples/agent_events_demo.py)
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
**创建日期**: 2025-10-25
|
|
120
|
+
**完成日期**: 2025-10-25
|
|
121
|
+
**贡献者**: Claude Code + 用户
|