浏览代码

Merge branch 'main' of https://git.yishihui.com/ai/knowledge

jihuaqiang 2 月之前
父节点
当前提交
d48ea12ae3
共有 9 个文件被更改,包括 319 次插入350 次删除
  1. 0 0
      1_fetch.py
  2. 0 0
      2_identify.py
  3. 286 0
      3_handle.py
  4. 0 0
      4_aggregate.py
  5. 0 96
      coze_client.py
  6. 0 204
      feishu_client.py
  7. 0 50
      prehandler.py
  8. 30 0
      prompt/handle.md
  9. 3 0
      utils/fei_shu.py

+ 0 - 0
1_fetch.py


+ 0 - 0
identify.py → 2_identify.py


+ 286 - 0
3_handle.py

@@ -0,0 +1,286 @@
+import os
+import json
+import time
+import sys
+import argparse
+from typing import Dict, Any, List, Optional
+from dotenv import load_dotenv
+
+# 导入自定义模块
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from utils.fei_shu import FeiShu
+from coze.coze_hook import CozeHook
+
+
+class ContentIdentifier:
+    def __init__(self, table_id: Optional[str] = None):
+        # 加载环境变量
+        load_dotenv()
+        
+        # 初始化飞书客户端
+        self.feishu = FeiShu()
+        
+        # 初始化Coze客户端
+        self.coze = CozeHook()
+        
+        # 获取表格ID:优先使用传入的参数,其次使用环境变量
+        self.table_id = table_id or os.getenv('FEISHU_TABLE_ID')
+        if not self.table_id:
+            raise ValueError("请设置环境变量 FEISHU_TABLE_ID 或在运行时传入 table_id 参数")
+        
+        # 字段名称配置
+        self.input_field = os.getenv('FEISHU_INPUT_FIELD', '抓取结果')
+        self.output_field = os.getenv('FEISHU_OUTPUT_FIELD', '识别结果')
+        
+    def extract_content_from_record(self, record) -> Dict[str, Any]:
+        """从飞书记录中提取内容"""
+        fields = record.fields
+        
+        # 提取抓取结果
+        crawl_result = fields.get(self.input_field, '')
+        title = ''
+        body_text = ''
+        image_url_list = []
+        
+        # 解析抓取结果
+        if crawl_result:
+            if isinstance(crawl_result, list) and len(crawl_result) > 0:
+                # 如果是数组格式,取第一个元素
+                crawl_data = crawl_result[0]
+                if isinstance(crawl_data, dict) and 'text' in crawl_data:
+                    try:
+                        # 解析JSON字符串
+                        json_data = json.loads(crawl_data['text'])
+                        
+                        # 提取标题
+                        title = json_data.get('title', '')
+                        
+                        # 提取正文内容
+                        body_text = json_data.get('body_text', '')
+                        
+                        # 提取图片链接
+                        image_data_list = json_data.get('image_url_list', [])
+                        for img_data in image_data_list:
+                            if isinstance(img_data, dict) and 'image_url' in img_data:
+                                image_url_list.append(img_data['image_url'])
+                        
+                    except json.JSONDecodeError as e:
+                        print(f"解析抓取结果JSON失败: {e}")
+                        # 如果解析失败,尝试直接使用文本内容
+                        if isinstance(crawl_data, dict) and 'text' in crawl_data:
+                            body_text = crawl_data['text']
+            elif isinstance(crawl_result, str):
+                # 如果是字符串格式,尝试直接解析
+                try:
+                    json_data = json.loads(crawl_result)
+                    title = json_data.get('title', '')
+                    body_text = json_data.get('body_text', '')
+                    image_data_list = json_data.get('image_url_list', [])
+                    for img_data in image_data_list:
+                        if isinstance(img_data, dict) and 'image_url' in img_data:
+                            image_url_list.append(img_data['image_url'])
+                except json.JSONDecodeError:
+                    body_text = crawl_result
+        
+        return {
+            'title': title,
+            'body_text': body_text,
+            'image_url_list': image_url_list,
+            'record_id': record.record_id
+        }
+    
+    def call_coze_workflow(self, title: str, body_text: str, image_url_list: List[str]) -> Dict[str, Any]:
+        """调用Coze工作流"""
+        try:
+            print(f"正在调用Coze工作流,标题: {title[:50]}...")
+            response = self.coze.run(title, body_text, image_url_list)
+            print("Coze工作流调用成功")
+            return response
+        except Exception as e:
+            print(f"调用Coze工作流失败: {e}")
+            return {"data": "{}"}
+    
+    def extract_coze_result(self, coze_response: Dict[str, Any]) -> Dict[str, str]:
+        """
+        从API响应中提取images_comprehension、title、body_text字段
+        """
+        try:
+            # 获取data字段
+            data = coze_response.get("data")
+            if not data:
+                print("响应中没有data字段")
+                return {"images_comprehension": "", "title": "", "body_text": ""}
+            
+            # 解析data字段(它是JSON字符串)
+            if isinstance(data, str):
+                try:
+                    data = json.loads(data)
+                except json.JSONDecodeError as e:
+                    print(f"data字段JSON解析失败: {e}")
+                    return {"images_comprehension": "", "title": "", "body_text": ""}
+            
+            # 从解析后的data中提取字段
+            extracted_fields = {
+                "images_comprehension": data.get("images_comprehension", ""),
+                "title": data.get("title", ""),
+                "body_text": data.get("body_text", "")
+            }
+            
+            return extracted_fields
+            
+        except Exception as e:
+            print(f"提取Coze结果失败: {e}")
+            return {"images_comprehension": "", "title": "", "body_text": ""}
+    
+    def update_feishu_record(self, record_id: str, result_dict: Dict[str, Any]):
+        """更新飞书表格中的记录"""
+        try:
+            import lark_oapi as lark
+                        # 创建更新记录
+            update_record = (lark.bitable.v1.AppTableRecord.builder()
+                           .record_id(record_id)
+                           .fields({
+                               self.output_field: json.dumps({
+                                    'images_comprehension': result_dict.get('images_comprehension', ''),
+                                    'title': result_dict.get('title', ''),
+                                    'body_text': result_dict.get('body_text', '')
+                                }, ensure_ascii=False)
+                           })
+                           .build())
+            
+            # 执行更新
+            self.feishu.update_record(self.table_id, update_record)
+            print(f"已更新记录 {record_id}")
+            
+        except Exception as e:
+            print(f"更新飞书记录失败: {e}")
+    
+    def process_single_record(self, record) -> bool:
+        """处理单条记录"""
+        try:
+            # 提取内容
+            content = self.extract_content_from_record(record)
+            
+            # 检查是否已经有识别结果
+            fields = record.fields
+            existing_result = fields.get(self.output_field, '')
+            
+            # 如果已有识别结果,则跳过
+            if existing_result and existing_result.strip():
+                try:
+                    # 尝试解析JSON,如果成功说明已有有效结果
+                    json.loads(existing_result)
+                    print(f"记录 {record.record_id} 已有识别结果,跳过")
+                    return True
+                except json.JSONDecodeError:
+                    # 如果JSON解析失败,说明可能是旧格式,继续处理
+                    pass
+            
+            # 检查是否有输入内容
+            if not content['body_text'] or not content['body_text'].strip():
+                print(f"记录 {record.record_id} 没有输入内容,跳过")
+                return True
+            
+            print(f"处理记录 {record.record_id}")
+            print(f"标题: {content['title'][:50]}...")
+            print(f"内容长度: {len(content['body_text'])} 字符")
+            print(f"图片数量: {len(content['image_url_list'])}")
+            
+            # 调用Coze工作流
+            coze_response = self.call_coze_workflow(
+                content['title'],
+                content['body_text'],
+                content['image_url_list']
+            )
+            
+            # 提取结果
+            result_dict = self.extract_coze_result(coze_response)
+            
+            # 更新飞书表格
+            self.update_feishu_record(record.record_id, result_dict)
+            
+            # 添加延迟避免API限制
+            time.sleep(1)
+            
+            return True
+            
+        except Exception as e:
+            print(f"处理记录 {record.record_id} 失败: {e}")
+            return False
+    
+    def process_all_records(self):
+        """处理所有记录"""
+        print(f"开始处理飞书表格 {self.table_id} 中的所有记录")
+        
+        page_token = None
+        total_processed = 0
+        total_success = 0
+        
+        while True:
+            try:
+                # 获取记录
+                result = self.feishu.get_all_records(self.table_id, page_token)
+                
+                if not result.items:
+                    print("没有找到记录")
+                    break
+                
+                print(f"获取到 {len(result.items)} 条记录")
+                
+                # 处理每条记录
+                for record in result.items:
+                    total_processed += 1
+                    if self.process_single_record(record):
+                        total_success += 1
+                
+                # 检查是否有下一页
+                if not result.has_more:
+                    break
+                
+                page_token = result.page_token
+                print(f"继续获取下一页,token: {page_token}")
+                
+            except Exception as e:
+                print(f"获取记录失败: {e}")
+                break
+        
+        print(f"处理完成!总共处理 {total_processed} 条记录,成功 {total_success} 条")
+
+
+def main():
+    """主函数"""
+    # 创建命令行参数解析器
+    parser = argparse.ArgumentParser(description='内容识别脚本 - 处理飞书表格数据')
+    parser.add_argument('table_id', nargs='?', help='飞书表格ID (可选,也可通过环境变量 FEISHU_TABLE_ID 设置)')
+    parser.add_argument('--page-token', help='分页token,用于从指定位置开始处理')
+    parser.add_argument('--dry-run', action='store_true', help='试运行模式,只显示会处理哪些记录,不实际调用API')
+    
+    args = parser.parse_args()
+    
+    try:
+        # 创建内容识别器实例
+        identifier = ContentIdentifier(table_id=args.table_id)
+        
+        print(f"使用表格ID: {identifier.table_id}")
+        
+        if args.dry_run:
+            print("试运行模式:只显示会处理的记录,不实际调用API")
+            # TODO: 实现试运行模式
+            identifier.process_all_records()
+        else:
+            # 正常处理模式
+            if args.page_token:
+                print(f"从分页token开始处理: {args.page_token}")
+                # TODO: 支持从指定分页token开始处理
+                identifier.process_all_records()
+            else:
+                identifier.process_all_records()
+                
+    except Exception as e:
+        print(f"程序执行失败: {e}")
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()

