|
@@ -68,19 +68,25 @@
|
|
|
## 核心流程:Agent Loop
|
|
## 核心流程:Agent Loop
|
|
|
|
|
|
|
|
```python
|
|
```python
|
|
|
-async def run(task: str, max_steps: int = 50):
|
|
|
|
|
|
|
+async def run(task: str, max_steps: int = 50) -> AsyncIterator[Union[Trace, Step]]:
|
|
|
# 1. 创建 Trace
|
|
# 1. 创建 Trace
|
|
|
- trace = Trace(trace_id=gen_id(), task=task, status="running")
|
|
|
|
|
- await trace_store.save(trace)
|
|
|
|
|
|
|
+ trace = Trace.create(mode="agent", task=task, status="in_progress")
|
|
|
|
|
+ await trace_store.create_trace(trace)
|
|
|
|
|
+ yield trace # 返回 Trace(表示开始)
|
|
|
|
|
|
|
|
- # 2. 检索 Experiences,构建 system prompt
|
|
|
|
|
|
|
+ # 2. 加载 Skills(内置 + 自定义)
|
|
|
|
|
+ # 内置 skills(agent/skills/core.md)自动加载
|
|
|
|
|
+ skills = load_skills_from_dir(skills_dir) # skills_dir 可选
|
|
|
|
|
+ skills_text = format_skills(skills)
|
|
|
|
|
+
|
|
|
|
|
+ # 3. 检索 Experiences,构建 system prompt
|
|
|
experiences = await search_experiences(task)
|
|
experiences = await search_experiences(task)
|
|
|
- system_prompt = build_system_prompt(experiences)
|
|
|
|
|
|
|
+ system_prompt = build_system_prompt(experiences, skills_text)
|
|
|
|
|
|
|
|
- # 3. 初始化消息
|
|
|
|
|
|
|
+ # 4. 初始化消息
|
|
|
messages = [{"role": "user", "content": task}]
|
|
messages = [{"role": "user", "content": task}]
|
|
|
|
|
|
|
|
- # 4. ReAct 循环
|
|
|
|
|
|
|
+ # 5. ReAct 循环
|
|
|
for step in range(max_steps):
|
|
for step in range(max_steps):
|
|
|
# 调用 LLM
|
|
# 调用 LLM
|
|
|
response = await llm.chat(
|
|
response = await llm.chat(
|
|
@@ -89,11 +95,15 @@ async def run(task: str, max_steps: int = 50):
|
|
|
tools=tool_registry.to_schema() # 包括 skill、task 等工具
|
|
tools=tool_registry.to_schema() # 包括 skill、task 等工具
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- # 记录 LLM 调用
|
|
|
|
|
- await add_step(trace, "llm_call", {
|
|
|
|
|
- "response": response.content,
|
|
|
|
|
- "tool_calls": response.tool_calls
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ # 记录 LLM 调用 Step
|
|
|
|
|
+ llm_step = Step.create(
|
|
|
|
|
+ trace_id=trace.trace_id,
|
|
|
|
|
+ step_type="thought",
|
|
|
|
|
+ status="completed",
|
|
|
|
|
+ data={"content": response.content, "tool_calls": response.tool_calls}
|
|
|
|
|
+ )
|
|
|
|
|
+ await trace_store.add_step(llm_step)
|
|
|
|
|
+ yield llm_step # 返回 Step
|
|
|
|
|
|
|
|
# 没有工具调用,完成
|
|
# 没有工具调用,完成
|
|
|
if not response.tool_calls:
|
|
if not response.tool_calls:
|
|
@@ -108,23 +118,44 @@ async def run(task: str, max_steps: int = 50):
|
|
|
# 执行工具(包括 skill、task 工具)
|
|
# 执行工具(包括 skill、task 工具)
|
|
|
result = await execute_tool(tool_call)
|
|
result = await execute_tool(tool_call)
|
|
|
|
|
|
|
|
- # 记录步骤
|
|
|
|
|
- await add_step(trace, "tool_call", {"tool": tool_call.name, "args": tool_call.args})
|
|
|
|
|
- await add_step(trace, "tool_result", {"output": result})
|
|
|
|
|
|
|
+ # 记录 action Step
|
|
|
|
|
+ action_step = Step.create(
|
|
|
|
|
+ trace_id=trace.trace_id,
|
|
|
|
|
+ step_type="action",
|
|
|
|
|
+ status="completed",
|
|
|
|
|
+ parent_id=llm_step.step_id,
|
|
|
|
|
+ data={"tool_name": tool_call.name, "arguments": tool_call.args}
|
|
|
|
|
+ )
|
|
|
|
|
+ await trace_store.add_step(action_step)
|
|
|
|
|
+ yield action_step
|
|
|
|
|
+
|
|
|
|
|
+ # 记录 result Step
|
|
|
|
|
+ result_step = Step.create(
|
|
|
|
|
+ trace_id=trace.trace_id,
|
|
|
|
|
+ step_type="result",
|
|
|
|
|
+ status="completed",
|
|
|
|
|
+ parent_id=action_step.step_id,
|
|
|
|
|
+ data={"output": result}
|
|
|
|
|
+ )
|
|
|
|
|
+ await trace_store.add_step(result_step)
|
|
|
|
|
+ yield result_step
|
|
|
|
|
|
|
|
# 添加到消息历史
|
|
# 添加到消息历史
|
|
|
messages.append({"role": "assistant", "tool_calls": [tool_call]})
|
|
messages.append({"role": "assistant", "tool_calls": [tool_call]})
|
|
|
messages.append({"role": "tool", "content": result})
|
|
messages.append({"role": "tool", "content": result})
|
|
|
|
|
|
|
|
- # 5. 完成
|
|
|
|
|
|
|
+ # 6. 完成
|
|
|
trace.status = "completed"
|
|
trace.status = "completed"
|
|
|
- await trace_store.save(trace)
|
|
|
|
|
|
|
+ await trace_store.update_trace(trace.trace_id, status="completed")
|
|
|
|
|
+ yield trace # 返回更新后的 Trace
|
|
|
|
|
|
|
|
return trace
|
|
return trace
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
**关键机制**:
|
|
**关键机制**:
|
|
|
|
|
+- **统一返回类型**:`AsyncIterator[Union[Trace, Step]]` - 实时返回执行状态
|
|
|
- **Doom Loop 检测**:跟踪最近 3 次工具调用,如果都是同一个工具且参数相同,中断循环
|
|
- **Doom Loop 检测**:跟踪最近 3 次工具调用,如果都是同一个工具且参数相同,中断循环
|
|
|
|
|
+- **Skills 自动加载**:`agent/skills/core.md` 总是自动加载,`skills_dir` 可选额外加载
|
|
|
- **动态工具加载**:Skill 通过 tool 动态加载,按需消耗 context
|
|
- **动态工具加载**:Skill 通过 tool 动态加载,按需消耗 context
|
|
|
- **Sub-Agent 支持**:通过 task 工具启动专门化的 Sub-Agent 处理子任务
|
|
- **Sub-Agent 支持**:通过 task 工具启动专门化的 Sub-Agent 处理子任务
|
|
|
|
|
|
|
@@ -176,7 +207,7 @@ class Trace:
|
|
|
context: Dict[str, Any] = field(default_factory=dict)
|
|
context: Dict[str, Any] = field(default_factory=dict)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现**:`agent/models/trace.py:Trace`
|
|
|
|
|
|
|
+**实现**:`agent/execution/models.py:Trace`
|
|
|
|
|
|
|
|
### Step(执行步骤)
|
|
### Step(执行步骤)
|
|
|
|
|
|
|
@@ -193,7 +224,7 @@ class Step:
|
|
|
summary: Optional[str] = None # 仅 evaluation 类型需要
|
|
summary: Optional[str] = None # 仅 evaluation 类型需要
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现**:`agent/models/trace.py:Step`
|
|
|
|
|
|
|
+**实现**:`agent/execution/models.py:Step`
|
|
|
|
|
|
|
|
**详细设计**:参考 [`docs/step-tree.md`](./step-tree.md)
|
|
**详细设计**:参考 [`docs/step-tree.md`](./step-tree.md)
|
|
|
|
|
|
|
@@ -274,8 +305,8 @@ async def my_tool(arg: str, ctx: ToolContext) -> ToolResult:
|
|
|
- 图片资源处理
|
|
- 图片资源处理
|
|
|
|
|
|
|
|
**实现**:
|
|
**实现**:
|
|
|
-- `agent/prompts/wrapper.py:SimplePrompt` - Prompt 包装器
|
|
|
|
|
-- `agent/llm/providers/gemini.py:_convert_messages_to_gemini` - 格式转换
|
|
|
|
|
|
|
+- `agent/llm/prompts/wrapper.py:SimplePrompt` - Prompt 包装器
|
|
|
|
|
+- `agent/llm/gemini.py:_convert_messages_to_gemini` - 格式转换
|
|
|
|
|
|
|
|
**使用示例**:`examples/feature_extract/run.py`
|
|
**使用示例**:`examples/feature_extract/run.py`
|
|
|
|
|
|
|
@@ -304,9 +335,9 @@ $user$
|
|
|
- 多模态消息支持(图片等)
|
|
- 多模态消息支持(图片等)
|
|
|
|
|
|
|
|
**实现**:
|
|
**实现**:
|
|
|
-- `agent/prompts/loader.py:load_prompt()` - 文件解析
|
|
|
|
|
-- `agent/prompts/loader.py:get_message()` - 参数替换
|
|
|
|
|
-- `agent/prompts/wrapper.py:SimplePrompt` - Prompt 包装器
|
|
|
|
|
|
|
+- `agent/llm/prompts/loader.py:load_prompt()` - 文件解析
|
|
|
|
|
+- `agent/llm/prompts/loader.py:get_message()` - 参数替换
|
|
|
|
|
+- `agent/llm/prompts/wrapper.py:SimplePrompt` - Prompt 包装器
|
|
|
|
|
|
|
|
**使用**:
|
|
**使用**:
|
|
|
```python
|
|
```python
|
|
@@ -347,7 +378,8 @@ await tools.execute("skill", {"skill_name": "browser-use"})
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
**实现**:
|
|
**实现**:
|
|
|
-- `agent/storage/skill_loader.py:SkillLoader` - Markdown 解析器
|
|
|
|
|
|
|
+- `agent/memory/skill_loader.py:SkillLoader` - Markdown 解析器
|
|
|
|
|
+- `agent/memory/skill_loader.py:load_skills_from_dir()` - Skills 自动加载(内置 + 自定义)
|
|
|
- `agent/tools/builtin/skill.py:skill()` - skill 工具实现
|
|
- `agent/tools/builtin/skill.py:skill()` - skill 工具实现
|
|
|
- `agent/tools/builtin/skill.py:list_skills()` - 列出可用 skills
|
|
- `agent/tools/builtin/skill.py:list_skills()` - 列出可用 skills
|
|
|
|
|
|
|
@@ -400,7 +432,7 @@ system_prompt = base_prompt + "\n\n# Learned Experiences\n" + "\n".join([
|
|
|
])
|
|
])
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现**:`agent/storage/experience_pg.py:ExperienceStore`
|
|
|
|
|
|
|
+**实现**:`agent/memory/stores.py:ExperienceStore`(待实现 PostgreSQL 版本)
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -426,7 +458,9 @@ class SkillLoader(Protocol):
|
|
|
"""加载指定 skill 的 Markdown 内容"""
|
|
"""加载指定 skill 的 Markdown 内容"""
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现**:`agent/storage/protocols.py`
|
|
|
|
|
|
|
+**实现**:
|
|
|
|
|
+- Trace/Step 协议:`agent/execution/protocols.py`
|
|
|
|
|
+- Memory 协议:`agent/memory/protocols.py`
|
|
|
|
|
|
|
|
**实现策略**:
|
|
**实现策略**:
|
|
|
- Trace/Step: 文件系统(JSON)
|
|
- Trace/Step: 文件系统(JSON)
|
|
@@ -435,27 +469,51 @@ class SkillLoader(Protocol):
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## 模块结构
|
|
|
|
|
|
|
+## 模块结构(v0.2.2)
|
|
|
|
|
|
|
|
```
|
|
```
|
|
|
agent/
|
|
agent/
|
|
|
-├── __init__.py
|
|
|
|
|
-├── runner.py # AgentRunner
|
|
|
|
|
-├── models/
|
|
|
|
|
-│ ├── trace.py # Trace, Step
|
|
|
|
|
-│ └── memory.py # Experience, Skill
|
|
|
|
|
-├── storage/
|
|
|
|
|
-│ ├── protocols.py # TraceStore, ExperienceStore, SkillLoader
|
|
|
|
|
-│ ├── trace_fs.py # 文件系统实现
|
|
|
|
|
-│ ├── experience_pg.py # PostgreSQL 实现
|
|
|
|
|
-│ └── skill_fs.py # 文件系统实现
|
|
|
|
|
-├── tools/
|
|
|
|
|
|
|
+├── __init__.py # 公开 API
|
|
|
|
|
+│
|
|
|
|
|
+├── core/ # 核心引擎
|
|
|
|
|
+│ ├── runner.py # AgentRunner
|
|
|
|
|
+│ └── config.py # AgentConfig, CallResult
|
|
|
|
|
+│
|
|
|
|
|
+├── execution/ # 执行追踪
|
|
|
|
|
+│ ├── models.py # Trace, Step
|
|
|
|
|
+│ ├── protocols.py # TraceStore
|
|
|
|
|
+│ ├── store.py # MemoryTraceStore
|
|
|
|
|
+│ ├── tree_dump.py # 可视化
|
|
|
|
|
+│ ├── api.py # RESTful API
|
|
|
|
|
+│ └── websocket.py # WebSocket
|
|
|
|
|
+│
|
|
|
|
|
+├── memory/ # 记忆系统
|
|
|
|
|
+│ ├── models.py # Experience, Skill
|
|
|
|
|
+│ ├── protocols.py # MemoryStore, StateStore
|
|
|
|
|
+│ ├── stores.py # 存储实现
|
|
|
|
|
+│ └── skill_loader.py # Skill 加载器(自动加载内置 skills)
|
|
|
|
|
+│
|
|
|
|
|
+├── tools/ # 工具系统
|
|
|
│ ├── registry.py # ToolRegistry
|
|
│ ├── registry.py # ToolRegistry
|
|
|
│ ├── models.py # ToolResult, ToolContext
|
|
│ ├── models.py # ToolResult, ToolContext
|
|
|
│ ├── schema.py # SchemaGenerator
|
|
│ ├── schema.py # SchemaGenerator
|
|
|
│ ├── url_matcher.py # URL 模式匹配
|
|
│ ├── url_matcher.py # URL 模式匹配
|
|
|
-│ └── sensitive.py # 敏感数据处理
|
|
|
|
|
-└── llm.py # LLMProvider Protocol
|
|
|
|
|
|
|
+│ ├── sensitive.py # 敏感数据处理
|
|
|
|
|
+│ ├── builtin/ # 核心工具
|
|
|
|
|
+│ ├── advanced/ # 高级工具
|
|
|
|
|
+│ └── adapters/ # 外部集成
|
|
|
|
|
+│
|
|
|
|
|
+├── llm/ # LLM 相关
|
|
|
|
|
+│ ├── gemini.py
|
|
|
|
|
+│ ├── openrouter.py
|
|
|
|
|
+│ └── prompts/
|
|
|
|
|
+│ ├── loader.py
|
|
|
|
|
+│ └── wrapper.py
|
|
|
|
|
+│
|
|
|
|
|
+├── skills/ # 内置 Skills(自动加载)
|
|
|
|
|
+│ └── core.md # 核心 skill,每次运行自动加载
|
|
|
|
|
+│
|
|
|
|
|
+└── subagents/ # Sub-agent
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
---
|
|
---
|
|
@@ -502,7 +560,7 @@ dump_tree(trace, steps)
|
|
|
watch -n 0.5 cat .trace/tree.txt
|
|
watch -n 0.5 cat .trace/tree.txt
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现**:`agent/debug/tree_dump.py`
|
|
|
|
|
|
|
+**实现**:`agent/execution/tree_dump.py`
|
|
|
|
|
|
|
|
**详细说明**:参考 [`docs/step-tree.md`](./step-tree.md#debug-工具)
|
|
**详细说明**:参考 [`docs/step-tree.md`](./step-tree.md#debug-工具)
|
|
|
|
|
|
|
@@ -535,13 +593,13 @@ GEMINI_API_KEY=xxx pytest tests/e2e/ -v -m e2e
|
|
|
|
|
|
|
|
| 概念 | 定义 | 存储 | 实现 |
|
|
| 概念 | 定义 | 存储 | 实现 |
|
|
|
|------|------|------|------|
|
|
|------|------|------|------|
|
|
|
-| **Trace** | 一次任务执行 | 文件系统(JSON) | `models/trace.py` |
|
|
|
|
|
-| **Step** | 执行步骤(树结构) | 文件系统(JSON) | `models/trace.py` |
|
|
|
|
|
-| **Goal Step** | 计划项/目标 | Step 的一种类型 | `models/trace.py` |
|
|
|
|
|
|
|
+| **Trace** | 一次任务执行 | 文件系统(JSON) | `execution/models.py` |
|
|
|
|
|
+| **Step** | 执行步骤(树结构) | 文件系统(JSON) | `execution/models.py` |
|
|
|
|
|
+| **Goal Step** | 计划项/目标 | Step 的一种类型 | `execution/models.py` |
|
|
|
| **Sub-Agent** | 专门化的子代理 | 独立 Trace | `tools/builtin/task.py` |
|
|
| **Sub-Agent** | 专门化的子代理 | 独立 Trace | `tools/builtin/task.py` |
|
|
|
-| **AgentDefinition** | Agent 类型定义 | 配置文件/代码 | `models/agent.py` |
|
|
|
|
|
-| **Skill** | 能力描述(Markdown) | 文件系统 | `storage/skill_fs.py` |
|
|
|
|
|
-| **Experience** | 经验规则(条件+规则) | 数据库 + 向量 | `storage/experience_pg.py` |
|
|
|
|
|
|
|
+| **AgentDefinition** | Agent 类型定义 | 配置文件/代码 | `subagents/` |
|
|
|
|
|
+| **Skill** | 能力描述(Markdown) | 文件系统 | `memory/skill_loader.py` |
|
|
|
|
|
+| **Experience** | 经验规则(条件+规则) | 数据库 + 向量 | `memory/stores.py` |
|
|
|
| **Tool** | 可调用的函数 | 内存(注册表) | `tools/registry.py` |
|
|
| **Tool** | 可调用的函数 | 内存(注册表) | `tools/registry.py` |
|
|
|
-| **Agent Loop** | ReAct 循环 | - | `runner.py` |
|
|
|
|
|
-| **Doom Loop** | 无限循环检测 | - | `runner.py` |
|
|
|
|
|
|
|
+| **Agent Loop** | ReAct 循环 | - | `core/runner.py` |
|
|
|
|
|
+| **Doom Loop** | 无限循环检测 | - | `core/runner.py` |
|