Files
xinmi/skills/preloaded/openmaic-classroom/scripts/rag-to-requirement.py
T

195 lines
6.0 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
RAG 检索结果 → OpenMAIC Requirement 转换器
将 WeKnora RAG 检索结果转换为结构化的 OpenMAIC 课程生成需求描述。
此脚本仅做数据转换,不涉及网络调用。
用法:
echo '{"chunks": [...], "audience": "初学者"}' | python scripts/rag-to-requirement.py
python scripts/rag-to-requirement.py --file results.json
输入格式 (JSON):
{
"chunks": [
{
"document_name": "文档A.pdf",
"content": "文档内容片段...",
"metadata": {"page": 5, "section": "第三章"}
}
],
"query": "用户原始查询(可选)",
"audience": "目标受众描述(可选,默认'相关领域的学习者')",
"depth": "教学深度: beginner|intermediate|advanced(可选,默认'intermediate')",
"language": "zh-CN|en-US(可选,默认'zh-CN')",
"focus_areas": ["重点领域1", "重点领域2"] # 可选
}
输出格式 (JSON):
{
"requirement": "结构化的教学需求描述",
"metadata": {
"source_documents": ["文档A.pdf", "文档B.pdf"],
"total_chunks": 5,
"audience": "初学者",
"depth": "intermediate",
"language": "zh-CN"
}
}
"""
import json
import sys
from typing import Any
def extract_key_topics(chunks: list[dict], max_topics: int = 5) -> list[str]:
"""从文档块中提取关键主题(基于内容摘要和 section 信息)。"""
topics = []
seen = set()
for chunk in chunks:
metadata = chunk.get("metadata", {})
# 优先使用 section/chapter 信息
for key in ("section", "chapter", "heading", "title"):
if key in metadata and metadata[key] not in seen:
topics.append(metadata[key])
seen.add(metadata[key])
if len(topics) >= max_topics:
return topics
# 如果 section 信息不足,从内容前 50 字提取
for chunk in chunks:
content = chunk.get("content", "")[:50].strip()
if content and content not in seen:
topics.append(content + "...")
seen.add(content)
if len(topics) >= max_topics:
break
return topics
def build_requirement(data: dict[str, Any]) -> str:
"""将 RAG 结果构建为 OpenMAIC requirement 字符串。"""
chunks = data.get("chunks", [])
query = data.get("query", "")
audience = data.get("audience", "相关领域的学习者")
depth = data.get("depth", "intermediate")
language = data.get("language", "zh-CN")
focus_areas = data.get("focus_areas", [])
depth_map = {
"beginner": "入门",
"intermediate": "中级",
"advanced": "高级",
}
depth_cn = depth_map.get(depth, "中级")
# 提取文档名称
source_docs = list({c.get("document_name", "未知文档") for c in chunks})
# 提取关键主题
key_topics = extract_key_topics(chunks)
# 构建 requirement
parts = []
# 开头:基于什么内容,为谁创建什么课程
if query:
parts.append(f"基于以下知识库内容,为{audience}创建一个{depth_cn}课程。")
parts.append(f"用户原始需求:{query}")
else:
parts.append(f"基于以下知识库内容,为{audience}创建一个{depth_cn}课程。")
# 内容来源
if source_docs:
docs_str = "、".join(source_docs[:5])
if len(source_docs) > 5:
docs_str += f" 等{len(source_docs)}个文档"
parts.append(f"\n内容来源:{docs_str}")
# 关键主题
if key_topics:
parts.append("\n核心主题:")
for i, topic in enumerate(key_topics, 1):
parts.append(f" {i}. {topic}")
# 重点领域
if focus_areas:
parts.append("\n重点覆盖:")
for area in focus_areas:
parts.append(f" - {area}")
# 语言要求
if language == "zh-CN":
parts.append("\n请使用中文生成课程内容。")
return "\n".join(parts)
def process(input_data: dict[str, Any]) -> dict[str, Any]:
"""主处理函数。"""
chunks = input_data.get("chunks", [])
if not chunks:
return {
"requirement": input_data.get("query", ""),
"metadata": {
"source_documents": [],
"total_chunks": 0,
"error": "未提供检索结果,使用原始查询作为 requirement",
},
}
requirement = build_requirement(input_data)
source_docs = list({c.get("document_name", "未知文档") for c in chunks})
return {
"requirement": requirement,
"metadata": {
"source_documents": source_docs,
"total_chunks": len(chunks),
"audience": input_data.get("audience", "相关领域的学习者"),
"depth": input_data.get("depth", "intermediate"),
"language": input_data.get("language", "zh-CN"),
},
}
def main() -> None:
"""入口:从 stdin 或文件读取输入,输出 JSON 结果。"""
import argparse
parser = argparse.ArgumentParser(description="RAG 结果 → OpenMAIC Requirement 转换器")
parser.add_argument("--file", "-f", help="输入 JSON 文件路径")
args = parser.parse_args()
# 读取输入
if args.file:
with open(args.file, "r", encoding="utf-8") as f:
input_data = json.load(f)
else:
input_text = sys.stdin.read()
if not input_text.strip():
print(
"错误: 未提供输入数据。用法:\n"
" echo '{\"chunks\": [...]}' | python rag-to-requirement.py\n"
" python rag-to-requirement.py --file input.json",
file=sys.stderr,
)
sys.exit(1)
try:
input_data = json.loads(input_text)
except json.JSONDecodeError as e:
print(f"错误: 输入 JSON 解析失败: {e}", file=sys.stderr)
sys.exit(1)
# 处理并输出
result = process(input_data)
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()