feat: lite deep researcher implementation

This commit is contained in:
He Tao
2025-04-07 16:25:55 +08:00
commit 03798ded08
58 changed files with 4242 additions and 0 deletions

42
src/config/__init__.py Normal file
View File

@@ -0,0 +1,42 @@
from .tools import TAVILY_MAX_RESULTS
from .loader import load_yaml_config
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Team configuration
TEAM_MEMBER_CONFIGRATIONS = {
"researcher": {
"name": "researcher",
"desc": (
"Responsible for searching and collecting relevant information, understanding user needs and conducting research analysis"
),
"desc_for_llm": (
"Uses search engines and web crawlers to gather information from the internet. "
"Outputs a Markdown report summarizing findings. Researcher can not do math or programming."
),
"is_optional": False,
},
"coder": {
"name": "coder",
"desc": (
"Responsible for code implementation, debugging and optimization, handling technical programming tasks"
),
"desc_for_llm": (
"Executes Python or Bash commands, performs mathematical calculations, and outputs a Markdown report. "
"Must be used for all mathematical computations."
),
"is_optional": True,
},
}
TEAM_MEMBERS = list(TEAM_MEMBER_CONFIGRATIONS.keys())
__all__ = [
# Other configurations
"TEAM_MEMBERS",
"TEAM_MEMBER_CONFIGRATIONS",
"TAVILY_MAX_RESULTS",
]

13
src/config/agents.py Normal file
View File

@@ -0,0 +1,13 @@
from typing import Literal
# Define available LLM types
LLMType = Literal["basic", "reasoning", "vision"]
# Define agent-LLM mapping
AGENT_LLM_MAP: dict[str, LLMType] = {
"coordinator": "basic", # 协调默认使用basic llm
"planner": "basic", # 计划默认使用basic llm
"researcher": "basic", # 简单搜索任务使用basic llm
"coder": "basic", # 编程任务使用basic llm
"reporter": "basic", # 报告使用basic llm
}

View File

@@ -0,0 +1,28 @@
import os
from dataclasses import dataclass, fields
from typing import Any, Optional
from langchain_core.runnables import RunnableConfig
@dataclass(kw_only=True)
class Configuration:
"""The configurable fields."""
max_plan_iterations: int = 2 # Maximum number of plan iterations
max_step_num: int = 5 # Maximum number of steps in a plan
@classmethod
def from_runnable_config(
cls, config: Optional[RunnableConfig] = None
) -> "Configuration":
"""Create a Configuration instance from a RunnableConfig."""
configurable = (
config["configurable"] if config and "configurable" in config else {}
)
values: dict[str, Any] = {
f.name: os.environ.get(f.name.upper(), configurable.get(f.name))
for f in fields(cls)
if f.init
}
return cls(**{k: v for k, v in values.items() if v})

49
src/config/loader.py Normal file
View File

@@ -0,0 +1,49 @@
import os
import yaml
from typing import Dict, Any
def replace_env_vars(value: str) -> str:
"""Replace environment variables in string values."""
if not isinstance(value, str):
return value
if value.startswith("$"):
env_var = value[1:]
return os.getenv(env_var, value)
return value
def process_dict(config: Dict[str, Any]) -> Dict[str, Any]:
"""Recursively process dictionary to replace environment variables."""
result = {}
for key, value in config.items():
if isinstance(value, dict):
result[key] = process_dict(value)
elif isinstance(value, str):
result[key] = replace_env_vars(value)
else:
result[key] = value
return result
_config_cache: Dict[str, Dict[str, Any]] = {}
def load_yaml_config(file_path: str) -> Dict[str, Any]:
"""Load and process YAML configuration file."""
# 如果文件不存在,返回{}
if not os.path.exists(file_path):
return {}
# 检查缓存中是否已存在配置
if file_path in _config_cache:
return _config_cache[file_path]
# 如果缓存中不存在,则加载并处理配置
with open(file_path, "r") as f:
config = yaml.safe_load(f)
processed_config = process_dict(config)
# 将处理后的配置存入缓存
_config_cache[file_path] = processed_config
return processed_config

2
src/config/tools.py Normal file
View File

@@ -0,0 +1,2 @@
# Tool configuration
TAVILY_MAX_RESULTS = 3