Talegorithm преди 1 месец
родител
ревизия
73928533ad
променени са 1 файла, в които са добавени 321 реда и са изтрити 179 реда
  1. 321 179
      docs/design.md

+ 321 - 179
docs/design.md

@@ -1,6 +1,8 @@
-# Reson Agent 设计文档
+# Agent 设计文档
 
 > **设计目标**:可扩展、可学习的 Agent 框架,支持执行追踪和持久记忆。
+>
+> **使用场景**:后台执行复杂任务,人类专家定期检查和反馈。
 
 ---
 
@@ -12,12 +14,10 @@
 |------|---------|-----------|
 | 循环次数 | 1 | N (可配置) |
 | 工具调用 | 可选 | 常用 |
-| 状态管理 | 无 | 有 (Task State) |
+| 状态管理 | 无 | 有 (Trace) |
 | 记忆检索 | 无 | 有 (Experience/Skill) |
 | 执行图 | 1 个节点 | N 个节点的 DAG |
 
-**统一抽象**:用同一套数据结构描述两者。
-
 ---
 
 ## 2. 三层记忆模型
@@ -25,93 +25,49 @@
 ```
 ┌─────────────────────────────────────────────────────────────┐
 │ Layer 3: Skills(技能库)                                     │
-│ - 从 Experience 归纳的高层策略                                │
-│ - 层次化组织,按领域/任务类型分类                              │
-│ - 执行前注入到 System Prompt                                 │
+│ - Markdown 文件,存储详细的能力描述                            │
+│ - 通过 skill 工具按需加载                                     │
 └─────────────────────────────────────────────────────────────┘
                               │ 归纳
 ┌─────────────────────────────────────────────────────────────┐
 │ Layer 2: Experience(经验库)                                 │
-│ - 条件 + 规则 + 证据                                         │
-│ - 来源:执行反馈、人工标注                                    │
-│ - 持久存储,跨任务复用                                        │
+│ - 数据库存储,条件 + 规则 + 证据                              │
+│ - 向量检索,注入到 system prompt                              │
 └─────────────────────────────────────────────────────────────┘
                               │ 提取
 ┌─────────────────────────────────────────────────────────────┐
 │ Layer 1: Task State(任务状态)                               │
 │ - 当前任务的工作记忆                                          │
-│ - 计划、进度、中间结论                                        │
-│ - 任务结束时重置                                              │
+│ - Trace/Step 记录执行过程                                    │
 └─────────────────────────────────────────────────────────────┘
 ```
 
----
-
-## 3. 执行图(Execution Graph)
-
-每次任务执行产生一张 DAG:
-
-```
-TaskNode(任务根节点)
-    │
-    ├─▶ LLMCallNode(第1次推理)
-    │       │
-    │       ├─▶ ToolCallNode(搜索工具)
-    │       │       │
-    │       │       └─▶ ToolResultNode(搜索结果)
-    │       │
-    │       └─▶ ConclusionNode(中间结论)
-    │
-    ├─▶ LLMCallNode(第2次推理)
-    │       │
-    │       └─▶ ConclusionNode(最终结论)
-    │
-    └─▶ FeedbackNode(人工反馈)
-            │
-            └─▶ ExperienceNode(提取的经验)
-```
-
-**节点类型**:
-
-| 类型 | 说明 | 数据 |
-|------|------|------|
-| `llm_call` | LLM 调用 | messages, response, model, tokens, cost |
-| `tool_call` | 工具调用 | tool_name, arguments |
-| `tool_result` | 工具结果 | result, duration_ms |
-| `conclusion` | 中间/最终结论 | content, is_final |
-| `feedback` | 人工反馈 | feedback_type, content, target_step_id |
-| `memory_read` | 读取记忆 | skills, experiences |
-| `memory_write` | 写入记忆 | experience_id |
+**注入方式**:
+- **Skills**:通过 `skill` 工具动态加载到对话历史
+- **Experiences**:检索后注入到 system prompt
 
 ---
 
-## 4. 数据模型
+## 3. 执行追踪
 
