Skip to content

Commit 3c866da

Browse files
authored
feat: support agent grader datasets for qwen3 model GRPO training (#143)
1 parent 8131b74 commit 3c866da

3 files changed

Lines changed: 164 additions & 18 deletions

File tree

cookbooks/training_judge_model/grpo/grader_rl_dataset.py

Lines changed: 141 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import copy
2+
import json
23
import logging
34
import os
45
from dataclasses import dataclass, field
@@ -502,61 +503,184 @@ def _format_template(self, messages: List[dict], example: dict) -> str:
502503
INSTRUCTION_FOLLOWING_PROMPT_EN = self._import_with_fallback(
503504
"openjudge.graders.common.instruction_following",
504505
"INSTRUCTION_FOLLOWING_PROMPT_EN",
505-
"Evaluate the instruction_following of the response to the query. Query: {query}, Response: {response}",
506+
"Evaluate the instruction following of the response to the query. Query: {query}, Response: {response}",
507+
)
508+
509+
ACTION_ALIGNMENT_PROMPT_EN = self._import_with_fallback(
510+
"openjudge.graders.agent.action.action_alignment",
511+
"ACTION_ALIGNMENT_PROMPT_EN",
512+
"Evaluate the action alignment of the response to the query. Query: {query}, Response: {response}",
513+
)
514+
515+
PLAN_FEASIBILITY_PROMPT_EN = self._import_with_fallback(
516+
"openjudge.graders.agent.plan.plan_feasibility",
517+
"PLAN_FEASIBILITY_PROMPT_EN",
518+
"Evaluate the plan feasibility of the response to the query. Query: {query}, Response: {response}",
519+
)
520+
521+
REFLECTION_ACCURACY_PROMPT_EN = self._import_with_fallback(
522+
"openjudge.graders.agent.reflection.reflection_accuracy",
523+
"REFLECTION_ACCURACY_PROMPT_EN",
524+
"Evaluate the reflection accuracy of the response to the query. Query: {query}, Response: {response}",
525+
)
526+
527+
REFLECTION_OUTCOME_UNDERSTANDING_PROMPT_EN = self._import_with_fallback(
528+
"openjudge.graders.agent.reflection.reflection_outcome_understanding",
529+
"REFLECTION_OUTCOME_UNDERSTANDING_PROMPT_EN",
530+
"Evaluate the reflection outcome understanding of the response to the query. Query: {query}, Response: {response}",
531+
)
532+
533+
REFLECTION_PROGRESS_AWARENESS_PROMPT_EN = self._import_with_fallback(
534+
"openjudge.graders.agent.reflection.reflection_progress_awareness",
535+
"REFLECTION_PROGRESS_AWARENESS_PROMPT_EN",
536+
"Evaluate the reflection progress awareness of the response to the query. Query: {query}, Response: {response}",
537+
)
538+
539+
TOOL_CALL_ACCURACY_PROMPT_EN = self._import_with_fallback(
540+
"openjudge.graders.agent.tool.tool_call_accuracy",
541+
"TOOL_CALL_ACCURACY_PROMPT_EN",
542+
"Evaluate the tool call accuracy of the response to the query. Query: {query}, Response: {response}",
543+
)
544+
545+
TOOL_CALL_SUCCESS_PROMPT_EN = self._import_with_fallback(
546+
"openjudge.graders.agent.tool.tool_call_success",
547+
"TOOL_CALL_SUCCESS_PROMPT_EN",
548+
"Evaluate the tool call success of the response to the query. Query: {query}, Response: {response}",
549+
)
550+
551+
TOOL_PARAMETER_CHECK_PROMPT_EN = self._import_with_fallback(
552+
"openjudge.graders.agent.tool.tool_parameter_check",
553+
"TOOL_PARAMETER_CHECK_PROMPT_EN",
554+
"Evaluate the tool parameter check of the response to the query. Query: {query}, Response: {response}",
555+
)
556+
557+
TOOL_SELECTION_PROMPT_EN = self._import_with_fallback(
558+
"openjudge.graders.agent.tool.tool_selection",
559+
"TOOL_SELECTION_PROMPT_EN",
560+
"Evaluate the tool selection of the response to the query. Query: {query}, Response: {response}",
506561
)
507562

508563
task_type = example.get("task_type", "unknown")
509564

510-
if task_type == "correctness":
565+
if "correctness" in task_type:
511566
grader_template = CORRECTNESS_PROMPT_EN
512-
elif task_type == "hallucination":
567+
elif "hallucination" in task_type:
513568
grader_template = HALLUCINATION_PROMPT_EN
514-
elif task_type == "relevance":
569+
elif "relevance" in task_type:
515570
grader_template = RELEVANCE_PROMPT_EN
516-
elif task_type == "harmlessness":
571+
elif "harmlessness" in task_type:
517572
grader_template = HARMFULNESS_PROMPT_EN
518-
elif task_type == "instruction_following":
573+
elif "instruction_following" in task_type:
519574
grader_template = INSTRUCTION_FOLLOWING_PROMPT_EN
575+
elif "action_alignment" in task_type:
576+
grader_template = ACTION_ALIGNMENT_PROMPT_EN
577+
elif "plan_feasibility" in task_type:
578+
grader_template = PLAN_FEASIBILITY_PROMPT_EN
579+
elif "reflection_accuracy" in task_type:
580+
grader_template = REFLECTION_ACCURACY_PROMPT_EN
581+
elif "reflection_outcome_understanding" in task_type:
582+
grader_template = REFLECTION_OUTCOME_UNDERSTANDING_PROMPT_EN
583+
elif "reflection_progress_awareness" in task_type:
584+
grader_template = REFLECTION_PROGRESS_AWARENESS_PROMPT_EN
585+
elif "tool_call_accuracy" in task_type:
586+
grader_template = TOOL_CALL_ACCURACY_PROMPT_EN
587+
elif "tool_call" in task_type:
588+
grader_template = TOOL_CALL_SUCCESS_PROMPT_EN
589+
elif "tool_parameter" in task_type:
590+
grader_template = TOOL_PARAMETER_CHECK_PROMPT_EN
591+
elif "tool_selection" in task_type:
592+
grader_template = TOOL_SELECTION_PROMPT_EN
520593
else:
521594
# Default to correctness if unknown template
522595
pprint(f"task type: {task_type}")
523596
raise ValueError(
524597
f"Unknown task type: {task_type}. Valid types: correctness, hallucination, relevance, "
525-
f"harmlessness, instruction_following"
598+
f"harmlessness, instruction_following, action_alignment, plan_feasibility, reflection_accuracy, "
599+
f"reflection_outcome_understanding, reflection_progress_awareness, "
600+
f"tool_call_accuracy, tool_call_success, tool_parameter_check, tool_selection, "
526601
)
527602
return self._format_grader_template(messages, example, grader_template)
528603

529604
def _format_grader_template(self, messages: List[dict], example: dict, grader_prompt: str) -> str:
530605
"""Format correctness evaluation template using openjudge prompt."""
606+
context = ""
607+
response = ""
608+
reference_response = ""
609+
tool_calls = ""
610+
tool_definitions = ""
611+
tool_responses = ""
612+
observation = ""
613+
plan = ""
614+
history = ""
615+
memory = ""
616+
action = ""
617+
reflection = ""
531618
if "input" in example and isinstance(example["input"], dict) and "query" in example["input"]:
532619
# New JSON format
533620
query = example["input"].get("query", "")
534-
context = example["input"].get("context") or "" # Handle null value
535-
reference_response = example["input"].get("reference", "")
621+
context = example["input"].get("context", "")
622+
if context:
623+
if isinstance(context, dict):
624+
# Extract fields directly if context is already a dictionary
625+
context = context.get("task_context", "")
626+
tool_definitions = context.get("tool_definitions", "")
627+
history = context.get("history", "")
628+
elif isinstance(context, str):
629+
try:
630+
# Attempt to parse JSON string into a dictionary
631+
parsed_data = json.loads(context)
632+
633+
# Ensure the parsed result is actually a dictionary before accessing keys
634+
if isinstance(parsed_data, dict):
635+
context = parsed_data.get("task_context", "")
636+
tool_definitions = parsed_data.get("tool_definitions", "")
637+
history = parsed_data.get("history", "")
638+
639+
except (json.JSONDecodeError, TypeError, Exception):
640+
# If parsing fails, continue without raising an error (keep default values)
641+
pass
536642

537-
response = ""
643+
reference_response = example["input"].get("reference", "")
538644
if "answer" in example and isinstance(example["answer"], dict):
539645
answer_response = example["answer"].get("response", {})
540646
if isinstance(answer_response, dict):
541647
response = answer_response.get("content", "")
648+
tool_calls = answer_response.get("tool_calls", "")
649+
tool_responses = answer_response.get("tool_responses", "")
650+
plan = answer_response.get("plan", "")
651+
observation = answer_response.get("observation", "")
652+
memory = answer_response.get("memory", "")
653+
action = answer_response.get("action", "")
654+
reflection = answer_response.get("reflection", "")
542655
# Also try 'response' field as fallback
543656
elif "response" in example and isinstance(example["response"], dict):
544657
response = example["response"].get("content", "")
545658
else:
546659
# Old format - extract from messages
547660
query = next((msg["content"] for msg in messages if msg["role"] == "user"), "")
548661
response = self._get_response_content(example)
549-
reference_response = None
550-
context = None
551662

552663
instruction = query
664+
available_tools = tool_definitions
665+
selected_tools = tool_calls
553666
# Replace placeholders in the grader prompt
554667
formatted_prompt = grader_prompt.format(
555-
query=query or "",
556-
response=response or "",
557-
reference_response=reference_response or "",
558-
context=str(context) or "",
559-
instruction=instruction or "",
668+
query=query,
669+
response=response,
670+
reference_response=reference_response,
671+
context=str(context),
672+
instruction=instruction,
673+
tool_calls=str(tool_calls),
674+
tool_definitions=str(tool_definitions),
675+
tool_responses=str(tool_responses),
676+
available_tools=str(available_tools),
677+
selected_tools=str(selected_tools),
678+
history=history,
679+
observation=observation,
680+
plan=plan,
681+
memory=memory,
682+
action=action,
683+
reflection=reflection,
560684
)
561685

562686
return [{"role": "user", "content": formatted_prompt}]

cookbooks/training_judge_model/grpo/pointwise/utils/preprocess_grader_data.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,28 @@ def process_single_file(data_file: str, split_ratio: float, seed: int, sample_nu
119119
# Extract task_type from item if available, otherwise use "unknown"
120120
task_type = item.get("task_type", "unknown")
121121

122+
try:
123+
if (
124+
item["chosen"]
125+
and item["chosen"]["response"]
126+
and "tool_calls" in item["chosen"]["response"]
127+
and isinstance(item["chosen"]["response"].get("tool_calls", []), list)
128+
):
129+
item["chosen"]["response"]["tool_calls"] = json.dumps(item["chosen"]["response"]["tool_calls"])
130+
131+
if (
132+
item["rejected"]
133+
and item["rejected"]["response"]
134+
and "tool_calls" in item["rejected"]["response"]
135+
and isinstance(item["rejected"]["response"].get("tool_calls", []), list)
136+
):
137+
item["rejected"]["response"]["tool_calls"] = json.dumps(item["rejected"]["response"]["tool_calls"])
138+
139+
if item["input"] and item["input"].get("context", "") and not isinstance(item["input"]["context"], str):
140+
item["input"]["context"] = json.dumps(item["input"]["context"])
141+
except Exception as e:
142+
raise e
143+
122144
output_data.append(
123145
{
124146
"input": item["input"],

openjudge/models/openai_chat_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def _is_valid_message(msg: dict) -> bool:
235235
kwargs.pop("tool_choice", None)
236236

237237
# Use simple json_object format for models that don't support complex JSON schema
238-
if "qwen" in self.model.lower() or "gemini" in self.model.lower():
238+
if "qwen" in self.model.lower() or "gemini" in self.model.lower() or "pai-judge" in self.model.lower():
239239
logger.info(
240240
f"Model '{self.model}' detected: Automatically switching to "
241241
"'json_object' response_format for compatibility"

0 commit comments

Comments
 (0)