Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions PULL_REQUEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Add Support for Qwen3-Thinking Models via Function Calling

## Overview

This PR introduces comprehensive support for **qwen3-thinking models**, which do not support Structured Output (SO) but work excellently through Function Calling (FC). The implementation maintains full compatibility with the SGR framework while enabling the use of thinking models that provide intermediate reasoning in their outputs.

## Key Features

### 1. Universal Pydantic to Function Calling Converter
- **File**: `sgr_deep_research/core/utils/pydantic_convert.py`
- Automatic JSON Schema generation from Pydantic models
- Support for complex types: `Literal`, `Optional`, `Union`, `List`, `Dict`
- Handles nested models and constraints (min/max, length, pattern)

### 2. Qwen3-Thinking Response Adapter
- **File**: `sgr_deep_research/core/adapters/qwen3_thinking_adapter.py`
- Three-strategy extraction from "dirty" thinking model outputs:
- Strategy 1: `tool_calls` with JSON in `arguments`
- Strategy 2: `content` with `<tool_call>...</tool_call>` tags (priority)
- Strategy 3: `content` with raw JSON (fallback)
- Robust parsing with detailed error diagnostics

### 3. SGRQwen3ThinkingAgent
- **File**: `sgr_deep_research/core/agents/sgr_qwen3_thinking_agent.py`
- Full-featured SGR agent adapted for thinking models
- Uses Function Calling instead of Structured Output
- Modified system prompt with thinking model instructions
- Maintains complete SGR architecture: reasoning → action → evaluation

## Documentation & Examples

- **Comprehensive Documentation**: `docs/QWEN3_THINKING_SUPPORT.md`
- Detailed component descriptions
- Configuration examples
- Usage patterns
- Troubleshooting guide
- Performance comparison with instruct models

- **Practical Examples**: `examples/qwen3_thinking_example.py`
- Basic usage
- Clarification handling
- Configuration loading

## ️ Architecture

```
SGRQwen3ThinkingAgent
├── Pydantic → FC Conversion (pydantic_convert.py)
│ └── Auto-generates OpenAI Function Calling schema
├── Modified System Prompt
│ └── Includes thinking model instructions + dynamic schema
├── Reasoning Phase (Function Calling)
│ └── LLM generates reasoning + tool call
├── Response Extraction (qwen3_thinking_adapter.py)
│ └── Extracts structured data from mixed output
└── Action & Evaluation
└── Standard SGR flow
```

## Design Decisions

1. **Function Calling over Structured Output**: Thinking models don't support SO, but FC provides equivalent functionality while preserving reasoning visibility

2. **Multi-Strategy Extraction**: Thinking models can output in various formats depending on vLLM configuration - the adapter handles all cases gracefully

3. **Modified System Prompt**: Incorporates base prompt from config + schema + thinking-specific instructions

4. **Full SGR Compatibility**: Maintains all SGR agent features (clarifications, planning, tool selection, etc.)

## Backward Compatibility

This PR:
- Does not modify existing agents or tools
- Adds new optional components
- Works alongside standard SGR agents
- Uses existing configuration system from `agents-config-definitions` branch

## Configuration

### config.yaml
```yaml
llm:
model: "Qwen/Qwen3-14B-thinking"
temperature: 0.3
max_tokens: 12000
```

### agents.yaml
```yaml
agents:
- name: "qwen3_thinking_agent"
base_class: "SGRQwen3ThinkingAgent"
llm:
model: "Qwen/Qwen3-14B-thinking"
tools:
- "WebSearchTool"
- "CreateReportTool"
- "FinalAnswerTool"
```

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ Recent benchmarks and validation experiments were conducted in collaboration wit

