ni.agentkit 0.3.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.
- agentkit/__init__.py +57 -0
- agentkit/_cli.py +72 -0
- agentkit/agents/__init__.py +0 -0
- agentkit/agents/agent.py +364 -0
- agentkit/agents/base_agent.py +59 -0
- agentkit/agents/orchestrators.py +62 -0
- agentkit/docs/Architecture.md +536 -0
- agentkit/docs/QuickStart.md +806 -0
- agentkit/docs/README.md +119 -0
- agentkit/docs/Reference.md +576 -0
- agentkit/docs/TestReport.md +80 -0
- agentkit/examples/__init__.py +0 -0
- agentkit/examples/ollama/01_basic_chat.py +34 -0
- agentkit/examples/ollama/02_tool_calling.py +70 -0
- agentkit/examples/ollama/03_skill_usage.py +86 -0
- agentkit/examples/ollama/04_multi_agent.py +84 -0
- agentkit/examples/ollama/05_guardrail.py +101 -0
- agentkit/examples/ollama/06_orchestration.py +123 -0
- agentkit/examples/ollama/07_sync_async_stream.py +180 -0
- agentkit/examples/ollama/08_memory.py +201 -0
- agentkit/examples/ollama/README.md +51 -0
- agentkit/examples/ollama/__init__.py +0 -0
- agentkit/examples/quickstart.py +204 -0
- agentkit/examples/standard/01_basic_chat.py +33 -0
- agentkit/examples/standard/02_tool_calling.py +70 -0
- agentkit/examples/standard/03_skill_usage.py +89 -0
- agentkit/examples/standard/04_multi_agent.py +87 -0
- agentkit/examples/standard/05_guardrail.py +104 -0
- agentkit/examples/standard/06_orchestration.py +122 -0
- agentkit/examples/standard/07_sync_async_stream.py +171 -0
- agentkit/examples/standard/08_memory.py +140 -0
- agentkit/examples/standard/README.md +31 -0
- agentkit/examples/standard/__init__.py +0 -0
- agentkit/examples/test_ollama.py +272 -0
- agentkit/llm/__init__.py +0 -0
- agentkit/llm/adapters/__init__.py +0 -0
- agentkit/llm/adapters/anthropic_adapter.py +192 -0
- agentkit/llm/adapters/google_adapter.py +184 -0
- agentkit/llm/adapters/ollama_adapter.py +250 -0
- agentkit/llm/adapters/openai_adapter.py +183 -0
- agentkit/llm/adapters/openai_compatible.py +35 -0
- agentkit/llm/base.py +66 -0
- agentkit/llm/cache.py +121 -0
- agentkit/llm/middleware.py +133 -0
- agentkit/llm/registry.py +138 -0
- agentkit/llm/types.py +191 -0
- agentkit/memory/__init__.py +0 -0
- agentkit/memory/base.py +47 -0
- agentkit/memory/mem0_provider.py +48 -0
- agentkit/runner/__init__.py +0 -0
- agentkit/runner/context.py +47 -0
- agentkit/runner/events.py +36 -0
- agentkit/runner/runner.py +105 -0
- agentkit/safety/__init__.py +0 -0
- agentkit/safety/guardrails.py +55 -0
- agentkit/safety/permissions.py +41 -0
- agentkit/skills/__init__.py +0 -0
- agentkit/skills/loader.py +90 -0
- agentkit/skills/models.py +106 -0
- agentkit/skills/registry.py +48 -0
- agentkit/tools/__init__.py +0 -0
- agentkit/tools/base_tool.py +44 -0
- agentkit/tools/function_tool.py +118 -0
- agentkit/tools/skill_toolset.py +199 -0
- agentkit/utils/__init__.py +0 -0
- agentkit/utils/schema.py +83 -0
- ni_agentkit-0.3.1.dist-info/METADATA +157 -0
- ni_agentkit-0.3.1.dist-info/RECORD +72 -0
- ni_agentkit-0.3.1.dist-info/WHEEL +5 -0
- ni_agentkit-0.3.1.dist-info/entry_points.txt +2 -0
- ni_agentkit-0.3.1.dist-info/licenses/LICENSE +21 -0
- ni_agentkit-0.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# AgentKit 示例测试报告
|
|
2
|
+
|
|
3
|
+
> 测试时间:2026-04-12
|
|
4
|
+
> 测试环境:macOS (Apple Silicon)
|
|
5
|
+
> 模型:Ollama `qwen3.5:cloud`
|
|
6
|
+
> AgentKit 版本:v0.3.1
|
|
7
|
+
> Thinking 模式:开启(默认)
|
|
8
|
+
> LLM 调用模式:非流式(默认)
|
|
9
|
+
> 缓存:开启(默认)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 测试结果
|
|
14
|
+
|
|
15
|
+
| # | 示例 | 文件 | 耗时 | 状态 | 说明 |
|
|
16
|
+
|---|------|------|-----:|:----:|------|
|
|
17
|
+
| 1 | 基础对话 | `01_basic_chat.py` | 27.7s | ✅ | 正确回答量子计算定义 |
|
|
18
|
+
| 2 | 工具调用 | `02_tool_calling.py` | 21.8s | ✅ | add=42 / multiply=21 / weather=晴25°C |
|
|
19
|
+
| 3 | Skill 使用 | `03_skill_usage.py` | 42.8s | ✅ | 深圳阵雨→带伞 / 成都阴→厚外套 / 广州晴→防晒 |
|
|
20
|
+
| 4 | 多 Agent 协作 | `04_multi_agent.py` | 116.6s | ✅ | as_tool 委派成功;Handoff 偶发返回 None |
|
|
21
|
+
| 5 | 安全护栏 | `05_guardrail.py` | 12.9s | ✅ | 敏感词拦截 ✅ / read_file 放行 ✅ / delete_file 拒绝 ✅ |
|
|
22
|
+
| 6 | 编排 Agent | `06_orchestration.py` | 195.6s | ✅ | Sequential 流水线 + Parallel 并行 + Loop 循环 |
|
|
23
|
+
| 7 | 同步/异步/流式 | `07_sync_async_stream.py` | 50.0s | ✅ | run_sync 4.3s / run 2.1s / 并发×3 4.5s / 流式 8.3s |
|
|
24
|
+
| 8 | 记忆系统 | `08_memory.py` | 144.2s | ✅ | 咖啡→拿铁→牛奶过敏→燕麦拿铁(4 轮完美记忆) |
|
|
25
|
+
| | **合计** | | **611.6s** | **8/8** | |
|
|
26
|
+
|
|
27
|
+
## 耗时分析
|
|
28
|
+
|
|
29
|
+
- **单次 LLM 调用**(含 thinking):约 5-15 秒
|
|
30
|
+
- **最快示例**:05 安全护栏(12.9s)——护栏拦截不经过 LLM
|
|
31
|
+
- **最慢示例**:06 编排 Agent(195.6s)——3 组编排模式,每组含多个 Agent 串行/并行调用
|
|
32
|
+
- **多轮示例耗时** ≈ LLM 调用次数 × 单次调用耗时
|
|
33
|
+
|
|
34
|
+
## 各示例 LLM 调用次数估算
|
|
35
|
+
|
|
36
|
+
| # | 示例 | LLM 调用次数 | 说明 |
|
|
37
|
+
|---|------|:-----------:|------|
|
|
38
|
+
| 1 | 基础对话 | 1 | 单次对话 |
|
|
39
|
+
| 2 | 工具调用 | 6 | 3 个查询 × 2 轮(调工具 + 生成回复) |
|
|
40
|
+
| 3 | Skill 使用 | 9 | 3 个查询 × 3 轮(load_skill + 调工具 + 回复) |
|
|
41
|
+
| 4 | 多 Agent 协作 | ~8 | 模式 A(经理+研究员)+ 模式 B(分诊+2 专家) |
|
|
42
|
+
| 5 | 安全护栏 | 3 | 1 次被拦截 + 2 次正常执行(含工具调用) |
|
|
43
|
+
| 6 | 编排 Agent | ~12 | Sequential(3) + Parallel(3) + Loop(~6) |
|
|
44
|
+
| 7 | 同步/异步/流式 | ~7 | 同步(1) + 异步(1) + 并发(3) + 流式(2) |
|
|
45
|
+
| 8 | 记忆系统 | ~10 | 无记忆(2) + 有记忆(4 轮 × 2 调用) |
|
|
46
|
+
|
|
47
|
+
## 已知问题
|
|
48
|
+
|
|
49
|
+
| 问题 | 严重程度 | 说明 |
|
|
50
|
+
|------|:--------:|------|
|
|
51
|
+
| 示例 04 Handoff 偶发返回 None | 低 | cloud 模型对 `transfer_to_xxx` 工具的理解偶发异常,非框架 bug |
|
|
52
|
+
| 示例 06 Loop 部分输出较简短 | 低 | 小模型在迭代优化场景下生成质量有限 |
|
|
53
|
+
|
|
54
|
+
## 配置说明
|
|
55
|
+
|
|
56
|
+
本次测试使用以下默认配置:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
Agent(
|
|
60
|
+
model="ollama/qwen3.5:cloud",
|
|
61
|
+
enable_cache=True, # LLM 响应缓存(默认开启)
|
|
62
|
+
cache_ttl=300, # 缓存有效期 300 秒
|
|
63
|
+
memory_async_write=True, # 记忆异步写入(示例 08 为 False)
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
OllamaAdapter 配置:
|
|
68
|
+
- **Thinking 模式**:开启(默认 `think=True`)
|
|
69
|
+
- **调用模式**:非流式(`stream=False`)
|
|
70
|
+
- **超时**:300 秒
|
|
71
|
+
|
|
72
|
+
## 运行方式
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# 运行单个示例
|
|
76
|
+
python examples/ollama/01_basic_chat.py
|
|
77
|
+
|
|
78
|
+
# 运行全部示例
|
|
79
|
+
for f in examples/ollama/0*.py; do python "$f"; done
|
|
80
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 1:最简 Agent — 纯对话(Ollama 本地版)
|
|
3
|
+
|
|
4
|
+
运行前请确保 Ollama 已启动:
|
|
5
|
+
ollama serve
|
|
6
|
+
ollama pull qwen3.5:cloud
|
|
7
|
+
|
|
8
|
+
运行:
|
|
9
|
+
python examples/ollama/01_basic_chat.py
|
|
10
|
+
"""
|
|
11
|
+
import sys, os
|
|
12
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
13
|
+
|
|
14
|
+
from agentkit import Agent, Runner
|
|
15
|
+
|
|
16
|
+
# 创建最简 Agent —— 使用本地 Ollama 模型
|
|
17
|
+
agent = Agent(
|
|
18
|
+
name="assistant",
|
|
19
|
+
instructions="你是一个有帮助的中文助手。回答尽量简洁。",
|
|
20
|
+
model="ollama/qwen3.5:cloud", # 本地 Ollama 模型,无需 API Key
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# 同步运行
|
|
24
|
+
result = Runner.run_sync(agent, input="什么是量子计算?请用一句话解释。")
|
|
25
|
+
|
|
26
|
+
if result.success:
|
|
27
|
+
print(f"✅ 回复: {result.final_output}")
|
|
28
|
+
else:
|
|
29
|
+
print(f"❌ 错误: {result.error}")
|
|
30
|
+
|
|
31
|
+
# 查看事件流
|
|
32
|
+
print("\n📋 事件流:")
|
|
33
|
+
for event in result.events:
|
|
34
|
+
print(f" [{event.type}] {str(event.data)[:80]}")
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 2:带工具的 Agent — Function Calling(Ollama 本地版)
|
|
3
|
+
|
|
4
|
+
演示 @function_tool 装饰器 + 本地 Ollama 的 Function Calling 能力。
|
|
5
|
+
|
|
6
|
+
运行前请确保 Ollama 已启动:
|
|
7
|
+
ollama serve
|
|
8
|
+
ollama pull qwen3.5:cloud
|
|
9
|
+
|
|
10
|
+
运行:
|
|
11
|
+
python examples/ollama/02_tool_calling.py
|
|
12
|
+
"""
|
|
13
|
+
import sys, os
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
15
|
+
|
|
16
|
+
from agentkit import Agent, Runner, function_tool
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ===== 定义工具 =====
|
|
20
|
+
|
|
21
|
+
@function_tool
|
|
22
|
+
def add(a: int, b: int) -> str:
|
|
23
|
+
"""两个数字相加"""
|
|
24
|
+
return str(a + b)
|
|
25
|
+
|
|
26
|
+
@function_tool
|
|
27
|
+
def multiply(a: int, b: int) -> str:
|
|
28
|
+
"""两个数字相乘"""
|
|
29
|
+
return str(a * b)
|
|
30
|
+
|
|
31
|
+
@function_tool
|
|
32
|
+
def get_weather(city: str) -> str:
|
|
33
|
+
"""获取指定城市的天气信息"""
|
|
34
|
+
weather_data = {
|
|
35
|
+
"北京": "晴,25°C",
|
|
36
|
+
"上海": "多云,22°C",
|
|
37
|
+
"深圳": "阵雨,28°C",
|
|
38
|
+
}
|
|
39
|
+
return weather_data.get(city, f"{city}:暂无数据")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ===== 创建 Agent =====
|
|
43
|
+
|
|
44
|
+
agent = Agent(
|
|
45
|
+
name="smart-assistant",
|
|
46
|
+
instructions="你是一个全能助手。可以做数学计算和查天气。根据用户需求选择合适的工具。回答简洁。",
|
|
47
|
+
model="ollama/qwen3.5:cloud",
|
|
48
|
+
tools=[add, multiply, get_weather],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# ===== 运行测试 =====
|
|
53
|
+
|
|
54
|
+
queries = [
|
|
55
|
+
"请计算 15 + 27 的结果",
|
|
56
|
+
"3 乘以 7 等于多少?",
|
|
57
|
+
"北京今天天气如何?",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
for q in queries:
|
|
61
|
+
print(f"\n用户: {q}")
|
|
62
|
+
result = Runner.run_sync(agent, input=q)
|
|
63
|
+
if result.success:
|
|
64
|
+
print(f"助手: {result.final_output}")
|
|
65
|
+
else:
|
|
66
|
+
print(f"❌ 错误: {result.error}")
|
|
67
|
+
|
|
68
|
+
for event in result.events:
|
|
69
|
+
if event.type == "tool_result":
|
|
70
|
+
print(f" 🔧 工具调用: {event.data}")
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 3:带 Skill 的 Agent — 领域知识包(Ollama 本地版)
|
|
3
|
+
|
|
4
|
+
演示 Skill 三级加载和 Skill + Tool 协作。
|
|
5
|
+
|
|
6
|
+
运行前请确保 Ollama 已启动:
|
|
7
|
+
ollama serve
|
|
8
|
+
ollama pull qwen3.5:cloud
|
|
9
|
+
|
|
10
|
+
运行:
|
|
11
|
+
python examples/ollama/03_skill_usage.py
|
|
12
|
+
"""
|
|
13
|
+
import sys, os
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
15
|
+
|
|
16
|
+
from agentkit import Agent, Runner, Skill, SkillFrontmatter, function_tool
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ===== 定义工具 =====
|
|
20
|
+
|
|
21
|
+
@function_tool
|
|
22
|
+
def get_weather(city: str) -> str:
|
|
23
|
+
"""获取指定城市的天气信息"""
|
|
24
|
+
weather_data = {
|
|
25
|
+
"北京": "晴,25°C",
|
|
26
|
+
"上海": "多云,22°C",
|
|
27
|
+
"深圳": "阵雨,28°C",
|
|
28
|
+
"广州": "晴,30°C",
|
|
29
|
+
"成都": "阴,18°C",
|
|
30
|
+
}
|
|
31
|
+
return weather_data.get(city, f"{city}:暂无数据")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ===== 定义 Skill =====
|
|
35
|
+
|
|
36
|
+
weather_skill = Skill(
|
|
37
|
+
frontmatter=SkillFrontmatter(
|
|
38
|
+
name="weather-analysis",
|
|
39
|
+
description="天气分析技能,查询天气并给出穿衣建议",
|
|
40
|
+
),
|
|
41
|
+
instructions="""## 天气分析步骤
|
|
42
|
+
|
|
43
|
+
1. 使用 get_weather 工具查询用户指定城市的天气
|
|
44
|
+
2. 根据天气情况给出穿衣建议:
|
|
45
|
+
- 温度 > 30°C:建议穿短袖、注意防晒
|
|
46
|
+
- 温度 20-30°C:建议穿薄外套
|
|
47
|
+
- 温度 < 20°C:建议穿厚外套
|
|
48
|
+
3. 如果有雨,额外提醒带伞
|
|
49
|
+
4. 用简洁的中文回复用户""",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ===== 创建带 Skill 的 Agent =====
|
|
54
|
+
|
|
55
|
+
agent = Agent(
|
|
56
|
+
name="skill-agent",
|
|
57
|
+
instructions="你是一个智能助手,可以使用专业技能来完成任务。",
|
|
58
|
+
model="ollama/qwen3.5:cloud",
|
|
59
|
+
skills=[weather_skill],
|
|
60
|
+
tools=[get_weather],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ===== 运行测试 =====
|
|
65
|
+
|
|
66
|
+
queries = [
|
|
67
|
+
"深圳今天适合穿什么衣服?",
|
|
68
|
+
"成都现在冷不冷?需要穿什么?",
|
|
69
|
+
"广州今天出门要注意什么?",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
for q in queries:
|
|
73
|
+
print(f"\n用户: {q}")
|
|
74
|
+
result = Runner.run_sync(agent, input=q)
|
|
75
|
+
if result.success:
|
|
76
|
+
print(f"助手: {result.final_output}")
|
|
77
|
+
else:
|
|
78
|
+
print(f"❌ 错误: {result.error}")
|
|
79
|
+
|
|
80
|
+
for event in result.events:
|
|
81
|
+
if event.type == "tool_result":
|
|
82
|
+
tool_name = event.data.get("tool", "")
|
|
83
|
+
if tool_name == "load_skill":
|
|
84
|
+
print(f" 📚 加载了 Skill: {event.data.get('result', {}).get('skill_name', '')}")
|
|
85
|
+
else:
|
|
86
|
+
print(f" 🔧 工具调用: {event.data}")
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 4:多 Agent 协作 — Handoff 与 as_tool(Ollama 本地版)
|
|
3
|
+
|
|
4
|
+
演示两种 Agent 协作模式。
|
|
5
|
+
|
|
6
|
+
运行前请确保 Ollama 已启动:
|
|
7
|
+
ollama serve
|
|
8
|
+
ollama pull qwen3.5:cloud
|
|
9
|
+
|
|
10
|
+
运行:
|
|
11
|
+
python examples/ollama/04_multi_agent.py
|
|
12
|
+
"""
|
|
13
|
+
import sys, os
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
15
|
+
|
|
16
|
+
from agentkit import Agent, Runner
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ============================================================
|
|
20
|
+
# 模式 A:as_tool(委派)
|
|
21
|
+
# ============================================================
|
|
22
|
+
|
|
23
|
+
print("=" * 50)
|
|
24
|
+
print(" 模式 A:as_tool(委派)")
|
|
25
|
+
print("=" * 50)
|
|
26
|
+
|
|
27
|
+
researcher = Agent(
|
|
28
|
+
name="researcher",
|
|
29
|
+
instructions="你是一个研究助手。收到问题后,给出简短的研究结论(不超过 3 句话)。",
|
|
30
|
+
model="ollama/qwen3.5:cloud",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
manager = Agent(
|
|
34
|
+
name="manager",
|
|
35
|
+
instructions="你是项目经理。需要研究信息时调用 research 工具。综合研究结果给出你的建议。",
|
|
36
|
+
model="ollama/qwen3.5:cloud",
|
|
37
|
+
tools=[
|
|
38
|
+
researcher.as_tool("research", "调用研究助手获取研究信息"),
|
|
39
|
+
],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
result = Runner.run_sync(manager, input="帮我调研 Python 异步编程的最佳实践")
|
|
43
|
+
if result.success:
|
|
44
|
+
print(f"\n✅ 回复: {result.final_output}")
|
|
45
|
+
else:
|
|
46
|
+
print(f"\n❌ 错误: {result.error}")
|
|
47
|
+
|
|
48
|
+
for event in result.events:
|
|
49
|
+
if event.type == "tool_result":
|
|
50
|
+
print(f" 🔧 研究员返回: {str(event.data)[:100]}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ============================================================
|
|
54
|
+
# 模式 B:Handoff(转介)
|
|
55
|
+
# ============================================================
|
|
56
|
+
|
|
57
|
+
print(f"\n{'=' * 50}")
|
|
58
|
+
print(" 模式 B:Handoff(转介)")
|
|
59
|
+
print("=" * 50)
|
|
60
|
+
|
|
61
|
+
billing_agent = Agent(
|
|
62
|
+
name="billing",
|
|
63
|
+
instructions="你是账单专家。处理所有账单相关的问题,给出专业的解答。",
|
|
64
|
+
model="ollama/qwen3.5:cloud",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
tech_agent = Agent(
|
|
68
|
+
name="tech",
|
|
69
|
+
instructions="你是技术支持专家。处理所有技术相关的问题。",
|
|
70
|
+
model="ollama/qwen3.5:cloud",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
triage_agent = Agent(
|
|
74
|
+
name="triage",
|
|
75
|
+
instructions="你是客服分诊员。根据用户问题类型,转交给合适的专家:账单问题转给 billing,技术问题转给 tech。",
|
|
76
|
+
model="ollama/qwen3.5:cloud",
|
|
77
|
+
handoffs=[billing_agent, tech_agent],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
result = Runner.run_sync(triage_agent, input="我的账单金额好像不对,比上个月多了很多")
|
|
81
|
+
if result.success:
|
|
82
|
+
print(f"\n✅ 最终由 [{result.last_agent}] 处理: {result.final_output}")
|
|
83
|
+
else:
|
|
84
|
+
print(f"\n❌ 错误: {result.error}")
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 5:安全护栏 — Guardrail 与权限控制(Ollama 本地版)
|
|
3
|
+
|
|
4
|
+
演示输入护栏、输出护栏和权限控制。
|
|
5
|
+
|
|
6
|
+
运行前请确保 Ollama 已启动:
|
|
7
|
+
ollama serve
|
|
8
|
+
ollama pull qwen3.5:cloud
|
|
9
|
+
|
|
10
|
+
运行:
|
|
11
|
+
python examples/ollama/05_guardrail.py
|
|
12
|
+
"""
|
|
13
|
+
import sys, os
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
15
|
+
|
|
16
|
+
from agentkit import (
|
|
17
|
+
Agent, Runner, function_tool,
|
|
18
|
+
input_guardrail, output_guardrail, GuardrailResult,
|
|
19
|
+
PermissionPolicy,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ===== 定义工具 =====
|
|
24
|
+
|
|
25
|
+
@function_tool
|
|
26
|
+
def read_file(filename: str) -> str:
|
|
27
|
+
"""读取文件内容"""
|
|
28
|
+
return f"[模拟] 文件 {filename} 的内容:Hello World"
|
|
29
|
+
|
|
30
|
+
@function_tool
|
|
31
|
+
def delete_file(filename: str) -> str:
|
|
32
|
+
"""删除文件"""
|
|
33
|
+
return f"[模拟] 已删除文件 {filename}"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ===== 定义护栏 =====
|
|
37
|
+
|
|
38
|
+
@input_guardrail
|
|
39
|
+
async def block_sensitive_words(ctx):
|
|
40
|
+
"""检查输入是否包含敏感词"""
|
|
41
|
+
sensitive = ["密码", "身份证", "银行卡号", "社保号"]
|
|
42
|
+
for word in sensitive:
|
|
43
|
+
if word in ctx.input:
|
|
44
|
+
return GuardrailResult(triggered=True, reason=f"包含敏感词: {word}")
|
|
45
|
+
return GuardrailResult(triggered=False)
|
|
46
|
+
|
|
47
|
+
@output_guardrail
|
|
48
|
+
async def check_output_safety(ctx, output):
|
|
49
|
+
"""检查输出是否安全"""
|
|
50
|
+
dangerous_words = ["删除成功", "已格式化"]
|
|
51
|
+
output_str = str(output)
|
|
52
|
+
for word in dangerous_words:
|
|
53
|
+
if word in output_str:
|
|
54
|
+
return GuardrailResult(triggered=True, reason=f"输出包含危险内容: {word}")
|
|
55
|
+
return GuardrailResult(triggered=False)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ===== 创建带安全护栏的 Agent =====
|
|
59
|
+
|
|
60
|
+
agent = Agent(
|
|
61
|
+
name="safe-agent",
|
|
62
|
+
instructions="你是一个安全的助手。可以帮用户读取文件,但不能删除文件。",
|
|
63
|
+
model="ollama/qwen3.5:cloud",
|
|
64
|
+
tools=[read_file, delete_file],
|
|
65
|
+
input_guardrails=[block_sensitive_words],
|
|
66
|
+
output_guardrails=[check_output_safety],
|
|
67
|
+
permission_policy=PermissionPolicy(
|
|
68
|
+
mode="ask",
|
|
69
|
+
allowed_tools={"read_file"},
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ===== 运行测试 =====
|
|
75
|
+
|
|
76
|
+
print("=" * 50)
|
|
77
|
+
print(" 测试输入护栏")
|
|
78
|
+
print("=" * 50)
|
|
79
|
+
|
|
80
|
+
# 测试 1:敏感请求 → 被拦截
|
|
81
|
+
result = Runner.run_sync(agent, input="请告诉我管理员的密码")
|
|
82
|
+
print(f"\n请求: '请告诉我管理员的密码'")
|
|
83
|
+
print(f"结果: {'🛡️ 已拦截 — ' + result.error if result.error else result.final_output}")
|
|
84
|
+
|
|
85
|
+
# 测试 2:正常请求 → 通过
|
|
86
|
+
result = Runner.run_sync(agent, input="请读取 config.txt 文件")
|
|
87
|
+
print(f"\n请求: '请读取 config.txt 文件'")
|
|
88
|
+
print(f"结果: {result.final_output if result.success else '❌ ' + str(result.error)}")
|
|
89
|
+
|
|
90
|
+
print(f"\n{'=' * 50}")
|
|
91
|
+
print(" 测试权限控制")
|
|
92
|
+
print("=" * 50)
|
|
93
|
+
|
|
94
|
+
# 测试 3:尝试调用未授权的工具
|
|
95
|
+
result = Runner.run_sync(agent, input="请删除 temp.txt 文件")
|
|
96
|
+
print(f"\n请求: '请删除 temp.txt 文件'")
|
|
97
|
+
print(f"结果: {result.final_output if result.success else '🔒 ' + str(result.error)}")
|
|
98
|
+
|
|
99
|
+
for event in result.events:
|
|
100
|
+
if event.type == "permission_denied":
|
|
101
|
+
print(f" 🔒 权限拒绝: {event.data}")
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 6:编排 Agent — 顺序执行、并行执行、循环执行(Ollama 本地版)
|
|
3
|
+
|
|
4
|
+
演示三种编排模式。
|
|
5
|
+
|
|
6
|
+
运行前请确保 Ollama 已启动:
|
|
7
|
+
ollama serve
|
|
8
|
+
ollama pull qwen3.5:cloud
|
|
9
|
+
|
|
10
|
+
运行:
|
|
11
|
+
python examples/ollama/06_orchestration.py
|
|
12
|
+
"""
|
|
13
|
+
import sys, os
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
15
|
+
|
|
16
|
+
from agentkit import Agent, Runner, SequentialAgent, ParallelAgent, LoopAgent
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
MODEL = "ollama/qwen3.5:cloud"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ============================================================
|
|
23
|
+
# 模式 A:顺序执行 — 报告生成流水线
|
|
24
|
+
# ============================================================
|
|
25
|
+
|
|
26
|
+
print("=" * 50)
|
|
27
|
+
print(" SequentialAgent — 报告生成流水线")
|
|
28
|
+
print("=" * 50)
|
|
29
|
+
|
|
30
|
+
pipeline = SequentialAgent(
|
|
31
|
+
name="report-pipeline",
|
|
32
|
+
sub_agents=[
|
|
33
|
+
Agent(
|
|
34
|
+
name="extractor",
|
|
35
|
+
instructions="你是数据提取专家。从用户输入中提取所有关键数据点,以列表形式输出。",
|
|
36
|
+
model=MODEL,
|
|
37
|
+
),
|
|
38
|
+
Agent(
|
|
39
|
+
name="analyzer",
|
|
40
|
+
instructions="你是数据分析专家。分析上文提取的数据点,找出趋势和规律,给出 2-3 条洞察。",
|
|
41
|
+
model=MODEL,
|
|
42
|
+
),
|
|
43
|
+
Agent(
|
|
44
|
+
name="reporter",
|
|
45
|
+
instructions="你是报告撰写专家。将上文的分析结果写成一段简洁的中文报告(不超过 100 字)。",
|
|
46
|
+
model=MODEL,
|
|
47
|
+
),
|
|
48
|
+
],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
result = Runner.run_sync(pipeline, input="今年Q1销售额1000万,Q2增长到1500万,Q3下降到1200万,Q4预计1800万")
|
|
52
|
+
print(f"\n输入: 今年Q1销售额1000万...")
|
|
53
|
+
for event in result.events:
|
|
54
|
+
if event.type == "final_output":
|
|
55
|
+
print(f" [{event.agent}] {str(event.data)[:120]}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ============================================================
|
|
59
|
+
# 模式 B:并行执行 — 多维度分析
|
|
60
|
+
# ============================================================
|
|
61
|
+
|
|
62
|
+
print(f"\n{'=' * 50}")
|
|
63
|
+
print(" ParallelAgent — 多维度并行分析")
|
|
64
|
+
print("=" * 50)
|
|
65
|
+
|
|
66
|
+
parallel = ParallelAgent(
|
|
67
|
+
name="multi-analysis",
|
|
68
|
+
sub_agents=[
|
|
69
|
+
Agent(
|
|
70
|
+
name="financial",
|
|
71
|
+
instructions="你是财务分析师。用一句话分析给定数据的财务状况。",
|
|
72
|
+
model=MODEL,
|
|
73
|
+
),
|
|
74
|
+
Agent(
|
|
75
|
+
name="market",
|
|
76
|
+
instructions="你是市场分析师。用一句话分析给定数据反映的市场趋势。",
|
|
77
|
+
model=MODEL,
|
|
78
|
+
),
|
|
79
|
+
Agent(
|
|
80
|
+
name="risk",
|
|
81
|
+
instructions="你是风险分析师。用一句话分析给定数据中的潜在风险。",
|
|
82
|
+
model=MODEL,
|
|
83
|
+
),
|
|
84
|
+
],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
result = Runner.run_sync(parallel, input="公司年收入 5 亿,同比增长 20%,但负债率从 30% 升至 45%")
|
|
88
|
+
print(f"\n输入: 公司年收入 5 亿...")
|
|
89
|
+
for event in result.events:
|
|
90
|
+
if event.type == "final_output":
|
|
91
|
+
print(f" [{event.agent}] {str(event.data)[:120]}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ============================================================
|
|
95
|
+
# 模式 C:循环执行 — 迭代优化
|
|
96
|
+
# ============================================================
|
|
97
|
+
|
|
98
|
+
print(f"\n{'=' * 50}")
|
|
99
|
+
print(" LoopAgent — 迭代优化循环")
|
|
100
|
+
print("=" * 50)
|
|
101
|
+
|
|
102
|
+
loop = LoopAgent(
|
|
103
|
+
name="improvement-loop",
|
|
104
|
+
max_iterations=3,
|
|
105
|
+
sub_agents=[
|
|
106
|
+
Agent(
|
|
107
|
+
name="writer",
|
|
108
|
+
instructions="你是一个文案写手。根据用户需求或上轮反馈,写一句广告语。只输出广告语本身。",
|
|
109
|
+
model=MODEL,
|
|
110
|
+
),
|
|
111
|
+
Agent(
|
|
112
|
+
name="critic",
|
|
113
|
+
instructions="你是一个文案评审。评估上面的广告语,如果已经很好则只输出'通过',否则给出简短的改进建议。",
|
|
114
|
+
model=MODEL,
|
|
115
|
+
),
|
|
116
|
+
],
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
result = Runner.run_sync(loop, input="为一款AI编程助手写一句吸引开发者的广告语")
|
|
120
|
+
print(f"\n输入: 为一款AI编程助手写广告语")
|
|
121
|
+
for event in result.events:
|
|
122
|
+
if event.type == "final_output":
|
|
123
|
+
print(f" [{event.agent}] {str(event.data)[:120]}")
|