feat: support function factory (#4)

This commit is contained in:
DanielWalnut
2026-01-15 22:05:54 +08:00
committed by GitHub
parent a39f799a7e
commit b44144dd2c
9 changed files with 133 additions and 22 deletions

75
backend/debug.py Normal file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python
"""
Debug script for lead_agent.
Run this file directly in VS Code with breakpoints.
Usage:
1. Set breakpoints in agent.py or other files
2. Press F5 or use "Run and Debug" panel
3. Input messages in the terminal to interact with the agent
"""
import asyncio
import os
import sys
# Ensure we can import from src
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Load environment variables
from dotenv import load_dotenv
from langchain_core.messages import HumanMessage
from src.agents import make_lead_agent
load_dotenv()
async def main():
# Create agent with default config
config = {
"configurable": {
"thread_id": "debug-thread-001",
"thinking_enabled": True,
# Uncomment to use a specific model
"model_name": "doubao-seed-1.8",
}
}
agent = make_lead_agent(config)
print("=" * 50)
print("Lead Agent Debug Mode")
print("Type 'quit' or 'exit' to stop")
print("=" * 50)
while True:
try:
user_input = input("\nYou: ").strip()
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("Goodbye!")
break
# Invoke the agent
state = {"messages": [HumanMessage(content=user_input)]}
result = await agent.ainvoke(state, config=config, context={"thread_id": "debug-thread-001"})
# Print the response
if result.get("messages"):
last_message = result["messages"][-1]
print(f"\nAgent: {last_message.content}")
except KeyboardInterrupt:
print("\nInterrupted. Goodbye!")
break
except Exception as e:
print(f"\nError: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -3,6 +3,6 @@
"dependencies": ["."], "dependencies": ["."],
"env": ".env", "env": ".env",
"graphs": { "graphs": {
"lead_agent": "src.agents:lead_agent" "lead_agent": "src.agents:make_lead_agent"
} }
} }

View File

@@ -1,4 +1,4 @@
from .lead_agent import lead_agent from .lead_agent import make_lead_agent
from .thread_state import SandboxState, ThreadState from .thread_state import SandboxState, ThreadState
__all__ = ["lead_agent", "SandboxState", "ThreadState"] __all__ = ["make_lead_agent", "SandboxState", "ThreadState"]

View File

@@ -1,3 +1,3 @@
from .agent import lead_agent from .agent import make_lead_agent
__all__ = ["lead_agent"] __all__ = ["make_lead_agent"]

View File

@@ -1,4 +1,5 @@
from langchain.agents import create_agent from langchain.agents import create_agent
from langchain_core.runnables import RunnableConfig
from src.agents.lead_agent.prompt import apply_prompt_template from src.agents.lead_agent.prompt import apply_prompt_template
from src.agents.middlewares.thread_data_middleware import ThreadDataMiddleware from src.agents.middlewares.thread_data_middleware import ThreadDataMiddleware
@@ -11,8 +12,13 @@ from src.tools import get_available_tools
# ThreadDataMiddleware must be before SandboxMiddleware to ensure thread_id is available # ThreadDataMiddleware must be before SandboxMiddleware to ensure thread_id is available
middlewares = [ThreadDataMiddleware(), SandboxMiddleware(), TitleMiddleware()] middlewares = [ThreadDataMiddleware(), SandboxMiddleware(), TitleMiddleware()]
lead_agent = create_agent(
model=create_chat_model(thinking_enabled=True), def make_lead_agent(config: RunnableConfig):
thinking_enabled = config.get("configurable", {}).get("thinking_enabled", True)
model_name = config.get("configurable", {}).get("model_name")
print(f"thinking_enabled: {thinking_enabled}, model_name: {model_name}")
return create_agent(
model=create_chat_model(name=model_name, thinking_enabled=thinking_enabled),
tools=get_available_tools(), tools=get_available_tools(),
middleware=middlewares, middleware=middlewares,
system_prompt=apply_prompt_template(), system_prompt=apply_prompt_template(),

View File

@@ -1,4 +1,3 @@
import os
from datetime import datetime from datetime import datetime
SYSTEM_PROMPT = f""" SYSTEM_PROMPT = f"""

View File

@@ -71,8 +71,9 @@ class ThreadDataMiddleware(AgentMiddleware[ThreadDataMiddlewareState]):
@override @override
def before_agent(self, state: ThreadDataMiddlewareState, runtime: Runtime) -> dict | None: def before_agent(self, state: ThreadDataMiddlewareState, runtime: Runtime) -> dict | None:
# Generate new thread ID and create directories # Generate new thread ID and create directories
print(runtime.context) thread_id = runtime.context.get("thread_id")
thread_id = runtime.context["thread_id"] if thread_id is None:
raise ValueError("Thread ID is required in the context")
paths = self._create_thread_directories(thread_id) paths = self._create_thread_directories(thread_id)
print(f"Created thread data directories for thread {thread_id}") print(f"Created thread data directories for thread {thread_id}")

View File

@@ -168,14 +168,16 @@ def read_file_tool(
runtime: ToolRuntime[ContextT, ThreadState], runtime: ToolRuntime[ContextT, ThreadState],
description: str, description: str,
path: str, path: str,
view_range: tuple[int, int] | None = None, start_line: int | None = None,
end_line: int | None = None,
) -> str: ) -> str:
"""Read the contents of a text file. """Read the contents of a text file. Use this to examine source code, configuration files, logs, or any text-based file.
Args: Args:
description: Explain why you are viewing this file in short words. ALWAYS PROVIDE THIS PARAMETER FIRST. description: Explain why you are reading this file in short words. ALWAYS PROVIDE THIS PARAMETER FIRST.
path: The **absolute** path to the file to read. path: The **absolute** path to the file to read.
view_range: The range of lines to view. The range is inclusive and starts at 1. For example, (1, 10) will view the first 10 lines of the file. start_line: Optional starting line number (1-indexed, inclusive). Use with end_line to read a specific range.
end_line: Optional ending line number (1-indexed, inclusive). Use with start_line to read a specific range.
""" """
try: try:
sandbox = sandbox_from_runtime(runtime) sandbox = sandbox_from_runtime(runtime)
@@ -185,9 +187,8 @@ def read_file_tool(
content = sandbox.read_file(path) content = sandbox.read_file(path)
if not content: if not content:
return "(empty)" return "(empty)"
if view_range: if start_line is not None and end_line is not None:
start, end = view_range content = "\n".join(content.splitlines()[start_line - 1 : end_line])
content = "\n".join(content.splitlines()[start - 1 : end])
return content return content
except Exception as e: except Exception as e:
return f"Error: {e}" return f"Error: {e}"

View File

@@ -13,5 +13,34 @@
"workspace": "deer-flow" "workspace": "deer-flow"
} }
] ]
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"name": "Debug Lead Agent",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/backend/debug.py",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/backend",
"env": {
"PYTHONPATH": "${workspaceFolder}/backend"
},
"justMyCode": false
},
{
"name": "Debug Lead Agent (justMyCode)",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/backend/debug.py",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/backend",
"env": {
"PYTHONPATH": "${workspaceFolder}/backend"
},
"justMyCode": true
}
]
} }
} }