Learn more about the company: [redmadrobot.ai](https://redmadrobot.ai/) ↗️


## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=vamplabAI/sgr-deep-research&type=Date)](https://star-history.com/#vamplabAI/sgr-deep-research&Date)
143 changes: 143 additions & 0 deletions agents.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Example Custom Agents Configuration
# =====================================
# This file demonstrates how to define custom agents for SGR Deep Research

# Notes:
# ------
# 1. Agent names must be unique or will be overridden
# 2. All tools must be registered in the tool registry
# 3. LLM, Search, Prompts, Execution, MCP settings are optional and inherit from global config
# 4. Agents override global settings by providing their own values

agents:
# Example 1: Simple custom research agent with overrides
- name: "custom_research_agent"
base_class: "SGRResearchAgent"

# Optional: Override LLM settings for this agent
llm:
model: "gpt-4o"
temperature: 0.3
max_tokens: 16000
# api_key: "your-custom-api-key" # Optional: use different API key
# base_url: "https://api.openai.com/v1" # Optional: use different endpoint
# proxy: "http://127.0.0.1:8080" # Optional: use proxy

# Optional: Override search settings
search:
max_results: 15
max_pages: 8
content_limit: 2000

# Optional: Custom prompts for this agent
prompts:
# Option 1: Use custom prompt files
system_prompt_file: "prompts/custom_system_prompt.txt"
initial_user_request_file: "prompts/custom_initial_request.txt"
clarification_response_file: "prompts/custom_clarification.txt"

# Option 2: Provide prompts directly (uncomment to use)
# system_prompt_str: "You are a specialized research agent..."
# initial_user_request_str: "Custom initial request template..."
# clarification_response_str: "Custom clarification template..."

# Optional: Execution configuration
execution:
max_steps: 8
max_iterations: 15
max_clarifications: 5
max_searches: 6
mcp_context_limit: 20000
logs_dir: "logs/custom_agent"
reports_dir: "reports/custom_agent"

# Optional: MCP configuration
mcp:
mcpServers:
deepwiki:
url: "https://mcp.deepwiki.com/mcp"

# Tools this agent can use (must be registered in tool registry)
tools:
- "WebSearchTool"
- "ExtractPageContentTool"
- "CreateReportTool"
- "ClarificationTool"
- "GeneratePlanTool"
- "AdaptPlanTool"
- "FinalAnswerTool"

# Example 2: Minimal agent with defaults
- name: "simple_agent"
base_class: "SGRToolCallingResearchAgent"

# Only override what's needed
llm:
model: "gpt-4o-mini"

tools:
- "WebSearchTool"
- "FinalAnswerTool"

# Example 3: Fast research agent optimized for speed
- name: "fast_research_agent"
base_class: "SGRToolCallingResearchAgent"

llm:
model: "gpt-4o-mini"
temperature: 0.1
max_tokens: 4000

execution:
max_steps: 4
max_iterations: 8
max_clarifications: 2
max_searches: 3

tools:
- "WebSearchTool"
- "CreateReportTool"
- "FinalAnswerTool"
- "ReasoningTool"

# Example 4: Specialized technical analyst with custom prompts
- name: "technical_analyst"
base_class: "SGRResearchAgent"

llm:
model: "gpt-4o"
temperature: 0.2

prompts:
system_prompt_file: "prompts/technical_analyst_prompt.txt"

execution:
max_steps: 10
max_iterations: 20
max_clarifications: 3
max_searches: 8

tools:
- "WebSearchTool"
- "ExtractPageContentTool"
- "CreateReportTool"
- "ClarificationTool"
- "FinalAnswerTool"

# Example 5: Agent using inline prompts instead of files
- name: "inline_prompt_agent"
base_class: "SGRResearchAgent"

prompts:
system_prompt_str: |
You are a helpful research assistant.
Your goal is to provide accurate and concise information.
initial_user_request_str: |
User request: {user_request}
Please analyze and respond.
clarification_response_str: |
I need clarification on: {clarification_needed}

tools:
- "WebSearchTool"
- "FinalAnswerTool"
6 changes: 3 additions & 3 deletions benchmark/run_simpleqa_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
grading_answer,
save_result,
)
from sgr_deep_research.settings import get_config
from sgr_deep_research.core.agent_config import GlobalConfig

project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
config_path = os.path.join(project_root, "config.yaml")
Expand All @@ -29,8 +29,8 @@


async def benchmark_agent(question, answer, model_config) -> Dict[str, Any]:
system_conf = get_config()
agent = BenchmarkAgent(task=question, max_iterations=system_conf.execution.max_steps)
system_conf = GlobalConfig()
agent = BenchmarkAgent(task=question, max_iterations=system_conf.execution.max_iterations)

