Docs update

This commit is contained in:
HyunjunJeon
2025-12-31 14:02:10 +09:00
parent 9cb01f4abe
commit 14628f22f6
16 changed files with 329 additions and 316 deletions

5
.gitignore vendored
View File

@@ -23,3 +23,8 @@ CLAUDE.md
GEMINI.md
QWEN.md
.serena/
# Others
.DS_Store
.vscode/
*.pdf

View File

@@ -219,9 +219,9 @@ graph LR
| Backend | 저장 위치 | 수명 | files_update 반환 | 사용 시나리오 |
|---------|----------|------|------------------|--------------|
| **StateBackend** | LangGraph State | 대화 스레드 | (상태 업데이트용) | 임시 작업 파일 |
| **FilesystemBackend** | 로컬 디스크 | 영구 | (이미 저장됨) | 실제 파일 조작 |
| **StoreBackend** | LangGraph Store | 스레드 간 영구 | (이미 저장됨) | 장기 메모리 |
| **StateBackend** | LangGraph State | 대화 스레드 | (상태 업데이트용) | 임시 작업 파일 |
| **FilesystemBackend** | 로컬 디스크 | 영구 | (이미 저장됨) | 실제 파일 조작 |
| **StoreBackend** | LangGraph Store | 스레드 간 영구 | (이미 저장됨) | 장기 메모리 |
| **CompositeBackend** | 라우팅 | 백엔드별 상이 | 백엔드별 상이 | 하이브리드 |
### 3.3 StateBackend 상세
@@ -278,7 +278,7 @@ class FilesystemBackend(BackendProtocol):
- Path traversal 차단: `..`, `~` 패턴 거부
**검색 최적화**:
- 1차: Ripgrep (`rg`) JSON 출력 사용
- 1차: ripgrep (`rg`) JSON 출력 사용
- 2차: Python 폴백 (Ripgrep 미설치 시)
### 3.5 CompositeBackend 상세

View File

