Browse Source

Merge branch 'main' of https://git.yishihui.com/howard/Agent

max_liu 1 tuần trước cách đây
mục cha
commit
6d54c3e0fb

+ 18 - 6
agent/core/presets.py

@@ -21,29 +21,41 @@ class AgentPreset:
     max_iterations: int = 30
     temperature: Optional[float] = None
 
+    # Skills(注入 system prompt 的 skill 名称列表;None = 加载全部)
+    skills: Optional[List[str]] = None
+
     # 描述
     description: Optional[str] = None
 
 
 # 内置预设
+_DEFAULT_SKILLS = ["planning", "research", "browser"]
+
 AGENT_PRESETS = {
     "default": AgentPreset(
         allowed_tools=None,
         max_iterations=30,
+        skills=_DEFAULT_SKILLS,
         description="默认 Agent,拥有全部工具权限",
     ),
+    "delegate": AgentPreset(
+        allowed_tools=None,
+        max_iterations=30,
+        skills=_DEFAULT_SKILLS,
+        description="委托子 Agent,拥有全部工具权限(由 agent 工具创建)",
+    ),
     "explore": AgentPreset(
         allowed_tools=["read", "glob", "grep", "list_files"],
         denied_tools=["write", "edit", "bash", "task"],
         max_iterations=15,
+        skills=["planning"],
         description="探索型 Agent,只读权限,用于代码分析",
     ),
-    "analyst": AgentPreset(
-        allowed_tools=["read", "glob", "grep", "web_search", "webfetch"],
-        denied_tools=["write", "edit", "bash", "task"],
-        temperature=0.3,
-        max_iterations=25,
-        description="分析型 Agent,用于深度分析和研究",
+    "evaluate": AgentPreset(
+        allowed_tools=["read_file", "grep_content", "glob_files", "goal"],
+        max_iterations=10,
+        skills=["planning"],
+        description="评估型 Agent,只读权限,用于结果评估",
     ),
 }
 

+ 31 - 7
agent/core/runner.py

@@ -61,6 +61,7 @@ class RunConfig:
     agent_type: str = "default"
     uid: Optional[str] = None
     system_prompt: Optional[str] = None        # None = 从 skills 自动构建
+    skills: Optional[List[str]] = None         # 注入 system prompt 的 skill 名称列表;None = 按 preset 决定
     enable_memory: bool = True
     auto_execute_tools: bool = True
     name: Optional[str] = None                 # 显示名称(空则由 utility_llm 自动生成)
@@ -290,16 +291,22 @@ class AgentRunner:
         self,
         messages: List[Dict],
         config: Optional[RunConfig] = None,
+        on_event: Optional[Callable] = None,
     ) -> Dict[str, Any]:
         """
         结果模式 — 消费 run(),返回结构化结果。
 
         主要用于 agent/evaluate 工具内部。
+
+        Args:
+            on_event: 可选回调,每个 Trace/Message 事件触发一次,用于实时输出子 Agent 执行过程。
         """
         last_assistant_text = ""
         final_trace: Optional[Trace] = None
 
         async for item in self.run(messages=messages, config=config):
+            if on_event:
+                on_event(item)
             if isinstance(item, Message) and item.role == "assistant":
                 content = item.content
                 text = ""
@@ -1427,16 +1434,33 @@ class AgentRunner:
     DEFAULT_SYSTEM_PREFIX = "你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。"
 
     async def _build_system_prompt(self, config: RunConfig) -> Optional[str]:
-        """构建 system prompt(注入 skills)"""
+        """构建 system prompt(注入 skills)
+
+        优先级:
+        1. config.skills 显式指定 → 按名称过滤
+        2. config.skills 为 None → 查 preset 的默认 skills 列表
+        3. preset 也无 skills(None)→ 加载全部(向后兼容)
+        """
+        from agent.core.presets import AGENT_PRESETS
+
         system_prompt = config.system_prompt
 
-        # 加载 Skills
-        skills_text = ""
-        skills = load_skills_from_dir(self.skills_dir)
-        if skills:
-            skills_text = self._format_skills(skills)
+        # 确定要加载哪些 skills
+        skills_filter: Optional[List[str]] = config.skills
+        if skills_filter is None:
+            preset = AGENT_PRESETS.get(config.agent_type)
+            if preset is not None:
+                skills_filter = preset.skills  # 可能仍为 None(加载全部)
+
+        # 加载并过滤
+        all_skills = load_skills_from_dir(self.skills_dir)
+        if skills_filter is not None:
+            skills = [s for s in all_skills if s.name in skills_filter]
+        else:
+            skills = all_skills
+
+        skills_text = self._format_skills(skills) if skills else ""
 
-        # 拼装:有自定义 system_prompt 则用它,否则用默认前缀
         if system_prompt:
             if skills_text:
                 system_prompt += f"\n\n## Skills\n{skills_text}"

+ 35 - 0
agent/memory/skills/browser.md

@@ -0,0 +1,35 @@
+---
+name: browser
+description: 浏览器自动化工具使用指南
+---
+
+## 浏览器工具使用指南
+
+所有浏览器工具都以 `browser_` 为前缀。浏览器会话会持久化,无需每次重新启动。
+
+### 基本工作流程
+
+1. **页面导航**: 使用 `browser_navigate_to_url` 或 `browser_search_web` 到达目标页面
+2. **等待加载**: 页面跳转后调用 `browser_wait(seconds=2)` 等待内容加载
+3. **获取元素索引**: 调用 `browser_get_visual_selector_map` 获取可交互元素的索引映射和当前界面的截图
+4. **执行交互**: 使用 `browser_click_element`、`browser_input_text` 等工具操作页面
+5. **提取内容**: 使用 `browser_extract_content`, `browser_read_long_content`, `browser_get_page_html` 获取数据
+
+### 关键原则
+
+- **禁止模拟结果**:不要输出你认为的搜索结果,而是要调用工具获取真实结果
+- **必须先获取索引**: 所有 `index` 参数都需要先通过 `browser_get_selector_map` 获取
+- **高级工具**:优先使用 `browser_extract_content`, `browser_read_long_content` 等工具获取数据,而不是使用 `browser_get_selector_map` 获取索引后手动解析
+- **操作后等待**: 任何可能触发页面变化的操作(点击、输入、滚动)后都要调用 `browser_wait`
+- **登录处理**:
+  - **正常登录**:当遇到需要登录的网页时,使用 `browser_load_cookies` 来登录
+  - **首次登录**:当没有该网站的 cookie 时,点击进入登录界面,然后等待人类来登录,登录后使用 `browser_export_cookies` 将账户信息存储下来
+- **复杂操作用JS**: 当标准工具无法满足时,使用 `browser_evaluate` 执行 JavaScript 代码
+
+### 工具分类
+
+**导航**: browser_navigate_to_url, browser_search_web, browser_go_back, browser_wait
+**交互**: browser_click_element, browser_input_text, browser_send_keys, browser_upload_file
+**视图**: browser_scroll_page, browser_find_text, browser_screenshot
+**提取**: browser_extract_content, browser_read_long_content, browser_get_page_html, browser_get_selector_map, browser_get_visual_selector_map
+**高级**: browser_evaluate, browser_load_cookies, browser_export_cookies, browser_wait_for_user_action, browser_download_direct_url

