Jelajahi Sumber

refactor: project config

Talegorithm 1 hari lalu
induk
melakukan
f6f0881786

+ 7 - 0
agent/utils/__init__.py

@@ -0,0 +1,7 @@
+"""
+工具函数模块
+"""
+
+from agent.utils.logging import setup_logging
+
+__all__ = ["setup_logging"]

+ 40 - 0
agent/utils/logging.py

@@ -0,0 +1,40 @@
+"""
+日志配置工具
+
+提供统一的日志配置方法。
+"""
+
+import logging
+from typing import Optional
+
+
+def setup_logging(
+    level: str = "INFO",
+    format: Optional[str] = None,
+    file: Optional[str] = None
+):
+    """
+    配置日志系统
+
+    Args:
+        level: 日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)
+        format: 日志格式(None 使用默认格式)
+        file: 日志文件路径(None 只输出到控制台)
+    """
+    log_level = getattr(logging, level.upper(), logging.INFO)
+    log_format = format or "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+
+    handlers = [logging.StreamHandler()]
+    if file:
+        handlers.append(logging.FileHandler(file, encoding="utf-8"))
+
+    logging.basicConfig(
+        level=log_level,
+        format=log_format,
+        handlers=handlers,
+        force=True
+    )
+
+    # 设置第三方库日志级别
+    logging.getLogger("httpx").setLevel(logging.WARNING)
+    logging.getLogger("httpcore").setLevel(logging.WARNING)

+ 34 - 77
examples/research/config.py

@@ -1,93 +1,50 @@
 """
 项目配置
 
-定义项目的运行配置,包括模型、知识管理、日志等
+定义项目的运行配置。
 """
 
-import logging
-from agent.core.runner import KnowledgeConfig
+from agent.core.runner import KnowledgeConfig, RunConfig
 
 
-# ===== 运行配置 =====
+# ===== Agent 运行配置 =====
 
-class RunnerConfig:
-    """运行配置"""
-    def __init__(self):
-        # 模型配置
-        self.model = "sonnet-4.5"
-        self.temperature = 0.3
-        self.max_iterations = 1000
+RUN_CONFIG = RunConfig(
+    # 模型配置
+    model="claude-sonnet-4.5",
+    temperature=0.3,
+    max_iterations=1000,
 
-        # 路径配置
-        self.skills_dir = "./skills"
-        self.trace_store_path = ".trace"
+    # 任务名称
+    name="Research Agent",
 
-        # 其他配置
-        self.debug = True
-        self.name = "Research Agent"
+    # 知识管理配置
+    knowledge=KnowledgeConfig(
+        # 压缩时提取(消息量超阈值触发压缩时,用完整 history 反思)
+        enable_extraction=True,
+        reflect_prompt="",  # 自定义反思 prompt;空则使用默认,见 agent/core/prompts/knowledge.py:REFLECT_PROMPT
 
-        # 知识管理配置
-        self.knowledge = KnowledgeConfig(
-            # 压缩时提取(消息量超阈值触发压缩时,在 Level 1 过滤前用完整 history 反思)
-            enable_extraction=True,
-            reflect_prompt="",  # 空则使用默认,见 agent/core/prompts/knowledge.py:REFLECT_PROMPT
+        # agent运行完成后提取(不代表任务完成,agent 可能中途退出等待人工评估)
+        enable_completion_extraction=True,
+        completion_reflect_prompt="",  # 自定义复盘 prompt;空则使用默认,见 agent/core/prompts/knowledge.py:COMPLETION_REFLECT_PROMPT
 
-            # agent运行完成后提取(不代表任务完成,agent 可能中途退出等待人工评估)
-            enable_completion_extraction=True,
-            completion_reflect_prompt="",  # 空则使用默认,见 agent/core/prompts/knowledge.py:COMPLETION_REFLECT_PROMPT
+        # 知识注入(agent切换当前工作的goal时,自动注入相关知识)
+        enable_injection=True,
 
-            # 知识注入(agent切换当前工作的goal时,自动注入相关知识)
-            enable_injection=True,
-
-            # 默认字段(保存/搜索时自动注入)
-            owner="user@example.com",  # 所有者(空则尝试从 git config user.email 获取,再空则用 agent:{agent_id})
-            default_tags={"project": "research", "domain": "ai_agent"},  # 默认 tags(会与工具调用参数合并)
-            default_scopes=["org:cybertogether"],  # 默认 scopes
-            default_search_types=["strategy", "tool"],  # 默认搜索类型过滤
-            default_search_owner="user@example.com"  # 默认搜索 owner 过滤(空则不过滤)
-        )
-
-        # 日志配置
-        class LoggingConfig:
-            level = "INFO"
-            console = True
-            file = None
-        self.logging = LoggingConfig()
-
-        # 自动反思配置(项目层面控制)
-        class AutoReflectConfig:
-            enabled = True
-            on_completion = True
-            on_failure = True
-            min_messages = 10
-            focus_on_failure = "本次任务执行失败了,请重点反思失败的原因、踩坑点以及未来应如何避免。"
-        self.auto_reflect = AutoReflectConfig()
-
-
-# 配置实例
-RUNNER_CONFIG = RunnerConfig()
-
-
-# ===== 工具函数 =====
-
-def setup_logging(config):
-    """配置日志系统"""
-    level = getattr(logging, config.level.upper(), logging.INFO)
-
-    handlers = []
-    if config.console:
-        handlers.append(logging.StreamHandler())
+        # 默认字段(保存/搜索时自动注入)
+        owner="",  # 所有者(空则尝试从 git config user.email 获取,再空则用 agent:{agent_id})
+        default_tags={"project": "research", "domain": "ai_agent"},  # 默认 tags(会与工具调用参数合并)
+        default_scopes=["org:cybertogether"],  # 默认 scopes
+        default_search_types=["strategy", "tool"],  # 默认搜索类型过滤
+        default_search_owner=""  # 默认搜索 owner 过滤(空则不过滤)
+    )
+)
 
