ni.agentkit 0.6.2__tar.gz → 0.6.3__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.
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/PKG-INFO +3 -3
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/README.md +2 -2
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/__init__.py +1 -1
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/docs/QuickStart.md +49 -24
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/docs/README.md +1 -1
- ni_agentkit-0.6.3/docs/TestReport.md +90 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/01_basic_chat.py +10 -9
- ni_agentkit-0.6.3/examples/ollama/05_human_in_the_loop.py +42 -0
- ni_agentkit-0.6.3/examples/ollama/08a_memory_simple_provider.py +59 -0
- ni_agentkit-0.6.3/examples/ollama/08b_memory_mem0_provider.py +57 -0
- ni_agentkit-0.6.3/examples/ollama/08c_memory_file_provider.py +102 -0
- ni_agentkit-0.6.3/examples/ollama/19_hitl_deterministic.py +57 -0
- ni_agentkit-0.6.3/examples/standard/08a_memory_simple_provider.py +59 -0
- ni_agentkit-0.6.3/examples/standard/08b_memory_mem0_provider.py +49 -0
- ni_agentkit-0.6.3/examples/standard/08c_memory_file_provider.py +102 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/ni.agentkit.egg-info/PKG-INFO +3 -3
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/ni.agentkit.egg-info/SOURCES.txt +16 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/pyproject.toml +1 -1
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/runner/events.py +32 -2
- ni_agentkit-0.6.2/docs/TestReport.md +0 -80
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/LICENSE +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/_cli.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/agents/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/agents/agent.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/agents/base_agent.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/agents/orchestrators.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/docs/Architecture.md +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/docs/Reference.md +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/02_tool_calling.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/03_skill_usage.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/04_multi_agent.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/05_guardrail.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/06_orchestration.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/07_sync_async_stream.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/08_memory.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/09a_structured_data_sql.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/09b_structured_data_graph.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/10_skill_lifecycle.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/11_orchestration_enhancement.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/12_run_context_serialization.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/13_human_in_the_loop.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/14_event_standardization.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/15_multi_tenant_isolation.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/16_lifecycle_hooks.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/17_checkpoint_handoff_resume.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/18_model_cosplay.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/README.md +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/ollama/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/quickstart.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/01_basic_chat.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/02_tool_calling.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/03_skill_usage.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/04_multi_agent.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/05_guardrail.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/06_orchestration.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/07_sync_async_stream.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/08_memory.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/09a_structured_data_sql.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/09b_structured_data_graph.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/10_skill_lifecycle.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/11_orchestration_enhancement.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/12_run_context_serialization.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/13_human_in_the_loop.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/14_event_standardization.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/15_multi_tenant_isolation.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/16_lifecycle_hooks.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/17_checkpoint_handoff_resume.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/18_model_cosplay.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/README.md +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/standard/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/examples/test_ollama.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/adapters/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/adapters/anthropic_adapter.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/adapters/google_adapter.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/adapters/ollama_adapter.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/adapters/openai_adapter.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/adapters/openai_compatible.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/base.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/cache.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/middleware.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/registry.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/llm/types.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/memory/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/memory/base.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/memory/mem0_provider.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/ni.agentkit.egg-info/dependency_links.txt +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/ni.agentkit.egg-info/entry_points.txt +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/ni.agentkit.egg-info/requires.txt +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/ni.agentkit.egg-info/top_level.txt +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/runner/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/runner/context.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/runner/context_store.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/runner/runner.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/safety/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/safety/guardrails.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/safety/permissions.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/setup.cfg +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/skills/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/skills/loader.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/skills/models.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/skills/registry.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tests/test_after_callback.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tests/test_quickstart_core.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tests/test_runner_checkpoint_resume.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tools/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tools/base_tool.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tools/function_tool.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tools/nebula_tool.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tools/skill_toolset.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tools/sqlite_tool.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/tools/structured_data.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/utils/__init__.py +0 -0
- {ni_agentkit-0.6.2 → ni_agentkit-0.6.3}/utils/schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ni.agentkit
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: A Python-native Agent framework with first-class Skill support and multi-LLM adapter
|
|
5
5
|
Author-email: Krix Tam <krix.tam@qq.com>
|
|
6
6
|
License: MIT
|
|
@@ -153,8 +153,8 @@ python "$(python -c "import agentkit, os; print(os.path.join(agentkit.get_exampl
|
|
|
153
153
|
|
|
154
154
|
```bash
|
|
155
155
|
dist/
|
|
156
|
-
├── ni_agentkit-0.6.
|
|
157
|
-
└── ni_agentkit-0.6.
|
|
156
|
+
├── ni_agentkit-0.6.3-py3-none-any.whl # pip install 用这个
|
|
157
|
+
└── ni_agentkit-0.6.3.tar.gz # 源码分发
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
## 📄 License
|
|
@@ -111,8 +111,8 @@ python "$(python -c "import agentkit, os; print(os.path.join(agentkit.get_exampl
|
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
113
|
dist/
|
|
114
|
-
├── ni_agentkit-0.6.
|
|
115
|
-
└── ni_agentkit-0.6.
|
|
114
|
+
├── ni_agentkit-0.6.3-py3-none-any.whl # pip install 用这个
|
|
115
|
+
└── ni_agentkit-0.6.3.tar.gz # 源码分发
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
## 📄 License
|
|
@@ -31,7 +31,7 @@ from .tools.function_tool import FunctionTool, function_tool
|
|
|
31
31
|
from .tools.structured_data import ResultFormatter, StructuredDataTool
|
|
32
32
|
from .tools.sqlite_tool import SQLiteTool, SQLiteResultFormatter
|
|
33
33
|
|
|
34
|
-
__version__ = "0.6.
|
|
34
|
+
__version__ = "0.6.3"
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def get_docs_dir() -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AgentKit 快速入门教程
|
|
2
2
|
|
|
3
|
-
> 本教程将带你从零开始,通过 18 组由简到繁的示例(含 9A/9B),掌握 AgentKit 的核心用法。
|
|
3
|
+
> 本教程将带你从零开始,通过 18 组由简到繁的示例(含 8A/8B/8C、9A/9B),掌握 AgentKit 的核心用法。
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -608,60 +608,45 @@ asyncio.run(stream_demo())
|
|
|
608
608
|
|
|
609
609
|
默认情况下,Agent 每次运行都是无状态的。配上记忆后,Agent 可以记住用户的偏好和历史。
|
|
610
610
|
|
|
611
|
-
###
|
|
611
|
+
### 8A:SimpleMemory
|
|
612
612
|
|
|
613
613
|
```python
|
|
614
614
|
from agentkit import Agent, Runner, BaseMemoryProvider, Memory
|
|
615
615
|
|
|
616
616
|
class SimpleMemory(BaseMemoryProvider):
|
|
617
|
-
"""轻量内存记忆——适合开发测试"""
|
|
618
617
|
def __init__(self):
|
|
619
618
|
self._store, self._counter = [], 0
|
|
620
|
-
|
|
621
619
|
async def add(self, content, *, user_id=None, agent_id=None, metadata=None):
|
|
622
620
|
self._counter += 1
|
|
623
621
|
m = Memory(id=str(self._counter), content=content)
|
|
624
622
|
self._store.append(m)
|
|
625
623
|
return [m]
|
|
626
|
-
|
|
627
624
|
async def search(self, query, *, user_id=None, agent_id=None, limit=10):
|
|
628
|
-
# 简单关键词匹配(生产环境用 Mem0 的向量搜索)
|
|
629
625
|
query_words = set(query)
|
|
630
626
|
scored = [(len(query_words & set(m.content)), m) for m in self._store]
|
|
631
627
|
scored.sort(reverse=True, key=lambda x: x[0])
|
|
632
628
|
return [m for s, m in scored[:limit] if s > 0]
|
|
633
|
-
|
|
634
629
|
async def get_all(self, *, user_id=None, agent_id=None):
|
|
635
630
|
return list(self._store)
|
|
636
|
-
|
|
637
631
|
async def delete(self, memory_id):
|
|
638
632
|
self._store = [m for m in self._store if m.id != memory_id]
|
|
639
633
|
return True
|
|
640
634
|
|
|
641
|
-
# 给 Agent 配上记忆
|
|
642
635
|
memory = SimpleMemory()
|
|
643
|
-
|
|
644
636
|
agent = Agent(
|
|
645
637
|
name="personal-assistant",
|
|
646
638
|
instructions="你是一个贴心的个人助手。根据记忆来个性化回答。",
|
|
647
639
|
model="ollama/qwen3.5:cloud",
|
|
648
|
-
memory=memory,
|
|
640
|
+
memory=memory,
|
|
641
|
+
memory_async_write=False,
|
|
649
642
|
)
|
|
650
643
|
|
|
651
|
-
# 第 1 轮:告诉偏好
|
|
652
644
|
Runner.run_sync(agent, input="我叫小明,我喜欢喝咖啡,讨厌喝茶", user_id="krix")
|
|
653
|
-
|
|
654
|
-
# 第 2 轮:Agent 会记住偏好,推荐咖啡而不是茶
|
|
655
645
|
result = Runner.run_sync(agent, input="帮我推荐一杯饮料", user_id="krix")
|
|
656
|
-
print(result.final_output) #
|
|
646
|
+
print(result.final_output) # 基于记忆推荐
|
|
657
647
|
```
|
|
658
648
|
|
|
659
|
-
|
|
660
|
-
- 默认不开启记忆,需要给 `Agent` 传 `memory=...` 参数
|
|
661
|
-
- 配好后**不需要写额外代码**——框架自动在对话前检索记忆、对话后存储记忆
|
|
662
|
-
- `user_id` 用于多用户隔离
|
|
663
|
-
|
|
664
|
-
### 生产环境使用 Mem0
|
|
649
|
+
### 8B:Mem0Provider(生产级)
|
|
665
650
|
|
|
666
651
|
```bash
|
|
667
652
|
pip install mem0ai
|
|
@@ -678,11 +663,47 @@ memory = Mem0Provider({
|
|
|
678
663
|
}
|
|
679
664
|
})
|
|
680
665
|
|
|
681
|
-
agent = Agent(memory=memory, ...)
|
|
666
|
+
agent = Agent(memory=memory, ...) # 先构建 memory,再注入 Agent
|
|
682
667
|
```
|
|
683
668
|
|
|
684
669
|
Mem0 相比 SimpleMemory 的优势:**语义搜索**(理解意思而非关键词)、**持久化**(重启不丢失)、**智能提取**(从对话中自动抽取关键信息)。
|
|
685
670
|
|
|
671
|
+
### 8C:自定义 Memory(文件持久化)
|
|
672
|
+
|
|
673
|
+
```python
|
|
674
|
+
import json
|
|
675
|
+
from pathlib import Path
|
|
676
|
+
from agentkit import BaseMemoryProvider, Memory, Agent
|
|
677
|
+
|
|
678
|
+
class FileMemoryProvider(BaseMemoryProvider):
|
|
679
|
+
def __init__(self, file_path: str):
|
|
680
|
+
self.path = Path(file_path)
|
|
681
|
+
self.records = json.loads(self.path.read_text()) if self.path.exists() else []
|
|
682
|
+
async def add(self, content, *, user_id=None, agent_id=None, metadata=None):
|
|
683
|
+
new_id = str(len(self.records) + 1)
|
|
684
|
+
self.records.append({"id": new_id, "content": content, "user_id": user_id})
|
|
685
|
+
self.path.write_text(json.dumps(self.records, ensure_ascii=False, indent=2))
|
|
686
|
+
return [Memory(id=new_id, content=content)]
|
|
687
|
+
async def search(self, query, *, user_id=None, agent_id=None, limit=10):
|
|
688
|
+
q = query.lower()
|
|
689
|
+
out = [r for r in self.records if q in r["content"].lower()]
|
|
690
|
+
return [Memory(id=r["id"], content=r["content"]) for r in out[:limit]]
|
|
691
|
+
async def get_all(self, *, user_id=None, agent_id=None):
|
|
692
|
+
return [Memory(id=r["id"], content=r["content"]) for r in self.records]
|
|
693
|
+
async def delete(self, memory_id):
|
|
694
|
+
self.records = [r for r in self.records if r["id"] != memory_id]
|
|
695
|
+
self.path.write_text(json.dumps(self.records, ensure_ascii=False, indent=2))
|
|
696
|
+
return True
|
|
697
|
+
|
|
698
|
+
memory = FileMemoryProvider("/tmp/agentkit_memory.json")
|
|
699
|
+
agent = Agent(memory=memory, ...)
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**要点**:
|
|
703
|
+
- 默认不开启记忆,需要给 `Agent` 传 `memory=...` 参数
|
|
704
|
+
- `SimpleMemory` 适合开发测试,`Mem0Provider` 适合生产语义检索
|
|
705
|
+
- 自定义 `FileMemoryProvider` 可实现轻量持久化(进程重启后仍可读取)
|
|
706
|
+
|
|
686
707
|
---
|
|
687
708
|
|
|
688
709
|
## 示例 9A:关系型数据库 — 防止 SQL 注入的参数化 Tool
|
|
@@ -1159,7 +1180,9 @@ agent = Agent(name="assistant", instructions="...")
|
|
|
1159
1180
|
| [`05_guardrail.py`](../examples/standard/05_guardrail.py) | 示例 5:安全护栏 |
|
|
1160
1181
|
| [`06_orchestration.py`](../examples/standard/06_orchestration.py) | 示例 6:编排 Agent |
|
|
1161
1182
|
| [`07_sync_async_stream.py`](../examples/standard/07_sync_async_stream.py) | 示例 7:同步/异步/流式运行 |
|
|
1162
|
-
| [`
|
|
1183
|
+
| [`08a_memory_simple_provider.py`](../examples/standard/08a_memory_simple_provider.py) | 示例 8A:记忆系统(SimpleMemory) |
|
|
1184
|
+
| [`08b_memory_mem0_provider.py`](../examples/standard/08b_memory_mem0_provider.py) | 示例 8B:记忆系统(Mem0Provider) |
|
|
1185
|
+
| [`08c_memory_file_provider.py`](../examples/standard/08c_memory_file_provider.py) | 示例 8C:记忆系统(文件持久化) |
|
|
1163
1186
|
| [`09a_structured_data_sql.py`](../examples/standard/09a_structured_data_sql.py) | 示例 9A:关系型数据库 |
|
|
1164
1187
|
| [`09b_structured_data_graph.py`](../examples/standard/09b_structured_data_graph.py) | 示例 9B:图数据库 |
|
|
1165
1188
|
| [`10_skill_lifecycle.py`](../examples/standard/10_skill_lifecycle.py) | 示例 10:Skill 生命周期 |
|
|
@@ -1183,7 +1206,9 @@ agent = Agent(name="assistant", instructions="...")
|
|
|
1183
1206
|
| [`05_guardrail.py`](../examples/ollama/05_guardrail.py) | 示例 5:安全护栏 |
|
|
1184
1207
|
| [`06_orchestration.py`](../examples/ollama/06_orchestration.py) | 示例 6:编排 Agent |
|
|
1185
1208
|
| [`07_sync_async_stream.py`](../examples/ollama/07_sync_async_stream.py) | 示例 7:同步/异步/流式运行 |
|
|
1186
|
-
| [`
|
|
1209
|
+
| [`08a_memory_simple_provider.py`](../examples/ollama/08a_memory_simple_provider.py) | 示例 8A:记忆系统(SimpleMemory) |
|
|
1210
|
+
| [`08b_memory_mem0_provider.py`](../examples/ollama/08b_memory_mem0_provider.py) | 示例 8B:记忆系统(Mem0Provider) |
|
|
1211
|
+
| [`08c_memory_file_provider.py`](../examples/ollama/08c_memory_file_provider.py) | 示例 8C:记忆系统(文件持久化) |
|
|
1187
1212
|
| [`09a_structured_data_sql.py`](../examples/ollama/09a_structured_data_sql.py) | 示例 9A:关系型数据库 |
|
|
1188
1213
|
| [`09b_structured_data_graph.py`](../examples/ollama/09b_structured_data_graph.py) | 示例 9B:图数据库 |
|
|
1189
1214
|
| [`10_skill_lifecycle.py`](../examples/ollama/10_skill_lifecycle.py) | 示例 10:Skill 生命周期 |
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Python 原生的 Agent 开发框架,内置一等公民级别的 Skill 支持和自研多模型适配层。
|
|
4
4
|
|
|
5
5
|
[](https://python.org)
|
|
6
|
-
[]()
|
|
7
7
|
[]()
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# AgentKit 示例测试报告
|
|
2
|
+
|
|
3
|
+
> 测试时间:`2026-04-23`
|
|
4
|
+
> 测试环境:`macOS (Apple Silicon)`
|
|
5
|
+
> 模型:Ollama `qwen3.5:cloud`
|
|
6
|
+
> AgentKit 版本:v0.6.3
|
|
7
|
+
> Thinking 模式:开启(默认)
|
|
8
|
+
> LLM 调用模式:非流式(默认)
|
|
9
|
+
> 缓存:开启(默认)
|
|
10
|
+
> 执行脚本:`examples/test_ollama.py`
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 测试结果
|
|
15
|
+
|
|
16
|
+
| # | 示例 | 文件 | 耗时 | 状态 | 说明 |
|
|
17
|
+
|---|------|------|-----:|:----:|------|
|
|
18
|
+
| 1 | 基础对话 | `01_basic_chat.py` | 16.51s | ✅ | 运行通过 |
|
|
19
|
+
| 2 | 工具调用 | `02_tool_calling.py` | 12.51s | ✅ | 运行通过 |
|
|
20
|
+
| 3 | Skill 使用 | `03_skill_usage.py` | 82.73s | ✅ | 运行通过 |
|
|
21
|
+
| 4 | 多 Agent 协作 | `04_multi_agent.py` | 97.29s | ✅ | 运行通过 |
|
|
22
|
+
| 5 | 安全护栏 | `05_guardrail.py` | 7.33s | ✅ | 运行通过 |
|
|
23
|
+
| 5H | HITL 工具触发(Playground 专用) | `05_human_in_the_loop.py` | 0.18s | ✅ | 运行通过 |
|
|
24
|
+
| 6 | 编排 Agent | `06_orchestration.py` | 290.84s | ✅ | 运行通过 |
|
|
25
|
+
| 7 | 同步/异步/流式 | `07_sync_async_stream.py` | 13.51s | ✅ | 运行通过 |
|
|
26
|
+
| 8 | 记忆系统(综合) | `08_memory.py` | 537.55s | ✅ | 运行通过(本轮最慢) |
|
|
27
|
+
| 8A | SimpleMemory | `08a_memory_simple_provider.py` | 45.82s | ✅ | 运行通过 |
|
|
28
|
+
| 8B | Mem0Provider | `08b_memory_mem0_provider.py` | 0.19s | ✅ | 未配置 `OPENAI_API_KEY` 时自动跳过 |
|
|
29
|
+
| 8C | 文件持久化 Memory | `08c_memory_file_provider.py` | 157.72s | ✅ | 运行通过 |
|
|
30
|
+
| 9A | 结构化数据(SQL) | `09a_structured_data_sql.py` | 5.72s | ✅ | 运行通过 |
|
|
31
|
+
| 9B | 结构化数据(图) | `09b_structured_data_graph.py` | 30.72s | ✅ | 运行通过 |
|
|
32
|
+
| 10 | Skill 生命周期 | `10_skill_lifecycle.py` | 2.36s | ✅ | 运行通过 |
|
|
33
|
+
| 11 | 编排增强 | `11_orchestration_enhancement.py` | 57.63s | ✅ | 运行通过 |
|
|
34
|
+
| 12 | 序列化协议 | `12_run_context_serialization.py` | 0.19s | ✅ | 运行通过 |
|
|
35
|
+
| 13 | Human in the Loop | `13_human_in_the_loop.py` | 8.06s | ✅ | 运行通过 |
|
|
36
|
+
| 14 | Event 标准化 | `14_event_standardization.py` | 5.70s | ✅ | 运行通过 |
|
|
37
|
+
| 15 | 多租户隔离 | `15_multi_tenant_isolation.py` | 0.29s | ✅ | 运行通过 |
|
|
38
|
+
| 16 | 生命周期 Hooks | `16_lifecycle_hooks.py` | 0.27s | ✅ | 运行通过 |
|
|
39
|
+
| 17 | Checkpoint + Handoff + Resume | `17_checkpoint_handoff_resume.py` | 0.18s | ✅ | 运行通过 |
|
|
40
|
+
| 18 | ModelCosplay | `18_model_cosplay.py` | 0.18s | ✅ | 运行通过 |
|
|
41
|
+
| 19 | HITL 确定性触发 | `19_hitl_deterministic.py` | 0.17s | ✅ | 运行通过(本轮最快) |
|
|
42
|
+
| | **合计** | | **1373.65s** | **24/24** | |
|
|
43
|
+
|
|
44
|
+
## 耗时分析
|
|
45
|
+
|
|
46
|
+
- **最快示例**:19 HITL 确定性触发(0.17s)
|
|
47
|
+
- **最慢示例**:8 记忆系统(537.55s)
|
|
48
|
+
- **耗时集中区间**:涉及多轮推理/记忆写入/编排循环的示例耗时显著更高
|
|
49
|
+
|
|
50
|
+
## 各示例 LLM 调用次数估算
|
|
51
|
+
|
|
52
|
+
| # | 示例 | LLM 调用次数 | 说明 |
|
|
53
|
+
|---|------|:-----------:|------|
|
|
54
|
+
| 1 | 基础对话 | 1 | 单次对话 |
|
|
55
|
+
| 2 | 工具调用 | ~6 | 多次工具调用与回复生成 |
|
|
56
|
+
| 3 | Skill 使用 | ~9 | load_skill + 工具调用 + 回复 |
|
|
57
|
+
| 4 | 多 Agent 协作 | ~8 | as_tool + handoff 链路 |
|
|
58
|
+
| 5 | 安全护栏 | ~3 | 拦截与放行混合路径 |
|
|
59
|
+
| 5H | HITL 工具触发(Playground 专用) | ~0 | 仅触发挂起事件,不走 LLM |
|
|
60
|
+
| 6 | 编排 Agent | ~12 | Sequential + Parallel + Loop |
|
|
61
|
+
| 7 | 同步/异步/流式 | ~7 | 三种运行模式覆盖 |
|
|
62
|
+
| 8 | 记忆系统(综合) | ~10 | 记忆读写与多轮对话 |
|
|
63
|
+
| 8A | SimpleMemory | ~2 | 轻量关键词检索 |
|
|
64
|
+
| 8B | Mem0Provider | ~0 | 未配置 `OPENAI_API_KEY` 时自动跳过 |
|
|
65
|
+
| 8C | 文件持久化 Memory | ~2 | 本地文件读写 + 记忆检索 |
|
|
66
|
+
| 9A | 结构化数据(SQL) | ~2 | 参数化查询 + 汇总 |
|
|
67
|
+
| 9B | 结构化数据(图) | ~2 | 图查询 + 汇总 |
|
|
68
|
+
| 10 | Skill 生命周期 | 1 | 单轮校验 |
|
|
69
|
+
| 11 | 编排增强 | ~4 | loop_condition + early_exit |
|
|
70
|
+
| 12 | RunContext 序列化 | 0 | 纯本地序列化 |
|
|
71
|
+
| 13 | HITL 断点续跑 | ~3 | 挂起 + 恢复 |
|
|
72
|
+
| 14 | 事件协议标准化 | 1 | 标准事件输出 |
|
|
73
|
+
| 15 | 多租户隔离 | ~3 | 多会话隔离验证 |
|
|
74
|
+
| 16 | 生命周期 Hooks | ~2 | Hook 链路验证 |
|
|
75
|
+
| 17 | Checkpoint Handoff 恢复 | 0 | 自定义事件流,不依赖 LLM |
|
|
76
|
+
| 18 | ModelCosplay | 0 | 仅验证模型改写开关与运行时覆盖逻辑,不调用 LLM |
|
|
77
|
+
| 19 | HITL 确定性触发 | 0 | 自定义事件流,不依赖 LLM |
|
|
78
|
+
|
|
79
|
+
## 已知问题
|
|
80
|
+
|
|
81
|
+
| 问题 | 严重程度 | 说明 |
|
|
82
|
+
|------|:--------:|------|
|
|
83
|
+
| 运行异常 | - | 无,24 个示例全部通过(8B 在无 `OPENAI_API_KEY` 时自动跳过) |
|
|
84
|
+
|
|
85
|
+
## 运行方式
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# 在 agentkit 目录执行
|
|
89
|
+
python examples/test_ollama.py
|
|
90
|
+
```
|
|
@@ -21,14 +21,15 @@ agent = Agent(
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
# 同步运行
|
|
24
|
-
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
result = Runner.run_sync(agent, input="什么是量子计算?请用一句话解释。")
|
|
25
26
|
|
|
26
|
-
if result.success:
|
|
27
|
-
|
|
28
|
-
else:
|
|
29
|
-
|
|
27
|
+
if result.success:
|
|
28
|
+
print(f"✅ 回复: {result.final_output}")
|
|
29
|
+
else:
|
|
30
|
+
print(f"❌ 错误: {result.error}")
|
|
30
31
|
|
|
31
|
-
# 查看事件流
|
|
32
|
-
print("\n📋 事件流:")
|
|
33
|
-
for event in result.events:
|
|
34
|
-
|
|
32
|
+
# 查看事件流
|
|
33
|
+
print("\n📋 事件流:")
|
|
34
|
+
for event in result.events:
|
|
35
|
+
print(f" [{event.type}] {str(event.data)[:80]}")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 5(Playground/HITL 专用):Human-in-the-loop Agent(Ollama 版)
|
|
3
|
+
|
|
4
|
+
用途:
|
|
5
|
+
- 提供一个可被 AgentHub `entry` 直接加载的 `agent` 实例;
|
|
6
|
+
- 在工具执行前通过 `request_human_input(...)` 触发挂起,便于在 Playground 中演示 HITL。
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
12
|
+
|
|
13
|
+
from agentkit import Agent
|
|
14
|
+
from agentkit.tools.base_tool import request_human_input
|
|
15
|
+
from agentkit.tools.function_tool import FunctionTool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def confirm_action(action: str) -> str:
|
|
19
|
+
"""敏感操作执行前请求人工确认。"""
|
|
20
|
+
request_human_input(f"即将执行敏感操作: {action},请确认 (approve/reject)")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def execute_action(action: str) -> str:
|
|
24
|
+
"""执行操作(示例返回)。"""
|
|
25
|
+
return f"操作 '{action}' 已执行。"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
confirm_tool = FunctionTool.from_function(confirm_action)
|
|
29
|
+
execute_tool = FunctionTool.from_function(execute_action)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
agent = Agent(
|
|
33
|
+
name="hitl-agent",
|
|
34
|
+
instructions=(
|
|
35
|
+
"你是一个需要人工审批的运维助手。"
|
|
36
|
+
"当用户要求执行敏感操作时,必须先调用 confirm_action 请求人工确认;"
|
|
37
|
+
"收到确认后再调用 execute_action。"
|
|
38
|
+
),
|
|
39
|
+
# 使用纯本地模型,避免 cloud 变体在未鉴权/网络抖动时触发远端 502。
|
|
40
|
+
model="ollama/qwen3.5:4b",
|
|
41
|
+
tools=[confirm_tool, execute_tool],
|
|
42
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
6
|
+
|
|
7
|
+
from agentkit import Agent, BaseMemoryProvider, Memory, Runner
|
|
8
|
+
|
|
9
|
+
MODEL = "ollama/qwen3.5:cloud"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SimpleMemory(BaseMemoryProvider):
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
self._store: list[Memory] = []
|
|
15
|
+
self._counter = 0
|
|
16
|
+
|
|
17
|
+
async def add(self, content, *, user_id=None, agent_id=None, metadata=None):
|
|
18
|
+
self._counter += 1
|
|
19
|
+
item = Memory(id=str(self._counter), content=content)
|
|
20
|
+
self._store.append(item)
|
|
21
|
+
return [item]
|
|
22
|
+
|
|
23
|
+
async def search(self, query, *, user_id=None, agent_id=None, limit=10):
|
|
24
|
+
query_chars = set(query)
|
|
25
|
+
scored: list[tuple[int, Memory]] = []
|
|
26
|
+
for item in self._store:
|
|
27
|
+
overlap = len(query_chars & set(item.content))
|
|
28
|
+
if overlap > 0:
|
|
29
|
+
scored.append((overlap, item))
|
|
30
|
+
scored.sort(reverse=True, key=lambda x: x[0])
|
|
31
|
+
return [m for _, m in scored[:limit]]
|
|
32
|
+
|
|
33
|
+
async def get_all(self, *, user_id=None, agent_id=None):
|
|
34
|
+
return list(self._store)
|
|
35
|
+
|
|
36
|
+
async def delete(self, memory_id):
|
|
37
|
+
self._store = [m for m in self._store if m.id != memory_id]
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def main() -> None:
|
|
42
|
+
print("=== 示例 8A:SimpleMemory ===")
|
|
43
|
+
memory = SimpleMemory()
|
|
44
|
+
agent = Agent(
|
|
45
|
+
name="remembering",
|
|
46
|
+
instructions="你是贴心助手。根据记忆回答,回答简洁。",
|
|
47
|
+
model=MODEL,
|
|
48
|
+
memory=memory,
|
|
49
|
+
memory_async_write=False,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
await Runner.run(agent, input="我叫小明,喜欢咖啡,讨厌茶。", user_id="user_001")
|
|
53
|
+
result = await Runner.run(agent, input="帮我推荐一杯饮料。", user_id="user_001")
|
|
54
|
+
print("推荐:", result.final_output)
|
|
55
|
+
print("记忆条数:", len(await memory.get_all()))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
6
|
+
|
|
7
|
+
from agentkit import Agent, Runner
|
|
8
|
+
|
|
9
|
+
MODEL = "ollama/qwen3.5:cloud"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def main() -> None:
|
|
13
|
+
print("=== 示例 8B:Mem0Provider ===")
|
|
14
|
+
try:
|
|
15
|
+
from agentkit.memory.mem0_provider import Mem0Provider
|
|
16
|
+
except Exception:
|
|
17
|
+
print("未安装 mem0ai,跳过运行。")
|
|
18
|
+
print("安装: pip install mem0ai")
|
|
19
|
+
print("并启动 qdrant: docker run -p 6333:6333 qdrant/qdrant")
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
if not os.getenv("OPENAI_API_KEY"):
|
|
23
|
+
print("未设置 OPENAI_API_KEY,跳过运行。")
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
memory = Mem0Provider(
|
|
28
|
+
{
|
|
29
|
+
"vector_store": {
|
|
30
|
+
"provider": "qdrant",
|
|
31
|
+
"config": {
|
|
32
|
+
"collection_name": "agentkit_quickstart",
|
|
33
|
+
"host": "localhost",
|
|
34
|
+
"port": 6333,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
print(f"Mem0 初始化失败,跳过运行: {exc}")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
agent = Agent(
|
|
44
|
+
name="mem0-assistant",
|
|
45
|
+
instructions="你是贴心助手。根据记忆回答,回答简洁。",
|
|
46
|
+
model=MODEL,
|
|
47
|
+
memory=memory,
|
|
48
|
+
memory_async_write=False,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
await Runner.run(agent, input="记住:我喜欢低糖拿铁。", user_id="user_001")
|
|
52
|
+
result = await Runner.run(agent, input="给我推荐一杯饮料。", user_id="user_001")
|
|
53
|
+
print("推荐:", result.final_output)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
8
|
+
|
|
9
|
+
from agentkit import Agent, BaseMemoryProvider, Memory, Runner
|
|
10
|
+
|
|
11
|
+
MODEL = "ollama/qwen3.5:cloud"
|
|
12
|
+
MEMORY_FILE = "/tmp/agentkit_memory_ollama.json"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FileMemoryProvider(BaseMemoryProvider):
|
|
16
|
+
def __init__(self, file_path: str) -> None:
|
|
17
|
+
self._path = Path(file_path)
|
|
18
|
+
self._records: list[dict] = []
|
|
19
|
+
self._counter = 0
|
|
20
|
+
self._load()
|
|
21
|
+
|
|
22
|
+
def _load(self) -> None:
|
|
23
|
+
if self._path.exists():
|
|
24
|
+
try:
|
|
25
|
+
self._records = json.loads(self._path.read_text(encoding="utf-8"))
|
|
26
|
+
except Exception:
|
|
27
|
+
self._records = []
|
|
28
|
+
if self._records:
|
|
29
|
+
self._counter = max(int(r.get("id", 0)) for r in self._records)
|
|
30
|
+
|
|
31
|
+
def _save(self) -> None:
|
|
32
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
self._path.write_text(json.dumps(self._records, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def _to_memory(record: dict) -> Memory:
|
|
37
|
+
return Memory(id=str(record["id"]), content=str(record["content"]))
|
|
38
|
+
|
|
39
|
+
async def add(self, content, *, user_id=None, agent_id=None, metadata=None):
|
|
40
|
+
self._counter += 1
|
|
41
|
+
record = {
|
|
42
|
+
"id": str(self._counter),
|
|
43
|
+
"content": content,
|
|
44
|
+
"user_id": user_id,
|
|
45
|
+
"agent_id": agent_id,
|
|
46
|
+
"metadata": metadata or {},
|
|
47
|
+
}
|
|
48
|
+
self._records.append(record)
|
|
49
|
+
self._save()
|
|
50
|
+
return [self._to_memory(record)]
|
|
51
|
+
|
|
52
|
+
async def search(self, query, *, user_id=None, agent_id=None, limit=10):
|
|
53
|
+
q = str(query).lower()
|
|
54
|
+
matched: list[Memory] = []
|
|
55
|
+
for record in self._records:
|
|
56
|
+
if user_id is not None and record.get("user_id") != user_id:
|
|
57
|
+
continue
|
|
58
|
+
if agent_id is not None and record.get("agent_id") != agent_id:
|
|
59
|
+
continue
|
|
60
|
+
if q in str(record.get("content", "")).lower():
|
|
61
|
+
matched.append(self._to_memory(record))
|
|
62
|
+
if len(matched) >= limit:
|
|
63
|
+
break
|
|
64
|
+
return matched
|
|
65
|
+
|
|
66
|
+
async def get_all(self, *, user_id=None, agent_id=None):
|
|
67
|
+
out: list[Memory] = []
|
|
68
|
+
for record in self._records:
|
|
69
|
+
if user_id is not None and record.get("user_id") != user_id:
|
|
70
|
+
continue
|
|
71
|
+
if agent_id is not None and record.get("agent_id") != agent_id:
|
|
72
|
+
continue
|
|
73
|
+
out.append(self._to_memory(record))
|
|
74
|
+
return out
|
|
75
|
+
|
|
76
|
+
async def delete(self, memory_id):
|
|
77
|
+
before = len(self._records)
|
|
78
|
+
self._records = [r for r in self._records if str(r.get("id")) != str(memory_id)]
|
|
79
|
+
self._save()
|
|
80
|
+
return len(self._records) < before
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def main() -> None:
|
|
84
|
+
print("=== 示例 8C:自定义 FileMemoryProvider(文件持久化) ===")
|
|
85
|
+
memory = FileMemoryProvider(MEMORY_FILE)
|
|
86
|
+
agent = Agent(
|
|
87
|
+
name="file-memory-assistant",
|
|
88
|
+
instructions="你是贴心助手。根据记忆回答,回答简洁。",
|
|
89
|
+
model=MODEL,
|
|
90
|
+
memory=memory,
|
|
91
|
+
memory_async_write=False,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
await Runner.run(agent, input="记住:我早餐喜欢美式咖啡。", user_id="user_001")
|
|
95
|
+
result = await Runner.run(agent, input="推荐一杯饮料。", user_id="user_001")
|
|
96
|
+
print("推荐:", result.final_output)
|
|
97
|
+
print("持久化文件:", MEMORY_FILE)
|
|
98
|
+
print("当前记忆数:", len(await memory.get_all(user_id="user_001")))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
示例 19(Playground/HITL 专用):确定性触发挂起(必现)
|
|
3
|
+
|
|
4
|
+
用途:
|
|
5
|
+
- 提供一个可被 AgentHub `entry` 直接加载的 `agent` 实例;
|
|
6
|
+
- 首次运行必然发出 `suspend_requested`,便于 Playground 稳定演示 HITL 闭环。
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
from typing import AsyncGenerator
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
15
|
+
|
|
16
|
+
from agentkit.agents.base_agent import BaseAgent
|
|
17
|
+
from agentkit.runner.context import RunContext
|
|
18
|
+
from agentkit.runner.events import Event, EventType
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DeterministicHITLAgent(BaseAgent):
|
|
22
|
+
async def _run_impl(self, ctx: RunContext) -> AsyncGenerator[Event, None]:
|
|
23
|
+
# 第一次进入会话时,确定性触发挂起。
|
|
24
|
+
if not ctx.state.get("deterministic_hitl_suspended_once"):
|
|
25
|
+
ctx.state["deterministic_hitl_suspended_once"] = True
|
|
26
|
+
suspension = ctx.register_suspension(
|
|
27
|
+
tool_call_id="det-hitl-1",
|
|
28
|
+
tool_name="manual_approval",
|
|
29
|
+
prompt="请审批该操作(approve/reject)",
|
|
30
|
+
)
|
|
31
|
+
yield Event(
|
|
32
|
+
agent=self.name,
|
|
33
|
+
type=EventType.SUSPEND_REQUESTED,
|
|
34
|
+
data={
|
|
35
|
+
"suspension_id": suspension.suspension_id,
|
|
36
|
+
"prompt": "请审批该操作(approve/reject)",
|
|
37
|
+
"tool": "manual_approval",
|
|
38
|
+
"tool_call_id": "det-hitl-1",
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
decision = "unknown"
|
|
44
|
+
for msg in reversed(ctx.messages):
|
|
45
|
+
if msg.get("role") == "tool" and msg.get("tool_call_id") == "det-hitl-1":
|
|
46
|
+
decision = msg.get("content", "unknown")
|
|
47
|
+
break
|
|
48
|
+
|
|
49
|
+
yield Event(
|
|
50
|
+
agent=self.name,
|
|
51
|
+
type=EventType.FINAL_OUTPUT,
|
|
52
|
+
data=f"已收到人工决策: {decision}",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
agent = DeterministicHITLAgent(name="deterministic-hitl-agent")
|
|
57
|
+
|