bug fixed for start point sidderent from current point
This commit is contained in:
87
uav_agent.py
87
uav_agent.py
@@ -20,26 +20,56 @@ import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# from langchain.agents import create_tool_calling_agent
|
||||
# from langchain.agents import create_react_agent as create_react_agent_anthropic
|
||||
# from langchain.agents import AgentExecutor as AgentExecutorAnthropic
|
||||
|
||||
class EnhancedChatOpenAI(ChatOpenAI):
|
||||
"""ChatOpenAI subclass that captures reasoning_content if provided by the API"""
|
||||
class EnhancedChatAnthropic(ChatAnthropic):
|
||||
"""
|
||||
针对 Anthropic 的增强类:
|
||||
1. 提取 'thinking' 块并将其转换为 ReAct 格式的 'Thought: ...'
|
||||
2. 防止因为只有工具调用而导致的 content 为空
|
||||
"""
|
||||
|
||||
def _create_chat_result(self, response: Any) -> ChatResult:
|
||||
result = super()._create_chat_result(response)
|
||||
def generate(self, response: Any) -> ChatResult:
|
||||
result = super().generate(response)
|
||||
|
||||
for generation in result.generations:
|
||||
message = generation.message
|
||||
raw_content = message.content
|
||||
|
||||
# --- 逻辑 A: 处理思维链 (Thinking Blocks) ---
|
||||
# 如果 content 是列表 (Anthropic 标准格式),提取 thinking
|
||||
if isinstance(raw_content, list):
|
||||
text_parts = []
|
||||
thought_parts = []
|
||||
|
||||
for block in raw_content:
|
||||
if isinstance(block, dict):
|
||||
if block.get("type") == "thinking":
|
||||
# 获取思考内容
|
||||
thinking = block.get("thinking", "")
|
||||
# 存入 additional_kwargs (保持与其他代码一致)
|
||||
message.additional_kwargs["reasoning_content"] = thinking
|
||||
thought_parts.append(f"Thought: {thinking}\n")
|
||||
elif block.get("type") == "text":
|
||||
text_parts.append(block.get("text", ""))
|
||||
|
||||
# 重组 Content,把思考放在最前面,欺骗 ReAct 解析器
|
||||
final_text = "".join(text_parts)
|
||||
if thought_parts and "Thought:" not in final_text:
|
||||
message.content = "".join(thought_parts) + final_text
|
||||
else:
|
||||
message.content = final_text
|
||||
|
||||
# --- 逻辑 B: 处理纯工具调用导致的空字符串 ---
|
||||
# 如果 content 为空,但有工具调用,我们手动补一个 Thought
|
||||
# 这样 ReAct 解析器就不会报错说 "No action found"
|
||||
if not message.content and message.tool_calls:
|
||||
tool_name = message.tool_calls[0]['name']
|
||||
# 伪造一个 Thought,让 Log 好看,也让解析器通过
|
||||
message.content = f"Thought: I should use the {tool_name} tool to proceed.\n"
|
||||
|
||||
if hasattr(response, "choices") and response.choices:
|
||||
for i, choice in enumerate(response.choices):
|
||||
# Handle MiniMax reasoning_details
|
||||
if hasattr(choice.message, "reasoning_details") and choice.message.reasoning_details:
|
||||
reasoning = choice.message.reasoning_details[0].get('text', '')
|
||||
if reasoning and i < len(result.generations):
|
||||
gen = result.generations[i]
|
||||
if isinstance(gen.message, AIMessage):
|
||||
# Store in additional_kwargs
|
||||
gen.message.additional_kwargs["reasoning_content"] = reasoning
|
||||
# Prepend to content for ReAct agent visibility
|
||||
if "Thought:" not in gen.message.content:
|
||||
gen.message.content = f"Thought: {reasoning}\n" + gen.message.content
|
||||
return result
|
||||
|
||||
|
||||
@@ -289,7 +319,7 @@ class UAVControlAgent:
|
||||
"api_key": llm_api_key,
|
||||
"base_url": final_base_url
|
||||
}
|
||||
self.llm = ChatAnthropic(**kwargs)
|
||||
self.llm = EnhancedChatAnthropic(**kwargs)
|
||||
else:
|
||||
kwargs = {
|
||||
"model": llm_model,
|
||||
@@ -328,11 +358,22 @@ class UAVControlAgent:
|
||||
# Create ReAct agent
|
||||
if self.debug:
|
||||
print("🤖 Creating ReAct agent...")
|
||||
self.agent = create_react_agent(
|
||||
llm=self.llm,
|
||||
tools=self.tools,
|
||||
prompt=self.prompt
|
||||
)
|
||||
if llm_provider in ["anthropic", "anthropic-compatible"]:
|
||||
if self.debug:
|
||||
print("🤖 Using Tool Calling Agent (Better for Claude)")
|
||||
self.agent = create_react_agent(
|
||||
llm=self.llm,
|
||||
tools=self.tools,
|
||||
prompt=self.prompt
|
||||
)
|
||||
else:
|
||||
if self.debug:
|
||||
print("🤖 Using React Agent (GPT-3 and older)")
|
||||
self.agent = create_react_agent(
|
||||
llm=self.llm,
|
||||
tools=self.tools,
|
||||
prompt=self.prompt
|
||||
)
|
||||
|
||||
if self.debug:
|
||||
print("✅ ReAct agent created")
|
||||
|
||||
@@ -460,6 +460,8 @@ def create_uav_tools(client: UAVAPIClient) -> list:
|
||||
return "Error: drone_id is required"
|
||||
|
||||
result = client.take_off(drone_id, altitude)
|
||||
if result["status"] == "success":
|
||||
result["message"] += f" Using `get_drone_status` to check if the drone\'s current position is start point. If not, fly to the start point first."
|
||||
return json.dumps(result, indent=2)
|
||||
except json.JSONDecodeError as e:
|
||||
return f"Error parsing JSON input: {str(e)}. Expected format: {{\"drone_id\": \"drone-001\", \"altitude\": 15.0}}"
|
||||
@@ -1248,8 +1250,10 @@ def create_uav_tools(client: UAVAPIClient) -> list:
|
||||
if current_coverage >= required_coverage:
|
||||
tool_states.explored_count = 0
|
||||
return f"Success: Target explored with coverage {current_coverage:.2%} (Visited {tool_states.explored_count}/{total_points} grid points)"
|
||||
|
||||
return f"Finished path. Final coverage: {current_coverage:.2%}"
|
||||
if math.isclose(current_coverage, 0.0):
|
||||
return f"Finished path. Final coverage: {current_coverage:.2%}. Please try call this tool again to continue exploring."
|
||||
else:
|
||||
return f"Finished path. Final coverage: {current_coverage:.2%}. Wait for a while and continue calling this function! return [TASK DONE] this time"
|
||||
|
||||
|
||||
# Return all tools
|
||||
|
||||
Reference in New Issue
Block a user