-### 4.1 Trace(执行轨迹
+### Trace(任务执行)
 
 ```python
 @dataclass
 class Trace:
     trace_id: str
     mode: Literal["call", "agent"]
-    prompt_name: Optional[str] = None
 
-    # Agent 模式特有
+    # 任务信息
     task: Optional[str] = None
     agent_type: Optional[str] = None
 
     # 状态
     status: Literal["running", "completed", "failed"] = "running"
 
-    # 统计
-    total_steps: int = 0
-    total_tokens: int = 0
-    total_cost: float = 0.0
-
-    # 上下文
-    uid: Optional[str] = None
+    # 上下文(灵活的元数据)
     context: Dict[str, Any] = field(default_factory=dict)
 
     # 时间
@@ -119,198 +75,384 @@ class Trace:
     completed_at: Optional[datetime] = None
 ```
 
-### 4.2 Step(执行步骤)
+**context 字段说明**:存储任务相关的元信息,用于管理和分析
+- `user_id`: 用户 ID
+- `project_id`: 项目 ID
+- `priority`: 优先级
+- `tags`: 标签列表
+- 其他业务相关数据
+
+### Step(执行步骤)
 
 ```python
 @dataclass
 class Step:
     step_id: str
     trace_id: str
-    step_type: StepType
-    sequence: int
+    step_type: str  # "llm_call", "tool_call", "tool_result", ...
 
     # DAG 结构
     parent_ids: List[str] = field(default_factory=list)
 
-    # 类型相关数据
+    # 灵活的步骤数据
     data: Dict[str, Any] = field(default_factory=dict)
 
     created_at: datetime
 ```
 
-### 4.3 Experience(经验)
+**常见 step_type**:
+- `llm_call`: LLM 调用(data: messages, response, tokens, cost)
+- `tool_call`: 工具调用(data: tool_name, arguments)
+- `tool_result`: 工具结果(data: output, metadata)
+- `reasoning`: 推理过程(data: content)
 
-```python
-@dataclass
-class Experience:
-    exp_id: str
-    scope: str  # "agent:{type}" 或 "user:{uid}"
+### 执行图示例
 
-    # 核心三元组
-    condition: str
-    rule: str
-    evidence: List[str]  # step_ids
+```
+Trace
+  │
+  ├─▶ Step(llm_call)
+  │     │
+  │     ├─▶ Step(tool_call: skill)
+  │     │     └─▶ Step(tool_result: "# Error Handling...")
+  │     │
+  │     └─▶ Step(tool_call: search_logs)
+  │           └─▶ Step(tool_result: "...")
+  │
+  └─▶ Step(llm_call)
+        └─▶ ...
+```
+
+---
 
-    # 元数据
-    source: Literal["execution", "feedback", "manual"]
-    confidence: float = 0.5
-    usage_count: int = 0
-    success_rate: float = 0.0
+## 4. Skills(技能库)
+
+### 存储
+
+Markdown 文件:
 
-    created_at: datetime
-    updated_at: datetime
 ```
+~/.reson/skills/           # 全局
+├── error-handling/SKILL.md
+└── data-processing/SKILL.md
 
-### 4.4 Skill(技能)
+./project/.reson/skills/   # 项目级
+└── api-integration/SKILL.md
+```
 
-```python
-@dataclass
-class Skill:
-    skill_id: str
-    scope: str
+### 格式
 
-    name: str
-    description: str
-    category: str
+```markdown
+---
+name: error-handling
+description: Error handling best practices
+---
+
+## When to use
+- Analyzing error logs
+- Debugging production issues
 
-    # 层次结构
-    parent_id: Optional[str] = None
+## Guidelines
+- Look for stack traces first
+- Check error frequency
+- Group by error type
+```
 
-    # 内容
-    guidelines: List[str]
-    derived_from: List[str]  # experience_ids
+### 加载
 