+ 0 - 103
agent/memory/skills/core.md

@@ -1,103 +0,0 @@
----
-name: core
-type: core
-description: 核心系统能力,自动加载到 System Prompt
----
-
-## 计划与执行
-
-使用 `goal` 工具管理执行计划。目标树是你的工作记忆——系统会定期将当前计划注入给你,帮助你追踪进度和关键结论。
-
-### 核心原则
-
-- **先明确目标再行动**:开始执行前,用 `goal` 明确当前要做什么
-- **灵活运用,不受约束**:
-  - 可以先做全局规划再行动:`goal(add="调研方案, 实现方案, 测试验证")`
-  - 可以走一步看一步,每次只规划下一个目标
-  - 行动中可以动态放弃并调整:`goal(abandon="方案不可行")`
-  - 规划本身可以作为一个目标(如 "调研并确定技术方案")
-- **简单任务只需一个目标**:`goal(add="将CSV转换为JSON")` 即可,不需要强制拆分
-
-### 使用方式
-
-创建目标:
-
-```
-goal(add="调研并确定方案, 执行方案, 评估结果")
-```
-
-聚焦并开始执行(使用计划视图中的 ID,如 "1", "2.1"):
-
-```
-goal(focus="1")
-```
-
-完成目标,记录**关键结论**(不是过程描述):
-
-```
-goal(done="最佳方案是openpose,精度高且支持多人检测")
-```
-
-完成并切换到下一个:
-
-```
-goal(done="openpose方案确认可行", focus="2")
-```
-
-添加子目标或同级目标:
-
-```
-goal(add="设计接口, 实现代码", under="2")
-goal(add="编写文档", after="2")
-```
-
-放弃不可行的目标:
-
-```
-goal(abandon="方案A需要Redis,环境没有")
-```
-
-### 使用规范
-
-1. **聚焦到具体目标**:始终将焦点放在你正在执行的最具体的子目标上,而不是父目标。创建子目标后立即 `focus` 到第一个要执行的子目标。完成后用 `done` + `focus` 切换到下一个。
-2. **同时只有一个目标处于执行中**:完成当前目标后再切换
-3. **summary 记录结论**:记录关键发现,而非 "已完成调研" 这样无信息量的描述
-4. **计划可调整**:根据执行情况随时追加、跳过或放弃目标
-5. **使用 ID 定位**:focus、after、under 参数使用目标的 ID(如 "1", "2.1")
-
-## 信息调研
-
-你可以通过联网搜索工具`search_posts`获取来自Github、小红书、微信公众号、知乎等渠道的信息。对于需要深度交互的网页内容,使用浏览器工具进行操作。
-
-调研过程可能需要多次搜索,比如基于搜索结果中获得的启发或信息启动新的搜索,直到得到令人满意的答案。你可以使用`goal`工具管理搜索的过程,或者使用文档记录搜索的中间或最终结果。
-
-## 浏览器工具使用指南
-
-所有浏览器工具都以 `browser_` 为前缀。浏览器会话会持久化,无需每次重新启动。
-
-### 基本工作流程
-
-1. **页面导航**: 使用 `browser_navigate_to_url` 或 `browser_search_web` 到达目标页面
-2. **等待加载**: 页面跳转后调用 `browser_wait(seconds=2)` 等待内容加载
-3. **获取元素索引**: 调用 `browser_get_visual_selector_map` 获取可交互元素的索引映射和当前界面的截图
-4. **执行交互**: 使用 `browser_click_element`、`browser_input_text` 等工具操作页面
-5. **提取内容**: 使用 `browser_extract_content`, `browser_read_long_content`, `browser_get_page_html` 获取数据
-
-### 关键原则
-
-- **禁止模拟结果**:不要输出你认为的搜索结果,而是要调用工具获取真实结果
-- **必须先获取索引**: 所有 `index` 参数都需要先通过 `browser_get_selector_map` 获取
-- **高级工具**:优先使用`browser_extract_content`, `browser_read_long_content`等工具获取数据,而不是使用`browser_get_selector_map`获取索引后手动解析
-- **操作后等待**: 任何可能触发页面变化的操作(点击、输入、滚动)后都要调用 `browser_wait`
-- **登录处理**:
-  - **正常登录**:当遇到需要登录的网页时,使用`browser_load_cookies`来登录
-  - **首次登录**:当没有该网站的cookie时,点击进入登录界面,然后等待人类来登录,登录后使用`browser_export_cookies`将账户信息存储下来
-- **复杂操作用JS**: 当标准工具无法满足时,使用 `browser_evaluate` 执行JavaScript代码
-
-### 工具分类
-
-**导航**: browser_navigate_to_url, browser_search_web, browser_go_back, browser_wait
-**交互**: browser_click_element, browser_input_text, browser_send_keys, browser_upload_file
-**视图**: browser_scroll_page, browser_find_text, browser_screenshot
-**提取**: browser_extract_content, browser_read_long_content, browser_get_page_html, browser_get_selector_map, browser_get_visual_selector_map
-**高级**: browser_evaluate, browser_load_cookies, browser_export_cookies, browser_wait_for_user_action, browser_download_direct_url

+ 65 - 0
agent/memory/skills/planning.md

