Kaynağa Gözat

how agent update

liuzhiheng 1 hafta önce
ebeveyn
işleme
ab2354d009

+ 62 - 0
examples_how/overall_derivation/extract_post_topic.py

@@ -0,0 +1,62 @@
+"""
+从帖子解构内容中提取选题点(灵感点/目的点/关键点 下 分词结果 中的 词),去重后输出。
+"""
+import json
+import argparse
+from pathlib import Path
+
+
+def extract_post_topic(account_name: str, post_id: str) -> list[str]:
+    """
+    从解构内容中提取选题点并去重。
+
+    :param account_name: 账号名
+    :param post_id: 帖子ID
+    :return: 去重后的选题点字符串列表
+    """
+    base = Path(__file__).resolve().parent
+    input_path = base / "input" / account_name / "原始数据" / "解构内容" / f"{post_id}.json"
+
+    with open(input_path, "r", encoding="utf-8") as f:
+        data = json.load(f)
+
+    topics: list[str] = []
+    for key in ("灵感点", "目的点", "关键点"):
+        for item in data.get(key, []):
+            for seg in item.get("分词结果", []):
+                word = seg.get("词")
+                if word and isinstance(word, str) and word.strip():
+                    topics.append(word.strip())
+
+    # 去重且保持首次出现顺序
+    seen = set()
+    unique_topics: list[str] = []
+    for w in topics:
+        if w not in seen:
+            seen.add(w)
+            unique_topics.append(w)
+
+    return unique_topics
+
+
+def main():
+    parser = argparse.ArgumentParser(description="从解构内容中提取选题点")
+    parser.add_argument("account_name", help="账号名")
+    parser.add_argument("post_id", help="帖子ID")
+    args = parser.parse_args()
+
+    topics = extract_post_topic(args.account_name, args.post_id)
+
+    base = Path(__file__).resolve().parent
+    out_dir = base / "input" / args.account_name / "post_topic"
+    out_dir.mkdir(parents=True, exist_ok=True)
+    out_path = out_dir / f"{args.post_id}.json"
+
+    with open(out_path, "w", encoding="utf-8") as f:
+        json.dump(topics, f, ensure_ascii=False, indent=2)
+
+    print(f"已写入 {len(topics)} 个选题点到 {out_path}")
+
+
+if __name__ == "__main__":
+    main()

+ 2 - 2
examples_how/overall_derivation/generate_visualize_data.py