-    if config.file:
-        handlers.append(logging.FileHandler(config.file, encoding="utf-8"))
 
-    logging.basicConfig(
-        level=level,
-        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
-        handlers=handlers,
-        force=True
-    )
+# ===== 基础设施配置 =====
 
-    # 设置第三方库日志级别
-    logging.getLogger("httpx").setLevel(logging.WARNING)
-    logging.getLogger("httpcore").setLevel(logging.WARNING)
+SKILLS_DIR = "./skills"
+TRACE_STORE_PATH = ".trace"
+DEBUG = True
+LOG_LEVEL = "INFO"
+LOG_FILE = None  # 设置为文件路径可以同时输出到文件

+ 1 - 1
examples/research/research.prompt

@@ -7,7 +7,7 @@ $system$
 你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。
 
 $user$
-帮我调研一下,openclaw在企业中的应用有哪些方案和经验?如何实现组织层面的知识共享?
+帮我调研一下,新产品面世时候怎么营销推广“投放”?是openclaw的社区产品,目标用户可能是使用openclaw等agent的科技尝鲜者。独特价值是给纷繁缭乱的各种agent skill提供社区评价。投放我不了解,我不知道一个产品走向市场都要做什么,我只是听过投放这个词,希望你帮我系统调研和讲解一下。
 
 - 先规划调研计划,再逐步完成调研
 - 要有可靠的依据(例如行业大牛的博客、榜单等等),要尽量全面,而不是随便选择一个平台深入

+ 36 - 70
examples/research/run.py

@@ -38,9 +38,10 @@ from agent.trace import (
 )
 from agent.llm import create_openrouter_llm_call
 from agent.cli import InteractiveController
+from agent.utils import setup_logging
 
 # 导入项目配置
-from config import RUNNER_CONFIG, setup_logging
+from config import RUN_CONFIG, SKILLS_DIR, TRACE_STORE_PATH, DEBUG, LOG_LEVEL, LOG_FILE
 
 
 async def main():
@@ -59,15 +60,11 @@ async def main():
     output_dir = base_dir / "output_1"
     output_dir.mkdir(exist_ok=True)
 
-    # 1. 使用配置
-    print("1. 加载配置...")
-    runner_config = RUNNER_CONFIG
-    print(f"   - 已加载配置")
+    # 1. 配置日志
+    setup_logging(level=LOG_LEVEL, file=LOG_FILE)
 
-    # 配置日志
-    setup_logging(runner_config.logging)
-
-    # 加载项目级 presets
+    # 2. 加载项目级 presets
+    print("2. 加载 presets...")
     presets_path = base_dir / "presets.json"
     if presets_path.exists():
         import json
@@ -77,63 +74,57 @@ async def main():
             register_preset(name, AgentPreset(**cfg))
         print(f"   - 已加载项目 presets: {list(project_presets.keys())}")
 
-    # Skills 目录
-    skills_dir = runner_config.skills_dir
-
-    # 任务名称
-    task_name = runner_config.name or base_dir.name
-    print("=" * 60)
-    print(f"{task_name} (Agent 模式 + 交互增强)")
-    print("=" * 60)
-    print()
-    print("💡 交互提示:")
-    print("   - 执行过程中输入 'p' 或 'pause' 暂停并进入交互模式")
-    print("   - 执行过程中输入 'q' 或 'quit' 停止执行")
-    print("=" * 60)
-    print()
-
-    # 2. 加载 prompt
-    print("2. 加载 prompt 配置...")
+    # 3. 加载 prompt
+    print("3. 加载 prompt...")
     prompt = SimplePrompt(prompt_path)
 