-    version: int = 1
-    created_at: datetime
-    updated_at: datetime
+通过 `skill` 工具:
+
+```python
+@tool(id="skill", description="Load a skill by name", parameters={"name": str})
+async def skill_tool(name: str) -> str:
+    # 扫描 Skills 目录
+    for dir in [Path.home() / ".reson/skills", Path.cwd() / ".reson/skills"]:
+        skill_file = dir / name / "SKILL.md"
+        if skill_file.exists():
+            return skill_file.read_text()
+
+    raise FileNotFoundError(f"Skill '{name}' not found")
 ```
 
+**本质**:读取文件的工具,返回字符串。
+
 ---
 
-## 5. 存储抽象
+## 5. Experiences(经验库)
+
+### 存储
+
+PostgreSQL + pgvector:
+
+```sql
+CREATE TABLE experiences (
+    exp_id TEXT PRIMARY KEY,
+    scope TEXT,           -- "agent:executor" 或 "user:123"
+    condition TEXT,       -- "当遇到数据库连接超时"
+    rule TEXT,            -- "增加重试次数到5次"
+    evidence JSONB,       -- 证据(step_ids)
+
+    source TEXT,          -- "execution", "feedback", "manual"
+    confidence FLOAT,
+    usage_count INT,
+    success_rate FLOAT,
 
-### Protocol 定义
+    embedding vector(1536),  -- 向量检索
+
+    created_at TIMESTAMP,
+    updated_at TIMESTAMP
+);
+```
+
+### 检索和注入
 
 ```python
-class TraceStore(Protocol):
-    async def create_trace(self, trace: Trace) -> str: ...
-    async def get_trace(self, trace_id: str) -> Optional[Trace]: ...
-    async def update_trace(self, trace_id: str, **updates) -> None: ...
-    async def add_step(self, step: Step) -> str: ...
-    async def get_trace_steps(self, trace_id: str) -> List[Step]: ...
-
-class MemoryStore(Protocol):
-    async def add_experience(self, exp: Experience) -> str: ...
-    async def search_experiences(self, scope: str, context: str, limit: int) -> List[Experience]: ...
-    async def add_skill(self, skill: Skill) -> str: ...
-    async def search_skills(self, scope: str, context: str, limit: int) -> List[Skill]: ...
+# 1. 检索相关 Experiences
+experiences = await db.query(
+    """
+    SELECT condition, rule, success_rate
+    FROM experiences
+    WHERE scope = $1
+    ORDER BY embedding <-> $2
+    LIMIT 10
+    """,
+    f"agent:{agent_type}",
+    embed(task)
+)
+
+# 2. 注入到 system prompt
+system_prompt = base_prompt + "\n\n# Learned Experiences\n" + "\n".join([
+    f"- When {e.condition}, then {e.rule} (success rate: {e.success_rate:.1%})"
+    for e in experiences
+])
 ```
 
 ---
 
-## 6. 事件系统
+## 6. Agent Loop
 
 ```python
-@dataclass
-class AgentEvent:
-    type: Literal[
-        "trace_started",
-        "memory_loaded",
-        "step_started",
-        "llm_delta",
-        "tool_executing",
-        "tool_result",
-        "conclusion",
-        "feedback_received",
-        "experience_extracted",
-        "trace_completed",
-        "trace_failed"
-    ]
-    data: Dict[str, Any]
+async def run(task: str, max_steps: int = 50):
+    # 1. 创建 Trace
+    trace = Trace(trace_id=gen_id(), task=task, status="running")
+    await trace_store.save(trace)
+
+    # 2. 检索 Experiences,构建 system prompt
+    experiences = await search_experiences(task)
+    system_prompt = build_system_prompt(experiences)
+
+    # 3. 初始化消息
+    messages = [{"role": "user", "content": task}]
+
+    # 4. ReAct 循环
+    for step in range(max_steps):
+        # 调用 LLM
+        response = await llm.chat(
+            messages=messages,
+            system=system_prompt,
+            tools=tool_registry.to_schema()  # 包括 skill 工具
+        )
+
+        # 记录 LLM 调用
+        await add_step(trace, "llm_call", {
+            "response": response.content,
+            "tool_calls": response.tool_calls
+        })
+
+        # 没有工具调用,完成
+        if not response.tool_calls:
+            break
+
+        # 执行工具
+        for tool_call in response.tool_calls:
+            # Doom loop 检测
+            if is_doom_loop(tool_call):
+                raise DoomLoopError()
+
+            # 执行工具(包括 skill 工具)
+            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})
+
+            # 添加到消息历史
+            messages.append({"role": "assistant", "tool_calls": [tool_call]})
+            messages.append({"role": "tool", "content": result})
+
+    # 5. 完成
+    trace.status = "completed"
+    await trace_store.save(trace)
+
+    return trace
 ```
 