+ 0 - 0
4_aggregate.py


+ 0 - 96
coze_client.py

@@ -1,96 +0,0 @@
-# coze_client.py
-import requests
-import json
-import logging
-
-class CozeClient:
-    """
-    封装Coze API的聊天完成操作。
-    """
-    def __init__(self, api_key: str):
-        self.api_key = api_key
-        self.api_url = "https://api.coze.com/v1/chat/completions" # Coze API 聊天完成端点
-
-    def send_message(self, bot_id: str, prompt_template: str, input_data: str) -> str | None:
-        """
-        向Coze机器人发送消息并获取回复。
-        Args:
-            bot_id (str): Coze 机器人的 Bot ID。
-            prompt_template (str): Coze API的提示模板字符串,需要包含 '{input_data}' 占位符。
-            input_data (str): 实际要填充到模板中的输入数据。
-        Returns:
-            str | None: Coze的回复内容,如果失败则返回 None。
-        Raises:
-            Exception: 如果Coze API调用失败或返回非预期格式。
-        """
-        headers = {
-            "Authorization": f"Bearer {self.api_key}",
-            "Content-Type": "application/json",
-            "Accept": "application/json"
-        }
-        
-        # 填充提示模板
-        user_message_content = prompt_template.format(input_data=input_data)
-
-        payload = {
-            "model": bot_id, # 在Coze API中,bot_id 通常作为 model 参数传入
-            "messages": [
-                {"role": "user", "content": user_message_content}
-            ],
-            "stream": False # 非流式响应
-        }
-
-        try:
-            logging.info(f"正在调用 Coze Bot '{bot_id}' (输入内容前50字: '{user_message_content[:50]}...')")
-            response = requests.post(self.api_url, headers=headers, json=payload, timeout=60)
-            response.raise_for_status() # 检查HTTP响应状态码
-            result = response.json()
-
-            # 检查Coze响应结构,提取内容
-            if result.get("choices") and result["choices"][0].get("message") and result["choices"][0]["message"].get("content"):
-                coze_output_content = result["choices"][0]["message"]["content"]
-                logging.info(f"Coze API 调用成功,返回内容长度: {len(coze_output_content)}")
-                return coze_output_content
-            else:
-                logging.warning(f"Coze API 未返回有效内容。原始响应: {json.dumps(result, ensure_ascii=False, indent=2)}")
-                raise Exception(f"Coze API returned no valid content: {result}")
-
-        except requests.exceptions.RequestException as e:
-            logging.error(f"请求Coze API发生网络错误: {e}")
-            raise Exception(f"Coze API Request Failed: {e}")
-        except (IndexError, KeyError) as e:
-            logging.error(f"Coze API 响应格式错误: {e}. 原始响应: {json.dumps(result, ensure_ascii=False, indent=2) if 'result' in locals() else 'N/A'}")
-            raise Exception(f"Coze API Response Format Error: {e}")
-        except Exception as e:
-            logging.error(f"处理Coze API响应失败: {e}")
-            raise Exception(f"Coze API Response Handling Failed: {e}")
-
-# 可以在这里添加一些简单的测试代码,但通常在main.py中进行集成测试
-if __name__ == '__main__':
-    # 仅作示例,请勿在生产环境直接硬编码敏感信息
-    # 配置日志,用于独立测试
-    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
-
-    print("--- Coze客户端独立测试 (请确保替换YOUR_...) ---")
-    TEST_COZE_API_KEY = "YOUR_COZE_API_KEY"
-    TEST_COZE_BOT_ID = "YOUR_COZE_BOT_ID"
-    TEST_PROMPT_TEMPLATE = "请用一句话总结以下内容: {input_data}"
-    TEST_INPUT_TEXT = "Python是一种高级编程语言,由Guido van Rossum创建,并于1991年首次发布。它以其清晰的语法和广泛的库而闻名,适用于Web开发、数据分析、人工智能等多个领域。"
-
-    if "YOUR_" in TEST_COZE_API_KEY:
-        logging.warning("请替换 coze_client.py 中的 YOUR_ 占位符为您的实际Coze API信息以运行测试。")
-    else:
-        try:
-            coze_client = CozeClient(TEST_COZE_API_KEY)
-            coze_output = coze_client.send_message(
-                TEST_COZE_BOT_ID,
-                TEST_PROMPT_TEMPLATE,
-                TEST_INPUT_TEXT
-            )
-            if coze_output:
-                print(f"\nCoze API 成功响应:\n{coze_output}")
-            else:
-                print("\nCoze API 未返回有效内容。")
-        except Exception as e:
-            logging.error(f"Coze客户端独立测试失败: {e}")
-    print("--- Coze客户端独立测试结束 ---")