@@ -0,0 +1,65 @@
+---
+name: planning
+description: 计划管理,使用 goal 工具管理执行计划和目标树
+---
+
+## 计划与执行
+
+使用 `goal` 工具管理执行计划。目标树是你的工作记忆——系统会定期将当前计划注入给你,帮助你追踪进度和关键结论。
+
+### 核心原则
+
+- **先明确目标再行动**:开始执行前,用 `goal` 明确当前要做什么
+- **灵活运用,不受约束**:
+  - 可以先做全局规划再行动:`goal(add="调研方案, 实现方案, 测试验证")`
+  - 可以走一步看一步,每次只规划下一个目标
+  - 行动中可以动态放弃并调整:`goal(abandon="方案不可行")`
+  - 规划本身可以作为一个目标(如 "调研并确定技术方案")
+- **简单任务只需一个目标**:`goal(add="将CSV转换为JSON")` 即可,不需要强制拆分
+
+### 使用方式
+
+创建目标:
+
+```
+goal(add="调研并确定方案, 执行方案, 评估结果")
+```
+
+聚焦并开始执行(使用计划视图中的 ID,如 "1", "2.1"):
+
+```
+goal(focus="1")
+```
+
+完成目标,记录**关键结论**(不是过程描述):
+
+```
+goal(done="最佳方案是openpose,精度高且支持多人检测")
+```
+
+完成并切换到下一个:
+
+```
+goal(done="openpose方案确认可行", focus="2")
+```
+
+添加子目标或同级目标:
+
+```
+goal(add="设计接口, 实现代码", under="2")
+goal(add="编写文档", after="2")
+```
+
+放弃不可行的目标:
+
+```
+goal(abandon="方案A需要Redis,环境没有")
+```
+
+### 使用规范
+
+1. **聚焦到具体目标**:始终将焦点放在你正在执行的最具体的子目标上,而不是父目标。创建子目标后立即 `focus` 到第一个要执行的子目标。完成后用 `done` + `focus` 切换到下一个。
+2. **同时只有一个目标处于执行中**:完成当前目标后再切换
+3. **summary 记录结论**:记录关键发现,而非 "已完成调研" 这样无信息量的描述
+4. **计划可调整**:根据执行情况随时追加、跳过或放弃目标
+5. **使用 ID 定位**:focus、after、under 参数使用目标的 ID(如 "1", "2.1")

+ 10 - 0
agent/memory/skills/research.md

@@ -0,0 +1,10 @@
+---
+name: research
+description: 信息调研,使用搜索工具和浏览器获取外部信息
+---
+
+## 信息调研
+
+你可以通过联网搜索工具 `search_posts` 获取来自 Github、小红书、微信公众号、知乎等渠道的信息。对于需要深度交互的网页内容,使用浏览器工具进行操作。
+
+调研过程可能需要多次搜索,比如基于搜索结果中获得的启发或信息启动新的搜索,直到得到令人满意的答案。你可以使用 `goal` 工具管理搜索的过程,或者使用文档记录搜索的中间或最终结果。

+ 61 - 4
agent/tools/builtin/subagent.py

@@ -276,6 +276,48 @@ def _build_evaluate_prompt(goal_description: str, messages: Optional[Messages])
     return "\n".join(lines)
 
 