+**Doom Loop 检测**:
+- 跟踪最近 3 次工具调用
+- 如果都是同一个工具且参数相同,中断循环
+
 ---
 
-## 7. 与 LLM 提供商的集成
+## 7. 工具系统
+
+### 定义
+
+```python
+@dataclass
+class ToolResult:
+    output: str
+    metadata: Dict[str, Any] = field(default_factory=dict)
+
+class Tool(Protocol):
+    id: str
+    description: str
+    parameters: Type[BaseModel]
+
+    async def execute(self, args: Dict, ctx: ToolContext) -> ToolResult: ...
+```
+
+### 装饰器
+
+```python
+@tool(id="read", description="Read a file", parameters={"path": str})
+async def read_tool(path: str) -> str:
+    return Path(path).read_text()
+
+@tool(id="skill", description="Load a skill", parameters={"name": str})
+async def skill_tool(name: str) -> str:
+    # 扫描并加载 Skill 文件
+    ...
+```
+
+### 注册
 
-Agent 模块**不绑定**特定 LLM 提供商。通过 `LLMProvider` Protocol 抽象:
+```python
+registry = ToolRegistry()
+registry.register(read_tool)
+registry.register(skill_tool)
+registry.register(search_tool)
+
+# 转换为 LLM schema
+tools_schema = registry.to_schema()
+```
+
+---
+
+## 8. 存储接口
 
 ```python
-class LLMProvider(Protocol):
-    async def chat(
-        self,
-        messages: List[Dict],
-        model: str,
-        tools: Optional[List[Dict]] = None,
-        **kwargs
-    ) -> AsyncIterator[LLMEvent]: ...
+class TraceStore(Protocol):
+    async def save(self, trace: Trace) -> None: ...
+    async def get(self, trace_id: str) -> Trace: ...
+    async def add_step(self, step: Step) -> None: ...
+    async def get_steps(self, trace_id: str) -> List[Step]: ...
+
+class ExperienceStore(Protocol):
+    async def search(self, scope: str, query: str, limit: int) -> List[Dict]: ...
+    async def add(self, exp: Dict) -> None: ...
+    async def update_stats(self, exp_id: str, success: bool) -> None: ...
+
+class SkillLoader(Protocol):
+    async def scan(self) -> List[str]:  # 返回 skill names
+        """扫描并返回所有可用的 skill 名称"""
+
+    async def load(self, name: str) -> str:  # 返回内容
+        """加载指定 skill 的 Markdown 内容"""
 ```
 
-宿主项目实现具体的 Provider(OpenAI、Anthropic、Azure 等)。
+**实现策略**:
+- Trace/Step: 文件系统(JSON)
+- Experience: PostgreSQL + pgvector
+- Skill: 文件系统(Markdown)
 
 ---
 
-## 8. 模块结构
+## 9. 模块结构
 
 ```
 reson_agent/
 ├── __init__.py
 ├── runner.py              # AgentRunner
-├── events.py              # AgentEvent
-├── models/
-│   ├── __init__.py
-│   ├── trace.py           # Trace, Step
-│   └── memory.py          # Experience, Skill
+├── models.py              # Trace, Step
 ├── storage/
-│   ├── __init__.py
-│   ├── protocols.py       # TraceStore, MemoryStore
-│   └── memory_impl.py     # 内存实现
-└── tools/
-    ├── __init__.py
-    ├── registry.py        # ToolRegistry, @tool
-    └── schema.py          # SchemaGenerator
+│   ├── protocols.py       # TraceStore, ExperienceStore, SkillLoader
+│   ├── trace_fs.py        # 文件系统实现
+│   ├── experience_pg.py   # PostgreSQL 实现
+│   └── skill_fs.py        # 文件系统实现
+├── tools/
+│   ├── registry.py        # ToolRegistry
+│   ├── decorator.py       # @tool
+│   └── builtin.py         # read, skill, search
+└── llm.py                 # LLMProvider Protocol
 ```
 
 ---
 