+ 0 - 204
feishu_client.py

@@ -1,204 +0,0 @@
-# feishu_client.py
-import requests
-import json
-import logging
-import time
-
-class FeishuClient:
-    """
-    封装飞书多维表格的API操作。
-    负责获取Access Token,以及对多维表格的读写操作。
-    """
-    def __init__(self, app_id: str, app_secret: str):
-        self.app_id = app_id
-        self.app_secret = app_secret
-        self._access_token = None
-        self._token_expires_at = 0 # Unix timestamp, 用于缓存Token过期时间
-
-    def _get_access_token(self) -> str:
-        """
-        获取飞书应用的 Access Token。
-        如果当前token未过期,则直接返回;否则,重新请求获取。
-        Token有效期通常为2小时,这里会提前120秒刷新。
-        """
-        # 检查缓存的token是否仍然有效
-        if self._access_token and self._token_expires_at > time.time():
-            return self._access_token
-
-        url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
-        headers = {"Content-Type": "application/json; charset=utf-8"}
-        payload = {
-            "app_id": self.app_id,
-            "app_secret": self.app_secret
-        }
-        try:
-            logging.info("正在请求飞书 Access Token...")
-            response = requests.post(url, headers=headers, json=payload, timeout=10)
-            response.raise_for_status() # 检查HTTP响应状态码,如果不是2xx则抛出HTTPError
-            data = response.json()
-            if data.get("code") == 0:
-                self._access_token = data.get("app_access_token")
-                # 记录过期时间,提前2分钟刷新
-                self._token_expires_at = time.time() + data.get("expire", 7200) - 120
-                logging.info("飞书 Access Token 获取成功。")
-                return self._access_token
-            else:
-                logging.error(f"获取飞书 Access Token 失败: {data.get('msg')} (Code: {data.get('code')})")
-                raise Exception(f"Feishu Token Error: {data.get('msg')}")
-        except requests.exceptions.RequestException as e:
-            logging.error(f"请求飞书 Access Token 发生网络错误: {e}")
-            raise Exception(f"Feishu Token Request Failed: {e}")
-        except Exception as e:
-            logging.error(f"处理飞书 Access Token 响应失败: {e}")
-            raise Exception(f"Feishu Token Response Handling Failed: {e}")
-
-    def read_records(self, base_id: str, table_id: str, field_names: list, page_size: int = 100) -> list:
-        """
-        从飞书多维表格读取记录。
-        Args:
-            base_id (str): 多维表格的 Base ID (应用 Token)。
-            table_id (str): 表的 Table ID。
-            field_names (list): 要读取的字段名称列表。
-            page_size (int): 每次请求的记录数 (最大500)。
-        Returns:
-            list: 包含记录字典的列表。
-        Raises:
-            Exception: 如果读取失败。
-        """
-        access_token = self._get_access_token()
-        url = (
-            f"https://open.feishu.cn/open-apis/bitable/v1/apps/{base_id}/tables/{table_id}/records"
-        )
-        headers = {
-            "Authorization": f"Bearer {access_token}",
-            "Content-Type": "application/json"
-        }
-        params = {
-            "page_size": min(page_size, 500), # 飞书API page_size 最大500
-            "field_names": json.dumps(field_names)
-        }
-        records = []
-        try:
-            logging.info(f"正在从飞书多维表格读取数据 (表: {table_id}, 请求字段: {field_names}, 每页: {params['page_size']})...")
-            response = requests.get(url, headers=headers, params=params, timeout=30)
-            response.raise_for_status()
-            data = response.json()
-            if data.get("code") == 0:
-                records.extend(data.get("data", {}).get("items", []))
-                # 注意:这里只处理了第一页数据。如果需要读取所有数据,您需要实现分页循环逻辑
-                if data.get("data", {}).get("has_more"):
-                    logging.warning(f"飞书多维表格 (表: {table_id}) 存在更多数据未读取。当前实现仅获取了第一页。")
-                logging.info(f"成功读取 {len(records)} 条记录。")
-                return records
-            else:
-                logging.error(f"从飞书多维表格读取数据失败: {data.get('msg')} (Code: {data.get('code')})")
-                raise Exception(f"Feishu Read Error: {data.get('msg')}")
-        except requests.exceptions.RequestException as e:
-            logging.error(f"请求飞书多维表格读取数据发生网络错误: {e}")
-            raise Exception(f"Feishu Read Request Failed: {e}")
-        except Exception as e:
-            logging.error(f"处理飞书多维表格读取响应失败: {e}")
-            raise Exception(f"Feishu Read Response Handling Failed: {e}")
-
-    def update_records(self, base_id: str, table_id: str, records_data: list) -> bool:
-        """
-        更新飞书多维表格中的记录。
-        Args:
-            base_id (str): 多维表格的 Base ID。
-            table_id (str): 表的 Table ID。
-            records_data (list): 包含要更新的记录字典的列表,每个字典需包含 record_id 和 fields。
-                                 例如: [{"record_id": "recxxxxxxxx", "fields": {"字段名": "新内容"}}]
-                                 每次批量更新最多支持500条记录。
-        Returns:
-            bool: True 如果更新成功,False 如果失败。
-        Raises:
-            Exception: 如果更新失败。
-        """
-        if not records_data:
-            logging.info("没有要更新的记录,跳过写入飞书操作。")
-            return True
-
-        access_token = self._get_access_token()
-        url = (
-            f"https://open.feishu.cn/open-apis/bitable/v1/apps/{base_id}/tables/{table_id}/records"
-        )
-        headers = {
-            "Authorization": f"Bearer {access_token}",
-            "Content-Type": "application/json"
-        }
-        
-        # 飞书批量更新API限制每批最多500条
-        chunk_size = 500
-        all_success = True
-
-        for i in range(0, len(records_data), chunk_size):
-            batch_records = records_data[i:i + chunk_size]
-            payload = {
-                "records": batch_records
-            }
-            try:
-                logging.info(f"正在将 {len(batch_records)} 条记录分批写回飞书多维表格 (表: {table_id}, 批次: {i//chunk_size + 1})...")
-                response = requests.post(url, headers=headers, json=payload, timeout=30)
-                response.raise_for_status()
-                data = response.json()
-                if data.get("code") == 0:
-                    logging.info(f"批次 {i//chunk_size + 1} 成功写入/更新 {len(batch_records)} 条记录。")
-                else:
-                    logging.error(f"将数据写入飞书多维表格失败 (批次 {i//chunk_size + 1}): {data.get('msg')} (Code: {data.get('code')})")
-                    all_success = False
-                    # 可以在这里选择抛出异常中断,或者继续处理下一批
-                    # raise Exception(f"Feishu Write Error in batch {i//chunk_size + 1}: {data.get('msg')}")
-            except requests.exceptions.RequestException as e:
-                logging.error(f"请求飞书多维表格写入数据发生网络错误 (批次 {i//chunk_size + 1}): {e}")
-                all_success = False
-                # raise Exception(f"Feishu Write Request Failed in batch {i//chunk_size + 1}: {e}")
-            except Exception as e:
-                logging.error(f"处理飞书多维表格写入响应失败 (批次 {i//chunk_size + 1}): {e}")
-                all_success = False
-                # raise Exception(f"Feishu Write Response Handling Failed in batch {i//chunk_size + 1}: {e}")
-        
-        return all_success
-
-# 可以在这里添加一些简单的测试代码,但通常在main.py中进行集成测试
-if __name__ == '__main__':
-    # 仅作示例,请勿在生产环境直接硬编码敏感信息
-    # 请替换为您的实际飞书应用信息
-    # 配置日志,用于独立测试
-    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
-
-    print("--- 飞书客户端独立测试 (请确保替换YOUR_...) ---")
-    TEST_APP_ID = "YOUR_FEISHU_APP_ID"
-    TEST_APP_SECRET = "YOUR_FEISHU_APP_SECRET"
-    TEST_BASE_ID = "YOUR_FEISHU_BASE_ID"
-    TEST_TABLE_ID = "YOUR_FEISHU_TABLE_ID"
-    TEST_INPUT_FIELD = "测试文本" # 确保您的表中有此字段
-    TEST_OUTPUT_FIELD = "测试输出" # 确保您的表中有此字段
-
-    if "YOUR_" in TEST_APP_ID:
-        logging.warning("请替换 feishu_client.py 中的 YOUR_ 占位符为您的实际飞书应用信息以运行测试。")
-    else:
-        try:
-            feishu_client = FeishuClient(TEST_APP_ID, TEST_APP_SECRET)
-            
-            # 测试读取
-            print("\n--- 测试读取记录 ---")
-            read_records_data = feishu_client.read_records(TEST_BASE_ID, TEST_TABLE_ID, [TEST_INPUT_FIELD, TEST_OUTPUT_FIELD], page_size=2)
-            for record in read_records_data:
-                print(f"Record ID: {record.get('record_id')}, Fields: {record.get('fields')}")
-
-            # 测试写入/更新 (仅更新第一条记录的某个字段)
-            if read_records_data:
-                print("\n--- 测试更新记录 ---")
-                first_record_id = read_records_data[0]['record_id']
-                update_payload = [{
-                    "record_id": first_record_id,
-                    "fields": {TEST_OUTPUT_FIELD: f"Updated by test at {time.strftime('%Y-%m-%d %H:%M:%S')}"}
-                }]
-                update_success = feishu_client.update_records(TEST_BASE_ID, TEST_TABLE_ID, update_payload)
-                print(f"更新操作是否成功: {update_success}")
-            else:
-                print("\n没有可读取的记录,跳过更新测试。")
-
-        except Exception as e:
-            logging.error(f"飞书客户端独立测试失败: {e}")
-    print("--- 飞书客户端独立测试结束 ---")

