2025-09-04 05:47:42 +00:00
|
|
|
|
import re
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
2025-09-08 10:20:48 +00:00
|
|
|
|
def call_ai_api(system_prompt, user_prompt, model="glm-4.5", base_url="https://open.bigmodel.cn/api/paas/v4/", api_key="ce39bdd4fcf34ec0aec75072bc9ff988.hAp7HZTVUwy7vImn", stream=False):
|
2025-09-04 05:47:42 +00:00
|
|
|
|
"""
|
|
|
|
|
|
调用AI API生成Markdown
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 尝试导入OpenAI客户端
|
|
|
|
|
|
try:
|
|
|
|
|
|
from openai import OpenAI
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
print("OpenAI库未安装,返回模拟数据")
|
2025-09-08 10:20:48 +00:00
|
|
|
|
if stream:
|
|
|
|
|
|
# 返回模拟流式数据
|
|
|
|
|
|
def mock_stream():
|
|
|
|
|
|
mock_content = f"""# {user_prompt}
|
|
|
|
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
{user_prompt}是一个重要的概念和领域。
|
|
|
|
|
|
|
|
|
|
|
|
## 核心要素
|
|
|
|
|
|
- 要素1
|
|
|
|
|
|
- 要素2
|
|
|
|
|
|
- 要素3
|
|
|
|
|
|
|
|
|
|
|
|
## 应用场景
|
|
|
|
|
|
- 场景1
|
|
|
|
|
|
- 场景2
|
|
|
|
|
|
- 场景3
|
|
|
|
|
|
|
|
|
|
|
|
## 发展趋势
|
|
|
|
|
|
- 趋势1
|
|
|
|
|
|
- 趋势2
|
|
|
|
|
|
- 趋势3"""
|
|
|
|
|
|
for char in mock_content:
|
|
|
|
|
|
yield char
|
|
|
|
|
|
return mock_stream()
|
|
|
|
|
|
else:
|
|
|
|
|
|
return f"""# {user_prompt}
|
2025-09-04 05:47:42 +00:00
|
|
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
{user_prompt}是一个重要的概念和领域。
|
|
|
|
|
|
|
|
|
|
|
|
## 核心要素
|
|
|
|
|
|
- 要素1
|
|
|
|
|
|
- 要素2
|
|
|
|
|
|
- 要素3
|
|
|
|
|
|
|
|
|
|
|
|
## 应用场景
|
|
|
|
|
|
- 场景1
|
|
|
|
|
|
- 场景2
|
|
|
|
|
|
- 场景3
|
|
|
|
|
|
|
|
|
|
|
|
## 发展趋势
|
|
|
|
|
|
- 趋势1
|
|
|
|
|
|
- 趋势2
|
|
|
|
|
|
- 趋势3"""
|
|
|
|
|
|
|
|
|
|
|
|
# 构建消息
|
|
|
|
|
|
messages = [
|
|
|
|
|
|
{"role": "system", "content": system_prompt},
|
|
|
|
|
|
{"role": "user", "content": user_prompt}
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
print(f"发送AI API请求到: {base_url}")
|
|
|
|
|
|
print(f"模型: {model}")
|
2025-09-08 10:20:48 +00:00
|
|
|
|
print(f"流式模式: {stream}")
|
2025-09-04 05:47:42 +00:00
|
|
|
|
|
|
|
|
|
|
# 创建OpenAI客户端
|
|
|
|
|
|
client = OpenAI(api_key=api_key, base_url=base_url)
|
|
|
|
|
|
|
2025-09-08 10:20:48 +00:00
|
|
|
|
# 根据stream参数决定是否使用流式调用
|
2025-09-04 05:47:42 +00:00
|
|
|
|
try:
|
|
|
|
|
|
response = client.chat.completions.create(
|
|
|
|
|
|
model=model,
|
|
|
|
|
|
messages=messages,
|
|
|
|
|
|
temperature=0.7,
|
2025-10-09 08:02:23 +00:00
|
|
|
|
max_tokens=8000, # 增加token限制,确保完整内容生成
|
2025-09-08 10:20:48 +00:00
|
|
|
|
stream=stream
|
2025-09-04 05:47:42 +00:00
|
|
|
|
)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"API调用失败: {e}")
|
|
|
|
|
|
# 如果API调用失败,抛出异常而不是返回模拟数据
|
|
|
|
|
|
raise Exception(f"AI API调用失败: {e}")
|
|
|
|
|
|
|
2025-09-08 10:20:48 +00:00
|
|
|
|
if stream:
|
|
|
|
|
|
# 流式响应处理
|
|
|
|
|
|
def generate_stream():
|
|
|
|
|
|
full_content = ""
|
|
|
|
|
|
try:
|
|
|
|
|
|
for chunk in response:
|
|
|
|
|
|
if chunk.choices[0].delta.content is not None:
|
|
|
|
|
|
content_chunk = chunk.choices[0].delta.content
|
|
|
|
|
|
full_content += content_chunk
|
|
|
|
|
|
yield content_chunk
|
|
|
|
|
|
print(f"流式响应完成,总长度: {len(full_content)}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"流式响应处理失败: {e}")
|
|
|
|
|
|
raise e
|
|
|
|
|
|
return full_content
|
|
|
|
|
|
|
|
|
|
|
|
return generate_stream()
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 非流式响应处理
|
|
|
|
|
|
content = response.choices[0].message.content
|
|
|
|
|
|
print(f"AI原始响应: {content}")
|
|
|
|
|
|
|
|
|
|
|
|
# 处理可能的JSON格式响应
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 尝试解析为JSON
|
|
|
|
|
|
json_data = json.loads(content)
|
|
|
|
|
|
if 'answer' in json_data:
|
|
|
|
|
|
content = json_data['answer']
|
|
|
|
|
|
print(f"从JSON中提取answer: {content[:100]}...")
|
|
|
|
|
|
elif 'content' in json_data:
|
|
|
|
|
|
content = json_data['content']
|
|
|
|
|
|
print(f"从JSON中提取content: {content[:100]}...")
|
|
|
|
|
|
elif 'markdown' in json_data:
|
|
|
|
|
|
content = json_data['markdown']
|
|
|
|
|
|
print(f"从JSON中提取markdown: {content[:100]}...")
|
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
|
# 不是JSON格式,直接使用内容
|
|
|
|
|
|
print("响应不是JSON格式,直接使用内容")
|
|
|
|
|
|
|
|
|
|
|
|
# 清理内容
|
|
|
|
|
|
content = content.strip()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果返回的内容包含代码块标记,提取其中的内容
|
|
|
|
|
|
markdown_match = re.search(r"```(?:markdown)?\n(.*?)```", content, re.DOTALL)
|
|
|
|
|
|
if markdown_match:
|
|
|
|
|
|
content = markdown_match.group(1).strip()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果内容为空,返回模拟数据
|
|
|
|
|
|
if not content:
|
|
|
|
|
|
print("AI返回内容为空,使用模拟数据")
|
|
|
|
|
|
return f"""# {user_prompt}
|
2025-09-04 05:47:42 +00:00
|
|
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
{user_prompt}是一个重要的概念和领域。
|
|
|
|
|
|
|
|
|
|
|
|
## 核心要素
|
|
|
|
|
|
- 要素1
|
|
|
|
|
|
- 要素2
|
|
|
|
|
|
- 要素3
|
|
|
|
|
|
|
|
|
|
|
|
## 应用场景
|
|
|
|
|
|
- 场景1
|
|
|
|
|
|
- 场景2
|
|
|
|
|
|
- 场景3
|
|
|
|
|
|
|
|
|
|
|
|
## 发展趋势
|
|
|
|
|
|
- 趋势1
|
|
|
|
|
|
- 趋势2
|
|
|
|
|
|
- 趋势3"""
|
2025-09-08 10:20:48 +00:00
|
|
|
|
|
|
|
|
|
|
return content
|
2025-09-04 05:47:42 +00:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"AI API调用异常: {e}")
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_markdown_from_text(text):
|
|
|
|
|
|
"""
|
|
|
|
|
|
从文本生成Markdown的便捷函数
|
|
|
|
|
|
"""
|
|
|
|
|
|
system_prompt = """你是一位Markdown格式转换专家。你的任务是将用户提供的文章内容精确转换为结构化的Markdown格式。请遵循以下步骤:
|
|
|
|
|
|
|
|
|
|
|
|
提取主标题: 识别文章最顶层的主标题(通常为文章题目或书名),并使用Markdown的 # 级别表示。
|
|
|
|
|
|
|
|
|
|
|
|
识别层级标题: 从文章内容中提取所有层级的内容标题(从主标题后的第一个标题开始,Level 1 至 Level 4)。判断层级依据:
|
|
|
|
|
|
|
|
|
|
|
|
视觉与结构特征: 如独立成行/段、位置(行首)、格式(加粗、编号如 1., 1.1, (1), - 等)。
|
|
|
|
|
|
|
|
|
|
|
|
语义逻辑: 标题之间的包含和并列关系。
|
|
|
|
|
|
|
|
|
|
|
|
在Markdown中,使用相应标题级别:
|
|
|
|
|
|
|
|
|
|
|
|
Level 1 标题用 ##
|
|
|
|
|
|
|
|
|
|
|
|
Level 2 标题用 ###
|
|
|
|
|
|
|
|
|
|
|
|
Level 3 标题用 ####
|
|
|
|
|
|
|
|
|
|
|
|
Level 4 标题用 #####
|
|
|
|
|
|
|
|
|
|
|
|
精确保留原文标题文字,不得修改、概括或润色。
|
|
|
|
|
|
|
|
|
|
|
|
处理正文内容: 对于每个标题下的正文内容区块(从该标题后开始,直到下一个同级或更高级别标题前):
|
|
|
|
|
|
|
|
|
|
|
|
直接保留原文文本,但根据内容结构适当格式化为Markdown。
|
|
|
|
|
|
|
|
|
|
|
|
如果内容是列表(如项目符号或编号列表),使用Markdown列表语法(例如 - 用于无序列表,1. 用于有序列表)。
|
|
|
|
|
|
|
|
|
|
|
|
保持段落和换行不变。
|
|
|
|
|
|
|
|
|
|
|
|
输出格式: 输出必须是纯Markdown格式的文本,不得包含任何额外说明、JSON或非Markdown元素。确保输出与示例风格一致。"""
|
|
|
|
|
|
|
|
|
|
|
|
user_prompt = f"请将以下内容转换为结构化的Markdown格式:\n\n{text}"
|
|
|
|
|
|
|
|
|
|
|
|
return call_ai_api(system_prompt, user_prompt)
|