-## 9. 设计决策
+## 10. 设计决策
+
+### Skills 通过工具加载 vs 预先注入
 
-### 为什么 Trace/Step 而不是复用 llm_call_history?
+**方案对比**:
 
-- `llm_call_history` 是扁平的调用日志
-- `Trace/Step` 是带因果关系的执行图
-- Step 支持多种类型(llm_call, tool_call, conclusion, feedback)
-- Step 之间可以形成 DAG(不只是链表)
+| 方案 | 优点 | 缺点 |
+|------|------|------|
+| 预先注入到 system prompt | 简单 | 浪费 token,Agent 无法选择 |
+| 作为工具动态加载 | 按需加载,Agent 自主选择 | 需要实现 skill 工具 |
+
+**选择**:动态加载(参考 OpenCode 和 Claude API 文档)
+
+### Skills 用文件 vs 数据库
 
-### 为什么存储可插拔?
+**选择**:文件系统
+- 易于编辑(Markdown)
+- 版本控制(Git)
+- 零依赖
 
-- 不同项目有不同的存储需求(PostgreSQL、Neo4j、MongoDB)
-- MVP 阶段用内存实现,快速验证
-- 生产环境再接入持久化存储
+### Experiences 用数据库 vs 文件
 
-### 为什么工具系统要独立?
+**选择**:数据库(PostgreSQL + pgvector)
+- 需要向量检索
+- 需要统计分析
+- 数量大,动态更新
 
-- 工具注册和 Schema 生成是通用能力
-- 可以在没有 Agent 的场景单独使用
-- 与 LLM Provider 解耦
+### 为什么不需要事件系统?
+
+**原因**:后台场景,不需要实时通知
+- Trace/Step 已记录所有信息
+- 需要告警时直接调用
 
 ---
 
-## 10. 后续计划
+## 11. 实现计划
 
-### Phase 1(当前):MVP
-- [x] 数据模型
-- [x] 存储接口 + 内存实现
-- [x] 工具系统
+### Phase 1:MVP
+- [ ] Trace/Step 数据模型
+- [ ] 文件系统 TraceStore
+- [ ] 文件系统 SkillLoader
 - [ ] AgentRunner 基础循环
-- [ ] 测试
+- [ ] Doom Loop 检测
+- [ ] 基础工具(read, skill)
 
 ### Phase 2:完善
-- [ ] Experience 提取
-- [ ] Skill 归纳
-- [ ] PostgreSQL 存储实现
+- [ ] PostgreSQL ExperienceStore
+- [ ] 向量检索(pgvector)
+- [ ] Experience 自动提取
+- [ ] Skill 自动归纳
+
+### Phase 3:集成
 - [ ] 与 Resonote 集成
+- [ ] 多 Agent 协作
+- [ ] 监控和成本分析
 
-### Phase 3:重构 LLM 模块
-- [ ] 将验证过的设计合并回 Resonote/llm
-- [ ] 统一 call/stream/run
+---
+
+## 附录:核心概念
+
+| 概念 | 定义 | 存储 |
+|------|------|------|
+| **Trace** | 一次任务执行 | 文件系统(JSON) |
+| **Step** | 执行步骤 | 文件系统(JSON) |
+| **Skill** | 能力描述(Markdown) | 文件系统 |
+| **Experience** | 经验规则(条件+规则) | 数据库 + 向量 |
+| **Agent Loop** | ReAct 循环 | - |
+| **Doom Loop** | 无限循环检测 | - |