226 lines
7.1 KiB
Python
226 lines
7.1 KiB
Python
import argparse
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
if sys.stdout.encoding.lower() == "gbk":
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
datefmt="%H:%M:%S",
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def cmd_process(args):
|
|
from meeting_processor import meeting_processor
|
|
|
|
filepath = args.file
|
|
if not os.path.exists(filepath):
|
|
print(f"错误: 文件不存在: {filepath}")
|
|
sys.exit(1)
|
|
|
|
print(f"正在处理会议文件: {filepath}")
|
|
vault_path = meeting_processor.process_meeting_file(filepath, force=getattr(args, 'force', False))
|
|
|
|
if vault_path:
|
|
print(f"\n✅ 会议处理完成!")
|
|
print(f"📝 Obsidian 笔记: {vault_path}")
|
|
print(f"📂 Obsidian Vault: {os.path.dirname(vault_path)}")
|
|
else:
|
|
print("\n❌ 会议处理失败")
|
|
sys.exit(1)
|
|
|
|
|
|
def cmd_text(args):
|
|
from meeting_processor import meeting_processor
|
|
|
|
text = args.text
|
|
print("正在处理会议文本...")
|
|
vault_path = meeting_processor.process_meeting_text(text, force=getattr(args, 'force', False))
|
|
|
|
if vault_path:
|
|
print(f"\n✅ 会议处理完成!")
|
|
print(f"📝 Obsidian 笔记: {vault_path}")
|
|
else:
|
|
print("\n❌ 会议处理失败")
|
|
|
|
|
|
def cmd_query(args):
|
|
from meeting_processor import meeting_processor
|
|
|
|
question = args.question
|
|
print(f"🔍 查询: {question}")
|
|
print("-" * 40)
|
|
result = meeting_processor.query(question, top_k=args.top_k)
|
|
if result:
|
|
print(result)
|
|
else:
|
|
print("未找到相关信息")
|
|
|
|
|
|
def cmd_stats(args):
|
|
from meeting_processor import meeting_processor
|
|
|
|
stats = meeting_processor.stats()
|
|
print("📊 会议记忆系统统计")
|
|
print("-" * 40)
|
|
print(f"Obsidian 会议笔记: {stats.get('obsidian_meetings', 0)}")
|
|
print(f"Obsidian 实体笔记: {stats.get('obsidian_entities', 0)}")
|
|
print(f"向量索引节点数: {stats.get('vector_index', {}).get('node_count', 0)}")
|
|
print(f"Vault 路径: {stats.get('vault_path', '')}")
|
|
|
|
|
|
def cmd_batch(args):
|
|
from meeting_processor import meeting_processor
|
|
import glob as glob_module
|
|
|
|
pattern = args.pattern
|
|
files = glob_module.glob(pattern, recursive=True)
|
|
force = getattr(args, 'force', False)
|
|
|
|
if not files:
|
|
print(f"未匹配到任何文件: {pattern}")
|
|
sys.exit(1)
|
|
|
|
print(f"找到 {len(files)} 个文件,开始批量处理...")
|
|
success = 0
|
|
for f in files:
|
|
try:
|
|
print(f"\n处理: {f}")
|
|
meeting_processor.process_meeting_file(f, force=force)
|
|
success += 1
|
|
except Exception as e:
|
|
logger.error(f"处理失败: {f} - {e}")
|
|
|
|
print(f"\n✅ 批量处理完成: {success}/{len(files)} 成功")
|
|
|
|
|
|
def cmd_interactive(args=None):
|
|
from meeting_processor import meeting_processor
|
|
|
|
print("📋 会议纪要长期记忆系统 — 交互模式")
|
|
print("=" * 50)
|
|
print("可用命令:")
|
|
print(" query <问题> 语义查询会议记忆")
|
|
print(" process <路径> 处理会议文件")
|
|
print(" stats 查看统计")
|
|
print(" help 显示帮助")
|
|
print(" exit/quit 退出")
|
|
print("=" * 50)
|
|
|
|
while True:
|
|
try:
|
|
line = input("\n> ").strip()
|
|
except (EOFError, KeyboardInterrupt):
|
|
print()
|
|
break
|
|
|
|
if not line:
|
|
continue
|
|
|
|
if line in ("exit", "quit", "q"):
|
|
break
|
|
|
|
if line == "help":
|
|
print("可用命令:")
|
|
print(" query <问题> — 语义查询会议记忆")
|
|
print(" process <路径> — 处理一个会议markdown文件")
|
|
print(" stats — 查看系统统计")
|
|
print(" help — 显示此帮助")
|
|
print(" exit/quit — 退出")
|
|
continue
|
|
|
|
if line == "stats":
|
|
stats = meeting_processor.stats()
|
|
print(f"📊 会议: {stats.get('obsidian_meetings', 0)} | "
|
|
f"实体: {stats.get('obsidian_entities', 0)} | "
|
|
f"向量节点: {stats.get('vector_index', {}).get('node_count', 0)}")
|
|
continue
|
|
|
|
if line.startswith("process "):
|
|
filepath = line[8:].strip()
|
|
if not os.path.exists(filepath):
|
|
print(f"❌ 文件不存在: {filepath}")
|
|
continue
|
|
print(f"正在处理: {filepath}")
|
|
vault_path = meeting_processor.process_meeting_file(filepath)
|
|
if vault_path:
|
|
print(f"✅ 完成: {vault_path}")
|
|
else:
|
|
print("❌ 处理失败")
|
|
continue
|
|
|
|
if line.startswith("query "):
|
|
question = line[6:].strip()
|
|
else:
|
|
question = line
|
|
|
|
print(f"🔍 查询中...", end="", flush=True)
|
|
result = meeting_processor.query(question, top_k=3)
|
|
print("\r" + " " * 30 + "\r", end="")
|
|
if result:
|
|
print(result[:2000])
|
|
if len(result) > 2000:
|
|
print("... (结果过长已截断)")
|
|
else:
|
|
print("未找到相关信息")
|
|
print("bye!")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="📋 会议纪要长期记忆系统",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
示例:
|
|
python main.py process meeting_example.md
|
|
python main.py query "弱光指标目标值是多少?"
|
|
python main.py stats
|
|
python main.py text "今天会议讨论了..."
|
|
|
|
无参数时进入交互模式。
|
|
|
|
Powered by LlamaIndex + Obsidian + LLM
|
|
""",
|
|
)
|
|
subparsers = parser.add_subparsers(dest="command", help="子命令")
|
|
|
|
p_process = subparsers.add_parser("process", help="处理会议 markdown 文件")
|
|
p_process.add_argument("file", help="会议纪要 markdown 文件路径")
|
|
p_process.add_argument("-f", "--force", action="store_true", help="重复时自动覆盖,跳过确认")
|
|
|
|
p_text = subparsers.add_parser("text", help="直接输入会议文本")
|
|
p_text.add_argument("text", help="会议文本内容")
|
|
p_text.add_argument("-f", "--force", action="store_true", help="重复时自动覆盖,跳过确认")
|
|
|
|
p_query = subparsers.add_parser("query", help="语义查询会议记忆")
|
|
p_query.add_argument("question", help="查询问题")
|
|
p_query.add_argument("--top-k", type=int, default=3, help="返回结果数量")
|
|
|
|
p_stats = subparsers.add_parser("stats", help="查看系统统计")
|
|
|
|
p_batch = subparsers.add_parser("batch", help="批量处理会议文件")
|
|
p_batch.add_argument("pattern", help="文件 glob 模式, 如 'meetings/*.md'")
|
|
p_batch.add_argument("-f", "--force", action="store_true", help="重复时自动覆盖,跳过确认")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.command == "process":
|
|
cmd_process(args)
|
|
elif args.command == "text":
|
|
cmd_text(args)
|
|
elif args.command == "query":
|
|
cmd_query(args)
|
|
elif args.command == "stats":
|
|
cmd_stats(args)
|
|
elif args.command == "batch":
|
|
cmd_batch(args)
|
|
else:
|
|
cmd_interactive(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |