更新了直接回答情况下的文本返回格式
Browse files- agent.py +5 -1
- tools/llm_client.py +64 -9
agent.py
CHANGED
|
@@ -23,6 +23,8 @@ Rules:
|
|
| 23 |
7. To finish, output:
|
| 24 |
{"thought":"...","action":"final_answer","answer":"...","confidence":"high|medium|low"}
|
| 25 |
8. Final answers must obey the requested format exactly. Do not include explanations.
|
|
|
|
|
|
|
| 26 |
""".strip()
|
| 27 |
|
| 28 |
|
|
@@ -157,7 +159,9 @@ class GaiaAgent:
|
|
| 157 |
+ tool_prompt()
|
| 158 |
+ "\n\nTask:\n"
|
| 159 |
+ json.dumps(task_payload, ensure_ascii=False)
|
| 160 |
-
+ "\n\nChoose the first tool call or return final_answer. Output JSON only."
|
|
|
|
|
|
|
| 161 |
)
|
| 162 |
return [
|
| 163 |
{"role": "system", "content": SYSTEM_PROMPT},
|
|
|
|
| 23 |
7. To finish, output:
|
| 24 |
{"thought":"...","action":"final_answer","answer":"...","confidence":"high|medium|low"}
|
| 25 |
8. Final answers must obey the requested format exactly. Do not include explanations.
|
| 26 |
+
9. Never answer in plain text. Even if the answer is obvious, return JSON.
|
| 27 |
+
10. For self-contained text/table/regex/list questions, call direct_answer_tool first.
|
| 28 |
""".strip()
|
| 29 |
|
| 30 |
|
|
|
|
| 159 |
+ tool_prompt()
|
| 160 |
+ "\n\nTask:\n"
|
| 161 |
+ json.dumps(task_payload, ensure_ascii=False)
|
| 162 |
+
+ "\n\nChoose the first tool call or return final_answer. Output JSON only.\n"
|
| 163 |
+
+ "Example tool call: {\"thought\":\"self-contained rule question\",\"action\":\"direct_answer_tool\",\"args\":{}}\n"
|
| 164 |
+
+ "Example final answer: {\"thought\":\"tool returned the answer\",\"action\":\"final_answer\",\"answer\":\"right\",\"confidence\":\"high\"}"
|
| 165 |
)
|
| 166 |
return [
|
| 167 |
{"role": "system", "content": SYSTEM_PROMPT},
|
tools/llm_client.py
CHANGED
|
@@ -27,23 +27,28 @@ def call_hf_chat(
|
|
| 27 |
model: str = HF_TEXT_MODEL,
|
| 28 |
max_tokens: int = 512,
|
| 29 |
temperature: float = 0.1,
|
|
|
|
| 30 |
) -> str:
|
| 31 |
token = get_hf_token()
|
| 32 |
if not token:
|
| 33 |
raise RuntimeError("未配置 HF_TOKEN。")
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
response = requests.post(
|
| 36 |
HF_ROUTER_URL,
|
| 37 |
headers={
|
| 38 |
"Authorization": f"Bearer {token}",
|
| 39 |
"Content-Type": "application/json",
|
| 40 |
},
|
| 41 |
-
json=
|
| 42 |
-
"model": model,
|
| 43 |
-
"messages": messages,
|
| 44 |
-
"temperature": temperature,
|
| 45 |
-
"max_tokens": max_tokens,
|
| 46 |
-
},
|
| 47 |
timeout=180,
|
| 48 |
)
|
| 49 |
if not response.ok:
|
|
@@ -51,7 +56,10 @@ def call_hf_chat(
|
|
| 51 |
f"HF chat 请求失败:status={response.status_code}, body={response.text[:500]}"
|
| 52 |
)
|
| 53 |
data = response.json()
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
|
| 57 |
def extract_json_object(text: str) -> dict[str, Any]:
|
|
@@ -94,9 +102,56 @@ def parse_agent_action(text: str) -> AgentAction:
|
|
| 94 |
)
|
| 95 |
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
def plan_next_action(messages: list[dict[str, Any]]) -> AgentAction:
|
| 98 |
-
raw =
|
| 99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
|
| 102 |
def answer_with_light_model(question: str, evidence: str) -> str:
|
|
|
|
| 27 |
model: str = HF_TEXT_MODEL,
|
| 28 |
max_tokens: int = 512,
|
| 29 |
temperature: float = 0.1,
|
| 30 |
+
response_format: dict[str, Any] | None = None,
|
| 31 |
) -> str:
|
| 32 |
token = get_hf_token()
|
| 33 |
if not token:
|
| 34 |
raise RuntimeError("未配置 HF_TOKEN。")
|
| 35 |
|
| 36 |
+
payload: dict[str, Any] = {
|
| 37 |
+
"model": model,
|
| 38 |
+
"messages": messages,
|
| 39 |
+
"temperature": temperature,
|
| 40 |
+
"max_tokens": max_tokens,
|
| 41 |
+
}
|
| 42 |
+
if response_format:
|
| 43 |
+
payload["response_format"] = response_format
|
| 44 |
+
|
| 45 |
response = requests.post(
|
| 46 |
HF_ROUTER_URL,
|
| 47 |
headers={
|
| 48 |
"Authorization": f"Bearer {token}",
|
| 49 |
"Content-Type": "application/json",
|
| 50 |
},
|
| 51 |
+
json=payload,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
timeout=180,
|
| 53 |
)
|
| 54 |
if not response.ok:
|
|
|
|
| 56 |
f"HF chat 请求失败:status={response.status_code}, body={response.text[:500]}"
|
| 57 |
)
|
| 58 |
data = response.json()
|
| 59 |
+
content = data["choices"][0]["message"].get("content")
|
| 60 |
+
if content is None:
|
| 61 |
+
return ""
|
| 62 |
+
return content
|
| 63 |
|
| 64 |
|
| 65 |
def extract_json_object(text: str) -> dict[str, Any]:
|
|
|
|
| 102 |
)
|
| 103 |
|
| 104 |
|
| 105 |
+
def call_planner_model(messages: list[dict[str, Any]]) -> str:
|
| 106 |
+
try:
|
| 107 |
+
return call_hf_chat(
|
| 108 |
+
messages,
|
| 109 |
+
max_tokens=700,
|
| 110 |
+
temperature=0.0,
|
| 111 |
+
response_format={"type": "json_object"},
|
| 112 |
+
)
|
| 113 |
+
except RuntimeError as exc:
|
| 114 |
+
error_text = str(exc).lower()
|
| 115 |
+
if "response_format" not in error_text and "json_object" not in error_text:
|
| 116 |
+
raise
|
| 117 |
+
return call_hf_chat(
|
| 118 |
+
messages,
|
| 119 |
+
max_tokens=700,
|
| 120 |
+
temperature=0.0,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
def plan_next_action(messages: list[dict[str, Any]]) -> AgentAction:
|
| 125 |
+
raw = call_planner_model(messages)
|
| 126 |
+
try:
|
| 127 |
+
return parse_agent_action(raw)
|
| 128 |
+
except Exception as first_error:
|
| 129 |
+
repair_messages = messages + [
|
| 130 |
+
{
|
| 131 |
+
"role": "assistant",
|
| 132 |
+
"content": raw or "<empty response>",
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"role": "user",
|
| 136 |
+
"content": (
|
| 137 |
+
"Your previous response was invalid because it was not one JSON object. "
|
| 138 |
+
"Return exactly one JSON object now, with no markdown and no prose. "
|
| 139 |
+
"Valid tool-call schema: "
|
| 140 |
+
"{\"thought\":\"...\",\"action\":\"tool_name\",\"args\":{}}. "
|
| 141 |
+
"Valid final-answer schema: "
|
| 142 |
+
"{\"thought\":\"...\",\"action\":\"final_answer\",\"answer\":\"...\",\"confidence\":\"high|medium|low\"}."
|
| 143 |
+
),
|
| 144 |
+
},
|
| 145 |
+
]
|
| 146 |
+
repaired_raw = call_planner_model(repair_messages)
|
| 147 |
+
try:
|
| 148 |
+
return parse_agent_action(repaired_raw)
|
| 149 |
+
except Exception as second_error:
|
| 150 |
+
raise ValueError(
|
| 151 |
+
"planner 连续两次没有输出合法 JSON。"
|
| 152 |
+
f" first_error={first_error}; first_raw={raw[:300]!r};"
|
| 153 |
+
f" second_error={second_error}; second_raw={repaired_raw[:300]!r}"
|
| 154 |
+
) from second_error
|
| 155 |
|
| 156 |
|
| 157 |
def answer_with_light_model(question: str, evidence: str) -> str:
|