@@ -1,16 +1,20 @@
# DeepAgent Context Engineering
FileSystem 기반 Context Engineering 을 원활히 수행하는 Multi Agent 구성을 위한 DeepAgent(From LangChain's deepagents library)
Agent 2.0 Paradigm 을 잘 구현하는 DeepAgent 를 활용해서, FileSystem 기반 Context Engineering 을 원활히 수행하는 Research 용 Multi Agent 구성(From LangChain's deepagents library)
![agent_20_paradigm](./agent_20_paradigm.png)
## Agent 1.0 vs Agent 2.0
![agent_versus_10_20](./agent_versus_10_20.jpeg)
## DeepAgent Technical Guide
[DeepAgent Technical Guide](./DeepAgents_Technical_Guide.md)
## DeepAgent - Research
## DeepAgent 기반의 Research 수행용 MAS(Multi Agent System)
### 모듈 구조 요약
```
```bash
research_agent/
├── agent.py # 메인 오케스트레이터 (create_deep_agent)
├── prompts.py # 오케스트레이터 및 Simple SubAgent 프롬프트
@@ -34,12 +38,12 @@ research_agent/
| 파일 | 역할 |
|------|------|
| `agent.py` | 메인 에이전트 생성 및 구성 |
| `researcher/agent.py` | CompiledSubAgent 패턴의 자율적 연구 에이전트 |
| `researcher/prompts.py` | "넓게 탐색 → 깊게 파기" 워크플로우 정의 |
| `prompts.py` | 오케스트레이터 워크플로우 및 위임 전략 |
| `researcher/agent.py` | 자율적으로 연구하게끔 구성된 에이전트 |
| `researcher/prompts.py` | "넓게 탐색 → 깊게 파기" 전략으로 구성된 워크플로우 정의 |
| `prompts.py` | 오케스트레이터(Main DeepAgent) 워크플로우 및 위임(Delegation) 전략 |
## DeepAgent UI(made by LangChain)
## DeepAgent UI(Made by LangChain)
```bash
git clone https://github.com/langchain-ai/deep-agents-ui.git
cd deep-agents-ui
@@ -54,3 +58,4 @@ yarn dev
- [LangChain DeepAgent Docs](https://docs.langchain.com/oss/python/deepagents/overview)
- [LangGraph CLI Docs](https://docs.langchain.com/langsmith/cli#configuration-file)
- [DeepAgent UI](https://github.com/langchain-ai/deep-agents-ui)
- [Agents-2.0-deep-agents](https://www.philschmid.de/agents-2.0-deep-agents)

BIN
agent_20_paradigm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

BIN
agent_versus_10_20.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

View File

@@ -54,7 +54,7 @@ INSTRUCTIONS = (
)
# =============================================================================
# SubAgent Definitions
# SubAgent 정의
# =============================================================================
# 1. Researcher SubAgent: 자율적 연구 DeepAgent (CompiledSubAgent)
@@ -125,7 +125,7 @@ def backend_factory(rt: ToolRuntime):
# =============================================================================
# Deep Agent 생성
# DeepAgent 생성
# =============================================================================
#
# 통합 구성:

View File

@@ -200,7 +200,7 @@ Your role is to coordinate research by delegating tasks from your TODO list to s
# =============================================================================
# EXPLORER SubAgent Instructions
# EXPLORER SubAgent 지침
# =============================================================================
EXPLORER_INSTRUCTIONS = """You are a fast exploration assistant specialized in quickly finding and analyzing information.
@@ -248,7 +248,7 @@ Structure your response as:
# =============================================================================
# SYNTHESIZER SubAgent Instructions
# SYNTHESIZER SubAgent 지침
# =============================================================================
SYNTHESIZER_INSTRUCTIONS = """You are a synthesis specialist that combines research findings into coherent, well-structured outputs.

View File

@@ -1,13 +1,13 @@
"""Autonomous researcher subagent module.
"""자율적 연구 SubAgent 모듈.
This module provides a self-planning, self-reflecting research agent
that follows a "breadth-first, depth-second" methodology.
이 모듈은 "넓게 탐색 → 깊게 파기" 방법론을 따르는
자체 계획 및 자체 반성 연구 에이전트를 제공한다.
Usage:
사용법:
from research_agent.researcher import get_researcher_subagent
researcher = get_researcher_subagent(model=model, backend=backend)
# Returns CompiledSubAgent for use in create_deep_agent(subagents=[...])
# create_deep_agent(subagents=[...])에 사용할 CompiledSubAgent 반환
"""
from research_agent.researcher.agent import (

View File

@@ -20,40 +20,40 @@ def create_researcher_agent(
model: str | BaseChatModel | None = None,
backend: BackendProtocol | BackendFactory | None = None,
) -> CompiledStateGraph:
"""Create an autonomous researcher DeepAgent.
"""자율적 연구 DeepAgent를 생성한다.
This agent has its own:
- Planning loop (write_todos via TodoListMiddleware)
- Research loop (tavily_search + think_tool)
- Context management (SummarizationMiddleware)
- File access (FilesystemMiddleware) for intermediate results
이 에이전트는 다음 기능을 자체적으로 보유한다:
- 계획 루프 (TodoListMiddleware를 통한 write_todos)
- 연구 루프 (tavily_search + think_tool)
- 컨텍스트 관리 (SummarizationMiddleware)
- 중간 결과 저장을 위한 파일 접근 (FilesystemMiddleware)
Essentially a "research SubGraph" that operates autonomously.
본질적으로 자율적으로 작동하는 "연구 SubGraph"이다.
Args:
model: LLM to use. Defaults to gpt-4.1 with temperature=0.
backend: Backend for file operations. If provided,
researcher can save intermediate results to filesystem.
model: 사용할 LLM. 기본값은 temperature=0인 gpt-4.1.
backend: 파일 작업용 백엔드. 제공되면
연구자가 중간 결과를 파일시스템에 저장할 수 있다.
Returns:
CompiledStateGraph: A fully autonomous research agent that can be
used standalone or as a CompiledSubAgent in an orchestrator.
CompiledStateGraph: 독립적으로 사용하거나 오케스트레이터의
CompiledSubAgent로 사용할 수 있는 완전 자율적 연구 에이전트.
Example:
# Standalone usage
# 독립 사용
researcher = create_researcher_agent()
result = researcher.invoke({
"messages": [HumanMessage("Research quantum computing trends")]
"messages": [HumanMessage("양자 컴퓨팅 트렌드 연구")]
})
# As SubAgent in orchestrator
# 오케스트레이터의 SubAgent로 사용
subagent = get_researcher_subagent()
orchestrator = create_deep_agent(subagents=[subagent, ...])
"""
if model is None:
model = ChatOpenAI(model="gpt-4.1", temperature=0.0)
# Format prompt with current date
# 현재 날짜로 프롬프트 포맷팅
current_date = datetime.now().strftime("%Y-%m-%d")
formatted_prompt = AUTONOMOUS_RESEARCHER_INSTRUCTIONS.format(date=current_date)
@@ -69,20 +69,20 @@ def get_researcher_subagent(
model: str | BaseChatModel | None = None,
backend: BackendProtocol | BackendFactory | None = None,
) -> dict:
"""Get researcher as a CompiledSubAgent for use in orchestrator.
"""오케스트레이터에서 사용할 CompiledSubAgent로 연구자를 가져온다.
This function creates an autonomous researcher agent and wraps it
in the CompiledSubAgent format expected by SubAgentMiddleware.
이 함수는 자율적 연구 에이전트를 생성하고 SubAgentMiddleware가
기대하는 CompiledSubAgent 형식으로 래핑한다.
Args:
model: LLM to use. Defaults to gpt-4.1.
backend: Backend for file operations.
model: 사용할 LLM. 기본값은 gpt-4.1.
backend: 파일 작업용 백엔드.
Returns:
dict: CompiledSubAgent with keys:
dict: 다음 키를 가진 CompiledSubAgent:
- name: "researcher"
- description: Used by orchestrator to decide when to delegate
- runnable: The autonomous researcher agent
- description: 오케스트레이터가 위임 결정 시 사용
- runnable: 자율적 연구 에이전트
Example:
from research_agent.researcher import get_researcher_subagent

View File

@@ -1,14 +1,14 @@
"""Skills module for research_agent.
"""research_agent용 Skills 모듈.
This module implements the Agent Skills pattern with progressive disclosure:
1. At session start, parse YAML frontmatter from SKILL.md files
2. Inject skill metadata (name + description) into system prompt
3. Agent reads full SKILL.md content when the skill is relevant
이 모듈은 점진적 공개(Progressive Disclosure) 패턴으로 Agent Skills 패턴을 구현한다:
1. 세션 시작 시 SKILL.md 파일에서 YAML 프론트매터 파싱
2. 스킬 메타데이터(이름 + 설명)를 시스템 프롬프트에 주입
3. 스킬이 관련될 때 에이전트가 전체 SKILL.md 콘텐츠 읽기
Public API:
- SkillsMiddleware: Middleware to integrate skills into agent execution
- list_skills: Load skill metadata from directories
- SkillMetadata: TypedDict for skill metadata structure
공개 API:
- SkillsMiddleware: 에이전트 실행에 스킬을 통합하는 미들웨어
- list_skills: 디렉토리에서 스킬 메타데이터 로드
- SkillMetadata: 스킬 메타데이터 구조용 TypedDict
"""
from research_agent.skills.load import SkillMetadata, list_skills

View File

@@ -1,26 +1,26 @@
"""Skill loader for parsing and loading agent skills from SKILL.md files.
"""SKILL.md 파일에서 에이전트 스킬을 파싱하고 로드하는 스킬 로더.
This module implements the Anthropic Agent Skills pattern via YAML frontmatter parsing.
Each skill is a directory containing a SKILL.md file with:
- YAML frontmatter (name, description required)
- Markdown instructions for the agent
- Optional supporting files (scripts, configs, etc.)
이 모듈은 YAML 프론트매터 파싱을 통해 Anthropic Agent Skills 패턴을 구현한다.
각 스킬은 다음을 포함하는 SKILL.md 파일이 있는 디렉토리이다:
- YAML 프론트매터 (name, description 필수)
- 에이전트용 마크다운 지침
- 선택적 지원 파일 (스크립트, 설정 등)
Example SKILL.md structure:
SKILL.md 구조 예시:
```markdown
---
name: web-research
description: A structured approach to conducting thorough web research
description: 철저한 웹 리서치를 수행하기 위한 구조화된 접근법
---
# Web Research Skill
# 웹 리서치 스킬
## When to Use
- When the user requests topic research
## 사용 시점
- 사용자가 주제 연구를 요청할 때
...
```
Adapted from deepagents-cli for use in research_agent project.
research_agent 프로젝트용으로 deepagents-cli에서 적응함.
"""
from __future__ import annotations
@@ -34,56 +34,56 @@ import yaml
logger = logging.getLogger(__name__)
# Maximum file size for SKILL.md files (10MB) - DoS protection
# SKILL.md 파일 최대 크기 (10MB) - DoS 방지
MAX_SKILL_FILE_SIZE = 10 * 1024 * 1024
# Agent Skills specification constraints (https://agentskills.io/specification)
# Agent Skills 명세 제약 조건 (https://agentskills.io/specification)
MAX_SKILL_NAME_LENGTH = 64
MAX_SKILL_DESCRIPTION_LENGTH = 1024
class SkillMetadata(TypedDict):
"""Skill metadata following Agent Skills specification."""
"""Agent Skills 명세를 따르는 스킬 메타데이터."""
name: str
"""Skill name (max 64 chars, lowercase alphanumeric and hyphens)."""
"""스킬 이름 (최대 64자, 소문자 영숫자와 하이픈만)."""
description: str
"""Description of what the skill does (max 1024 chars)."""
"""스킬이 하는 일에 대한 설명 (최대 1024자)."""
path: str
"""Path to the SKILL.md file."""
"""SKILL.md 파일 경로."""
source: str
"""Source of the skill ('user' or 'project')."""
"""스킬 출처 ('user' 또는 'project')."""
# Optional fields per Agent Skills specification
# Agent Skills 명세에 따른 선택적 필드
license: NotRequired[str | None]
"""License name or reference to bundled license file."""
"""라이선스 이름 또는 번들된 라이선스 파일 참조."""
compatibility: NotRequired[str | None]
"""Environment requirements (max 500 chars)."""
"""환경 요구사항 (최대 500자)."""
metadata: NotRequired[dict[str, str] | None]
"""Arbitrary key-value mapping for additional metadata."""
"""추가 메타데이터용 임의 키-값 매핑."""
allowed_tools: NotRequired[str | None]
"""Space-separated list of pre-approved tools."""
"""사전 승인된 도구의 공백 구분 목록."""
def _is_safe_path(path: Path, base_dir: Path) -> bool:
"""Check if path is safely contained within base_dir.
"""경로가 base_dir 내에 안전하게 포함되어 있는지 확인한다.
Prevents directory traversal attacks via symlinks or path manipulation.
Resolves both paths to canonical form (following symlinks) and verifies
the target path is within the base directory.
심볼릭 링크나 경로 조작을 통한 디렉토리 탐색 공격을 방지한다.
두 경로 모두 정규 형식으로 변환(심볼릭 링크 따라감)하고
대상 경로가 기본 디렉토리 내에 있는지 확인한다.
Args:
path: Path to validate
base_dir: Base directory that should contain the path
path: 검증할 경로
base_dir: 경로가 포함되어야 하는 기본 디렉토리
Returns:
True if path is safely within base_dir, False otherwise
경로가 base_dir 내에 안전하게 있으면 True, 그렇지 않으면 False
"""
try:
resolved_path = path.resolve()
@@ -91,113 +91,116 @@ def _is_safe_path(path: Path, base_dir: Path) -> bool:
resolved_path.relative_to(resolved_base)
return True
except ValueError:
# Path is not a subdirectory of base_dir
# 경로가 base_dir의 하위 디렉토리가 아님
return False
except (OSError, RuntimeError):
# Error resolving path (e.g., circular symlinks)
# 경로 해석 오류 (예: 순환 심볼릭 링크)
return False
def _validate_skill_name(name: str, directory_name: str) -> tuple[bool, str]:
"""Validate skill name per Agent Skills specification.
"""Agent Skills 명세에 따라 스킬 이름을 검증한다.
Requirements:
- Max 64 characters
- Lowercase alphanumeric and hyphens only (a-z, 0-9, -)
- Cannot start or end with hyphen
- No consecutive hyphens
- Must match parent directory name
요구사항:
- 최대 64자
- 소문자 영숫자와 하이픈만 (a-z, 0-9, -)
- 하이픈으로 시작하거나 끝날 수 없음
- 연속 하이픈 없음
- 부모 디렉토리 이름과 일치해야 함
Args:
name: Skill name from YAML frontmatter
directory_name: Parent directory name
name: YAML 프론트매터의 스킬 이름
directory_name: 부모 디렉토리 이름
Returns:
(is_valid, error_message) tuple. Error message is empty if valid.
(is_valid, error_message) 튜플. 유효하면 에러 메시지는 빈 문자열.
"""
if not name:
return False, "Name is required"
return False, "이름은 필수입니다"
if len(name) > MAX_SKILL_NAME_LENGTH:
return False, "Name exceeds 64 characters"
# Pattern: lowercase alphanumeric, single hyphens between segments
return False, "이름이 64자를 초과합니다"
# 패턴: 소문자 영숫자, 세그먼트 사이에 단일 하이픈
if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", name):
return False, "Name must use only lowercase alphanumeric and single hyphens"
return False, "이름은 소문자 영숫자와 단일 하이픈만 사용해야 합니다"
if name != directory_name:
return False, f"Name '{name}' must match directory name '{directory_name}'"
return (
False,
f"이름 '{name}'은 디렉토리 이름 '{directory_name}'과 일치해야 합니다",
)
return True, ""
def _parse_skill_metadata(skill_md_path: Path, source: str) -> SkillMetadata | None:
"""Parse YAML frontmatter from SKILL.md file per Agent Skills specification.
"""Agent Skills 명세에 따라 SKILL.md 파일에서 YAML 프론트매터를 파싱한다.
Args:
skill_md_path: Path to SKILL.md file
source: Skill source ('user' or 'project')
skill_md_path: SKILL.md 파일 경로
source: 스킬 출처 ('user' 또는 'project')
Returns:
SkillMetadata with all fields, or None if parsing fails
모든 필드가 있는 SkillMetadata, 파싱 실패 시 None
"""
try:
# Security: Check file size to prevent DoS
# 보안: DoS 방지를 위한 파일 크기 확인
file_size = skill_md_path.stat().st_size
if file_size > MAX_SKILL_FILE_SIZE:
logger.warning(
"Skipping %s: file too large (%d bytes)", skill_md_path, file_size
"%s 건너뜀: 파일이 너무 큼 (%d 바이트)", skill_md_path, file_size
)
return None
content = skill_md_path.read_text(encoding="utf-8")
# Match YAML frontmatter between --- delimiters
# --- 구분자 사이의 YAML 프론트매터 매칭
frontmatter_pattern = r"^---\s*\n(.*?)\n---\s*\n"
match = re.match(frontmatter_pattern, content, re.DOTALL)
if not match:
logger.warning(
"Skipping %s: no valid YAML frontmatter found", skill_md_path
"%s 건너뜀: 유효한 YAML 프론트매터를 찾을 수 없음", skill_md_path
)
return None
frontmatter_str = match.group(1)
# Parse YAML with safe_load for proper nested structure support
# 적절한 중첩 구조 지원을 위해 safe_load로 YAML 파싱
try:
frontmatter_data = yaml.safe_load(frontmatter_str)
except yaml.YAMLError as e:
logger.warning("Invalid YAML in %s: %s", skill_md_path, e)
logger.warning("%s의 YAML이 유효하지 않음: %s", skill_md_path, e)
return None
if not isinstance(frontmatter_data, dict):
logger.warning("Skipping %s: frontmatter is not a mapping", skill_md_path)
logger.warning("%s 건너뜀: 프론트매터가 매핑이 아님", skill_md_path)
return None
# Validate required fields
# 필수 필드 검증
name = frontmatter_data.get("name")
description = frontmatter_data.get("description")
if not name or not description:
logger.warning(
"Skipping %s: missing required 'name' or 'description'", skill_md_path
"%s 건너뜀: 필수 'name' 또는 'description' 누락", skill_md_path
)
return None
# Validate name format per spec (warn but load for backward compatibility)
# 명세에 따른 이름 형식 검증 (역호환성을 위해 경고하지만 로드)
directory_name = skill_md_path.parent.name
is_valid, error = _validate_skill_name(str(name), directory_name)
if not is_valid:
logger.warning(
"Skill '%s' in %s does not follow Agent Skills spec: %s. "
"Consider renaming for spec compliance.",
"'%s' 스킬 (%s)이 Agent Skills 명세를 따르지 않음: %s. "
"명세 준수를 위해 이름 변경을 고려하세요.",
name,
skill_md_path,
error,
)
# Validate description length (spec: max 1024 chars)
# 설명 길이 검증 (명세: 최대 1024자)
description_str = str(description)
if len(description_str) > MAX_SKILL_DESCRIPTION_LENGTH:
logger.warning(
"Description in %s exceeds %d chars, truncating",
"%s의 설명이 %d자를 초과하여 잘림",
skill_md_path,
MAX_SKILL_DESCRIPTION_LENGTH,
)
@@ -215,35 +218,35 @@ def _parse_skill_metadata(skill_md_path: Path, source: str) -> SkillMetadata | N
)
except (OSError, UnicodeDecodeError) as e:
logger.warning("Error reading %s: %s", skill_md_path, e)
logger.warning("%s 읽기 오류: %s", skill_md_path, e)
return None
def _list_skills_from_dir(skills_dir: Path, source: str) -> list[SkillMetadata]:
"""List all skills from a single skills directory (internal helper).
"""단일 스킬 디렉토리에서 모든 스킬을 나열한다 (내부 헬퍼).
Scans the skills directory for subdirectories containing SKILL.md files,
parses YAML frontmatter, and returns skill metadata.
스킬 디렉토리를 스캔하여 SKILL.md 파일을 포함하는 하위 디렉토리를 찾고,
YAML 프론트매터를 파싱하여 스킬 메타데이터를 반환한다.
Skills organization:
스킬 구조:
skills/
├── skill-name/
│ ├── SKILL.md # Required: instructions with YAML frontmatter
│ ├── script.py # Optional: supporting files
│ └── config.json # Optional: supporting files
│ ├── SKILL.md # 필수: YAML 프론트매터가 있는 지침
│ ├── script.py # 선택: 지원 파일
│ └── config.json # 선택: 지원 파일
Args:
skills_dir: Path to skills directory
source: Skill source ('user' or 'project')
skills_dir: 스킬 디렉토리 경로
source: 스킬 출처 ('user' 또는 'project')
Returns:
List of skill metadata dictionaries with name, description, path, and source
name, description, path, source가 있는 스킬 메타데이터 딕셔너리 목록
"""
skills_dir = skills_dir.expanduser()
if not skills_dir.exists():
return []
# Resolve base directory for security checks
# 보안 검사를 위한 기본 디렉토리 해석
try:
resolved_base = skills_dir.resolve()
except (OSError, RuntimeError):
@@ -251,25 +254,25 @@ def _list_skills_from_dir(skills_dir: Path, source: str) -> list[SkillMetadata]:
skills: list[SkillMetadata] = []
# Iterate over subdirectories
# 하위 디렉토리 순회
for skill_dir in skills_dir.iterdir():
# Security: catch symlinks pointing outside skills directory
# 보안: 스킬 디렉토리 외부를 가리키는 심볼릭 링크 포착
if not _is_safe_path(skill_dir, resolved_base):
continue
if not skill_dir.is_dir():
continue
# Look for SKILL.md file
# SKILL.md 파일 찾기
skill_md_path = skill_dir / "SKILL.md"
if not skill_md_path.exists():
continue
# Security: validate SKILL.md path before reading
# 보안: 읽기 전에 SKILL.md 경로 검증
if not _is_safe_path(skill_md_path, resolved_base):
continue
# Parse metadata
# 메타데이터 파싱
metadata = _parse_skill_metadata(skill_md_path, source=source)
if metadata:
skills.append(metadata)
@@ -282,32 +285,32 @@ def list_skills(
user_skills_dir: Path | None = None,
project_skills_dir: Path | None = None,
) -> list[SkillMetadata]:
"""List skills from user and/or project directories.
"""사용자 및/또는 프로젝트 디렉토리에서 스킬을 나열한다.
When both directories are provided, project skills with the same name
as user skills will override the user skills.
두 디렉토리가 모두 제공되면, 사용자 스킬과 동일한 이름의 프로젝트 스킬이
사용자 스킬을 오버라이드한다.
Args:
user_skills_dir: Path to user-level skills directory
project_skills_dir: Path to project-level skills directory
user_skills_dir: 사용자 레벨 스킬 디렉토리 경로
project_skills_dir: 프로젝트 레벨 스킬 디렉토리 경로
Returns:
Merged list of skill metadata from both sources, with project skills
taking precedence over user skills when names conflict
두 출처의 스킬 메타데이터를 병합한 목록.
이름이 충돌할 때 프로젝트 스킬이 우선됨
"""
all_skills: dict[str, SkillMetadata] = {}
# Load user skills first (baseline)
# 사용자 스킬을 먼저 로드 (기본)
if user_skills_dir:
user_skills = _list_skills_from_dir(user_skills_dir, source="user")
for skill in user_skills:
all_skills[skill["name"]] = skill
# Load project skills second (override/extend)
# 프로젝트 스킬을 두 번째로 로드 (오버라이드/확장)
if project_skills_dir:
project_skills = _list_skills_from_dir(project_skills_dir, source="project")
for skill in project_skills:
# Project skills override user skills with same name
# 프로젝트 스킬이 같은 이름의 사용자 스킬을 오버라이드
all_skills[skill["name"]] = skill
return list(all_skills.values())

View File

@@ -1,20 +1,20 @@
"""Middleware for loading and exposing agent skills to the system prompt.
"""에이전트 스킬을 시스템 프롬프트에 로드하고 노출하기 위한 미들웨어.
This middleware implements Anthropic's "Agent Skills" pattern via progressive disclosure:
1. At session start, parse YAML frontmatter from SKILL.md files
2. Inject skill metadata (name + description) into system prompt
3. Agent reads full SKILL.md content when the skill is relevant
이 미들웨어는 점진적 공개를 통해 Anthropic "Agent Skills" 패턴을 구현한다:
1. 세션 시작 시 SKILL.md 파일에서 YAML 프론트매터 파싱
2. 스킬 메타데이터(이름 + 설명)를 시스템 프롬프트에 주입
3. 스킬이 관련될 때 에이전트가 전체 SKILL.md 콘텐츠 읽기
Skills directory structure (project-level):
스킬 디렉토리 구조 (프로젝트 레벨):
{PROJECT_ROOT}/skills/
├── web-research/
│ ├── SKILL.md # Required: YAML frontmatter + instructions
│ └── helper.py # Optional: supporting files
│ ├── SKILL.md # 필수: YAML 프론트매터 + 지침
│ └── helper.py # 선택: 지원 파일
├── code-review/
│ ├── SKILL.md
│ └── checklist.md
Adapted from deepagents-cli for use in research_agent project.
research_agent 프로젝트용으로 deepagents-cli에서 적응함.
"""
from collections.abc import Awaitable, Callable
@@ -33,20 +33,20 @@ from research_agent.skills.load import SkillMetadata, list_skills
class SkillsState(AgentState):
"""State for skills middleware."""
"""스킬 미들웨어용 상태."""
skills_metadata: NotRequired[list[SkillMetadata]]
"""List of loaded skill metadata (name, description, path)."""
"""로드된 스킬 메타데이터 목록 (이름, 설명, 경로)."""
class SkillsStateUpdate(TypedDict):
"""State update for skills middleware."""
"""스킬 미들웨어용 상태 업데이트."""
skills_metadata: list[SkillMetadata]
"""List of loaded skill metadata (name, description, path)."""
"""로드된 스킬 메타데이터 목록 (이름, 설명, 경로)."""
# Skills system documentation template
# 스킬 시스템 문서 템플릿
SKILLS_SYSTEM_PROMPT = """
## Skills System
@@ -94,21 +94,21 @@ Note: Skills are tools that make you more capable and consistent. When in doubt,
class SkillsMiddleware(AgentMiddleware):
"""Middleware for loading and exposing agent skills.
"""에이전트 스킬을 로드하고 노출하기 위한 미들웨어.
This middleware implements Anthropic's Agent Skills pattern:
- At session start: load skill metadata (name, description) from YAML frontmatter
- Inject skill list into system prompt for discoverability
- Agent reads full SKILL.md content when skill is relevant (progressive disclosure)
이 미들웨어는 Anthropic Agent Skills 패턴을 구현한다:
- 세션 시작 시: YAML 프론트매터에서 스킬 메타데이터(이름, 설명) 로드
- 발견 가능성을 위해 시스템 프롬프트에 스킬 목록 주입
- 스킬이 관련될 때 에이전트가 전체 SKILL.md 콘텐츠 읽기 (점진적 공개)
Supports both user-level and project-level skills:
- Project skills: {PROJECT_ROOT}/skills/
- Project skills override user skills with the same name
사용자 레벨과 프로젝트 레벨 스킬 모두 지원:
- 프로젝트 스킬: {PROJECT_ROOT}/skills/
- 프로젝트 스킬이 같은 이름의 사용자 스킬을 오버라이드
Args:
skills_dir: Path to user-level skills directory (agent-specific).
assistant_id: Agent identifier for path references in prompt.
project_skills_dir: Optional path to project-level skills directory.
skills_dir: 사용자 레벨 스킬 디렉토리 경로 (에이전트별).
assistant_id: 프롬프트의 경로 참조용 에이전트 식별자.
project_skills_dir: 프로젝트 레벨 스킬 디렉토리 경로 (선택).
"""
state_schema = SkillsState
@@ -120,24 +120,24 @@ class SkillsMiddleware(AgentMiddleware):
assistant_id: str,
project_skills_dir: str | Path | None = None,
) -> None:
"""Initialize skills middleware.
"""스킬 미들웨어를 초기화한다.
Args:
skills_dir: Path to user-level skills directory.
assistant_id: Agent identifier.
project_skills_dir: Optional path to project-level skills directory.
skills_dir: 사용자 레벨 스킬 디렉토리 경로.
assistant_id: 에이전트 식별자.
project_skills_dir: 프로젝트 레벨 스킬 디렉토리 경로 (선택).
"""
self.skills_dir = Path(skills_dir).expanduser()
self.assistant_id = assistant_id
self.project_skills_dir = (
Path(project_skills_dir).expanduser() if project_skills_dir else None
)
# Store paths for prompt display
# 프롬프트 표시용 경로 저장
self.user_skills_display = f"~/.deepagents/{assistant_id}/skills"
self.system_prompt_template = SKILLS_SYSTEM_PROMPT
def _format_skills_locations(self) -> str:
"""Format skill locations for system prompt display."""
"""시스템 프롬프트 표시용 스킬 위치를 포맷팅한다."""
locations = [f"**User Skills**: `{self.user_skills_display}`"]
if self.project_skills_dir:
locations.append(
@@ -146,20 +146,20 @@ class SkillsMiddleware(AgentMiddleware):
return "\n".join(locations)
def _format_skills_list(self, skills: list[SkillMetadata]) -> str:
"""Format skill metadata for system prompt display."""
"""시스템 프롬프트 표시용 스킬 메타데이터를 포맷팅한다."""
if not skills:
locations = [f"{self.user_skills_display}/"]
if self.project_skills_dir:
locations.append(f"{self.project_skills_dir}/")
return f"(No skills available. You can create skills in {' or '.join(locations)})"
# Group skills by source
# 출처별로 스킬 그룹화
user_skills = [s for s in skills if s["source"] == "user"]
project_skills = [s for s in skills if s["source"] == "project"]
lines = []
# Display user skills
# 사용자 스킬 표시
if user_skills:
lines.append("**User Skills:**")
for skill in user_skills:
@@ -167,7 +167,7 @@ class SkillsMiddleware(AgentMiddleware):
lines.append(f" → To read full instructions: `{skill['path']}`")
lines.append("")
# Display project skills
# 프로젝트 스킬 표시
if project_skills:
lines.append("**Project Skills:**")
for skill in project_skills:
@@ -179,19 +179,19 @@ class SkillsMiddleware(AgentMiddleware):
def before_agent(
self, state: SkillsState, runtime: Runtime
) -> SkillsStateUpdate | None:
"""Load skill metadata before agent execution.
"""에이전트 실행 전에 스킬 메타데이터를 로드한다.
This runs once at session start to discover available skills from
both user-level and project-level directories.
세션 시작 시 한 번 실행되어 사용자 레벨과 프로젝트 레벨
디렉토리 모두에서 사용 가능한 스킬을 발견한다.
Args:
state: Current agent state.
runtime: Runtime context.
state: 현재 에이전트 상태.
runtime: 런타임 컨텍스트.
Returns:
Updated state with skills_metadata populated.
skills_metadata가 채워진 업데이트된 상태.
"""
# Reload skills on each interaction to catch directory changes
# 디렉토리 변경을 캐치하기 위해 각 상호작용마다 스킬 다시 로드
skills = list_skills(
user_skills_dir=self.skills_dir,
project_skills_dir=self.project_skills_dir,
@@ -203,25 +203,25 @@ class SkillsMiddleware(AgentMiddleware):
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
"""Inject skill documentation into system prompt.
"""시스템 프롬프트에 스킬 문서를 주입한다.
This runs before every model call to ensure skill information is available.
스킬 정보가 사용 가능하도록 모든 모델 호출 전에 실행된다.
Args:
request: Model request being processed.
handler: Handler function to call with modified request.
request: 처리 중인 모델 요청.
handler: 수정된 요청으로 호출할 핸들러 함수.
Returns:
Model response from handler.
핸들러의 모델 응답.
"""
# Get skill metadata from state
# 상태에서 스킬 메타데이터 가져오기
skills_metadata = request.state.get("skills_metadata", [])
# Format skill locations and list
# 스킬 위치와 목록 포맷팅
skills_locations = self._format_skills_locations()
skills_list = self._format_skills_list(skills_metadata)
# Format skill documentation
# 스킬 문서 포맷팅
skills_section = self.system_prompt_template.format(
skills_locations=skills_locations,
skills_list=skills_list,
@@ -239,30 +239,30 @@ class SkillsMiddleware(AgentMiddleware):
request: ModelRequest,
handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
) -> ModelResponse:
"""(Async) Inject skill documentation into system prompt.
"""(비동기) 시스템 프롬프트에 스킬 문서를 주입한다.
Args:
request: Model request being processed.
handler: Handler function to call with modified request.
request: 처리 중인 모델 요청.
handler: 수정된 요청으로 호출할 핸들러 함수.
Returns:
Model response from handler.
핸들러의 모델 응답.
"""
# State is guaranteed to be SkillsState due to state_schema
# state_schema로 인해 상태가 SkillsState임이 보장됨
state = cast("SkillsState", request.state)
skills_metadata = state.get("skills_metadata", [])
# Format skill locations and list
# 스킬 위치와 목록 포맷팅
skills_locations = self._format_skills_locations()
skills_list = self._format_skills_list(skills_metadata)
# Format skill documentation
# 스킬 문서 포맷팅
skills_section = self.system_prompt_template.format(
skills_locations=skills_locations,
skills_list=skills_list,
)
# Inject into system prompt
# 시스템 프롬프트에 주입
if request.system_prompt:
system_prompt = request.system_prompt + "\n\n" + skills_section
else:

View File

@@ -1,17 +1,17 @@
"""SubAgents module for research_agent.
"""research_agent용 SubAgents 모듈.
This module implements a Claude Code-inspired SubAgent system with:
- Multiple specialized agent types (researcher, explorer, synthesizer)
- SubAgent registry for dynamic agent management
- Type-based routing via the task tool
이 모듈은 Claude Code에서 영감을 받은 SubAgent 시스템을 구현한다:
- 다중 전문화 에이전트 타입 (researcher, explorer, synthesizer)
- 동적 에이전트 관리를 위한 SubAgent 레지스트리
- task 도구를 통한 타입 기반 라우팅
Architecture:
아키텍처:
Main Orchestrator Agent
├── researcher SubAgent (deep web research)
├── explorer SubAgent (fast codebase exploration)
└── synthesizer SubAgent (research synthesis)
├── researcher SubAgent (심층 웹 연구)
├── explorer SubAgent (빠른 코드베이스 탐색)
└── synthesizer SubAgent (연구 결과 통합)
Usage:
사용법:
from research_agent.subagents import get_all_subagents
agent = create_deep_agent(

View File

@@ -1,17 +1,17 @@
"""SubAgent definitions for research_agent.
"""research_agent용 SubAgent 정의.
This module defines specialized SubAgent specifications following the
Claude Code subagent_type pattern. Each SubAgent has:
- Unique name (used as subagent_type)
- Clear description for delegation decisions
- Specialized system prompt
- Curated tool set
- Optional model override
이 모듈은 Claude Code subagent_type 패턴을 따르는
전문화된 SubAgent 명세를 정의한다. 각 SubAgent:
- 고유 이름 (subagent_type으로 사용)
- 위임 결정을 위한 명확한 설명
- 전문화된 시스템 프롬프트
- 엄선된 도구 세트
- 선택적 모델 오버라이드
SubAgent Types:
researcher: Deep web research with reflection
explorer: Fast read-only codebase/document exploration
synthesizer: Research synthesis and report generation
SubAgent 타입:
researcher: 반성을 포함한 심층 웹 연구
explorer: 빠른 읽기 전용 코드베이스/문서 탐색
synthesizer: 연구 통합 및 보고서 생성
"""
from datetime import datetime
@@ -22,7 +22,7 @@ from research_agent.prompts import (
SYNTHESIZER_INSTRUCTIONS,
)
# Current date for dynamic prompts
# 동적 프롬프트용 현재 날짜
_current_date = datetime.now().strftime("%Y-%m-%d")
@@ -34,7 +34,7 @@ EXPLORER_AGENT = {
"name": "explorer",
"description": "Fast read-only exploration of codebases and documents. Use for finding files, searching patterns, and quick information retrieval. Cannot modify files.",
"system_prompt": EXPLORER_INSTRUCTIONS,
"tools": [], # Will be populated with read-only tools at runtime
"tools": [], # 런타임에 읽기 전용 도구로 채워짐
"capabilities": ["explore", "search", "read"],
}
@@ -47,7 +47,7 @@ RESEARCHER_AGENT = {
"name": "researcher",
"description": "Deep web research with reflection. Use for comprehensive topic research, gathering sources, and in-depth analysis. Includes tavily_search and think_tool.",
"system_prompt": RESEARCHER_INSTRUCTIONS.format(date=_current_date),
"tools": [], # Will be populated with tavily_search, think_tool at runtime
"tools": [], # 런타임에 tavily_search, think_tool로 채워짐
"capabilities": ["research", "web", "analysis"],
}
@@ -60,25 +60,25 @@ SYNTHESIZER_AGENT = {
"name": "synthesizer",
"description": "Synthesize multiple research findings into coherent reports. Use for combining sub-agent results, creating summaries, and writing final reports.",
"system_prompt": SYNTHESIZER_INSTRUCTIONS,
"tools": [], # Will be populated with read_file, write_file, think_tool at runtime
"tools": [], # 런타임에 read_file, write_file, think_tool로 채워짐
"capabilities": ["synthesize", "write", "analysis"],
}
# =============================================================================
# Utility Functions
# 유틸리티 함수
# =============================================================================
def get_all_subagents() -> list[dict]:
"""Get all SubAgent definitions as a list.
"""모든 SubAgent 정의를 목록으로 반환한다.
Returns:
List of SubAgent specification dictionaries.
SubAgent 명세 딕셔너리 목록.
Note:
Tools are empty and should be populated at agent creation time
based on available tools in the runtime.
도구는 비어 있으며 런타임에서 사용 가능한 도구를 기반으로
에이전트 생성 시 채워져야 한다.
"""
return [
RESEARCHER_AGENT,
@@ -88,13 +88,13 @@ def get_all_subagents() -> list[dict]:
def get_subagent_by_name(name: str) -> dict | None:
"""Get a specific SubAgent definition by name.
"""이름으로 특정 SubAgent 정의를 가져온다.
Args:
name: SubAgent name (e.g., "researcher", "explorer", "synthesizer")
name: SubAgent 이름 (예: "researcher", "explorer", "synthesizer")
Returns:
SubAgent specification dict if found, None otherwise.
찾으면 SubAgent 명세 딕셔너리, 그렇지 않으면 None.
"""
agents = {
"researcher": RESEARCHER_AGENT,
@@ -105,10 +105,10 @@ def get_subagent_by_name(name: str) -> dict | None:
def get_subagent_descriptions() -> str:
"""Get formatted descriptions of all SubAgents.
"""모든 SubAgent의 포맷된 설명을 가져온다.
Returns:
Formatted string listing all SubAgents and their descriptions.
모든 SubAgent와 설명을 나열한 포맷된 문자열.
"""
descriptions = []
for agent in get_all_subagents():

View File

@@ -1,22 +1,22 @@
"""SubAgent Registry for managing specialized agent types.
"""전문화된 에이전트 타입 관리를 위한 SubAgent 레지스트리.
This module provides a registry pattern inspired by Claude Code's subagent_type system,
allowing dynamic registration and lookup of SubAgent specifications.
이 모듈은 Claude Code subagent_type 시스템에서 영감을 받은 레지스트리 패턴을 제공하며,
SubAgent 명세의 동적 등록과 조회를 허용한다.
The registry supports:
- Type-based agent lookup (by name)
- Capability-based filtering (by tags)
- Runtime agent discovery
레지스트리 지원 기능:
- 타입 기반 에이전트 조회 (이름으로)
- 능력 기반 필터링 (태그로)
- 런타임 에이전트 발견
Example:
registry = SubAgentRegistry()
registry.register(RESEARCHER_AGENT)
registry.register(EXPLORER_AGENT)
# Get specific agent
# 특정 에이전트 가져오기
researcher = registry.get("researcher")
# Get all agents with "research" capability
# "research" 능력을 가진 모든 에이전트 가져오기
research_agents = registry.get_by_capability("research")
"""
@@ -26,43 +26,43 @@ from typing_extensions import NotRequired
class SubAgentSpec(TypedDict):
"""Specification for a SubAgent.
"""SubAgent 명세.
This follows the DeepAgents SubAgent TypedDict pattern with additional
fields for capability-based routing.
DeepAgents SubAgent TypedDict 패턴을 따르며
능력 기반 라우팅을 위한 추가 필드를 포함한다.
"""
name: str
"""Unique identifier for the SubAgent (used as subagent_type)."""
"""SubAgent의 고유 식별자 (subagent_type으로 사용)."""
description: str
"""Description shown to main agent for delegation decisions."""
"""위임 결정을 위해 메인 에이전트에게 표시되는 설명."""
system_prompt: str
"""System prompt defining SubAgent behavior."""
"""SubAgent 동작을 정의하는 시스템 프롬프트."""
tools: list[Any]
"""Tools available to this SubAgent."""
"""이 SubAgent에서 사용 가능한 도구."""
model: NotRequired[str]
"""Optional model override (defaults to parent's model)."""
"""선택적 모델 오버라이드 (기본값은 부모의 모델)."""
capabilities: NotRequired[list[str]]
"""Capability tags for filtering (e.g., ['research', 'web'])."""
"""필터링용 능력 태그 (예: ['research', 'web'])."""
class SubAgentRegistry:
"""Registry for managing SubAgent specifications.
"""SubAgent 명세 관리를 위한 레지스트리.
This class provides Claude Code-style SubAgent management with:
- Registration and deregistration of agents
- Name-based lookup (subagent_type matching)
- Capability-based filtering
이 클래스는 Claude Code 스타일의 SubAgent 관리를 제공한다:
- 에이전트 등록 및 등록 해제
- 이름 기반 조회 (subagent_type 매칭)
- 능력 기반 필터링
Example:
registry = SubAgentRegistry()
# Register agents
# 에이전트 등록
registry.register({
"name": "researcher",
"description": "Deep web research",
@@ -71,81 +71,81 @@ class SubAgentRegistry:
"capabilities": ["research", "web"],
})
# Lookup by name
# 이름으로 조회
agent = registry.get("researcher")
# Filter by capability
# 능력으로 필터링
web_agents = registry.get_by_capability("web")
"""
def __init__(self) -> None:
"""Initialize empty registry."""
"""빈 레지스트리를 초기화한다."""
self._agents: dict[str, SubAgentSpec] = {}
def register(self, agent_spec: SubAgentSpec) -> None:
"""Register a SubAgent specification.
"""SubAgent 명세를 등록한다.
Args:
agent_spec: SubAgent specification dictionary.
agent_spec: SubAgent 명세 딕셔너리.
Raises:
ValueError: If agent with same name already registered.
ValueError: 같은 이름의 에이전트가 이미 등록된 경우.
"""
name = agent_spec["name"]
if name in self._agents:
msg = f"SubAgent '{name}' is already registered"
msg = f"SubAgent '{name}'은(는) 이미 등록되어 있습니다"
raise ValueError(msg)
self._agents[name] = agent_spec
def unregister(self, name: str) -> None:
"""Remove a SubAgent from the registry.
"""레지스트리에서 SubAgent를 제거한다.
Args:
name: Name of the SubAgent to remove.
name: 제거할 SubAgent 이름.
Raises:
KeyError: If agent not found.
KeyError: 에이전트를 찾을 수 없는 경우.
"""
if name not in self._agents:
msg = f"SubAgent '{name}' not found in registry"
msg = f"SubAgent '{name}'을(를) 레지스트리에서 찾을 수 없습니다"
raise KeyError(msg)
del self._agents[name]
def get(self, name: str) -> SubAgentSpec | None:
"""Get a SubAgent specification by name.
"""이름으로 SubAgent 명세를 가져온다.
Args:
name: SubAgent name (subagent_type).
name: SubAgent 이름 (subagent_type).
Returns:
SubAgent specification if found, None otherwise.
찾으면 SubAgent 명세, 그렇지 않으면 None.
"""
return self._agents.get(name)
def list_all(self) -> list[SubAgentSpec]:
"""List all registered SubAgent specifications.
"""등록된 모든 SubAgent 명세를 나열한다.
Returns:
List of all SubAgent specs.
모든 SubAgent 명세 목록.
"""
return list(self._agents.values())
def list_names(self) -> list[str]:
"""List all registered SubAgent names.
"""등록된 모든 SubAgent 이름을 나열한다.
Returns:
List of SubAgent names.
SubAgent 이름 목록.
"""
return list(self._agents.keys())
def get_by_capability(self, capability: str) -> list[SubAgentSpec]:
"""Get SubAgents that have a specific capability.
"""특정 능력을 가진 SubAgent를 가져온다.
Args:
capability: Capability tag to filter by.
capability: 필터링할 능력 태그.
Returns:
List of SubAgents with the specified capability.
지정된 능력을 가진 SubAgent 목록.
"""
return [
agent
@@ -154,26 +154,26 @@ class SubAgentRegistry:
]
def get_descriptions(self) -> dict[str, str]:
"""Get a mapping of agent names to descriptions.
"""에이전트 이름과 설명의 매핑을 가져온다.
Useful for displaying available agents to the main orchestrator.
메인 오케스트레이터에 사용 가능한 에이전트를 표시할 때 유용하다.
Returns:
Dictionary mapping agent names to their descriptions.
에이전트 이름을 설명에 매핑하는 딕셔너리.
"""
return {name: agent["description"] for name, agent in self._agents.items()}
def __contains__(self, name: str) -> bool:
"""Check if a SubAgent is registered.
"""SubAgent가 등록되어 있는지 확인한다.
Args:
name: SubAgent name to check.
name: 확인할 SubAgent 이름.
Returns:
True if agent is registered.
에이전트가 등록되어 있으면 True.
"""
return name in self._agents
def __len__(self) -> int:
"""Get number of registered SubAgents."""
"""등록된 SubAgent 수를 반환한다."""
return len(self._agents)

View File

@@ -47,17 +47,17 @@ def tavily_search(
Literal["general", "news", "finance"], InjectedToolArg
] = "general",
) -> str:
"""Search the web for information on a given query.
"""주어진 쿼리로 웹을 검색한다.
Uses Tavily to discover relevant URLs, then fetches and returns full webpage content as markdown.
Tavily를 사용해 관련 URL을 찾고, 전체 웹페이지 콘텐츠를 마크다운으로 가져와 반환한다.
Args:
query: Search query to execute
max_results: Maximum number of results to return (default: 1)
topic: Topic filter - 'general', 'news', or 'finance' (default: 'general')
query: 실행할 검색 쿼리
max_results: 반환할 최대 결과 수 (기본값: 1)
topic: 주제 필터 - 'general', 'news', 또는 'finance' (기본값: 'general')
Returns:
Formatted search results with full webpage content
전체 웹페이지 콘텐츠가 포함된 포맷팅된 검색 결과
"""
# Tavily 를 사용해 관련 URL 목록을 조회한다
search_results = tavily_client.search(
@@ -94,27 +94,27 @@ def tavily_search(
@tool()
def think_tool(reflection: str) -> str:
"""Tool for strategic reflection on research progress and decision-making.
"""연구 진행 상황과 의사결정을 위한 전략적 성찰 도구.
Use this tool after each search to analyze results and plan next steps systematically.
This creates a deliberate pause in the research workflow for quality decision-making.
각 검색 후 결과를 분석하고 다음 단계를 체계적으로 계획하기 위해 이 도구를 사용한다.
이는 품질 높은 의사결정을 위해 연구 워크플로우에 의도적인 멈춤을 만든다.
When to use:
- After receiving search results: What key information did I find?
- Before deciding next steps: Do I have enough to answer comprehensively?
- When assessing research gaps: What specific information am I still missing?
- Before concluding research: Can I provide a complete answer now?
사용 시점:
- 검색 결과를 받은 후: 어떤 핵심 정보를 찾았는가?
- 다음 단계를 결정하기 전: 포괄적으로 답변할 수 있을 만큼 충분한가?
- 연구 공백을 평가할 때: 아직 누락된 구체적인 정보는 무엇인가?
- 연구를 마무리하기 전: 지금 완전한 답변을 제공할 수 있는가?
Reflection should address:
1. Analysis of current findings - What concrete information have I gathered?
2. Gap assessment - What crucial information is still missing?
3. Quality evaluation - Do I have sufficient evidence/examples for a good answer?
4. Strategic decision - Should I continue searching or provide my answer?
성찰에 포함해야 할 내용:
1. 현재 발견의 분석 - 어떤 구체적인 정보를 수집했는가?
2. 공백 평가 - 어떤 중요한 정보가 아직 누락되어 있는가?
3. 품질 평가 - 좋은 답변을 위한 충분한 증거/예시가 있는가?
4. 전략적 결정 - 검색을 계속해야 하는가, 답변을 제공해야 하는가?
Args:
reflection: Your detailed reflection on research progress, findings, gaps, and next steps
reflection: 연구 진행 상황, 발견, 공백, 다음 단계에 대한 상세한 성찰
Returns:
Confirmation that reflection was recorded for decision-making
의사결정을 위해 성찰이 기록되었다는 확인
"""
return f"Reflection recorded: {reflection}"
return f"성찰 기록됨: {reflection}"