+ 0 - 50
prehandler.py

@@ -1,50 +0,0 @@
-import config
-import logging
-import os
-from feishu_client import FeishuClient
-from coze_client import CozeClient
-
-# 配置日志
-logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
-
-if __name__ == "__main__":
-    # --- 环境变量/配置信息加载 ---
-    # 推荐使用环境变量加载敏感信息,而不是硬编码。
-    # 例如:export FEISHU_APP_ID="your_id"
-    # 或者从配置文件 (如 config.ini, .env 文件) 中加载
-
-    # 飞书配置
-    FEISHU_APP_ID = config.FEISHU_APP_ID
-    FEISHU_APP_SECRET = config.FEISHU_APP_SECRET
-    FEISHU_BASE_ID = config.FEISHU_BASE_ID
-    FEISHU_TABLE_ID = config.FEISHU_TABLE_ID
-
-    FEISHU_INPUT_FIELD = config.FEISHU_INPUT_FIELD # 你的飞书表格中用于输入的列名
-    FEISHU_OUTPUT_FIELD = config.FEISHU_OUTPUT_FIELD # 你的飞书表格中用于输出的列名
-
-    # Coze 配置
-    COZE_API_KEY = config.COZE_API_KEY
-    COZE_BOT_ID = config.COZE_BOT_ID # 例如: "7343685511394590740"
-
-    # Coze 提示模板,请确保包含 {input_data} 占位符
-    # 这是一个示例,你可以根据你的机器人功能设计更复杂的提示
-    COZE_PROMPT_TEMPLATE = os.getenv("COZE_PROMPT_TEMPLATE", "请作为一位专业的编辑,总结以下文章的核心内容,要求言简意赅,200字以内: {input_data}")
-
-    # --- 执行流程 ---
-    if "YOUR_" in FEISHU_APP_ID or "YOUR_" in COZE_API_KEY:
-        logging.error("⛔️ 请检查 main.py 或环境变量,确保所有 'YOUR_' 占位符都已替换为您的实际配置信息!⛔️")
-        logging.error("流程未执行。")
-    else:
-        process_feishu_data_with_coze_flow(
-            feishu_app_id=FEISHU_APP_ID,
-            feishu_app_secret=FEISHU_APP_SECRET,
-            feishu_base_id=FEISHU_BASE_ID,
-            feishu_table_id=FEISHU_TABLE_ID,
-            feishu_input_field_name=FEISHU_INPUT_FIELD,
-            feishu_output_field_name=FEISHU_OUTPUT_FIELD,
-            coze_api_key=COZE_API_KEY,
-            coze_bot_id=COZE_BOT_ID,
-            coze_prompt_template=COZE_PROMPT_TEMPLATE,
-            max_records_to_process=10, # 每次运行最多处理10条记录
-            overwrite_existing_output=True # 总是覆盖输出字段
-        )