@@ -264,7 +264,7 @@ def generate_visualize_data(account_name: str, post_id: str, log_id: str, base_d
     """
     if base_dir is None:
         base_dir = Path(__file__).resolve().parent
-    input_dir = base_dir / "input" / account_name / "解构内容"
+    input_dir = base_dir / "input" / account_name / "原始数据" / "解构内容"
     log_dir = base_dir / "output" / account_name / "推导日志" / post_id / log_id
     result_dir = base_dir / "output" / account_name / "整体推导结果"
     visualize_dir = base_dir / "output" / account_name / "整体推导路径可视化"
@@ -306,5 +306,5 @@ def main(account_name, post_id, log_id):
 if __name__ == "__main__":
     account_name="家有大志"
     post_id = "68fb6a5c000000000302e5de"
-    log_id="20260303221927"
+    log_id="20260304161832"
     main(account_name, post_id, log_id)

+ 18 - 0
examples_how/overall_derivation/input/家有大志/post_topic/68fb6a5c000000000302e5de.json

@@ -0,0 +1,18 @@
+[
+  "鞋架",
+  "顶部",
+  "夸张堆叠",
+  "分享",
+  "养宠烦恼",
+  "肇事者",
+  "柴犬形象",
+  "被啃坏",
+  "拖鞋物证",
+  "补充说明式",
+  "图片文字",
+  "递进式",
+  "叙事结构",
+  "幽默化标题",
+  "标题",
+  "居家生活场景"
+]

+ 17 - 0
examples_how/overall_derivation/input/家有大志/post_topic/6921937a000000001b0278d1.json

@@ -0,0 +1,17 @@
+[
+  "蟑螂饭店",
+  "分享",
+  "创意改造",
+  "蟑螂屋",
+  "日常物品",
+  "DIY改造",
+  "饭店式",
+  "促销标语",
+  "微缩场景模型",
+  "丈夫",
+  "第一人称视角",
+  "模拟",
+  "动物视角",
+  "视觉聚焦",
+  "夸张道具"
+]

+ 0 - 0
examples_how/overall_derivation/input/家有大志/解构内容/68fb6a5c000000000302e5de.json → examples_how/overall_derivation/input/家有大志/原始数据/解构内容/68fb6a5c000000000302e5de.json


+ 0 - 0
examples_how/overall_derivation/input/家有大志/解构内容/6921937a000000001b0278d1.json → examples_how/overall_derivation/input/家有大志/原始数据/解构内容/6921937a000000001b0278d1.json


+ 3 - 2
examples_how/overall_derivation/overall_derivation_agent_run.py

@@ -253,8 +253,9 @@ async def main():
 
     print("3. 创建 Agent Runner...")
     print(f"   - Skills 目录: {skills_dir}")
-    model_key = prompt.config.get("model", "gemini-3-flash-preview")
-    model_id = f"google/{model_key}" if not model_key.startswith("google/") else model_key
+    model_key = prompt.config.get("model", "google/gemini-3-flash-preview")
+    # model_id = f"google/{model_key}" if not model_key.startswith("google/") else model_key
+    model_id = model_key
     print(f"   - 模型: {model_id}")
 
     store = FileSystemTraceStore(base_path=".trace")

+ 10 - 3
examples_how/overall_derivation/production.prompt

@@ -1,5 +1,5 @@
 ---
-model: gemini-3-flash-preview
+model: anthropic/claude-sonnet-4.6
 temperature: 0.3
 ---
 
@@ -68,6 +68,7 @@ $system$
   "round": 1,
   "derivation_results": [
     {
+      "id": 1,
       "method": "推导方法名称(如:人设常量选起点、账号pattern模式复用、人设联想、信息搜索等)",
       "input": {
         "tree_nodes": ["人设节点名称1", "人设节点名称2"],
@@ -93,6 +94,8 @@ $system$
   - `derivation_results`: **表示该轮的多条推导路径**。数组中的每一项是一条推导路径;同一轮内可能有多条路径,每条路径彼此独立(方法或输入不同)。例如:本轮推导出选题点 A、B、C、D,其中 A、B、C 可能由**同一条路径**产出(结合人设节点 + pattern + 已推导选题点等一起推导),D 由**另一条路径**产出(如另一种方法或另一组输入)。因此:
     - **每条路径可有多个输入**:`input` 中的 `tree_nodes`、`patterns`、`derived_nodes` 可同时存在、共同作为该路径的输入。
     - **每条路径可有多个输出**:`output` 为该路径产出的一个或多个待评估选题点(字符串数组)。
+  - `id`: 推导路径ID,数字,从1开始
+  - `method`: 推导方法名称
   - `input.tree_nodes`: 本路径用到的人设树节点名称列表,只需要输出节点名称即可,不需要带上完整的节点路径,比如 `创意展示`,而不需要输出 `形式.内容风格.氛围特征.创意性.创意展示`
   - `input.patterns`: 本路径用到的 pattern 选题点拼接列表(与 processed_edge_data.json 中 `i` 格式一致,如 `"名称1+名称2"`)
   - `input.derived_nodes`: 本路径用到的已推导成功选题点名称列表
@@ -100,6 +103,8 @@ $system$
   - `reason`: 必须详细、可追溯,禁止牵强或凭空联想
   - `tools`: 本路径使用的工具列表;若使用搜索工具,必须包含 `query`、`result`(数据摘要或关键内容)以及 `raw_result`(搜索工具返回的原始数据)
 
+**注意**: 每条推导路径遵循最小输入输出原子化规则:即用最少输入数据可以推导出哪些必要的选题点;从数据上看,每一条推导路径中的所有输入都是输出每个选题点的必要输入数据;逻辑上可以分开推导路径不要混在一起。
+
 ### 2. 评估日志(每轮一份)
 - **路径**: `output/家有大志/推导日志/{帖子ID}/%log_id%/{轮次}_评估.json`
 - **作用**: 记录该轮评估结果与推导进度,**内容主要由调用评估子 agent 的返回结果整理得到**
@@ -109,6 +114,7 @@ $system$
   "round": 1,
   "eval_results": [
     {
+      "id": 1,
       "derivation_output_point": "本轮推导输出的待评估选题点名称",
       "match_result": "匹配 或 不匹配",
       "match_post_point": "若匹配,则为帖子解构中匹配到的选题点(分词结果.词);若不匹配则省略或为空"
@@ -124,6 +130,7 @@ $system$
 - **说明**:
   - `round`: 当前轮次,与同轮推导日志一致
   - `eval_results`: 与评估子 agent 返回的「匹配结果列表」一一对应
+    - `id`: 推导路径ID
     - `derivation_output_point`: 对应子 agent 的「推导选题点名称」
     - `match_result`: 对应子 agent 的「是否匹配」,取值为 **匹配** 或 **不匹配**
     - `match_post_point`: 对应子 agent 的「匹配到的帖子选题点」(仅当匹配时填写,且必须是帖子解构中分词结果里的「词」)
@@ -138,8 +145,8 @@ $system$
 1. **推导**:以人设分类树、人设 pattern 表、**已推导成功的选题点集合**为输入(不包含帖子单帖解构内容,首轮已推导成功的选题点集合为空),使用某一下述定义的推导方法,产出**本轮推导出的可能选题点**(含推导过程数据,如边、detail 等)。
 -**注意**: 推导的总轮次数是浮动不确定的,不由推导部分决定,而由下面的第2环节验证评估来决定。每轮验证评估结束后,会通知推导环节是否要进行新一轮推导的决定。**总轮次的上限为20轮**
 2. **评估验证**:调用评估子 agent(evaluate_derivation agent)进行验证。注意:
-   - 使用内置的 `agent` 工具,传入 `agent_type="evaluate_derivation"`,在 `task` 参数中给出:1)历史已推导成功的选题点(JSON);2)本轮推导出的可能选题点(含推导过程数据);3)**帖子ID**(不传解构内容路径)
-   - 评估子 agent 会自动加载本目录下的 skill:`examples_how/overall_derivation/skills/derivation_eval.md` 作为 system prompt;它不直接接收帖子单帖解构内容路径,而是根据传入的**帖子ID**在内部读取 `input/家有大志/解构内容/{帖子ID}.json` 获取单帖解构内容
+   - 使用内置的 `agent` 工具,传入 `agent_type="evaluate_derivation"`,在 `task` 参数中给出:1)历史已推导成功的选题点(JSON);2)本轮推导出的可能选题点(含推导过程数据、推导路径ID);3)**帖子ID**。
+   - 评估子 agent 会自动加载本目录下的 skill:`examples_how/overall_derivation/skills/derivation_eval.md` 作为 system prompt;它不直接接收帖子单帖解构选题点数据,而是根据传入的**帖子ID**在内部获取单帖解构选题点数据
    - 子 agent 职责:判断本轮推导的选题点与帖子解构选题点是否语义相似或接近,返回匹配结果;判断是否还有未推导成功的选题点,若有则告知主 agent 需进行下一轮推导。
    - 主 agent 后续动作:根据子 agent 返回的「本轮匹配成功的选题点」加入已推导成功的选题点集合;未匹配的不加入;根据「是否需要进行下一轮推导」决定是否继续推导。
 3. **推导失败后改进重试及探索游走**:在评估验证阶段,若本轮推导出的可能选题点经子 agent 评估后均与帖子选题点不匹配,则需要更换使用的推导方法,或改变原方法的调用输入信息,再次执行第 1 步(推导)。在第 1~3 步中循环多次执行;若以同样输入重试次数达到 3 次,则不再以此节点进行推导。

+ 7 - 35
examples_how/overall_derivation/skills/derivation_eval.md

@@ -6,10 +6,10 @@ description: 选题点推导评估任务 - 判断推导产出的选题点与帖
 # 选题点推导评估任务
 
 ## 角色
-你是选题点匹配评估专家,负责判断「推导产出的选题点」与「帖子解构中的选题点」是否语义一致或高度接近。
+你是选题点匹配评估专家,负责判断「推导产出的选题点」与「帖子解构内容选题点」是否语义一致或高度接近。
 
 ## 任务描述
-根据**本轮推导出的可能选题点**(含推导过程数据)和**帖子单帖解构内容**,逐条判断:本轮推导的每个选题点是否对应帖子解构内容中的某个选题点。语义相似或接近即视为匹配,不要求字面完全一致。
+根据**本轮推导出的可能选题点**(含推导过程数据)和**帖子解构内容选题点**,逐条判断:本轮推导的每个选题点是否对应帖子解构内容中的某个选题点。语义相似或接近即视为匹配,不要求字面完全一致。
 
 ## 输入
 
@@ -21,36 +21,9 @@ description: 选题点推导评估任务 - 判断推导产出的选题点与帖
 
 仅基于「本轮推导出的选题点」的语义与帖子解构选题点做匹配判断,过程数据用于可解释性。
 
-### 3. 帖子单帖解构内容
-- **来源**:本评估子 agent **不直接接收**帖子单帖解构内容路径。主 agent 传入的是**帖子ID**;你需根据帖子ID 自行读取文件:`input/家有大志/解构内容/{帖子ID}.json` 获取单帖解构内容。
-- **作用**: 该 JSON 包含帖子的选题点数据(灵感点、目的点、关键点),每个点包含分词结果、实质、形式、意图等分类信息
-
-**重要:帖子中「选题点」的定义**  
-帖子解构里,**用于匹配的选题点**是**选题点对象中「分词结果」数组里每个词对象的「词」字段**,不是选题点对象顶层的「点」字段。  
-- **选题点** = `分词结果[].词`(每个这样的「词」才是一个待匹配的选题点)  
-- **点** = 该条目的原始/整段名称,仅作上下文参考,**不能**当作选题点做匹配依据。
-
-- **顶层字段**:
-  - `帖子ID`: 字符串,帖子的唯一标识
-  - `处理说明`: 字符串(可选),处理流程的说明
-- **选题点数组**:
-  - `灵感点`: 数组,每条为选题点对象;其内**分词结果**中的每个「词」才是选题点
-  - `目的点`: 同上
-  - `关键点`: 同上
-- **选题点对象结构**(每条对象下,真正的选题点来自其「分词结果」):
-  - `点`: 字符串,该条目的原始名称(**不是**选题点,不用于匹配)
-  - `点描述`: 字符串,该条目的详细描述(辅助理解)
-  - **`分词结果`**: 数组,**此处每个元素的「词」字段即一个选题点**,匹配时必须与「词」对应:
-    - **`词`**: 字符串,**这才是选题点的名称**,与推导产出的选题点做语义匹配时以「词」为准
-    - `详细描述`: 字符串,该词的详细描述(匹配时的主要语义依据)
-    - `来源`: 字符串("原文保留"、"智能重组"等)
-    - `应用原则`: 字符串("粒度控制"、"智能重组"等)
-    - `处理理由`: 字符串,处理该词的理由
-    - `归一化原词`: 字符串(可选),归一化后的原词
-  - `分词评估`: 对象(可选),包含评估结果
-  - `实质` / `形式` / `意图`: 维度分类(辅助理解)
-
-匹配时:**以「分词结果」中每个词对象的「词」和「详细描述」作为帖子选题点的语义依据**;「点」「点描述」仅作辅助理解,不可用「点」作为选题点进行匹配。
+### 3. 帖子解构内容选题点
+- **来源**:本评估子 agent **不直接接收**帖子解构内容选题点数据。主 agent 传入的是**帖子ID**;你需根据帖子ID 自行读取文件:`input/家有大志/post_topic/{帖子ID}.json` 帖子解构内容选题点数据。
+- **作用**: 该 JSON 包含帖子的选题点数据
 
 ## 输出要求
 
@@ -60,7 +33,7 @@ description: 选题点推导评估任务 - 判断推导产出的选题点与帖
    对「本轮推导出的每个选题点」给出一条记录:
    - `推导选题点名称`:本轮推导的节点 name
    - `是否匹配`:是 / 否
-   - `匹配到的帖子选题点`(若匹配):帖子解构中对应**分词结果中的「词」**(即选题点名称);不要用顶层的「点」字段作为选题点标识
+   - `匹配到的帖子选题点`(若匹配):帖子解构内容中的选题点
 
 2. **判断是否需要进行下一轮推导**
    - `帖子选题点数量`
@@ -74,7 +47,6 @@ description: 选题点推导评估任务 - 判断推导产出的选题点与帖
 - **不匹配**:含义无关、或仅弱相关不足以视为同一选题点,则判为不匹配。
 
 ## 约束
-- **帖子侧的选题点**仅指「分词结果」中每个词对象的「**词**」字段;不得用选题点对象顶层的「点」字段作为选题点进行匹配或输出。
 - 仅做匹配判断,不修改或补充帖子解构内容。
-- 不臆造帖子中不存在的选题点;若推导点无法对应到任一帖子选题点(即任一分词结果.词),则判为不匹配。
+- 不臆造帖子中不存在的选题点;若推导点无法对应到任一帖子选题点,则判为不匹配。
 - 输出格式需便于主 agent 解析(建议 JSON 或清晰的键值结构)。