处理工具调用格式的补丁
This commit is contained in:
1 parent
1690fead4e
commit
5e5a4b7376
7 files changed
+138
-7
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -47,9 +47,9 @@ def call_model(state: AgentState) -> dict:
|
||||
# ----- 打印工具调用信息 -----
|
||||
if hasattr(response, 'tool_calls') and response.tool_calls:
|
||||
for tc in response.tool_calls:
|
||||
tool_name = tc.get('name', 'unknown')
|
||||
args = tc.get('args', {})
|
||||
# LangChain 中 tool_call 的 args 可能是 JSON 字符串,尝试解析
|
||||
# tc 是 ToolCall 对象,可直接访问 .name 和 .args
|
||||
tool_name = tc.get('name') if isinstance(tc, dict) else tc.name
|
||||
args = tc.get('args') if isinstance(tc, dict) else tc.args
|
||||
if isinstance(args, str):
|
||||
try:
|
||||
args = json.loads(args)
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": 2,
|
||||
"turns": [
|
||||
{
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我打开b站"
|
||||
}
|
||||
]
|
||||
},
|
||||
"assistant": {
|
||||
"text": "<tool_call>\n<function=browser_control>\n<parameter=action>\nopen\n</parameter>\n<parameter=url>\nhttps://www.bilibili.com\n</parameter>\n</function>\n</tool_call>",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "现在几点了"
|
||||
}
|
||||
]
|
||||
},
|
||||
"assistant": {
|
||||
"text": "<tool_call>\n<function=date_query_class>\n</function>\n</tool_call>",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": 2,
|
||||
"turns": [
|
||||
{
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "现在几点了"
|
||||
}
|
||||
]
|
||||
},
|
||||
"assistant": {
|
||||
"text": "现在是 2026年6月3日 17:56:36。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"user": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "帮我打开b站和虎牙"
|
||||
}
|
||||
]
|
||||
},
|
||||
"assistant": {
|
||||
"text": "好的!从截图中可以看到:\n\n1. **B站已经打开了**:第5个标签页显示的是\"啊哈啊哈! - bilibili\"\n2. **虎牙也打开了**:当前页面正在浏览虎牙直播网站,可以看到虎牙的Logo、导航栏(首页、直播、分类、视频、游戏等),以及正在播放的直播内容和右侧的推荐直播间\n\n两个网站都已经成功打开。当前显示的是虎牙直播页面,如果您想查看B站,可以点击第5个标签页切换到B站。",
|
||||
"output_type": "text",
|
||||
"autoplay": false,
|
||||
"audio_duration_seconds": null,
|
||||
"audio_relpath": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+57
-4
@@ -12,7 +12,7 @@ from datetime import datetime as dt
|
||||
import requests
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage, SystemMessage
|
||||
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage, SystemMessage, ToolCall
|
||||
from langchain_core.outputs import ChatResult, ChatGeneration
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
@@ -329,6 +329,19 @@ class ChatQwen(ChatOpenAI):
|
||||
return result
|
||||
|
||||
def _create_chat_result(self, response, generation_info=None) -> ChatResult:
|
||||
# ---------- 打印原始 tool_calls ----------
|
||||
try:
|
||||
raw = response if isinstance(response, dict) else response.model_dump()
|
||||
print("[RAW_RESPONSE]", json.dumps(raw, indent=2, ensure_ascii=False))
|
||||
choices = raw.get("choices", [])
|
||||
if choices:
|
||||
msg = choices[0].get("message", {})
|
||||
tool_calls = msg.get("tool_calls")
|
||||
if tool_calls:
|
||||
print("[RAW_TOOLCALLS]", json.dumps(tool_calls, indent=2, ensure_ascii=False))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
result = super()._create_chat_result(response, generation_info)
|
||||
try:
|
||||
dumped = response if isinstance(response, dict) else response.model_dump(exclude={"choices": {"__all__": {"message": {"parsed"}}}})
|
||||
@@ -338,6 +351,7 @@ class ChatQwen(ChatOpenAI):
|
||||
lc_msg = result.generations[i].message
|
||||
msg = choice.get("message") or {}
|
||||
ak = lc_msg.additional_kwargs
|
||||
# 优先处理 reasoning 内容
|
||||
merged = ""
|
||||
for key in ("reasoning_content", "reasoning_details", "reasoning"):
|
||||
merged = assistant_text(msg.get(key))
|
||||
@@ -350,27 +364,66 @@ class ChatQwen(ChatOpenAI):
|
||||
lc_msg.content = merged
|
||||
elif raw_content:
|
||||
lc_msg.content = raw_content
|
||||
# print(f"raw_content={raw_content!r}, merged={merged!r}, final={lc_msg.content!r}")
|
||||
|
||||
content_text = lc_msg.content if isinstance(lc_msg.content, str) else ""
|
||||
|
||||
# 匹配 <tool_call>...</tool_call> 块
|
||||
pattern = re.compile(r'<tool_call>\s*(.*?)\s*</tool_call>', re.DOTALL)
|
||||
matches = pattern.findall(content_text)
|
||||
if matches:
|
||||
tool_calls = []
|
||||
for idx, match in enumerate(matches):
|
||||
# 先尝试 JSON 解析(兼容旧格式)
|
||||
try:
|
||||
call_data = json.loads(match)
|
||||
tool_calls.append({
|
||||
"id": f"call_{idx}",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": call_data.get("name", ""),
|
||||
"arguments": json.dumps(call_data.get("arguments", {}))
|
||||
}
|
||||
})
|
||||
continue # 解析成功,跳过 XML 尝试
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 再尝试 XML 解析
|
||||
func_match = re.search(r'<function=(\w+)>', match)
|
||||
if not func_match:
|
||||
continue
|
||||
func_name = func_match.group(1)
|
||||
|
||||
params = {}
|
||||
for param_match in re.finditer(r'<parameter=(\w+)>\s*(.*?)\s*</parameter>', match, re.DOTALL):
|
||||
key = param_match.group(1)
|
||||
value = param_match.group(2).strip()
|
||||
# 可选:智能类型转换,先保持简单
|
||||
params[key] = value
|
||||
|
||||
tool_calls.append({
|
||||
"id": f"call_{idx}",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": call_data.get("name", ""),
|
||||
"arguments": json.dumps(call_data.get("arguments", {}))
|
||||
"name": func_name,
|
||||
"arguments": json.dumps(params)
|
||||
}
|
||||
})
|
||||
|
||||
if tool_calls:
|
||||
ak["tool_calls"] = tool_calls
|
||||
# 构造 LangChain 的 ToolCall 对象列表
|
||||
lc_tool_calls = []
|
||||
for idx, tc in enumerate(tool_calls):
|
||||
lc_tool_calls.append(
|
||||
ToolCall(
|
||||
id=tc["id"],
|
||||
name=tc["function"]["name"],
|
||||
args=json.loads(tc["function"]["arguments"])
|
||||
)
|
||||
)
|
||||
lc_msg.tool_calls = lc_tool_calls
|
||||
# 清理内容中的 XML/JSON 工具调用痕迹
|
||||
cleaned = pattern.sub("", content_text).strip()
|
||||
lc_msg.content = cleaned if cleaned else ""
|
||||
except Exception:
|
||||
|
||||
Reference in new issue
Block a user