+ 30 - 0
prompt/handle.md

@@ -0,0 +1,30 @@
+# 角色:专家级内容编辑
+
+## 个人档案
+你是一位专家级的内容编辑与结构分析师。你的核心能力是梳理和整合零散的信息片段(例如一段正文和多张图片中的笔记),将其合并成一篇单一、连贯、且结构清晰的文档。你一丝不苟、以事实为依据,并且非常擅长使用Markdown格式创建清晰易懂的指南或列表。
+
+## 核心目标
+将一个包含标题、正文、以及从多张图片中提取的文本数组的混乱JSON输入,转换成一篇结构完整、逻辑清晰、易于阅读的Markdown格式文章。
+
+## 约束条件
+- **严格忠于原文内容:** 你 **绝对不能** 添加任何未在输入JSON中提供的信息、观点或细节。你的任务是重组和格式化,而不是内容创作。
+- **禁止杜撰:** 不要虚构任何步骤、要求或功能。输出中的每一条信息都必须能追溯到原始输入。
+- **保留关键术语:** 对于专业术语、工具名称(如“美图秀秀”、“豆包”)和特定短语,必须保持原文用词。
+- **逻辑流程优先:** 最终的输出必须有一个清晰、合乎逻辑的进展。你需要识别内容的内在结构(无论是教程步骤还是对比列表),并以此为基础进行组织。
+- **智能合并与去噪:** 你需要将分散在不同图片中的同类信息(如“喵星人”的所有行为)合并到同一个标题下。同时,忽略无意义的文本(如“图中无文字内容”、“昨天18:32”等)。
+
+## 工作流程 (思考步骤)
+1.  **识别主标题:** 使用JSON中的 `title` 字段作为一级标题 (`#`)。
+2.  **处理引言:** 将 `body_text` 的内容作为文章的引言或开场白。如果内容不完整,忠实呈现原文即可。
+3.  **分析并整合核心内容(核心任务):**
+    -   通读 `images_comprehension` 数组中的所有文本,理解其整体内容结构。判断这是“步骤式教程”、“对比清单”还是其他类型。
+    -   识别出核心的类别或步骤标题(如“第一步”、“喵星人”、“汪星人”等)。
+    -   遍历所有输入,将所有相关的信息点(包括其详细描述)归类到相应的主标题之下。确保将分散在多处的内容合并到一起。
+    -   对于重复出现的主标题(如“屁股社交”),如果其描述性内容不同,则应作为独立条目保留,以确保信息的完整性。
+4.  **应用格式化:**
+    -   使用Markdown的二级标题 (`##`) 来组织主要类别或步骤。
+    -   使用项目符号(`-` 或 `*`)和粗体 (`**文字**`) 来构建清晰的列表和层次结构,突出重点。
+5.  **处理结尾和标签(如果存在):** 如果输入内容包含明确的结尾或 `#话题标签`,则将它们放在文档的末尾。
+
+## 输入
+用户将提供一个包含 `title`、`body_text` 和 `images_comprehension` 的JSON对象。

+ 3 - 0
utils/fei_shu.py

@@ -33,8 +33,11 @@ class FeiShu(object):
                                         .fields([
                                             lark.bitable.v1.AppTableCreateHeader.builder().field_name('原文链接').type(15).build(),
                                             lark.bitable.v1.AppTableCreateHeader.builder().field_name('抓取结果').type(1).build(),
+                                            lark.bitable.v1.AppTableCreateHeader.builder().field_name('标题').type(1).build(),
+                                            lark.bitable.v1.AppTableCreateHeader.builder().field_name('用户链接').type(15).build(),
                                             lark.bitable.v1.AppTableCreateHeader.builder().field_name('识别结果').type(1).build(),
                                             lark.bitable.v1.AppTableCreateHeader.builder().field_name('初步理解').type(1).build(),
+                                            lark.bitable.v1.AppTableCreateHeader.builder().field_name('物理聚合').type(1).build(),
                                         ])
                                         .build())
                                  .build())