LangGraph工具调用节点工作异常问题求助
我现在在LangGraph里做一个简单的Demo,想让LLM在需要的时候调用维基百科搜索工具,但遇到了奇怪的问题:
有时候LLM返回的结果里明明有类似<function=search-wikipedia{"query": "Palmyra"}</function>的内容,但工具调用列表Tool Calls []却是空的,而且第一次运行经常触发不了工具节点,第二次运行又能正常执行工具函数,偶尔第一次也能成功,完全摸不准规律。
下面是我拿到的LLM返回结果:
The resp: content='<function=search-wikipedia{"query": "Palmyra"}</function>' additional_kwargs={} response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 247, 'total_tokens': 264, 'completion_time': 0.061818182, 'prompt_time': 0.034164437, 'queue_time': 0.666112673, 'total_time': 0.095982619}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_3884478861', 'finish_reason': 'stop', 'logprobs': None} id='run-cd05b340-619d-4672-bdda-578f095985ad-0' usage_metadata={'input_tokens': 247, 'output_tokens': 17, 'total_tokens': 264}
同时工具调用显示:Tool Calls []
我的代码如下:
@tool("search-wikipedia", args_schema=SearchWikiInput) def get_res(query: str) -> str: print(bcolors.WARNING + f'Using the wikipedia tool:...\n' + bcolors.ENDC) search_query = model.invoke([SystemMessage(content=f'''Convert the user queryinto search keyword in wikipedia, if the query is a simple greeting, return an empty response only, else return only and only the keyword: {query}''')]).content if search_query == "": return {'wiki_content': ''} pages = search_for_page(query) content = get_wiki_content(pages) if not content: return "No results found." formatted_results = [] for page in content: formatted_results.append( f"Title: {page.title}\n" f"Summary: {page.summary[:800]}...\n" f"URL: {page.url}\n" ) results = '\n\n'.join(formatted_results) print(bcolors.WARNING + f'Returned Results from wikipedia' + bcolors.ENDC) return {'wiki_content': results} tools = [get_res] tools_dict = {tool.name: tool for tool in tools} model = ChatGroq(model_name=model_name, temperature=0.1).bind_tools(tools) def tools_node(state: State): """Tool call node that executes the tools based on the plan.""" outputs = [] print('Hello from the tools node') for tool_call in state['messages'][-1].tool_calls: tool_results = tools_dict[tool_call['name']].invoke(tool_call['args']) outputs.append( ToolMessage( content=json.dumps(tool_results), name=tool_call['name'], tool_call_id=tool_call['id'] ) ) return {'messages': outputs} def agent_node(state: State): """Agent call node that uses the LLM with tools to answer the user query.""" agent_prompt = 'You are a helpful assistant, use the external tools only when needed' system_prompt = SystemMessage(content=agent_prompt) response = model.invoke([system_prompt] + state["messages"]) print(f'The resp: {response}') return {"messages": [response]} def should_continue(state:State): messages = state["messages"] last_message = messages[-1] print(f'Tool Calls {last_message.content}') if last_message.tool_calls: return 'continue' else: return 'END' workflow = StateGraph(State) workflow.add_node("agent", agent_node) workflow.add_node("tools", tools_node) workflow.add_edge(START,"agent") workflow.add_edge('tools',"agent") workflow.add_conditional_edges("agent",should_continue,{'continue':'tools','END':END}) graph = workflow.compile()
问题分析与解决建议
我之前也遇到过类似的情况,核心问题是LLM返回的工具调用格式和LangGraph期望的标准格式不匹配:
格式不匹配是核心原因
从返回结果看,你的LLM用了自定义的<function>标签格式返回工具调用,但LangChain的bind_tools默认期望模型返回符合标准结构的tool_calls字段,所以last_message.tool_calls才会是空的。至于偶尔第二次成功,可能是因为上下文里保留了之前的成功调用示例,模型自动调整了输出格式。具体修复方案
方案一:强制模型使用标准工具调用格式
修改你的agent_prompt,明确告诉模型必须用LangChain的标准工具调用格式,不能用自定义标签。比如:
agent_prompt = '''你是一个乐于助人的助手,只有在需要外部信息时才使用提供的工具。 当你需要调用工具时,必须使用以下标准格式返回,不要使用任何自定义标签: {"tool_calls": [{"name": "工具名称", "args": {"参数名": "参数值"}}]} '''
同时,bind_tools已经会自动给模型注入工具的描述信息,不用手动在prompt里重复说明工具,保持prompt简洁明确即可。
方案二:手动解析自定义格式(如果模型顽固不化)
如果模型还是坚持输出<function>标签,可以在agent_node里手动解析这个格式,把它转换成LangChain能识别的tool_calls结构:
import re import json from langchain_core.messages import ToolCall def agent_node(state: State): """Agent call node that uses the LLM with tools to answer the user query.""" agent_prompt = 'You are a helpful assistant, use the external tools only when needed' system_prompt = SystemMessage(content=agent_prompt) response = model.invoke([system_prompt] + state["messages"]) print(f'The resp: {response}') # 手动解析自定义的<function>标签格式 content = response.content tool_call_match = re.search(r'<function=(\w+)\{(.+)\}</function>', content) if tool_call_match and not response.tool_calls: tool_name = tool_call_match.group(1) try: tool_args = json.loads(tool_call_match.group(2)) # 构造标准的ToolCall对象 response.tool_calls = [ ToolCall(name=tool_name, args=tool_args, id="1") ] except json.JSONDecodeError: # 解析失败就忽略,不触发工具调用 pass return {"messages": [response]}
额外优化建议
- 你的
get_res工具内部又调用了一次模型来转换查询关键词,这会额外消耗token,还可能引入错误。建议把这个关键词转换逻辑移到agent_node里,让模型在生成工具调用时直接传入正确的搜索关键词。 - 确保
SearchWikiInput的schema定义正确,和工具的query参数类型完全匹配,避免模型生成的参数不兼容。
- 验证修复效果
修改后,第一次运行时观察last_message.tool_calls是否有内容,如果有,就会正常进入tools_node执行工具了。
希望这些方案能帮到你!
备注:内容来源于stack exchange,提问作者MDD