try:
await agent.execute()
Expand Down
97 changes: 47 additions & 50 deletions config.yaml.example
Original file line number Diff line number Diff line change
@@ -1,58 +1,55 @@
# SGR Research Agent - Configuration Template
# Production-ready configuration for Schema-Guided Reasoning
# Copy this file to config.yaml and fill in your API keys

# OpenAI API Configuration
openai:
api_key: "your-openai-api-key-here" # Required: Your OpenAI API key
base_url: "https://api.openai.com/v1" # Optional: Alternative URL (e.g., for proxy LiteLLM/vLLM)
model: "gpt-4o-mini" # Model to use
max_tokens: 8000 # Maximum number of tokens
temperature: 0.4 # Generation temperature (0.0-1.0)
proxy: "" # Example: "socks5://127.0.0.1:1081" or "http://127.0.0.1:8080" or leave empty for no proxy

# Tavily Search Configuration
tavily:
api_key: "your-tavily-api-key-here" # Required: Your Tavily API key
api_base_url: "https://api.tavily.com" # Tavily API base URL

# Search Settings
# SGR Deep Research Agent - Configuration Template
# Copy this file to config.yaml and fill in your data

# LLM Configuration
llm:
api_key: "your-openai-api-key-here" # Your OpenAI API key
base_url: "https://api.openai.com/v1" # API base URL
model: "gpt-4o-mini" # Model name
max_tokens: 8000 # Max output tokens
temperature: 0.4 # Temperature (0.0-1.0)
# proxy: "socks5://127.0.0.1:1081" # Optional proxy (socks5:// or http://)

# Search Configuration (Tavily)
search:
max_results: 10 # Maximum number of search results

# Scraping Settings
scraping:
enabled: false # Enable full text scraping of found pages
max_pages: 5 # Maximum pages to scrape per search
content_limit: 1500 # Character limit for full content per source
tavily_api_key: "your-tavily-api-key-here" # Tavily API key (get at tavily.com)
tavily_api_base_url: "https://api.tavily.com" # Tavily API URL
max_results: 10 # Max search results
max_pages: 5 # Max pages to scrape
content_limit: 1500 # Content char limit per source

# Execution Settings
execution:
max_steps: 6 # Maximum number of execution steps
reports_dir: "reports" # Directory for saving reports
logs_dir: "logs" # Directory for saving reports

# Prompts Settings
max_steps: 6 # Max execution steps
max_clarifications: 3 # Max clarification requests
max_iterations: 10 # Max iterations per step
max_searches: 4 # Max search operations
mcp_context_limit: 15000 # Max context length from MCP server response
logs_dir: "logs" # Directory for saving agent execution logs
reports_dir: "reports" # Directory for saving agent reports

# Prompts Configuration
prompts:
prompts_dir: "prompts" # Directory with prompts
system_prompt_file: "system_prompt.txt" # System prompt file

# Logging Settings
logging:
config_file: "logging_config.yaml" # Logging configuration file path
# Option 1: Use file paths (absolute or relative to project root)
system_prompt_file: "sgr_deep_research/core/prompts/system_prompt.txt"
initial_user_request_file: "sgr_deep_research/core/prompts/initial_user_request.txt"
clarification_response_file: "sgr_deep_research/core/prompts/clarification_response.txt"

mcp:

# Limit on the result of MCP tool invocation.
# A balanced constant value: not too large to avoid filling the entire context window with potentially unimportant data,
# yet not too small to ensure critical information from a single MCP fits through
context_limit: 15000
# Option 2: Provide prompts directly as strings (uncomment to use)
# system_prompt_str: "Your custom system prompt here..."
# initial_user_request_str: "Your custom initial request template..."
# clarification_response_str: "Your custom clarification template..."

# https://gofastmcp.com/clients/transports#mcp-json-configuration-transport
transport_config:
mcpServers:
deepwiki:
url: "https://mcp.deepwiki.com/mcp"
# Note: If both file and string are provided, string takes precedence

context7:
url: "https://mcp.context7.com/mcp"
# MCP (Model Context Protocol) Configuration
mcp:
mcpServers:
deepwiki:
url: "https://mcp.deepwiki.com/mcp"

# Add more MCP servers here:
# your_server:
# url: "https://your-mcp-server.com/mcp"
# headers:
# Authorization: "Bearer your-token"
Loading