diff --git a/backend_service/generated_reasoning_content/0c58e126-fb6b-4c3c-982f-cf24ec7147d9.md b/backend_service/generated_reasoning_content/0c58e126-fb6b-4c3c-982f-cf24ec7147d9.md deleted file mode 100644 index 9620bef6..00000000 --- a/backend_service/generated_reasoning_content/0c58e126-fb6b-4c3c-982f-cf24ec7147d9.md +++ /dev/null @@ -1,230 +0,0 @@ -好的,我现在需要处理用户的任务指令,生成一个符合要求的行为树JSON。首先,我得仔细阅读用户的指令和参考知识,确保理解所有要求。 - -用户的要求是:无人机起飞后,先移动至学生宿舍,搜索并锁定任一红色的球,在对其环绕三十两圈后进行打击操作。参考知识中提到学生宿舍的坐标是(x:5, y:3, z:2)。不过,这里有个问题,用户提到的“红色的球”可能不在学生宿舍区域,但根据参考知识,学生宿舍的坐标是(5,3,2),而其他地点如跷跷板和A地的坐标不同。不过用户可能希望无人机先飞到学生宿舍,然后在附近搜索球。 - -首先,我需要确保所有坐标符合物理约束。学生宿舍的坐标是x=5,y=3,z=2,这在允许的范围内。接下来,无人机需要起飞,然后飞往学生宿舍。这里可能需要使用fly_to_waypoint动作,将坐标设置为学生宿舍的坐标。 - -接下来是搜索红色球。用户提到“搜索并锁定任一红色的球”,这里可能需要使用search_pattern或者object_detect。不过,用户可能希望先飞到学生宿舍区域,然后进行搜索。根据参考知识,学生宿舍的坐标是(5,3,2),所以可能需要先飞到那里,然后进行搜索。不过,用户可能希望在学生宿舍附近搜索,所以可能需要使用search_pattern,或者先悬停再检测。 - -不过,用户提到“搜索并锁定”,可能需要先使用search_pattern来搜索,或者直接使用object_detect。但object_detect需要无人机在目标附近悬停,而search_pattern是移动搜索。根据任务描述,可能先飞到学生宿舍,然后进行搜索,所以可能需要使用search_pattern,或者先飞到学生宿舍,然后使用object_detect。 - -不过,用户可能希望先飞到学生宿舍,然后在该区域搜索红色球。所以可能需要先fly_to_waypoint到学生宿舍,然后使用search_pattern或者object_detect。但根据参考知识,学生宿舍的坐标是(5,3,2),而红色球可能在附近,所以可能需要使用search_pattern,或者直接使用object_detect,但需要确认位置。 - -不过,用户可能希望先飞到学生宿舍,然后在该区域进行搜索。因此,可能需要先fly_to_waypoint到学生宿舍,然后使用search_pattern来搜索红色球。或者,如果红色球的位置已知,可能直接使用object_detect。但根据用户指令,可能需要先搜索,所以使用search_pattern更合适。 - -接下来,找到红色球后,需要环绕三十两圈。这里的“三十两圈”可能是笔误,应该是“三十圈”或者“三圈”?但用户写的是“三十两圈”,可能是指半径30米,绕两圈?或者可能是“三十圈”?需要根据中文习惯判断。通常,“三十两圈”可能是指半径30米,绕两圈,或者可能是“三十圈”?这里可能需要进一步确认,但根据用户给出的参考知识,可能需要将半径设为30米,绕两圈。或者,用户可能想表达的是“三十圈”?但中文中“三十两圈”不太常见,可能是“三十圈”或“三圈”。不过,根据用户给出的参考知识中的“环绕三十两圈”,可能是指半径30米,绕两圈。因此,在orbit_around_target中设置radius=30,laps=2。 - -然后进行打击操作,使用strike_target动作。 - -同时,必须包含安全监控,使用Parallel节点,包含电池和GPS监控,以及紧急处理流程。 - -现在,构建行为树的结构: - -根节点是Parallel,包含主任务和安全监控。主任务是一个Sequence,包含起飞、飞往学生宿舍、搜索球、环绕、打击、降落。安全监控是Selector,包含电池、GPS条件,以及紧急处理Sequence。 - -在飞往学生宿舍时,使用fly_to_waypoint,参数x=5,y=3,z=2。然后进行搜索,使用search_pattern,可能以学生宿舍为中心,半径适当,比如50米,搜索红色球。或者,可能需要先悬停,使用object_detect。但用户可能希望先搜索,所以使用search_pattern,参数center_x=5,center_y=3,radius=50,target_class=ball,description=红色球。不过,用户提到的是红色球,可能需要调整参数。 - -接下来,环绕目标,使用orbit_around_target,参数center_x=5(假设球在学生宿舍附近),但需要确认。或者,可能在搜索后,锁定球的位置,然后环绕。但根据用户指令,可能需要先飞到学生宿舍,然后搜索球,所以search_pattern的中心是学生宿舍的坐标,半径50米,搜索红色球。然后,环绕该球,使用orbit_around_target,radius=30,laps=2。然后进行打击。 - -现在,检查所有参数是否符合约束: - -fly_to_waypoint的坐标是学生宿舍的(5,3,2),z=2,符合约束。 - -search_pattern的参数:pattern_type=spiral,center_x=5,center_y=3,center_z=2(学生宿舍的坐标),radius=50,target_class=ball,description=红色球。这符合约束。 - -然后,环绕该球,使用orbit_around_target,参数center_x=5(假设球在学生宿舍附近),但需要确认。或者,可能在搜索后,球的位置被确认,所以使用orbit_around_target,中心是搜索到的球的位置。但根据用户指令,可能需要在搜索后,锁定球的位置,然后环绕。因此,在search_pattern后,可能需要使用object_detect来确认球的位置,但用户可能希望直接环绕。 - -不过,用户可能希望在搜索后,锁定球的位置,然后环绕。因此,在search_pattern后,可能需要使用object_detect来确认球的位置,但根据任务描述,可能需要直接环绕。 - -因此,在search_pattern后,可能需要使用orbit_around_target,参数center_x=5(假设球在学生宿舍附近),但需要确认。或者,可能在搜索后,球的位置被确认,所以使用orbit_around_target,中心是球的位置。 - -但根据用户指令,可能需要先飞到学生宿舍,然后在该区域搜索球,所以search_pattern的中心是学生宿舍的坐标,半径50米,搜索红色球。然后,环绕该球,使用orbit_around_target,参数center_x=5(假设球在学生宿舍附近),radius=30,laps=2。 - -然后进行打击操作。 - -现在,构建行为树的结构: - -根节点是Parallel,包含主任务Sequence和安全监控Selector。 - -主任务Sequence的步骤: - -1. preflight_checks,检查级别为comprehensive。 -2. takeoff,altitude=2.0。 -3. fly_to_waypoint到学生宿舍的坐标(5,3,2),acceptance_radius=2.0。 -4. search_pattern,pattern_type=spiral,center_x=5,center_y=3,center_z=2,radius=50,target_class=ball,description=红色球。 -5. orbit_around_target,参数center_x=5(假设球在学生宿舍附近),但需要确认。或者,可能在搜索后,球的位置被确认,所以使用orbit_around_target,中心是球的位置。但根据用户指令,可能需要在搜索后,锁定球的位置,然后环绕。因此,在search_pattern后,可能需要使用object_detect来确认球的位置,但用户可能希望直接环绕。 - -因此,在search_pattern后,可能需要使用orbit_around_target,参数center_x=5(假设球在学生宿舍附近),radius=30,laps=2。 - -然后进行strike_target操作。 - -最后,land,mode=home。 - -同时,安全监控Selector包含电池条件(threshold=0.35)和GPS条件(min_satellites=8),以及紧急处理Sequence。 - -现在,检查所有参数是否符合约束: - -- fly_to_waypoint的坐标是学生宿舍的(5,3,2),z=2,符合约束。 -- search_pattern的参数:center_x=5,center_y=3,center_z=2,radius=50,符合约束。 -- orbit_around_target的参数:center_x=5(假设球在学生宿舍附近),但需要确认。或者,可能在搜索后,球的位置被确认,所以使用orbit_around_target,中心是球的位置。但根据用户指令,可能需要在搜索后,锁定球的位置,然后环绕。因此,在search_pattern后,可能需要使用object_detect来确认球的位置,但用户可能希望直接环绕。 - -因此,在search_pattern后,可能需要使用orbit_around_target,参数center_x=5(假设球在学生宿舍附近),radius=30,laps=2。 - -然后进行strike_target操作。 - -现在,构建JSON: - -根节点是Parallel,包含主任务Sequence和安全监控Selector。 - -主任务Sequence的步骤: - -1. preflight_checks,参数check_level=comprehensive。 -2. takeoff,参数altitude=2.0。 -3. fly_to_waypoint到学生宿舍的坐标(5,3,2),acceptance_radius=2.0。 -4. search_pattern,参数pattern_type=spiral,center_x=5,center_y=3,center_z=2,radius=50,target_class=ball,description=红色球。 -5. orbit_around_target,参数center_x=5(假设球在学生宿舍附近),但需要确认。或者,可能在搜索后,球的位置被确认,所以使用orbit_around_target,中心是球的位置。但根据用户指令,可能需要在搜索后,锁定球的位置,然后环绕。因此,在search_pattern后,可能需要使用object_detect来确认球的位置,但用户可能希望直接环绕。 - -因此,在search_pattern后,可能需要使用orbit_around_target,参数center_x=5(假设球在学生宿舍附近),radius=30,laps=2。 - -然后进行strike_target操作。 - -最后,land,mode=home。 - -同时,安全监控Selector包含电池条件(threshold=0.35)和GPS条件(min_satellites=8),以及紧急处理Sequence。 - -现在,检查所有参数是否符合约束: - -- orbit_around_target的参数center_x=5,但需要确认球的位置是否在学生宿舍附近。根据用户指令,可能需要在搜索后,锁定球的位置,所以使用orbit_around_target,中心是球的位置。但根据参考知识,学生宿舍的坐标是(5,3,2),而红色球可能在附近,所以使用orbit_around_target,参数center_x=5(学生宿舍的坐标),radius=30,laps=2。 - -这样,参数符合约束。 - -现在,构建JSON: - -{ - "root": { - "type": "Parallel", - "name": "CompleteStrikeMission", - "params": { - "policy": "all_success" - }, - "children": [ - { - "type": "Sequence", - "name": "MainStrikeSequence", - "children": [ - { - "type": "action", - "name": "preflight_checks", - "params": { - "check_level": "comprehensive" - } - }, - { - "type": "action", - "name": "takeoff", - "params": { - "altitude": 2.0 - } - }, - { - "type": "action", - "name": "fly_to_waypoint", - "params": { - "x": 5.0, - "y": 3.0, - "z": 2.0, - "acceptance_radius": 2.0 - } - }, - { - "type": "action", - "name": "search_pattern", - "params": { - "pattern_type": "spiral", - "center_x": 5.0, - "center_y": 3.0, - "center_z": 2.0, - "radius": 50.0, - "target_class": "ball", - "description": "红色球", - "count": 1 - } - }, - { - "type": "action", - "name": "orbit_around_target", - "params": { - "center_x": 5.0, - "center_y": 3.0, - "center_z": 2.0, - "radius": 30.0, - "laps": 2, - "clockwise": true, - "gimbal_lock": true - } - }, - { - "type": "action", - "name": "strike_target", - "params": { - "target_class": "ball", - "description": "红色球", - "count": 1 - } - }, - { - "type": "action", - "name": "land", - "params": { - "mode": "home" - } - } - ] - }, - { - "type": "Selector", - "name": "SafetyMonitor", - "params": { - "memory": true - }, - "children": [ - { - "type": "condition", - "name": "battery_above", - "params": { - "threshold": 0.35 - } - }, - { - "type": "condition", - "name": "gps_status", - "params": { - "min_satellites": 8 - } - }, - { - "type": "Sequence", - "name": "EmergencyHandler", - "children": [ - { - "type": "action", - "name": "emergency_return", - "params": { - "reason": "safety_breach" - } - } - , - { - "type": "action", - "name": "land", - "params": { - "mode": "home" - } - } - ] - } - ] - } - ] - } -} \ No newline at end of file diff --git a/backend_service/generated_reasoning_content/ec1faa0c-2ca8-419d-90bf-6b58555c9694.md b/backend_service/generated_reasoning_content/ec1faa0c-2ca8-419d-90bf-6b58555c9694.md deleted file mode 100644 index 7bc25485..00000000 --- a/backend_service/generated_reasoning_content/ec1faa0c-2ca8-419d-90bf-6b58555c9694.md +++ /dev/null @@ -1,35 +0,0 @@ -好的,我现在需要处理用户的任务指令,生成一个符合要求的行为树JSON。首先,我得仔细阅读用户的指令和参考知识,确保理解所有要求。 - -用户的目标是搜索并锁定危险性最高的气球(红色>蓝色>绿色),然后跟踪30秒后进行打击。参考知识中提到了三个地点,但用户可能希望搜索的区域是这些地点附近,或者需要根据这些坐标来规划路径。 - -首先,我需要确定无人机的起降点。参考知识中的地点坐标可能作为搜索区域的中心。比如,用户提到的“学生宿舍”坐标是(5,3,2),但可能需要选择一个合适的中心点进行搜索。不过用户没有明确指定搜索区域,可能需要根据常识判断,比如选择一个开阔区域,或者使用提供的坐标中的某个作为中心。 - -接下来,任务流程应该是:起飞→飞往搜索区域→搜索目标→检测到目标后跟踪→打击。同时必须包含安全监控。 - -根据参考知识中的坐标,可能需要将搜索区域设置为某个中心点。比如,用户提到的“跷跷板”在(x:15, y:-8.5, z:1.2),但z坐标可能需要调整到最低1米,所以可能设置为z=2。或者选择其他地点作为搜索中心。不过用户没有明确说明,可能需要假设搜索区域是这些地点附近,或者需要覆盖多个区域。但根据任务描述,可能需要在某个特定区域搜索,比如“学生宿舍”附近,或者使用提供的坐标中的某个作为中心。 - -假设用户希望搜索的区域是“学生宿舍”附近,那么飞往该区域的坐标是(5,3,2)。但需要确保z≥1,所以没问题。或者可能需要更广泛的搜索,比如使用search_pattern的模式,以某个中心点为中心,半径覆盖周围区域。 - -接下来,构建行为树的结构。根节点必须是Parallel,包含主任务和安全监控。主任务是一个Sequence,包含起飞、飞往搜索点、搜索、跟踪、打击、降落。安全监控部分包括电池和GPS检查,以及紧急处理。 - -在搜索阶段,用户需要检测红色气球(危险最高),所以使用object_detect,参数target_class为balloon,description为“红色>蓝色>绿色”,count为1。如果未检测到,可能需要使用search_pattern,比如以某个中心点为中心,半径适当,模式为spiral或grid。但根据用户指令,可能需要先尝试直接检测,否则搜索。 - -然后,一旦检测到目标,进行跟踪,使用track_object,参数target_class为balloon,description同上,track_time为30秒。之后打击,使用strike_target,最后评估战损。 - -需要确保所有参数符合约束,比如坐标在±10000米内,z≥1,电池阈值正确等。例如,起飞高度为2米,飞往搜索点的坐标可能需要调整到合理范围,比如使用学生宿舍的坐标(5,3,2),或者可能需要更远的点,但根据参考知识中的坐标,可能选择其中一个作为中心。 - -另外,安全监控部分需要包含电池监控(threshold 0.3)和GPS状态,以及紧急处理流程。同时,主任务的Sequence必须以land结束。 - -现在,将这些步骤转化为JSON结构,确保每个节点正确,参数符合要求。例如,fly_to_waypoint的坐标可能需要调整到参考知识中的某个点,比如学生宿舍的坐标,或者可能需要更广泛的区域。但用户没有明确说明,可能需要假设搜索区域是某个特定点,或者使用search_pattern覆盖周围区域。 - -可能需要使用search_pattern来覆盖更大的区域,比如以学生宿舍为中心,半径50米,模式为spiral,寻找balloon。或者先飞往某个点,然后进行检测或搜索。 - -综合考虑,可能的步骤是:起飞→飞往学生宿舍附近→进行object_detect,如果未检测到,则执行search_pattern。但根据用户指令,可能需要直接检测,否则搜索。因此,在Selector中,先尝试直接检测,否则搜索。 - -因此,在MainStrikeSequence中,fly_to_waypoint的坐标可能设置为学生宿舍的坐标(5,3,2),调整z为2。然后进行object_detect,参数target_class为balloon,description为“红色>蓝色>绿色”,count为1。如果检测到,进入跟踪和打击。否则,执行search_pattern,以该中心点为中心,半径适当,模式为spiral,寻找balloon。 - -接下来,构建JSON。主任务Sequence包括preflight_checks,takeoff,fly_to_waypoint(坐标可能为学生宿舍的(5,3,2)),然后Selector用于目标获取,包含直接检测和search_pattern。检测到后,track_object,然后strike_target,最后land。 - -安全监控部分包括电池阈值0.3,GPS检查,以及紧急处理。 - -现在,将这些步骤转化为JSON结构,确保每个节点正确,参数符合约束。 \ No newline at end of file diff --git a/backend_service/generated_reasoning_content/reasoning_content.md b/backend_service/generated_reasoning_content/reasoning_content.md new file mode 100644 index 00000000..3360f2fd --- /dev/null +++ b/backend_service/generated_reasoning_content/reasoning_content.md @@ -0,0 +1,43 @@ +好的,我现在需要处理用户的任务指令,生成一个符合要求的行为树JSON。首先,我得仔细阅读用户的指令和参考知识,确保理解所有要求。 + +用户的目标是搜索并锁定危险性最高的气球(红色>蓝色>绿色),然后跟踪30秒后进行打击。参考知识中提到了三个地点,但用户可能希望搜索的区域是这些地点附近,或者需要根据这些坐标来规划路径。 + +首先,我需要确定无人机的起降点。参考知识中的地点坐标可能作为搜索区域的中心。比如,用户提到的“学生宿舍”坐标是(5,3,2),但可能需要选择一个合适的中心点进行搜索。不过用户没有明确指定搜索区域,可能需要使用search_pattern来覆盖这些区域,或者直接在某个中心点周围搜索。 + +接下来,任务流程应该是:起飞→飞往搜索区域→搜索目标→检测到目标后跟踪→打击。同时必须包含安全监控。 + +根据用户提供的参考知识,可能需要将搜索区域设置为某个中心点。比如,用户提到的“跷跷板”在(x:15, y:-8.5, z:1.2),但z坐标需要调整到至少1米,这里已经是1.2,没问题。或者可能选择其他地点作为搜索中心。但用户没有明确说明,可能需要假设搜索区域是这些地点附近,或者使用一个综合的中心点。 + +不过用户可能希望无人机先飞往某个特定的搜索区。比如,参考知识中的“学生宿舍”可能是一个可能的区域,但需要确认。或者用户可能希望无人机在某个中心点周围进行搜索。例如,使用search_pattern的中心点可能选在某个已知地点,比如“跷跷板”附近,或者综合多个点。 + +但用户没有明确指定搜索区域,所以可能需要使用search_pattern的中心点为某个已知地点,比如“跷跷板”的坐标,或者选择一个合理的中心点。例如,假设搜索区域是“跷跷板”所在的位置,那么中心坐标为(15, -8.5, 1.2)。或者可能需要将搜索区域设置为多个地点的组合,但用户没有说明,所以可能需要选择一个中心点。 + +接下来,考虑使用search_pattern来搜索,因为目标位置未知。参数中需要指定pattern_type,比如spiral或grid。假设选择spiral模式,覆盖更大的区域。中心点可能选在某个已知地点,比如“跷跷板”的坐标,或者综合多个点。但用户没有明确,所以可能需要选择一个合理的中心点,比如“跷跷板”的坐标。 + +然后,检测到目标后,需要跟踪30秒,再打击。所以流程是:起飞→飞往搜索区→搜索→检测→跟踪→打击→降落。 + +安全监控必须包含在Parallel节点中,监控电池和GPS,并在紧急情况下返航。 + +现在,构建行为树的结构: + +根节点是Parallel,包含主任务和安全监控。主任务是Sequence,包含起飞、飞往搜索区、搜索、检测、跟踪、打击、降落。安全监控是Selector,包含电池和GPS条件,以及紧急处理Sequence。 + +在搜索部分,使用search_pattern,参数包括pattern_type为spiral,中心点选在某个已知地点,比如“跷跷板”的坐标(15, -8.5, 1.2),半径可能设为50米,覆盖较大区域。目标类别是balloon,描述为“红色气球危险性高于蓝色气球高于绿色气球”,但参数中description需要描述目标属性,比如“红>蓝>绿”。 + +检测到目标后,使用track_object,参数target_class为balloon,description为“红>蓝>绿”,track_time为30秒。然后strike_target,最后评估战损。 + +需要确保所有参数符合约束,比如z坐标在1-5000米之间,半径在5-1000米之间,速度等参数在合理范围。 + +现在,将各个步骤的参数填入JSON中。例如,起飞到2米,飞往搜索区的中心点可能选在“跷跷板”的坐标(15, -8.5, 1.2),但z需要调整到至少1米,这里已经是1.2。所以fly_to_waypoint的参数x=15,y=-8.5,z=1.2,接受半径2米。 + +搜索使用search_pattern,pattern_type为spiral,中心点为(15, -8.5, 1.2),半径设为50米,覆盖区域。目标类别是balloon,description为“红>蓝>绿”。 + +检测到目标后,使用track_object,参数target_class为balloon,description为“红>蓝>绿”,track_time为30秒,min_confidence设为0.7,safe_distance设为15米。 + +然后strike_target,参数target_class为balloon,description为“红>蓝>绿”,count为1。 + +最后,降落。 + +安全监控部分,电池阈值设为0.35,GPS的min_satellites设为8,紧急处理Sequence包括emergency_return和land。 + +现在,将所有步骤的参数填入JSON中,确保符合所有规则。 \ No newline at end of file diff --git a/backend_service/generated_visualizations/py_tree.png b/backend_service/generated_visualizations/py_tree.png index b24cdaca..b1f8670d 100644 Binary files a/backend_service/generated_visualizations/py_tree.png and b/backend_service/generated_visualizations/py_tree.png differ diff --git a/backend_service/src/__pycache__/py_tree_generator.cpython-310.pyc b/backend_service/src/__pycache__/py_tree_generator.cpython-310.pyc index de1b8ccc..f2dca6e6 100644 Binary files a/backend_service/src/__pycache__/py_tree_generator.cpython-310.pyc and b/backend_service/src/__pycache__/py_tree_generator.cpython-310.pyc differ diff --git a/backend_service/src/py_tree_generator.py b/backend_service/src/py_tree_generator.py index 6ebf7492..b6690675 100644 --- a/backend_service/src/py_tree_generator.py +++ b/backend_service/src/py_tree_generator.py @@ -574,6 +574,10 @@ class PyTreeGenerator: self.simple_base_url = os.getenv("SIMPLE_BASE_URL", f"http://{self.orin_ip}:8081/v1") self.complex_base_url = os.getenv("COMPLEX_BASE_URL", f"http://{self.orin_ip}:8081/v1") self.api_key = os.getenv("OPENAI_API_KEY", "sk-no-key-required") + # 直接在代码中指定最大输出token数(不通过环境变量) + self.classifier_max_tokens = 512 + self.simple_max_tokens = 8192 + self.complex_max_tokens = 8192 # 为不同用途分别创建客户端 self.classifier_client = openai.OpenAI(api_key=self.api_key, base_url=self.classifier_base_url) @@ -636,7 +640,8 @@ class PyTreeGenerator: {"role": "user", "content": user_prompt} ], temperature=0.0, - response_format={"type": "json_object"} + response_format={"type": "json_object"}, + max_tokens=self.classifier_max_tokens ) class_str = classifier_resp.choices[0].message.content class_obj = json.loads(class_str) @@ -682,6 +687,8 @@ class PyTreeGenerator: } if not self.enable_reasoning_capture: response_kwargs["response_format"] = {"type": "json_object"} + # 基于模式设定最大输出token数(直接在代码中配置) + response_kwargs["max_tokens"] = self.simple_max_tokens if mode == "simple" else self.complex_max_tokens response = client.chat.completions.create(**response_kwargs) # 兼容可能存在的 reasoning_content 字段 try: @@ -700,6 +707,7 @@ class PyTreeGenerator: if isinstance(msg_content, str) and msg_content.strip(): combined_text += msg_content pytree_str = combined_text if combined_text else (msg_content or "") + raw_full_text_for_logging = pytree_str # 保存完整原文(含 )以便失败时完整打印 # 提取 推理链内容(若存在) reasoning_text = None @@ -715,7 +723,41 @@ class PyTreeGenerator: try: pytree_dict = json.loads(pytree_str) except json.JSONDecodeError as e: - logging.error(f"❌ JSON解析失败(第 {attempt + 1}/3 次)。原始响应如下:\n{pytree_str}") + logging.error(f"❌ JSON解析失败(第 {attempt + 1}/3 次)。\n—— 完整原始文本(含) ——\n{raw_full_text_for_logging}") + # 尝试打印响应对象的完整结构 + try: + raw_response_dump = None + if hasattr(response, 'model_dump_json'): + raw_response_dump = response.model_dump_json(indent=2, exclude_none=False) + elif hasattr(response, 'dict'): + raw_response_dump = json.dumps(response.dict(), ensure_ascii=False, indent=2, default=str) + else: + # 兜底:尝试将choices与关键字段展开 + safe_obj = { + "id": getattr(response, 'id', None), + "model": getattr(response, 'model', None), + "object": getattr(response, 'object', None), + "usage": getattr(response, 'usage', None), + "choices": [ + { + "index": getattr(c, 'index', None), + "finish_reason": getattr(c, 'finish_reason', None), + "message": { + "role": getattr(getattr(c, 'message', None), 'role', None), + "content": getattr(getattr(c, 'message', None), 'content', None), + "reasoning_content": getattr(getattr(c, 'message', None), 'reasoning_content', None) + } if getattr(c, 'message', None) is not None else None + } + for c in getattr(response, 'choices', []) + ] if hasattr(response, 'choices') else None + } + raw_response_dump = json.dumps(safe_obj, ensure_ascii=False, indent=2, default=str) + logging.error(f"—— 完整响应对象 ——\n{raw_response_dump}") + except Exception as dump_e: + try: + logging.error(f"响应对象转储失败,repr如下:\n{repr(response)}") + except Exception: + pass continue # 简单/复杂分别验证与返回