|
|
@@ -0,0 +1,562 @@
|
|
|
+# 快速实现自己的 Agent - 生产使用指南
|
|
|
+
|
|
|
+## 🚀 三步快速开始
|
|
|
+
|
|
|
+### 第一步:安装依赖
|
|
|
+
|
|
|
+```bash
|
|
|
+# 安装基础依赖
|
|
|
+pip install -r requirements.txt
|
|
|
+
|
|
|
+# 安装 browser 模块依赖(如果需要浏览器工具)
|
|
|
+pip install dbutils pymysql
|
|
|
+
|
|
|
+# 配置环境变量
|
|
|
+cp .env.example .env
|
|
|
+# 编辑 .env 填入你的 API Key
|
|
|
+```
|
|
|
+
|
|
|
+### 第二步:创建你的 Agent
|
|
|
+
|
|
|
+创建 `my_agent/run.py`:
|
|
|
+
|
|
|
+```python
|
|
|
+import asyncio
|
|
|
+import sys
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+# 添加项目根目录到 Python 路径
|
|
|
+sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
+
|
|
|
+from agent import AgentRunner, RunConfig, FileSystemTraceStore
|
|
|
+from agent.llm import create_openrouter_llm_call
|
|
|
+
|
|
|
+async def main():
|
|
|
+ # 1. 初始化存储
|
|
|
+ trace_store = FileSystemTraceStore(base_path=".cache/traces")
|
|
|
+
|
|
|
+ # 2. 初始化 LLM
|
|
|
+ llm_call = create_openrouter_llm_call(
|
|
|
+ model="anthropic/claude-sonnet-4.5"
|
|
|
+ )
|
|
|
+
|
|
|
+ # 3. 创建 Runner
|
|
|
+ runner = AgentRunner(
|
|
|
+ llm_call=llm_call,
|
|
|
+ trace_store=trace_store,
|
|
|
+ )
|
|
|
+
|
|
|
+ # 4. 运行 Agent
|
|
|
+ async for item in runner.run(
|
|
|
+ messages=[{"role": "user", "content": "你的任务描述"}],
|
|
|
+ config=RunConfig(
|
|
|
+ model="anthropic/claude-sonnet-4.5",
|
|
|
+ max_iterations=30,
|
|
|
+ ),
|
|
|
+ ):
|
|
|
+ print(item)
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ asyncio.run(main())
|
|
|
+```
|
|
|
+
|
|
|
+### 第三步:运行
|
|
|
+
|
|
|
+```bash
|
|
|
+python my_agent/run.py
|
|
|
+```
|
|
|
+
|
|
|
+## 📦 核心组件
|
|
|
+
|
|
|
+### 1. AgentRunner - 执行引擎
|
|
|
+
|
|
|
+```python
|
|
|
+from agent import AgentRunner, RunConfig
|
|
|
+
|
|
|
+runner = AgentRunner(
|
|
|
+ llm_call=llm_call, # LLM 调用函数
|
|
|
+ trace_store=trace_store, # Trace 存储
|
|
|
+ memory_store=memory_store, # 记忆存储(可选)
|
|
|
+ skills_dir="./skills", # Skills 目录(可选)
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### 2. LLM Providers
|
|
|
+
|
|
|
+框架支持三种 LLM 提供商:
|
|
|
+
|
|
|
+```python
|
|
|
+# OpenRouter(推荐,支持多种模型)
|
|
|
+from agent.llm import create_openrouter_llm_call
|
|
|
+llm_call = create_openrouter_llm_call(
|
|
|
+ model="anthropic/claude-sonnet-4.5",
|
|
|
+ api_key="your-api-key", # 或从环境变量读取
|
|
|
+)
|
|
|
+
|
|
|
+# Gemini
|
|
|
+from agent.llm import create_gemini_llm_call
|
|
|
+llm_call = create_gemini_llm_call(
|
|
|
+ model="gemini-2.0-flash-exp",
|
|
|
+ api_key="your-api-key",
|
|
|
+)
|
|
|
+
|
|
|
+# Yescode(内部)
|
|
|
+from agent.llm import create_yescode_llm_call
|
|
|
+llm_call = create_yescode_llm_call(
|
|
|
+ model="gpt-4o",
|
|
|
+ api_key="your-api-key",
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 自定义工具
|
|
|
+
|
|
|
+使用 `@tool` 装饰器注册工具:
|
|
|
+
|
|
|
+```python
|
|
|
+from agent import tool, ToolResult, ToolContext
|
|
|
+
|
|
|
+@tool(description="查询产品库存")
|
|
|
+async def check_inventory(
|
|
|
+ product_id: str,
|
|
|
+ warehouse: str = "default",
|
|
|
+ ctx: ToolContext = None,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 查询指定仓库的产品库存
|
|
|
+
|
|
|
+ Args:
|
|
|
+ product_id: 产品唯一标识符
|
|
|
+ warehouse: 仓库编码,默认为主仓库
|
|
|
+ ctx: 工具上下文(自动注入)
|
|
|
+ """
|
|
|
+ # 你的业务逻辑
|
|
|
+ stock = await query_database(product_id, warehouse)
|
|
|
+
|
|
|
+ return ToolResult(
|
|
|
+ title="库存查询成功",
|
|
|
+ output=f"产品 {product_id} 在 {warehouse} 仓库的库存: {stock}",
|
|
|
+ data={"product_id": product_id, "stock": stock},
|
|
|
+ )
|
|
|
+```
|
|
|
+
|
|
|
+**重要**:确保定义工具的模块在 `runner.run()` 之前被 import。
|
|
|
+
|
|
|
+### 4. Skills - Agent 能力定义
|
|
|
+
|
|
|
+创建 `skills/my-skill.md`:
|
|
|
+
|
|
|
+```markdown
|
|
|
+---
|
|
|
+name: my-skill
|
|
|
+description: 我的自定义技能
|
|
|
+category: custom
|
|
|
+---
|
|
|
+
|
|
|
+# 我的技能
|
|
|
+
|
|
|
+## 何时使用
|
|
|
+
|
|
|
+- 场景1:当需要...
|
|
|
+- 场景2:当遇到...
|
|
|
+
|
|
|
+## 使用指南
|
|
|
+
|
|
|
+1. 首先...
|
|
|
+2. 然后...
|
|
|
+3. 最后...
|
|
|
+
|
|
|
+## 最佳实践
|
|
|
+
|
|
|
+- 建议1
|
|
|
+- 建议2
|
|
|
+```
|
|
|
+
|
|
|
+加载 Skills:
|
|
|
+
|
|
|
+```python
|
|
|
+runner = AgentRunner(
|
|
|
+ llm_call=llm_call,
|
|
|
+ trace_store=trace_store,
|
|
|
+ skills_dir="./skills", # 指定 skills 目录
|
|
|
+)
|
|
|
+
|
|
|
+# 或在 RunConfig 中指定
|
|
|
+config = RunConfig(
|
|
|
+ skills=["my-skill", "planning"], # 只加载指定的 skills
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### 5. Agent Presets - 预设配置
|
|
|
+
|
|
|
+使用内置预设:
|
|
|
+
|
|
|
+```python
|
|
|
+from agent import RunConfig
|
|
|
+
|
|
|
+# 默认 Agent(全部工具权限)
|
|
|
+config = RunConfig(agent_type="default")
|
|
|
+
|
|
|
+# 探索型 Agent(只读权限)
|
|
|
+config = RunConfig(agent_type="explore")
|
|
|
+
|
|
|
+# 评估型 Agent(只读+评估)
|
|
|
+config = RunConfig(agent_type="evaluate")
|
|
|
+```
|
|
|
+
|
|
|
+自定义预设:
|
|
|
+
|
|
|
+```python
|
|
|
+from agent import AgentPreset
|
|
|
+from agent.core.presets import register_preset
|
|
|
+
|
|
|
+# 定义预设
|
|
|
+my_preset = AgentPreset(
|
|
|
+ allowed_tools=["read_file", "grep_content", "my_custom_tool"],
|
|
|
+ denied_tools=["bash_command"],
|
|
|
+ max_iterations=20,
|
|
|
+ skills=["my-skill"],
|
|
|
+ description="我的自定义 Agent",
|
|
|
+)
|
|
|
+
|
|
|
+# 注册预设
|
|
|
+register_preset("my-agent", my_preset)
|
|
|
+
|
|
|
+# 使用预设
|
|
|
+config = RunConfig(agent_type="my-agent")
|
|
|
+```
|
|
|
+
|
|
|
+## 🎯 实战案例
|
|
|
+
|
|
|
+### 案例1:内容寻找 Agent
|
|
|
+
|
|
|
+参考 `examples/content_finder/`:
|
|
|
+
|
|
|
+```python
|
|
|
+# 1. 定义工具
|
|
|
+@tool(description="从抖音搜索视频")
|
|
|
+async def douyin_search(keywords: str, ctx: ToolContext = None) -> ToolResult:
|
|
|
+ results = await call_douyin_api(keywords)
|
|
|
+ return ToolResult(output=f"找到 {len(results)} 条内容", data=results)
|
|
|
+
|
|
|
+# 2. 定义 Skill
|
|
|
+# skills/content-finder.md
|
|
|
+
|
|
|
+# 3. 运行 Agent
|
|
|
+runner = AgentRunner(
|
|
|
+ llm_call=llm_call,
|
|
|
+ trace_store=trace_store,
|
|
|
+ skills_dir="./skills",
|
|
|
+)
|
|
|
+
|
|
|
+async for item in runner.run(
|
|
|
+ messages=[{"role": "user", "content": "搜索美食类视频"}],
|
|
|
+ config=RunConfig(skills=["content-finder"]),
|
|
|
+):
|
|
|
+ if isinstance(item, Message):
|
|
|
+ print(item.content)
|
|
|
+```
|
|
|
+
|
|
|
+### 案例2:数据分析 Agent
|
|
|
+
|
|
|
+```python
|
|
|
+@tool(description="查询数据库")
|
|
|
+async def query_db(sql: str, ctx: ToolContext = None) -> ToolResult:
|
|
|
+ results = await execute_sql(sql)
|
|
|
+ return ToolResult(
|
|
|
+ title="查询成功",
|
|
|
+ output=f"返回 {len(results)} 条记录",
|
|
|
+ data=results,
|
|
|
+ )
|
|
|
+
|
|
|
+@tool(description="生成图表")
|
|
|
+async def create_chart(data: list, chart_type: str, ctx: ToolContext = None) -> ToolResult:
|
|
|
+ chart_url = await generate_chart(data, chart_type)
|
|
|
+ return ToolResult(
|
|
|
+ title="图表已生成",
|
|
|
+ output=f"图表类型: {chart_type}",
|
|
|
+ data={"url": chart_url},
|
|
|
+ )
|
|
|
+
|
|
|
+# 运行
|
|
|
+async for item in runner.run(
|
|
|
+ messages=[{"role": "user", "content": "分析最近一周的销售数据并生成图表"}],
|
|
|
+ config=RunConfig(
|
|
|
+ skills=["data-analysis"],
|
|
|
+ max_iterations=50,
|
|
|
+ ),
|
|
|
+):
|
|
|
+ pass
|
|
|
+```
|
|
|
+
|
|
|
+### 案例3:自动化测试 Agent
|
|
|
+
|
|
|
+```python
|
|
|
+@tool(description="运行测试用例")
|
|
|
+async def run_tests(test_path: str, ctx: ToolContext = None) -> ToolResult:
|
|
|
+ result = await execute_tests(test_path)
|
|
|
+ return ToolResult(
|
|
|
+ title="测试完成",
|
|
|
+ output=f"通过: {result.passed}, 失败: {result.failed}",
|
|
|
+ data=result.to_dict(),
|
|
|
+ )
|
|
|
+
|
|
|
+@tool(description="生成测试报告")
|
|
|
+async def generate_report(test_results: dict, ctx: ToolContext = None) -> ToolResult:
|
|
|
+ report_path = await create_html_report(test_results)
|
|
|
+ return ToolResult(
|
|
|
+ title="报告已生成",
|
|
|
+ output=f"报告路径: {report_path}",
|
|
|
+ )
|
|
|
+```
|
|
|
+
|
|
|
+## 🔧 高级功能
|
|
|
+
|
|
|
+### 1. 子 Agent
|
|
|
+
|
|
|
+创建子 Agent 处理子任务:
|
|
|
+
|
|
|
+```python
|
|
|
+from agent.tools.builtin.subagent import agent
|
|
|
+
|
|
|
+# Agent 会自动调用 agent 工具创建子 Agent
|
|
|
+# 在 system prompt 中说明何时使用子 Agent
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 记忆系统
|
|
|
+
|
|
|
+使用记忆存储跨会话数据:
|
|
|
+
|
|
|
+```python
|
|
|
+from agent.memory.stores import MemoryMemoryStore
|
|
|
+
|
|
|
+memory_store = MemoryMemoryStore()
|
|
|
+
|
|
|
+runner = AgentRunner(
|
|
|
+ llm_call=llm_call,
|
|
|
+ trace_store=trace_store,
|
|
|
+ memory_store=memory_store,
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### 3. Goal Tree - 计划管理
|
|
|
+
|
|
|
+Agent 自动使用 `goal` 工具管理计划:
|
|
|
+
|
|
|
+```python
|
|
|
+# Agent 会自动创建和更新 Goal
|
|
|
+# 查看 Goal Tree
|
|
|
+from agent.trace import FileSystemTraceStore
|
|
|
+
|
|
|
+trace_store = FileSystemTraceStore(base_path=".cache/traces")
|
|
|
+trace = await trace_store.get_trace(trace_id)
|
|
|
+goal_tree = trace.goal_tree
|
|
|
+```
|
|
|
+
|
|
|
+### 4. 续跑和回溯
|
|
|
+
|
|
|
+从指定消息后继续执行:
|
|
|
+
|
|
|
+```python
|
|
|
+# 续跑
|
|
|
+async for item in runner.run(
|
|
|
+ messages=[],
|
|
|
+ config=RunConfig(
|
|
|
+ trace_id="existing-trace-id",
|
|
|
+ after_sequence=10, # 从第10条消息后继续
|
|
|
+ ),
|
|
|
+):
|
|
|
+ pass
|
|
|
+
|
|
|
+# 回溯重跑
|
|
|
+async for item in runner.run(
|
|
|
+ messages=[],
|
|
|
+ config=RunConfig(
|
|
|
+ trace_id="existing-trace-id",
|
|
|
+ after_sequence=5, # 回到第5条消息重新执行
|
|
|
+ ),
|
|
|
+):
|
|
|
+ pass
|
|
|
+```
|
|
|
+
|
|
|
+### 5. API Server - 可视化
|
|
|
+
|
|
|
+启动 API Server 查看执行过程:
|
|
|
+
|
|
|
+```bash
|
|
|
+python api_server.py
|
|
|
+```
|
|
|
+
|
|
|
+访问:
|
|
|
+- `http://localhost:8000/api/traces` - 查看所有 Traces
|
|
|
+- `http://localhost:8000/api/traces/{id}` - 查看 Trace 详情
|
|
|
+- WebSocket: `ws://localhost:8000/api/traces/{id}/watch` - 实时监控
|
|
|
+
|
|
|
+## 📝 最佳实践
|
|
|
+
|
|
|
+### 1. 工具设计原则
|
|
|
+
|
|
|
+- **单一职责**:每个工具只做一件事
|
|
|
+- **清晰描述**:description 要准确描述工具功能
|
|
|
+- **详细文档**:docstring 要包含参数说明
|
|
|
+- **错误处理**:捕获异常并返回友好的错误信息
|
|
|
+
|
|
|
+```python
|
|
|
+@tool(description="发送邮件")
|
|
|
+async def send_email(
|
|
|
+ to: str,
|
|
|
+ subject: str,
|
|
|
+ body: str,
|
|
|
+ ctx: ToolContext = None,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 发送邮件
|
|
|
+
|
|
|
+ Args:
|
|
|
+ to: 收件人邮箱地址
|
|
|
+ subject: 邮件主题
|
|
|
+ body: 邮件正文
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ await email_service.send(to, subject, body)
|
|
|
+ return ToolResult(
|
|
|
+ title="邮件发送成功",
|
|
|
+ output=f"已发送到 {to}",
|
|
|
+ )
|
|
|
+ except Exception as e:
|
|
|
+ return ToolResult(
|
|
|
+ title="邮件发送失败",
|
|
|
+ output=f"错误: {str(e)}",
|
|
|
+ error=str(e),
|
|
|
+ )
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Skill 编写原则
|
|
|
+
|
|
|
+- **明确场景**:清楚说明何时使用这个 Skill
|
|
|
+- **提供指南**:给出具体的操作步骤
|
|
|
+- **包含示例**:展示最佳实践
|
|
|
+- **避免冗余**:不要重复框架已有的功能
|
|
|
+
|
|
|
+### 3. 性能优化
|
|
|
+
|
|
|
+```python
|
|
|
+# 1. 限制迭代次数
|
|
|
+config = RunConfig(max_iterations=30)
|
|
|
+
|
|
|
+# 2. 使用合适的模型
|
|
|
+llm_call = create_openrouter_llm_call(
|
|
|
+ model="anthropic/claude-haiku-4.5", # 更快更便宜
|
|
|
+)
|
|
|
+
|
|
|
+# 3. 启用 Prompt Caching(Claude 模型)
|
|
|
+config = RunConfig(enable_prompt_caching=True)
|
|
|
+
|
|
|
+# 4. 限制工具权限
|
|
|
+config = RunConfig(
|
|
|
+ agent_type="explore", # 只读权限,更快
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### 4. 错误处理
|
|
|
+
|
|
|
+```python
|
|
|
+try:
|
|
|
+ async for item in runner.run(messages=messages, config=config):
|
|
|
+ if isinstance(item, Message):
|
|
|
+ # 处理消息
|
|
|
+ pass
|
|
|
+except Exception as e:
|
|
|
+ logger.error(f"Agent 执行失败: {e}")
|
|
|
+ # 错误处理逻辑
|
|
|
+```
|
|
|
+
|
|
|
+### 5. 日志和监控
|
|
|
+
|
|
|
+```python
|
|
|
+import logging
|
|
|
+
|
|
|
+logging.basicConfig(
|
|
|
+ level=logging.INFO,
|
|
|
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
|
+)
|
|
|
+
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
+
|
|
|
+# 在关键位置添加日志
|
|
|
+logger.info("开始执行 Agent")
|
|
|
+logger.debug(f"配置: {config}")
|
|
|
+```
|
|
|
+
|
|
|
+## 🚨 常见问题
|
|
|
+
|
|
|
+### Q1: 如何调试 Agent?
|
|
|
+
|
|
|
+```python
|
|
|
+# 1. 打印所有消息
|
|
|
+async for item in runner.run(messages=messages, config=config):
|
|
|
+ print(f"[{type(item).__name__}] {item}")
|
|
|
+
|
|
|
+# 2. 查看 Trace
|
|
|
+trace = await trace_store.get_trace(trace_id)
|
|
|
+for msg in trace.messages:
|
|
|
+ print(f"{msg.role}: {msg.content}")
|
|
|
+
|
|
|
+# 3. 启动 API Server 可视化
|
|
|
+python api_server.py
|
|
|
+```
|
|
|
+
|
|
|
+### Q2: 工具没有被调用?
|
|
|
+
|
|
|
+检查:
|
|
|
+1. 工具是否正确注册(使用 `@tool` 装饰器)
|
|
|
+2. 工具模块是否被 import
|
|
|
+3. 工具描述是否清晰
|
|
|
+4. Agent 是否有权限使用该工具(检查 `allowed_tools`)
|
|
|
+
|
|
|
+### Q3: Agent 陷入循环?
|
|
|
+
|
|
|
+```python
|
|
|
+# 1. 限制迭代次数
|
|
|
+config = RunConfig(max_iterations=20)
|
|
|
+
|
|
|
+# 2. 在 Skill 中明确终止条件
|
|
|
+# 3. 检查工具返回是否有明确的成功/失败标识
|
|
|
+```
|
|
|
+
|
|
|
+### Q4: 如何处理长时间运行的任务?
|
|
|
+
|
|
|
+```python
|
|
|
+# 使用异步处理
|
|
|
+import asyncio
|
|
|
+
|
|
|
+async def long_running_task():
|
|
|
+ async for item in runner.run(messages=messages, config=config):
|
|
|
+ # 定期保存状态
|
|
|
+ if isinstance(item, Trace):
|
|
|
+ await save_checkpoint(item)
|
|
|
+
|
|
|
+# 支持中断和恢复
|
|
|
+config = RunConfig(
|
|
|
+ trace_id=last_trace_id,
|
|
|
+ after_sequence=last_sequence,
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+## 📚 参考资源
|
|
|
+
|
|
|
+- **框架文档**: `agent/README.md`
|
|
|
+- **架构设计**: `agent/docs/architecture.md`
|
|
|
+- **工具系统**: `agent/docs/tools.md`
|
|
|
+- **Skills 指南**: `agent/docs/skills.md`
|
|
|
+- **示例项目**: `examples/`
|
|
|
+ - `examples/content_finder/` - 内容寻找 Agent
|
|
|
+ - `examples/how/` - 完整示例
|
|
|
+ - `examples/research/` - 研究型 Agent
|
|
|
+
|
|
|
+## 🎓 下一步
|
|
|
+
|
|
|
+1. **阅读示例代码**: 从 `examples/content_finder/demo.py` 开始
|
|
|
+2. **创建第一个工具**: 实现一个简单的业务工具
|
|
|
+3. **编写 Skill**: 定义 Agent 的工作方式
|
|
|
+4. **测试运行**: 小规模测试验证功能
|
|
|
+5. **生产部署**: 添加监控、日志、错误处理
|