-    # 3. 构建消息
-    print("3. 构建任务消息...")
+    # 4. 构建任务消息
+    print("4. 构建任务消息...")
     messages = prompt.build_messages()
 
-    # 4. 创建 Agent Runner
-    print("4. 创建 Agent Runner...")
-    print(f"   - Skills 目录: {skills_dir}")
-    print(f"   - 模型: {runner_config.model}")
-    print(f"   - 日志级别: {runner_config.logging.level}")
+    # 5. 创建 Agent Runner
+    print("5. 创建 Agent Runner...")
+    print(f"   - Skills 目录: {SKILLS_DIR}")
+    print(f"   - 模型: {RUN_CONFIG.model}")
 
-    store = FileSystemTraceStore(base_path=runner_config.trace_store_path)
+    store = FileSystemTraceStore(base_path=TRACE_STORE_PATH)
     runner = AgentRunner(
         trace_store=store,
-        llm_call=create_openrouter_llm_call(model=f"anthropic/claude-{runner_config.model}"),
-        skills_dir=skills_dir,
-        debug=runner_config.debug,
-        knowledge_config=runner_config.knowledge
+        llm_call=create_openrouter_llm_call(model=f"anthropic/{RUN_CONFIG.model}"),
+        skills_dir=SKILLS_DIR,
+        debug=DEBUG
     )
 
-    # 5. 创建交互控制器
+    # 6. 创建交互控制器
     interactive = InteractiveController(
         runner=runner,
         store=store,
         enable_stdin_check=True
     )
 
-    # 6. 判断是新建还是恢复
+    # 7. 任务信息
+    task_name = RUN_CONFIG.name or base_dir.name
+    print("=" * 60)
+    print(f"{task_name}")
+    print("=" * 60)
+    print("💡 交互提示:")
+    print("   - 执行过程中输入 'p' 或 'pause' 暂停并进入交互模式")
+    print("   - 执行过程中输入 'q' 或 'quit' 停止执行")
+    print("=" * 60)
+    print()
+
+    # 8. 判断是新建还是恢复
     resume_trace_id = args.trace
     if resume_trace_id:
         existing_trace = await store.get_trace(resume_trace_id)
         if not existing_trace:
             print(f"\n错误: Trace 不存在: {resume_trace_id}")
             sys.exit(1)
-        print(f"5. 恢复已有 Trace: {resume_trace_id[:8]}...")
+        print(f"恢复已有 Trace: {resume_trace_id[:8]}...")
         print(f"   - 状态: {existing_trace.status}")
         print(f"   - 消息数: {existing_trace.total_messages}")
     else:
-        print(f"5. 启动新 Agent 模式...")
+        print(f"启动新 Agent...")
 
     print()
 
@@ -144,22 +135,13 @@ async def main():
 
     try:
         # 配置
+        run_config = RUN_CONFIG
         if resume_trace_id:
             initial_messages = None
-            run_config = RunConfig(
-                model=f"claude-{runner_config.model}",
-                temperature=runner_config.temperature,
-                max_iterations=runner_config.max_iterations,
-                trace_id=resume_trace_id,
-            )
+            run_config.trace_id = resume_trace_id
         else:
             initial_messages = messages
-            run_config = RunConfig(
-                model=f"claude-{runner_config.model}",
-                temperature=runner_config.temperature,
-                max_iterations=runner_config.max_iterations,
-                name=f"{task_name}:调研任务",
-            )
+            run_config.name = f"{task_name}:调研任务"
 
         while not should_exit:
             if current_trace_id:
@@ -297,22 +279,6 @@ async def main():
 
             # Runner 退出后显示交互菜单
             if current_trace_id:
-                # 自动触发反思(如果配置启用)
-                check_trace = await store.get_trace(current_trace_id)
-                if check_trace and check_trace.status in ("completed", "failed"):
-                    auto_reflect = runner_config.auto_reflect
-                    if auto_reflect.enabled:
-                        should_reflect = False
-                        if check_trace.status == "completed" and auto_reflect.on_completion:
-                            should_reflect = True
-                        elif check_trace.status == "failed" and auto_reflect.on_failure:
-                            should_reflect = True
-
-                        if should_reflect and check_trace.total_messages >= auto_reflect.min_messages:
-                            print(f"\n⚙️ 任务已结束 (状态: {check_trace.status}),正在自动触发经验总结...")
-                            auto_focus = auto_reflect.focus_on_failure if check_trace.status == "failed" else ""
-                            await interactive.perform_reflection(current_trace_id, focus=auto_focus)
-
                 menu_result = await interactive.show_menu(current_trace_id, current_sequence)
 
                 if menu_result["action"] == "stop":