Files
dsProject/dsLightRag/Util/GGBUtil.py
2025-08-14 15:45:08 +08:00

263 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import logging
from openai import AsyncOpenAI
from zai import ZhipuAiClient
from Config.Config import ZHIPU_API_KEY
# 设置日志
logger = logging.getLogger(__name__)
def create_llm_client() -> AsyncOpenAI:
"""初始化并返回异步OpenAI客户端"""
return AsyncOpenAI(
#api_key=LLM_API_KEY,
#base_url=LLM_BASE_URL
api_key="sk-f6da0c787eff4b0389e4ad03a35a911f",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 【5】如果在使用Segment创建两个点的线段时需要指定一个标签名然后使用ShowLabel隐藏这个标签名注意使用的是ShowLabel而不是SetVisibleInView而且只是Segment创建两个点的线段时才隐藏标签名
async def getGgbCommand(QvqResult):
"""异步版本的getGgb函数 - 支持流式输出"""
custom_prompt = """
# Role: GeoGebra 几何图形生成器
## Profile
- language: 中文
- description: 专门用于根据初中几何题的题干和几何图形的描述来生成Geogebra指令集的角色确保生成的指令集严格遵循几何约束和动态坐标系构建规则并具备错误熔断机制。
- background: 拥有深厚的几何学和计算机科学背景熟悉Geogebra软件的操作和指令集编写。
- personality: 严谨、细致、高效
- expertise: 几何学、计算机编程、Geogebra软件应用
**几何图形生成规则(通用强制约束版)**
请严格按以下优先级执行,不受题目具体内容影响:
1. **几何约束最高优先级**
- 描述中提到的水平线段,一定要保持水平,不可以进行变更
- 当文字描述与坐标/位置冲突时,以文字描述的几何关系为准
- 必须自动验证:{长度|角度|中点|平行|垂直}关系
- 验证失败时:重新计算坐标并报告修正
- 有动点的,要设置滑块。没有动点的,不要设置滑块。
- 检查描述中提到的最长的边做为X轴最长边的左端点做为原点
2. **动态坐标系构建**
```mermaid
graph TB
A[定位原点] --> B{原点坐标明确?}
B -->|是| C[直接采用]
B -->|否| D[根据相对关系推算]
E[建立坐标系] --> F{存在水平/垂直要求?}
F -->|是| G[调整坐标满足]
F -->|否| H[保持默认方向]
```
3、错误熔断机制
检测到矛盾 → 触发修正流程:
if not satisfy_constraint(description):
new_coords = recalculate_by_geometry()
log_adjustment(old_coords, new_coords, reason)
4、元素生成协议
顺序:固定点 → 固定线 → 动态点(初始位) → 动态元素
依赖检查:确保父元素先于子元素生成
5、输出结构强制
[修正报告] (若有调整)
[坐标摘要] (最终使用坐标)
[Geogebra指令] (完整可执行代码,以```geogebra 开头,以 ```结束。【注意】```geogebra 前面一定要加上 \n 换行!)
注意:
【1】GGB指令集中不要添加注释不要添加除指令集外的其它描述性文字需要可以直接批量执行的
【2】仔细检查原图中是线段还是直线要严格进行识别
【3】仔细检查原图中的每一条边是否都正确进行了线段标识
【4】不显示网格使用ShowGrid(false)不显示坐标轴使用ShowAxes(false)
【5】注意不要使用Perpendicular需要使用PerpendicularLine来替代
【6】注意不要使用SetLabel因为会提示“未知的指令”。
【7】注意不要使用Angle无需标注角度。
【8】如果有滑块使用Slider
【9】如果没有动点就无需添加滑块。
【10】注意不要使用TangentPoint因为会提示“未知的指令”。
6、题目的文字描述信息
```
{QvqResult}
```
"""
try:
# 创建异步OpenAI客户端
client = create_llm_client()
# 使用异步流式调用
completion = await client.chat.completions.create(
model="deepseek-r1-0528",
#model="deepseek-reasoner",
messages=[{"role": "user", "content": custom_prompt.replace("{QvqResult}", QvqResult)}],
stream=True
)
is_answering = False
async for chunk in completion:
delta = chunk.choices[0].delta
# 打印思考过程
if hasattr(delta, 'reasoning_content') and delta.reasoning_content != None:
yield delta.reasoning_content
else:
# 开始回复
if delta.content != "" and is_answering is False:
print("\n" + "=" * 20 + "完整回复" + "=" * 20 + "\n")
is_answering = True
# 打印回复过程
yield delta.content
except Exception as e:
print(f"异步getGgb调用错误: {str(e)}")
yield f"错误: {str(e)}"
async def translate_to_ggb(natural_language_cmd):
"""将自然语言指令翻译成GeoGebra指令集异步版本"""
# 精简的prompt专注于单句指令翻译
custom_prompt = f"""
请将以下自然语言指令翻译为GeoGebra指令集
"{natural_language_cmd}"
要求:
1. 仅返回GeoGebra指令不包含任何解释性文字
2. 指令必须可直接在GeoGebra中执行
3. 保持指令简洁明确
4. 以```geogebra 开头,以 ```结束 【注意】```geogebra 前面一定要加上 \n 换行!
5. 用户提到的元素比如点A,线段AB等都视为已存在不要重新创建
"""
try:
# 创建异步OpenAI客户端
client = create_llm_client()
# 使用异步流式调用
completion = await client.chat.completions.create(
model="deepseek-r1-0528",
messages=[{"role": "user", "content": custom_prompt}],
stream=True
)
is_answering = False
async for chunk in completion:
delta = chunk.choices[0].delta
# 打印思考过程
if hasattr(delta, 'reasoning_content') and delta.reasoning_content != None:
yield delta.reasoning_content
else:
# 开始回复
if delta.content != "" and is_answering is False:
print("\n" + "=" * 20 + "完整回复" + "=" * 20 + "\n")
is_answering = True
# 打印回复过程
yield delta.content
except Exception as e:
print(f"翻译指令错误: {str(e)}")
yield f"错误: {str(e)}"
# 示例用法
if __name__ == '__main__':
# file_name = "QvqResult_14.txt"
# with open(file_name, 'r', encoding='utf-8') as file:
# QvqResult = file.read() # 直接获取整个文件内容为字符串
#
# async def test():
# async for content in getGgbCommand(QvqResult):
# if content:
# print(content, end="", flush=True)
#
#
# asyncio.run(test())
async def test_translate():
async for content in translate_to_ggb("把A和B连接起来"):
if content:
print(content, end="", flush=True)
# 运行测试
asyncio.run(test_translate())
async def process_geometry_image(image_url: str):
"""
真正的流式处理几何题图片
"""
client = ZhipuAiClient(api_key=ZHIPU_API_KEY)
prompt = """你是"初中几何题-图像结构提取器",专为初中生设计。任务只有一项:
看到几何题图片后,用中文输出"完整的题干原文 + 图形客观结构描述",绝口不提解题思路或答案。
【返回格式】标准markdown+latex格式
目标:
让后续 GeoGebra 生成器仅凭你的输出即可复刻原图,同时让初中生一眼看懂图形。
输出格式(固定四段)
题干原文OCR 结果,逐字照录)
基本元素表(点、线、圆、角、标注长度/角度,按出现顺序编号)
关系表(平行、垂直、相切、全等、相似、共线、共点等)
给出各元素大概位置或坐标
写作规范:
一律用中文,术语用课本标准词。
描述顺序:先整体外形(如"梯形 ABCD"),再局部细节(如"AB∥CD")。
长度、角度直接写数值并带单位;若图中仅给符号,则照抄符号。
位置关系用"//、⊥、≅、∼、∈"等符号,后接括号说明。
注意几何图形的形状和线段的长度
不使用"可能、大约"等模糊词。
不解释题意,不提示解法。
注意:
一定要以图为主,不要根据题干内容随意发挥。除非题干中提到了:长度、角度等有实际意义的才进行参考
图中没有线段一定不要出现,图中存在的线段一定不要缺失。
示例模板:
【题干原文】
如图,梯形 ABCD 中AB∥CD …(以下略)
【基本元素表】
A、B、C、D、E对角线交点
线段AB=6 cmCD=10 cmAD=4 cmBC=4 cmACBD
∠DAB=60°∠ABC=120°
圆:无
【关系表】
AB // CD
AD = BC
AC 与 BD 交于 E
∠DAB 与 ∠ABC 互补
【元素位置】
A点在左上B点在右上C点在A点和B点的下方
——请严格按以上格式输出,不要省略任何字段。"""
try:
messages = [{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{"type": "image_url", "image_url": {"url": image_url}}
]
}]
# 使用智谱SDK的流式调用同步调用包装为异步
stream = await asyncio.to_thread(
client.chat.completions.create,
model="glm-4.1v-thinking-flash",
messages=messages,
stream=True
)
# 异步处理流式输出
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content is not None:
content = chunk.choices[0].delta.content
yield content
except Exception as e:
logger.error(f"流式处理图片时发生错误: {str(e)}")
yield ""