+def _make_event_printer(label: str):
+    """
+    创建子 Agent 执行过程打印函数。
+
+    当父 runner.debug=True 时,传给 run_result(on_event=...),
+    实时输出子 Agent 的工具调用和助手消息。
+    """
+    prefix = f"  [{label}]"
+
+    def on_event(item):
+        from agent.trace.models import Trace, Message
+        if isinstance(item, Message):
+            if item.role == "assistant":
+                content = item.content
+                if isinstance(content, dict):
+                    text = content.get("text", "")
+                    tool_calls = content.get("tool_calls")
+                    if text:
+                        preview = text[:120] + "..." if len(text) > 120 else text
+                        print(f"{prefix} {preview}")
+                    if tool_calls:
+                        for tc in tool_calls:
+                            name = tc.get("function", {}).get("name", "unknown")
+                            print(f"{prefix} 🛠️  {name}")
+            elif item.role == "tool":
+                content = item.content
+                if isinstance(content, dict):
+                    name = content.get("tool_name", "unknown")
+                    desc = item.description or ""
+                    desc_short = (desc[:60] + "...") if len(desc) > 60 else desc
+                    suffix = f": {desc_short}" if desc_short else ""
+                    print(f"{prefix} ✅ {name}{suffix}")
+        elif isinstance(item, Trace):
+            if item.status == "completed":
+                print(f"{prefix} ✓ 完成")
+            elif item.status == "failed":
+                err = (item.error_message or "")[:80]
+                print(f"{prefix} ✗ 失败: {err}")
+
+    return on_event
+
+
 # ===== 统一内部执行函数 =====
 
 async def _run_agents(
@@ -283,6 +325,8 @@ async def _run_agents(
     per_agent_msgs: List[Messages],
     continue_from: Optional[str],
     store, trace_id: str, goal_id: str, runner, context: dict,
+    agent_type: Optional[str] = None,
+    skills: Optional[List[str]] = None,
 ) -> Dict[str, Any]:
     """
     统一 agent 执行逻辑。
@@ -317,7 +361,7 @@ async def _run_agents(
             # continue_from 已经设置了 sub_trace_id
             pass
         else:
-            agent_type = "delegate" if single else "explore"
+            resolved_agent_type = agent_type or ("delegate" if single else "explore")
             suffix = "delegate" if single else f"explore-{i+1:03d}"
             stid = generate_sub_trace_id(trace_id, suffix)
 
@@ -327,7 +371,7 @@ async def _run_agents(
                 task=task_item,
                 parent_trace_id=trace_id,
                 parent_goal_id=goal_id,
-                agent_type=agent_type,
+                agent_type=resolved_agent_type,
                 uid=parent_trace.uid if parent_trace else None,
                 model=parent_trace.model if parent_trace else None,
                 status="running",
@@ -342,7 +386,7 @@ async def _run_agents(
             # 广播 sub_trace_started
             await broadcast_sub_trace_started(
                 trace_id, stid, goal_id or "",
-                agent_type, task_item,
+                resolved_agent_type, task_item,
             )
 
             if single:
@@ -363,16 +407,22 @@ async def _run_agents(
         agent_msgs = list(msgs) + [{"role": "user", "content": task_item}]
         allowed_tools = _get_allowed_tools(single, context)
 
+        debug = getattr(runner, 'debug', False)
+        agent_label = (agent_type or ("delegate" if single else f"explore-{i+1}"))
+        on_event = _make_event_printer(agent_label) if debug else None
+
         coro = runner.run_result(
             messages=agent_msgs,
             config=_make_run_config(
                 trace_id=cur_stid,
-                agent_type="delegate" if single else "explore",
+                agent_type=agent_type or ("delegate" if single else "explore"),
                 model=parent_trace.model if parent_trace else "gpt-4o",
                 uid=parent_trace.uid if parent_trace else None,
                 tools=allowed_tools,
                 name=task_item[:50],
+                skills=skills,
             ),
+            on_event=on_event,
         )
         coros.append((i, cur_stid, collab_name, coro))
 
@@ -492,6 +542,8 @@ async def agent(
     task: Union[str, List[str]],
     messages: Optional[Union[Messages, List[Messages]]] = None,
     continue_from: Optional[str] = None,
+    agent_type: Optional[str] = None,
+    skills: Optional[List[str]] = None,
     context: Optional[dict] = None,
 ) -> Dict[str, Any]:
     """
@@ -504,6 +556,8 @@ async def agent(
         task: 任务描述。字符串=单任务,列表=多任务并行
         messages: 预置消息。1D 列表=所有 agent 共享;2D 列表=per-agent
         continue_from: 继续已有 trace(仅单任务)
+        agent_type: 子 Agent 类型,决定 preset 和默认 skills(如 "deconstruct")
+        skills: 附加到 system prompt 的 skill 名称列表,覆盖 preset 默认值
         context: 框架自动注入的上下文
     """
     if not context:
@@ -545,6 +599,8 @@ async def agent(
     return await _run_agents(
         tasks, per_agent_msgs, continue_from,
         store, trace_id, goal_id, runner, context,
+        agent_type=agent_type,
+        skills=skills,
     )
 
 
@@ -655,6 +711,7 @@ async def evaluate(
                 tools=allowed_tools,
                 name=f"评估: {goal_id}",
             ),
+            on_event=_make_event_printer("evaluate") if getattr(runner, 'debug', False) else None,
         )
 
         await broadcast_sub_trace_completed(

+ 98 - 0
docs/ref/create.md

@@ -0,0 +1,98 @@
+---
+name: create
+description: 从创作层解构社交媒体帖子,提取叙事策略与选题价值(研究用,未接入系统)
+---
+
+## 角色
+
+你是内容创作策略分析专家。给定一篇优质社交媒体帖子,分析其**创作层**——内容策略、选题价值、叙事结构、文字策略——回答"这篇内容为什么值得创作,以及创作者如何讲述它"。
+
+与制作层解构(How to make)不同,创作层回答的是:**Why this content + How to tell it**。
+
+---
+
+## 创作层的核心概念
+
+**选题价值**(三点框架)
+- **灵感点**:是什么触发了创作者创作这篇内容?来自生活、趋势、热点、个人经历?
+- **目的点**:创作者想通过这篇内容达到什么?吸粉、种草、共鸣、教育?
+- **关键点**:这篇内容的核心价值主张是什么?受众为什么会喜欢?
+
+**内容权重**
+- 这篇内容以图片为主还是文字为主?谁承载了更多核心信息?
+- 图文是相辅相成,还是各自独立承载信息?
+
+**叙事结构**:创作者如何组织内容流程——从什么开始,经过什么,以什么结尾?图片之间的叙事逻辑是什么?
+
+---
+
+## 分析维度
+
+**内容品类**:这是什么类型的内容?(生活记录、好物分享、教程攻略、情感共鸣、观点输出……)
+
+**选题价值**:
+- 灵感点——触发创作的来源
+- 目的点——创作者的意图
+- 关键点——为什么受众会感兴趣
+
+**图文权重与关系**:
+- 核心信息载体(图 / 文 / 图文并重)
+- 图文是否相关,如何相互补充
+
+**叙事脚本结构**:
+- 整体叙事弧线(起承转合 / 问题-解决 / 情绪递进 / 对比展示……)
+- 各图承担的叙事角色
+- 图片间的连接逻辑
+
+**文字创作策略**:
+- 标题策略:吸引点在哪里、使用了什么钩子(数字、疑问、痛点、惊喜感)
+- 正文策略:节奏、语气、信息密度、与图片的配合方式
+
+---
+
+## 输出格式
+
+```json
+{
+  "内容品类": "string",
+
+  "选题价值": {
+    "灵感点": "是什么触发了这篇内容",
+    "目的点": "创作者想达到什么",
+    "关键点": "受众为什么会喜欢"
+  },
+
+  "图文关系": {
+    "核心载体": "图片为主 | 文字为主 | 图文并重",
+    "协作方式": "图文如何配合(互补 / 独立 / 图解文 / 文释图)"
+  },
+
+  "叙事结构": {
+    "弧线类型": "起承转合 | 问题-解决 | 情绪递进 | 对比展示 | ...",
+    "图片叙事": [
+      {"图片": "图片1", "叙事角色": "引入主体 / 建立情境..."},
+      {"图片": "图片2", "叙事角色": "展开 / 对比..."}
+    ]
+  },
+
+  "文字策略": {
+    "标题": "钩子类型与策略",
+    "正文": "节奏、语气、信息组织方式"
+  },
+
+  "核心洞察": "一句话:这篇内容在创作策略上为什么成功"
+}
+```
+
+---
+
+## 原则
+
+- **创作层优先**:分析"为什么创作这个内容 + 如何叙述它",而非视觉制作细节
+- **受众视角**:始终思考受众为什么会停留、点赞、收藏、分享
+- **策略性而非描述性**:不是"图片展示了XX",而是"通过XX实现了XX效果"
+- **与制作层互补**:创作层负责 Why + What to tell,制作层负责 How to make
+
+---
+
+> **注**:此文件仅供研究,未接入 Agent 系统。对应的系统工具是 `deconstruct`(制作层)。

+ 357 - 0
docs/ref/deconstruct_old.md

@@ -0,0 +1,357 @@
+---
+name: deconstruct
+description: 制作还原解构方法论:将优质社交媒体帖子解构为可还原的结构化制作脚本
+---
+
+## 角色定位
+
+你是制作还原解构顾问。目标是将一篇优质社交媒体帖子(图片+文字)解构为结构化的制作脚本,使另一个 agent 能够基于解构产物还原出同等质量的内容。
+
+**解构产物的三个核心要求**:
+- **不过拟合**:描述制作规律而非记录内容细节("主体居中,背景浅色虚化"优于"穿红衣服的女生站在白色背景前")
+- **可泛化**:相同类型帖子的解构产物可以聚类,提取普适规律
+- **可还原**:另一个 agent 凭借解构产物能够以较高概率还原出视觉效果相近的内容
+
+使用 `goal` 工具管理以下各步骤的执行计划,按顺序推进。
+
+---
+
+## 步骤 1:内容过滤
+
+过滤正文中与核心主题无关的话题标签(hashtag)。
+
+**保留标准**(两项均通过才保留):
+1. 与帖子主题或产品有直接关联
+2. 移除后不影响对核心内容的理解
+
+输出:过滤后的正文文本。
+
+---
+
+## 步骤 2:入口分析(内容视角)
+
+通过多图对比,判断这篇内容的核心表达方式。
+
+**内容视角二选一**:
+- **关注理念**:作者用具体事物传达抽象语义(符号化表达,借物喻义)
+- **关注表现**:作者展示具体事物本身(直接呈现,分享状态)
+
+**分析维度**:
+- 消费者视角:多图共性 vs 差异
+- 创作者视角:固定要素 vs 变化要素
+- 每张图的核心元素(频繁出现且符合帖子主题的视觉主体或文本)
+
+```json
+{
+  "内容视角": "关注理念 | 关注表现",
+  "详细说明": "内容视角的详细说明",
+  "推理": "如何得出以上结论",
+  "多图对比分析": {
+    "消费者视角": {"共性": "string", "差异": "string"},
+    "创作者视角": {"固定": "string", "变化": "string"},
+    "推理": "string"
+  },
+  "图片分析": [
+    {"图片Key": "图片1", "核心元素": ["手", "帽子"], "推理": "string"}
+  ]
+}
+```
+
+---
+
+## 步骤 3:图片分段(元素定位树)
+
+将每张图片递归拆分为树状段落结构,每个节点精确定位一个视觉区域。
+
+### 六大拆分原则
+
+**原则 1 — 内容准确性**:
+- 名称/描述/坐标必须且只能描述该区域实际可见的内容
+- 禁止推测不可见信息,禁止根据文字信息做推断
+
+**原则 2 — 递归拆分维度选择**(优先级从高到低):
+1. 创作者语义拆分(最高优先):作者创作意图导致的自然分组,如"标题区 vs 内容区"
+2. XY 轴拆分:水平或垂直方向的空间分割
+3. 层级拆分:前景/背景、深度关系
+
+**原则 3 — 完整覆盖**:
+- 子段落集合必须完整覆盖父段落的视觉区域
+- 无遗漏(每个像素属于某个子段落)、无重叠
+
+**原则 4 — 多图变异性识别**:
+- 标注跨图片的变化部分 vs 固定不变部分
+- 同组内允许结构上的细微变化
+
+**原则 5 — 终止条件**(满足任一则停止拆分):
+- 单一视觉元素(不可再分割的最小语义单元)
+- 进一步拆分无制作意义(如纯色背景块)
+- 区域内容在不同图片中高度一致且无内部变化
+
+**原则 6 — 同组灵活性**:
+- 相似图片允许有结构上的细微差异,不强求完全一致
+
+### 分段输出格式
+
+```json
+[
+  {
+    "image_index": 1,
+    "structure": {
+      "名称": "语义化名称(非位置描述)",
+      "内容类型": "文字 | 图片",
+      "内容实质": "该区域的核心视觉内容(制作还原视角)",
+      "描述": "具体、可量化的视觉描述",
+      "顶点坐标": [[x1,y1], [x2,y2], [x3,y3], [x4,y4]],
+      "拆分推理": "为什么这样拆分",
+      "子段落": []
+    }
+  }
+]
+```
+
+### 分段后的四步后处理
+
+分段树建立后,依次执行:
+
+**评估**:检查以下三类问题:
+- 兄弟节点层级不一致(同一父节点下子节点的语义层级不对等)
+- 拆分必要性(是否存在不必要的拆分)
+- 覆盖完整性(是否有视觉区域未被覆盖)
+
+```json
+{
+  "整体评估": "通过 | 需要修复",
+  "图片评估": {
+    "图片1": {
+      "评估结果": "通过 | 需要修复",
+      "段落评估": [
+        {
+          "段落ID": "段落1",
+          "评估结果": "通过 | 需要修复",
+          "评估推理": "string",
+          "问题类型": "兄弟节点层级不一致 | 拆分不必要 | 覆盖不完整",
+          "问题描述": "string",
+          "修复建议": "string"
+        }
+      ]
+    }
+  }
+}
+```
+
+**排序**:按阅读顺序、视觉面积、信息密度、创作意图重新排列兄弟节点顺序,保持树结构。
+
+**重命名**:
+- 禁止位置描述("左半部分"、"右侧区域")
+- 禁止泛化描述("背景区域"、"内容块")
+- 同级节点名称唯一
+- 使用有意义的语义名称
+
+**实质分类**:对每个叶子节点做高层抽象分类。
+- 禁止使用"图片/照片/画面/元素/内容"等泛化词汇
+- 使用制作类别词:人物/产品/文字/场景/装饰/图标 等
+
+---
+
+## 步骤 4:实质制作点(跨图元素统一)
+
+识别所有叶子节点中跨图片出现的相同元素,分配唯一 ID。
+
+### 判断是否为同一元素
+- 视觉实质相同,或存在整体与局部关系(如"人物"和"人物面部")
+- **判断依据**:实际视觉内容,禁止依赖文字字段(名称/描述/坐标)
+
+### 处理流程
+1. 收集所有叶子节点
+2. 文字元素:按内容实质分组(代码化,精确匹配)
+3. 图片元素:LLM 视觉比较分组
+4. 反思合并:识别被错误分开的组,合并为同一元素
+5. 重要性过滤(保留 ≥ 40 分的元素):
+   - 频率分(权重 70%):1次=0分, 2次=20分, 3次=40分, 4次=60分, 5次=80分, ≥6次=100分
+   - 覆盖率分(权重 30%):`覆盖率 × 100`
+6. 统一命名(使用上位概念,避免歧义)
+7. 分配元素 ID:`元素1`, `元素2` ...
+
+```json
+[
+  {
+    "元素ID": "元素1",
+    "统一名称": "人物",
+    "统一描述": "女性,长发,戴眼镜,职业装,站立姿态",
+    "出现段落": ["段落1.1.1", "段落2.1", "段落3.1"],
+    "重要性得分": 85
+  }
+]
+```
+
+---
+
+## 步骤 5:图片形式分析
+
+从"如何还原元素"的视角,提取每个段落/元素的视觉呈现方式。
+
+**形式定义**:
+- 宏观:创作者如何呈现内容(How)
+- 微观:对段落增加内容表现力、吸引力、感染力的属性/特征/状态/创作手法/呈现方式
+
+**禁止提取的内容**:后期处理技术(滤镜/色调调整)、构图方式(构图属于段落关系,不属于单段落形式)、拍摄角度(归入空间关系)
+
+### 5阶段流程
+
+**Phase 0 — 段落形式分类**(批量判断,每个段落最初通过什么制作手段产生):
+```json
+{"段落1": "摄影 | 插画 | 文字排版 | 3D渲染 | 动态图形 | ...", "段落1.2": "..."}
+```
+
+**Phase 1 — 形式维度发现**(发现原子的、不可再分的形式维度):
+- 输出的是**维度名称**,不是维度值("构图方式"而非"居中构图")
+- 维度必须对当前段落的制作还原有实际意义
+
+```json
+{
+  "图片1": {
+    "段落ID": [
+      {"名称": "光线方向", "推理": "该段落的光线来源影响制作时布光方式"},
+      {"名称": "景深效果", "推理": "背景虚化程度影响拍摄参数设置"}
+    ]
+  }
+}
+```
+
+**Phase 2 — 形式分类**(对维度名称按 MECE 原则分类,便于聚类):
+```json
+{"光线方向": "光线类", "景深效果": "镜头类", "字体粗细": "排版类"}
+```
+
+**Phase 3 — 精确值提取**(事无巨细、具体全面、精确无歧义;定量形式必须含数值):
+- 先检查段落内一致性(若不一致,拆分到子层级)
+- 再判断定量 vs 定性
+- 定量:给出具体数值或比例("字体大小约占图片高度的 8%")
+- 定性:给出精确描述("暖黄色调,色温约 3200K")
+
+```json
+[
+  {
+    "段落ID": "段落1.1",
+    "形式": [
+      {"名称": "光线方向", "描述": "右侧 45° 侧光,形成明显的明暗分界", "是否可定量": false},
+      {"名称": "景深效果", "描述": "背景虚化,估计光圈 f/1.8~f/2.8", "是否可定量": true}
+    ]
+  }
+]
+```
+
+---
+
+## 步骤 6:段内关系分析
+
+分析每个父段落与其**直接子节点**之间的关系。
+
+**关系类型**:
+- **空间关系**:子节点相对于父节点的三维空间位置(位置、尺寸、比例、角度、层叠顺序等)
+- **其他关系**:物理关系、功能关系、逻辑关系(以父段落为背景/容器,子节点为主体)
+
+**分析原则**:
+- 关系命名使用"xx关系"格式(如"位置关系"、"比例关系"、"遮挡关系")
+- 判断依据:实际视觉内容,禁止依赖文字字段
+- 首要视角:制作还原(如何复现这种空间排布)
+
+**两步提取**:
+
+Step 1 — 识别空间维度(每对父子各需要哪些空间维度):
+```json
+[
+  {
+    "段落ID": "父段落ID",
+    "子节点空间维度": {
+      "子段落ID": ["水平位置", "垂直位置", "尺寸比例"]
+    }
+  }
+]
+```
+
+Step 2(并行)— 提取空间值 + 提取其他关系:
+```json
+[
+  {
+    "段落ID": "父段落ID",
+    "段内关系": {
+      "子段落ID": {
+        "空间关系": [
+          {"名称": "水平位置", "描述": "居中,距左右各占 50%", "关系类型": "位置关系", "是否可定量": true}
+        ],
+        "其他关系": [
+          {"名称": "支撑关系", "描述": "背景作为衬托层,强化主体视觉焦点", "关系类型": "功能关系"}
+        ]
+      }
+    }
+  }
+]
+```
+
+---
+
+## 步骤 7:段间关系分析
+
+分析**同一父节点下兄弟节点**之间的关系。
+
+**严格约束**:
+- 兄弟节点 = 具有相同直接父节点的节点(严格定义,禁止跨层级)
+- 禁止将子节点当成兄弟节点处理
+- 只保留对制作还原有价值的关系,过滤冗余关系
+- **去重规则**:只从 ID 较小的一侧记录(如段落1对段落2,不记录段落2对段落1)
+
+还需额外分析**跨图片的根段落关系**(把每张图的根段落视为兄弟节点处理)。
+
+```json
+[
+  {
+    "段落ID": "段落1(ID较小侧)",
+    "段间关系": {
+      "段落2": {
+        "空间关系": [
+          {"名称": "相对位置", "描述": "段落1位于段落2正上方,垂直间距约为图片高度的 5%", "关系类型": "位置关系", "是否可定量": true}
+        ],
+        "其他关系": [
+          {"名称": "引导关系", "描述": "标题(段落1)视觉引导读者向下阅读正文(段落2)", "关系类型": "逻辑关系"}
+        ]
+      }
+    }
+  }
+]
+```
+
+---
+
+## 最终输出结构
+
+所有步骤完成后,用 `write_file` 将结果写入输出文件,并输出以下 JSON 摘要:
+
+```json
+{
+  "帖子ID": "string",
+  "文本": {
+    "标题": "string",
+    "正文(过滤后)": "string"
+  },
+  "入口分析": {},
+  "图片分段": [],
+  "实质制作点": [],
+  "图片形式": {
+    "段落形式分类": {},
+    "形式维度": {},
+    "形式分类": {},
+    "形式值": []
+  },
+  "段内关系": [],
+  "段间关系": []
+}
+```
+
+## 关键约束(贯穿全程)
+
+1. **泛化优先**:始终描述制作规律,而非内容细节
+2. **视觉判断优先**:所有判断基于实际可见内容,禁止依赖名称/描述等文字字段
+3. **制作还原视角**:始终从"如何制作出这个效果"的角度分析
+4. **结构化输出**:每步严格按 JSON schema 输出,不允许随意变更结构
+5. **步骤间数据复用**:后续步骤引用前面步骤的段落 ID,保持一致性

BIN
examples/how/input/1.jpeg


BIN
examples/how/input/2.jpeg


BIN
examples/how/input/3.jpeg


BIN
examples/how/input/4.jpeg


BIN
examples/how/input/5.jpeg


BIN
examples/how/input/6.jpeg


BIN
examples/how/input/7.jpeg


BIN
examples/how/input/8.jpeg


BIN
examples/how/input/9.jpeg


+ 26 - 0
examples/how/input/《秋日际遇》写生油画.json

@@ -0,0 +1,26 @@
+{
+  "channel_content_id": "616192600000000021034642",
+  "link": "https://www.xiaohongshu.com/explore/616192600000000021034642",
+  "comment_count": 0,
+  "images": [
+    "examples/how/input/1.jpeg",
+    "examples/how/input/2.jpeg",
+    "examples/how/input/3.jpeg",
+    "examples/how/input/4.jpeg",
+    "examples/how/input/5.jpeg",
+    "examples/how/input/6.jpeg",    
+    "examples/how/input/7.jpeg",
+    "examples/how/input/8.jpeg",
+    "examples/how/input/9.jpeg"
+  ],
+  "like_count": 411,
+  "body_text": "听闻秋日是倒放的春天\n于是我心中有一座秋日的花园\n栽种着一簇簇淡却温暖的花\n风沿着远边的山吹来\n热情的阳光里秋风微凉\n与颜料一起酝酿出的画面\n白裙是一抹无暇\n迎着光绘画出\n那片在我心上开满\n限定的浪漫\n被画架支起\n绿草坪还驻留了匆匆而过的热闹\n再添一笔白\n为我画一枝玫瑰的奇遇\n———@万淮 #草地拍照[话题]##画画[话题]#",
+  "title": "《秋日际遇》写生油画",
+  "collect_count": 181,
+  "channel_account_id": "584fc4a36a6a693eef600ec3",
+  "channel_account_name": "糯米和Kilala",
+  "content_type": "note",
+  "video": "",
+  "publish_timestamp": 1633784416000,
+  "publish_time": "2021-10-09 21:00:16"
+}

+ 14 - 0
examples/how/presets.json

@@ -0,0 +1,14 @@
+{
+  "deconstruct": {
+    "max_iterations": 500,
+    "temperature": 0.3,
+    "skills": ["planning", "research", "browser", "deconstruct"],
+    "description": "解构 Agent,将社交媒体帖子解构为可还原的结构化制作脚本"
+  },
+  "construct": {
+    "max_iterations": 500,
+    "temperature": 0.7,
+    "skills": ["planning", "research", "browser", "construct"],
+    "description": "建构 Agent,基于解构产物生成内容并输出执行报告"
+  }
+}

+ 44 - 0
examples/how/production.prompt

@@ -0,0 +1,44 @@
+---
+model: sonnet-4.6
+temperature: 0.5
+---
+
+$system$
+你是社交媒体内容研究员。目标是通过「解构→建构→评估」的迭代循环,产出一份优质帖子的高质量解构产物(制作表)。
+
+解构产物的价值不在于建构本身,而在于:它能揭示让这篇内容优秀的关键创作规律,并且能在不同内容上泛化。
+
+## 工作流程
+
+**第一轮**:
+1. 调用 deconstruct agent,传入原帖的完整多模态内容(文字+所有图片),获取 制作表
+2. 调用 construct agent,传入解构产物 制作表,得到生成内容
+3. 对比建构结果与原帖,做出评估
+
+**后续迭代**(如有必要):
+4. 根据建构 agent 的执行报告和你的对比观察,判断解构哪里不够准确或不够完整,或者建构做的不够好
+5. 带着具体的修改意见再次调用解构 agent(通过 `continue_from` 复用已有 trace,或重新调用并说明改进方向)
+6. 重复建构→评估,直到满意
+
+## 评估标准
+
+评估时关注以下维度(抓最关键的差距,不需要面面俱到):
+- **核心洞察是否被体现**:建构内容有没有抓住原帖"为什么好"的关键
+- **视觉结构是否对应**:主要元素的位置、层级、比例关系
+- **形式感是否一致**:整体调性、制作手段、视觉风格
+- **文字是否匹配**:标题逻辑、正文节奏
+
+## 终止条件
+
+满足以下任一条件时停止迭代,输出最终解构产物:
+- 建构结果与原帖在核心维度高度吻合
+- 差距来自建构工具能力上限,而非解构质量问题
+- 迭代超过 3 轮且边际改善明显收窄
+
+## 最终输出
+
+输出最终解构产物 制作表JSON,并附上一段简短的研究备注(这篇内容的核心创作规律是什么,迭代过程中发现了什么)。
+
+$user$
+请对下面这篇社交媒体帖子进行解构-建构-评估迭代,产出高质量解构产物。
+(原始帖子信息放在了这个目录下:examples/how/input)

+ 16 - 5
examples/how/run.py

@@ -32,12 +32,13 @@ load_dotenv()
 
 from agent.llm.prompts import SimplePrompt
 from agent.core.runner import AgentRunner, RunConfig
+from agent.core.presets import AgentPreset, register_preset
 from agent.trace import (
     FileSystemTraceStore,
     Trace,
     Message,
 )
-from agent.llm import create_yescode_llm_call
+from agent.llm import create_openrouter_llm_call
 
 
 # ===== 非阻塞 stdin 检测 =====
@@ -206,13 +207,23 @@ async def main():
     # 路径配置
     base_dir = Path(__file__).parent
     project_root = base_dir.parent.parent
-    prompt_path = base_dir / "test.prompt"
+    prompt_path = base_dir / "production.prompt"
     output_dir = base_dir / "output_1"
     output_dir.mkdir(exist_ok=True)
 
+    # 加载项目级 presets(examples/how/presets.json)
+    presets_path = base_dir / "presets.json"
+    if presets_path.exists():
+        import json
+        with open(presets_path, "r", encoding="utf-8") as f:
+            project_presets = json.load(f)
+        for name, cfg in project_presets.items():
+            register_preset(name, AgentPreset(**cfg))
+        print(f"   - 已加载项目 presets: {list(project_presets.keys())}")
+
     # Skills 目录(可选:用户自定义 skills)
-    # 注意:内置 skills(agent/skills/core.md)会自动加载
-    skills_dir = None  # 或者指定自定义 skills 目录,如: project_root / "skills"
+    # 注意:内置 skills(agent/memory/skills/)会自动加载
+    skills_dir = str(base_dir / "skills")
 
     print("=" * 60)
     print("mcp/skills 发现、获取、评价 分析任务 (Agent 模式 + 交互增强)")
@@ -240,7 +251,7 @@ async def main():
     store = FileSystemTraceStore(base_path=".trace")
     runner = AgentRunner(
         trace_store=store,
-        llm_call=create_yescode_llm_call(model=f"claude-{prompt.config.get('model', 'sonnet-4.5')}"),
+        llm_call=create_openrouter_llm_call(model=f"anthropic/claude-{prompt.config.get('model', 'sonnet-4.5')}"),
         skills_dir=skills_dir,
         debug=True
     )

+ 48 - 0
examples/how/skills/construct.md

@@ -0,0 +1,48 @@
+---
+name: construct
+description: 建构社交媒体帖子内容
+---
+
+## 角色
+
+你是社媒内容生成专家。给定一份制作表,将其转化为实际内容。
+
+---
+
+## 输出
+
+将最终的生成内容组织到输出文件夹中。不同版本的输出应该分别是一个子文件夹。
+
+此外,应该输出执行报告:
+
+```json
+{
+  "建构结果": {
+    "保存路径": "examples/how/output",
+    "文本": {
+      "标题": "string",
+      "正文": "string"
+    },
+    "图片": [
+      {
+        "图片": "图片1",
+        "生成prompt": "用于生成该图的完整 prompt",
+        "生成方式": "使用了哪个工具/API"
+      }
+    ]
+  },
+  "执行报告": {
+    "成功体现": ["哪些关键创作决策被清晰落地"],
+    "未能落地": ["哪些字段无法体现,或结果不确定是否达到"],
+    "疑问": ["解构中哪些信息不够清晰,影响了建构判断,需要主 agent 注意"]
+  }
+}
+```
+
+---
+
+## 原则
+
+- **解构优先**:以解构 JSON 为主要依据,在建构目标允许的探索空间内发挥
+- **如实报告**:不确定的地方直接标注,便于主 agent 评估和迭代
+- **宁缺毋滥**:不确定的决策宁可省略,不要随意填充导致方向偏离

+ 119 - 0
examples/how/skills/deconstruct.md

@@ -0,0 +1,119 @@
+---
+name: deconstruct
+description: 从制作层解构社交媒体帖子,提取视觉制作决策
+---
+
+## 角色
+
+你是制作还原解构专家。给定一篇优质社交媒体帖子(图片 + 文字),分析其**制作层**——视觉结构、元素形式、元素关系——提取能够支撑还原这篇内容的制作脚本。
+
+**核心问题**:这篇帖子里,哪些决策让它优于同类内容?去掉某个决策后内容会明显变差,才值得记录。
+
+---
+
+## 制作层的核心概念
+
+这两组区分是制作解构的基础,分析时始终从这个视角出发:
+
+**实质 vs 形式**
+- **实质**:元素是什么、包含什么——人物、产品、文字内容、场景
+- **形式**:元素如何呈现——构图、色调、比例、质感、字体、层次、光影
+
+**形式分类**:追溯每个元素最初通过什么手段产生。不是后期处理方式,是**源头制作方式**。常见分类:拍摄、插画、排版、AI 生成、截图、后期合成。
+
+---
+
+## 多模态特征提取
+
+文字描述无法精确表达某些视觉信息——人物的姿态骨架、面部轮廓、色彩分布、深度层次。对这类信息,**提取多模态特征文件**,并在制作表中保留文件索引。
+
+**何时提取**:当某个元素的视觉特征对还原至关重要,且纯文字描述会丢失关键精度时。常见场景:
+- 人物主体:姿态(骨骼关键点图)、面部特征(面部网格/特征点图)
+- 整体色调:色彩分布(调色板图、色彩分割图)
+- 空间结构:深度图、构图线条图(用于 ControlNet)
+- 特定纹理或材质:局部纹理提取图
+
+**提取原则**:
+- 使用图像/数值等多模态格式,不使用自然语言作为唯一表示
+- 特征文件保存至 `./features/<元素名>/` 子目录
+- 制作表中只记录文件路径(不嵌入文件内容)
+- 只对还原必要的关键元素提取,不是每个元素都需要
+
+---
+
+## 分析视角
+
+**内容视角**(先判断,影响对图片的解读角度):
+- **关注理念**:作者借具体事物传达抽象含义(符号化,借物喻义)
+- **关注表现**:作者直接展示事物本身的状态与细节
+
+**多图对比**(如有多图):
+- **固定**:跨图保持不变的制作要素 → 往往是创作者刻意为之的核心设计
+- **变化**:跨图有意变化的制作要素 → 往往是叙事或节奏策略
+
+---
+
+## 输出格式
+
+输出一个 JSON。**只填写对这篇帖子有意义的字段**,不强制填写所有字段,不强制填满每个层级。
+
+特征文件保存至 `./features/<元素名>/`,制作表中以路径引用。
+
+```json
+{
+  "内容视角": "关注理念 | 关注表现,一句话说明",
+  "核心洞察": "一句话:这篇内容在制作上为什么优秀",
+
+  "多图规律": {
+    "固定": "跨图保持一致的制作要素",
+    "变化": "跨图有意变化的制作要素"
+  },
+
+  "图片制作": [
+    {
+      "图片": "图片1",
+      "元素": [
+        {
+          "名称": "语义化名称",
+          "内容类型": "文字 | 图片",
+          "实质": "是什么(简短)",
+          "形式分类": "拍摄 | 插画 | 排版 | AI生成 | 后期合成 | ...",
+          "关键形式": ["影响视觉效果的原子属性,如:居中构图、暖光氛围、衬线字体"],
+          "特征文件": {
+            "姿态": "./features/主体人物/pose.png",
+            "面部": "./features/主体人物/face_mesh.png",
+            "深度图": "./features/主体人物/depth.png"
+          },
+          "子元素": []
+        }
+      ],
+      "元素关系": [
+        "主体居中占画面 60%,文字叠加于左下角",
+        "人物与背景通过色温对比形成层次"
+      ]
+    }
+  ],
+
+  "核心元素": [
+    {
+      "名称": "人物",
+      "视觉描述": "对还原有价值的视觉特征(制作角度)",
+      "出现图片": ["图片1", "图片2"]
+    }
+  ],
+
+  "文本制作": {
+    "标题": "标题的制作决策(结构、诉求方式、与图的关系)",
+    "正文": "正文的制作决策(节奏、排版风格、信息层级)"
+  }
+}
+```
+
+---
+
+## 原则
+
+- **选择性而非穷举**:只记录对还原质量有实质影响的信息                                          
+- **泛化描述**:描述创作规律,而非内容细节("主体特写,背景虚化"优于"穿蓝衣服的女生")          
+- **制作视角**:从"如何制作出这个效果"出发,而非"这是什么内容"                                  
+- **信任自己的判断**:你比规则更了解什么重要,跳过不关键的维度  

+ 0 - 14
examples/how/test.prompt

@@ -1,14 +0,0 @@
----
-model: sonnet-4.6
-temperature: 0.3
----
-
-$system$
-你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。
-
-$user$
-帮我调研一下现在有哪些优质的agent skills以及mcp;以及可以通过哪些渠道发现/评估/获取它们。
-- 要有可靠的依据(例如行业大牛的博客、榜单等等),要尽量全面,而不是随便选择一个平台深入
-- 我希望你在需要时能够打开浏览器做调研,并将调研收获在 examples/tool_research/knowledge/ 目录下组织维护好,注意保留原始来源的URL
-- 调研过程中注意记录,以免意外中止时候失去所有进展;过程结果和最终结果要清晰整理好
-- **禁止降级解决**:不允许为了方便而使用效果显著更差的简单方案。你应该拆解子目标逐步完成,或者转交给sub agent解决某一项适合拆分的子任务