update model without structure output
This commit is contained in:
@@ -21,7 +21,7 @@ def main() -> None:
|
||||
)
|
||||
parser.add_argument(
|
||||
"--reasoning-model",
|
||||
default="GLM-4.5-Air",
|
||||
default="GLM-4.7",
|
||||
help="Model for the final answer",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -9,21 +9,21 @@ class Configuration(BaseModel):
|
||||
"""The configuration for the agent."""
|
||||
|
||||
query_generator_model: str = Field(
|
||||
default="GLM-4.5-Air",
|
||||
default="GLM-4.7",
|
||||
metadata={
|
||||
"description": "The name of the language model to use for the agent's query generation."
|
||||
},
|
||||
)
|
||||
|
||||
reflection_model: str = Field(
|
||||
default="GLM-4.5-Air",
|
||||
default="GLM-4.7",
|
||||
metadata={
|
||||
"description": "The name of the language model to use for the agent's reflection."
|
||||
},
|
||||
)
|
||||
|
||||
answer_model: str = Field(
|
||||
default="GLM-4.5-Air",
|
||||
default="GLM-4.7",
|
||||
metadata={
|
||||
"description": "The name of the language model to use for the agent's answer."
|
||||
},
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import os
|
||||
from typing import TypeVar
|
||||
|
||||
from agent.tools_and_schemas import SearchQueryList, Reflection
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from langchain_core.messages import AIMessage
|
||||
from langgraph.types import Send
|
||||
from langgraph.graph import StateGraph
|
||||
@@ -38,6 +40,7 @@ if os.getenv("TAVILY_API_KEY") is None:
|
||||
raise ValueError("TAVILY_API_KEY is not set")
|
||||
|
||||
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
|
||||
SchemaT = TypeVar("SchemaT", bound=BaseModel)
|
||||
|
||||
|
||||
def create_llm(model: str, temperature: float) -> ChatOpenAI:
|
||||
@@ -51,6 +54,63 @@ def create_llm(model: str, temperature: float) -> ChatOpenAI:
|
||||
)
|
||||
|
||||
|
||||
def _extract_json_payload(content: str) -> str:
|
||||
"""Extract a JSON object from a model response."""
|
||||
text = content.strip()
|
||||
|
||||
if text.startswith("```"):
|
||||
lines = text.splitlines()
|
||||
if lines:
|
||||
lines = lines[1:]
|
||||
if lines and lines[-1].strip() == "```":
|
||||
lines = lines[:-1]
|
||||
text = "\n".join(lines).strip()
|
||||
|
||||
if text.startswith("json"):
|
||||
text = text[4:].lstrip()
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def _invoke_json_model(
|
||||
llm: ChatOpenAI,
|
||||
prompt: str,
|
||||
schema: type[SchemaT],
|
||||
) -> SchemaT:
|
||||
"""Invoke the model for JSON text and validate it with Pydantic."""
|
||||
result = llm.invoke(prompt)
|
||||
content = result.content if isinstance(result.content, str) else str(result.content)
|
||||
json_payload = _extract_json_payload(content)
|
||||
|
||||
try:
|
||||
return schema.model_validate_json(json_payload)
|
||||
except ValidationError as exc:
|
||||
raise ValueError(
|
||||
f"Model returned invalid JSON for {schema.__name__}: {json_payload[:500]!r}"
|
||||
) from exc
|
||||
|
||||
|
||||
def _supports_native_structured_output(model: str) -> bool:
|
||||
"""Return whether the selected model should use LangChain structured output."""
|
||||
normalized = model.strip().lower()
|
||||
return normalized.startswith("glm-4.7")
|
||||
|
||||
|
||||
def _invoke_structured_model(
|
||||
llm: ChatOpenAI,
|
||||
model: str,
|
||||
prompt: str,
|
||||
schema: type[SchemaT],
|
||||
) -> SchemaT:
|
||||
"""Use native structured output when supported, otherwise fall back to JSON parsing."""
|
||||
if _supports_native_structured_output(model):
|
||||
try:
|
||||
return llm.with_structured_output(schema).invoke(prompt)
|
||||
except ValueError:
|
||||
return _invoke_json_model(llm, prompt, schema)
|
||||
return _invoke_json_model(llm, prompt, schema)
|
||||
|
||||
|
||||
# Nodes
|
||||
def generate_query(state: OverallState, config: RunnableConfig) -> QueryGenerationState:
|
||||
"""LangGraph node that generates search queries based on the User's question.
|
||||
@@ -71,7 +131,6 @@ def generate_query(state: OverallState, config: RunnableConfig) -> QueryGenerati
|
||||
state["initial_search_query_count"] = configurable.number_of_initial_queries
|
||||
|
||||
llm = create_llm(configurable.query_generator_model, temperature=1.0)
|
||||
structured_llm = llm.with_structured_output(SearchQueryList)
|
||||
|
||||
current_date = get_current_date()
|
||||
formatted_prompt = query_writer_instructions.format(
|
||||
@@ -79,7 +138,12 @@ def generate_query(state: OverallState, config: RunnableConfig) -> QueryGenerati
|
||||
research_topic=get_research_topic(state["messages"]),
|
||||
number_queries=state["initial_search_query_count"],
|
||||
)
|
||||
result = structured_llm.invoke(formatted_prompt)
|
||||
result = _invoke_structured_model(
|
||||
llm,
|
||||
configurable.query_generator_model,
|
||||
formatted_prompt,
|
||||
SearchQueryList,
|
||||
)
|
||||
return {"search_query": result.query}
|
||||
|
||||
|
||||
@@ -153,7 +217,12 @@ def reflection(state: OverallState, config: RunnableConfig) -> ReflectionState:
|
||||
summaries="\n\n---\n\n".join(state["web_research_result"]),
|
||||
)
|
||||
llm = create_llm(reasoning_model, temperature=1.0)
|
||||
result = llm.with_structured_output(Reflection).invoke(formatted_prompt)
|
||||
result = _invoke_structured_model(
|
||||
llm,
|
||||
reasoning_model,
|
||||
formatted_prompt,
|
||||
Reflection,
|
||||
)
|
||||
|
||||
return {
|
||||
"is_sufficient": result.is_sufficient,
|
||||
|
||||
@@ -15,6 +15,7 @@ Instructions:
|
||||
- Queries should be diverse, if the topic is broad, generate more than 1 query.
|
||||
- Don't generate multiple similar queries, 1 is enough.
|
||||
- Query should ensure that the most current information is gathered. The current date is {current_date}.
|
||||
- Return only valid JSON. Do not include markdown fences or any extra text.
|
||||
|
||||
Format:
|
||||
- Format your response as a JSON object with ALL two of these exact keys:
|
||||
@@ -27,7 +28,7 @@ Topic: What revenue grew more last year apple stock or the number of people buyi
|
||||
```json
|
||||
{{
|
||||
"rationale": "To answer this comparative growth question accurately, we need specific data points on Apple's stock performance and iPhone sales metrics. These queries target the precise financial information needed: company revenue trends, product-specific unit sales figures, and stock price movement over the same fiscal period for direct comparison.",
|
||||
"query": ["Apple total revenue growth fiscal year 2024", "iPhone unit sales growth fiscal year 2024", "Apple stock price growth fiscal year 2024"],
|
||||
"query": ["Apple total revenue growth fiscal year 2024", "iPhone unit sales growth fiscal year 2024", "Apple stock price growth fiscal year 2024"]
|
||||
}}
|
||||
```
|
||||
|
||||
@@ -57,6 +58,7 @@ Instructions:
|
||||
|
||||
Requirements:
|
||||
- Ensure the follow-up query is self-contained and includes necessary context for web search.
|
||||
- Return only valid JSON. Do not include markdown fences or any extra text.
|
||||
|
||||
Output Format:
|
||||
- Format your response as a JSON object with these exact keys:
|
||||
|
||||
Reference in New Issue
Block a user