Flows에서의 인간 피드백

Flows에서의 인간 피드백 (Human Feedback in Flows)

@human_feedback 데코레이터를 사용하면 CrewAI Flows 안에서 바로 인간-인-더-루프(HITL, Human-in-the-Loop) 워크플로우를 만들 수 있어요. flow 실행을 일시 중지하고, 출력을 인간에게 보여주고, 피드백을 수집하고, 결과에 따라 다른 listener로 라우팅할 수 있어요.

출처: 문서

본문

Overview (개요)

@human_feedback 데코레이터는 CrewAI 1.8.0 이상이 필요해요. 이 기능을 사용하기 전에 설치를 업데이트했는지 확인하세요.

@human_feedback 데코레이터는 CrewAI Flows 안에서 직접 인간-인-더-루프(HITL) 워크플로우를 가능하게 해요. flow 실행을 일시 중지하고, 출력을 인간이 검토하도록 보여주고, 피드백을 수집하고, 선택적으로 피드백 결과에 따라 다른 listener로 라우팅할 수 있게 해 줘요.

특히 다음에 유용해요:

  • 품질 보증(Quality assurance) — AI 생성 콘텐츠를 다운스트림에서 사용하기 전에 검토
  • 의사결정 게이트(Decision gates) — 자동화된 워크플로우에서 인간이 중요한 결정을 내리게 함
  • 승인 워크플로우(Approval workflows) — approve/reject/revise 패턴 구현
  • 대화형 개선(Interactive refinement) — 출력을 반복적으로 개선하기 위해 피드백 수집

Quick Start (빠른 시작)

flow에 인간 피드백을 추가하는 가장 간단한 방법은 이래요:

from crewai.flow.flow import Flow, start, listen
from crewai.flow.human_feedback import human_feedback

class SimpleReviewFlow(Flow):
    @start()
    @human_feedback(message="Please review this content:")
    def generate_content(self):
        return "This is AI-generated content that needs review."

    @listen(generate_content)
    def process_feedback(self, result):
        print(f"Content: {result.output}")
        print(f"Human said: {result.feedback}")

flow = SimpleReviewFlow()
flow.kickoff()

이 flow가 실행되면:

  • generate_content를 실행하고 문자열을 반환해요.
  • 출력을 요청 메시지와 함께 사용자에게 표시해요.
  • 사용자가 피드백을 입력할 때까지 기다려요 (또는 Enter를 눌러 건너뛰기).
  • HumanFeedbackResult 객체를 process_feedback에 전달해요.

The @human_feedback Decorator

Parameters (파라미터)

Parameter Type Required Description
message str Yes 인간에게 메서드 출력과 함께 보여지는 메시지
emit Sequence[str] No 가능한 결과 목록. 피드백이 이 중 하나로 축약되며 @listen 데코레이터를 트리거
llm str | BaseLLM emit 지정 시 피드백을 해석해 결과로 매핑하는 LLM
default_outcome str No 피드백이 없을 때 사용할 결과. 반드시 emit에 있어야 함
metadata dict No 엔터프라이즈 통합을 위한 추가 데이터
provider HumanFeedbackProvider No 비동기/비차단 피드백용 커스텀 프로바이더. Async Human Feedback 참조
learn bool No HITL 학습 활성화: 피드백에서 교훈을 추출해 향후 출력을 사전 검토. 기본값 False
learn_limit int No 사전 검토에 회상할 과거 교훈 최대 수. 기본값 5

Basic Usage (No Routing)

emit을 지정하지 않으면 데코레이터는 단순히 피드백을 수집해 다음 listener에 HumanFeedbackResult를 전달해요:

@start()
@human_feedback(message="What do you think of this analysis?")
def analyze_data(self):
    return "Analysis results: Revenue up 15%, costs down 8%"

@listen(analyze_data)
def handle_feedback(self, result):
    # result is a HumanFeedbackResult
    print(f"Analysis: {result.output}")
    print(f"Feedback: {result.feedback}")

Routing with emit

emit을 지정하면 데코레이터가 라우터가 돼요. 인간의 자유 형식 피드백을 LLM이 해석해서 지정된 결과 중 하나로 축약해요:

from crewai.flow.flow import Flow, start, listen, or_
from crewai.flow.human_feedback import human_feedback

class ReviewFlow(Flow):
    @start()
    def generate_content(self):
        return "Draft blog post content here..."

    @human_feedback(
        message="Do you approve this content for publication?",
        emit=["approved", "rejected", "needs_revision"],
        llm="gpt-4o-mini",
        default_outcome="needs_revision",
    )
    @listen(or_("generate_content", "needs_revision"))
    def review_content(self):
        return "Draft blog post content here..."

    @listen("approved")
    def publish(self, result):
        print(f"Publishing! User said: {result.feedback}")

    @listen("rejected")
    def discard(self, result):
        print(f"Discarding. Reason: {result.feedback}")

인간이 "needs more detail" 같은 말을 하면 LLM이 그걸 "needs_revision"으로 축약하고, or_()를 통해 review_content를 다시 트리거해요 — 즉 수정 루프(revision loop)를 만드는 거예요. 결과가 "approved" 또는 "rejected"가 될 때까지 루프가 계속돼요.

LLM은 가능할 때 구조화된 출력(function calling)을 사용해 응답이 지정한 결과 중 하나임을 보장해요. 덕분에 라우팅이 신뢰할 수 있고 예측 가능해요.

@start() 메서드는 flow 시작 시 한 번만 실행돼요. 수정 루프가 필요하다면 start 메서드와 review 메서드를 분리하고, review 메서드에 @listen(or_("trigger", "revision_outcome"))를 사용해 셀프 루프를 활성화하세요.

HumanFeedbackResult

HumanFeedbackResult dataclass는 인간 피드백 상호작용에 대한 모든 정보를 담아요:

from crewai.flow.human_feedback import HumanFeedbackResult

@dataclass
class HumanFeedbackResult:
    output: Any              # The original method output shown to the human
    feedback: str            # The raw feedback text from the human
    outcome: str | None      # The collapsed outcome (if emit was specified)
    timestamp: datetime      # When the feedback was received
    method_name: str         # Name of the decorated method
    metadata: dict           # Any metadata passed to the decorator

Accessing in Listeners

emit이 있는 @human_feedback 메서드에 의해 listener가 트리거되면 HumanFeedbackResult를 받아요:

@listen("approved")
def on_approval(self, result: HumanFeedbackResult):
    print(f"Original output: {result.output}")
    print(f"User feedback: {result.feedback}")
    print(f"Outcome: {result.outcome}")  # "approved"
    print(f"Received at: {result.timestamp}")

Accessing Feedback History (피드백 기록 접근)

Flow 클래스는 인간 피드백에 접근하는 두 가지 속성을 제공해요.

last_human_feedback

가장 최근의 HumanFeedbackResult를 반환해요:

@listen(some_method)
def check_feedback(self):
    if self.last_human_feedback:
        print(f"Last feedback: {self.last_human_feedback.feedback}")

human_feedback_history

flow 중 수집된 모든 HumanFeedbackResult 객체의 목록이에요:

@listen(final_step)
def summarize(self):
    print(f"Total feedback collected: {len(self.human_feedback_history)}")
    for i, fb in enumerate(self.human_feedback_history):
        print(f"{i+1}. {fb.method_name}: {fb.outcome or 'no routing'}")

각 HumanFeedbackResult는 human_feedback_history에 추가되므로, 여러 피드백 단계가 서로 덮어쓰지 않아요. 이 목록을 사용해 flow 중 수집된 모든 피드백에 접근하세요.

Complete Example: Content Approval Workflow (전체 예시: 콘텐츠 승인 워크플로우)

수정 루프가 있는 콘텐츠 검토·승인 워크플로우의 전체 예시예요:

from crewai.flow.flow import Flow, start, listen, or_
from crewai.flow.human_feedback import human_feedback, HumanFeedbackResult
from pydantic import BaseModel


class ContentState(BaseModel):
    draft: str = ""
    revision_count: int = 0
    status: str = "pending"


class ContentApprovalFlow(Flow[ContentState]):
    """A flow that generates content and loops until the human approves."""

    @start()
    def generate_draft(self):
        self.state.draft = "# AI Safety\n\nThis is a draft about AI Safety..."
        return self.state.draft

    @human_feedback(
        message="Please review this draft. Approve, reject, or describe what needs changing:",
        emit=["approved", "rejected", "needs_revision"],
        llm="gpt-4o-mini",
        default_outcome="needs_revision",
    )
    @listen(or_("generate_draft", "needs_revision"))
    def review_draft(self):
        self.state.revision_count += 1
        return f"{self.state.draft} (v{self.state.revision_count})"

    @listen("approved")
    def publish_content(self, result: HumanFeedbackResult):
        self.state.status = "published"
        print(f"Content approved and published! Reviewer said: {result.feedback}")
        return "published"

    @listen("rejected")
    def handle_rejection(self, result: HumanFeedbackResult):
        self.state.status = "rejected"
        print(f"Content rejected. Reason: {result.feedback}")
        return "rejected"


flow = ContentApprovalFlow()
result = flow.kickoff()
print(f"\nFlow completed. Status: {flow.state.status}, Reviews: {flow.state.revision_count}")

실행 출력 예시:

==================================================
OUTPUT FOR REVIEW:
==================================================
# AI Safety

This is a draft about AI Safety... (v1)
==================================================

Please review this draft. Approve, reject, or describe what needs changing:
(Press Enter to skip, or type your feedback)

Your feedback: Needs more detail on alignment research

==================================================
OUTPUT FOR REVIEW:
==================================================
# AI Safety

This is a draft about AI Safety... (v2)
==================================================

Please review this draft. Approve, reject, or describe what needs changing:
(Press Enter to skip, or type your feedback)

Your feedback: Looks good, approved!

Content approved and published! Reviewer said: Looks good, approved!

Flow completed. Status: published, Reviews: 2

핵심 패턴은 @listen(or_("generate_draft", "needs_revision"))이에요 — review 메서드가 초기 트리거와 자신의 수정 결과를 모두 듣기 때문에, 인간이 승인하거나 거부할 때까지 반복되는 셀프 루프가 생겨요.

Combining with Other Decorators (다른 데코레이터와 조합)

@human_feedback 데코레이터는 @start(), @listen(), or_()와 함께 동작해요. 두 데코레이터 순서 모두 동작하지만(프레임워크가 양방향으로 속성을 전파), 권장 패턴은 이래요:

# One-shot review at the start of a flow (no self-loop)
@start()
@human_feedback(message="Review this:", emit=["approved", "rejected"], llm="gpt-4o-mini")
def my_start_method(self):
    return "content"

# Linear review on a listener (no self-loop)
@listen(other_method)
@human_feedback(message="Review this too:", emit=["good", "bad"], llm="gpt-4o-mini")
def my_listener(self, data):
    return f"processed: {data}"

# Self-loop: review that can loop back for revisions
@human_feedback(message="Approve or revise?", emit=["approved", "revise"], llm="gpt-4o-mini")
@listen(or_("upstream_method", "revise"))
def review_with_loop(self):
    return "content for review"

Self-loop pattern (셀프 루프 패턴)

수정 루프를 만들려면 review 메서드가 or_()를 사용해 상위 트리거와 자신의 수정 결과 둘 다 들어야 해요:

@start()
def generate(self):
    return "initial draft"

@human_feedback(
    message="Approve or request changes?",
    emit=["revise", "approved"],
    llm="gpt-4o-mini",
    default_outcome="approved",
)
@listen(or_("generate", "revise"))
def review(self):
    return "content"

@listen("approved")
def publish(self):
    return "published"

결과가 "revise"면 flow가 review로 다시 라우팅돼요 (or_()를 통해 "revise"를 듣기 때문). 결과가 "approved"면 flow가 publish로 계속 진행돼요. flow 엔진이 라우터를 "한 번만 실행(unfire-once)" 규칙에서 면제해 주므로, 각 루프 반복마다 다시 실행될 수 있거든요.

Chained routers (연결된 라우터)

한 라우터의 결과에 의해 트리거된 listener가 자신도 라우터가 될 수 있어요:

@start()
def generate(self):
    return "draft content"

@human_feedback(message="First review:", emit=["approved", "rejected"], llm="gpt-4o-mini")
@listen("generate")
def first_review(self):
    return "draft content"

@human_feedback(message="Final review:", emit=["publish", "hold"], llm="gpt-4o-mini")
@listen("approved")
def final_review(self, prev):
    return "final content"

@listen("publish")
def on_publish(self, prev):
    return "published"

@listen("hold")
def on_hold(self, prev):
    return "held for later"

Limitations (제한 사항)

  • @start() 메서드는 한 번만 실행돼요 : @start() 메서드는 셀프 루프할 수 없어요. 수정 주기가 필요하다면 별도의 @start() 메서드를 진입점으로 사용하고 @human_feedback을 @listen() 메서드에 두세요.
  • 같은 메서드에 @start() + @listen() 불가 : Flow 프레임워크 제약이에요. 메서드는 시작 지점이거나 listener이거나 둘 중 하나예요.

Best Practices (모범 사례)

1. 명확한 요청 메시지 작성

message 파라미터는 인간이 보는 것이에요. 실행 가능하게 만드세요:

# ✅ Good - clear and actionable
@human_feedback(message="Does this summary accurately capture the key points? Reply 'yes' or explain what's missing:")

# ❌ Bad - vague
@human_feedback(message="Review this:")

2. 의미 있는 결과 선택

emit을 사용할 때는 인간의 응답에 자연스럽게 매핑되는 결과를 고르세요:

# ✅ Good - natural language outcomes
emit=["approved", "rejected", "needs_more_detail"]

# ❌ Bad - technical or unclear
emit=["state_1", "state_2", "state_3"]

3. 항상 기본 결과 제공

사용자가 입력 없이 Enter를 누르는 경우를 처리하려면 default_outcome을 사용하세요:

@human_feedback(
    message="Approve? (press Enter to request revision)",
    emit=["approved", "needs_revision"],
    llm="gpt-4o-mini",
    default_outcome="needs_revision",  # Safe default
)

4. 감사 추적을 위해 피드백 기록 사용

human_feedback_history에 접근해 감사 로그를 만들어요:

@listen(final_step)
def create_audit_log(self):
    log = []
    for fb in self.human_feedback_history:
        log.append({
            "step": fb.method_name,
            "outcome": fb.outcome,
            "feedback": fb.feedback,
            "timestamp": fb.timestamp.isoformat(),
        })
    return log

5. 라우팅 여부에 따른 피드백 처리

워크플로우를 설계할 때 라우팅이 필요한지 고려하세요:

Scenario Use
피드백 텍스트만 필요한 간단한 검토 No emit
응답에 따라 다른 경로로 분기해야 할 때 Use emit
approve/reject/revise 승인 게이트 Use emit
로깅용 의견 수집만 No emit

Async Human Feedback (비차단)

기본적으로 @human_feedback은 콘솔 입력을 기다리며 실행을 차단해요. 프로덕션 애플리케이션에서는 Slack, 이메일, 웹훅, API 같은 외부 시스템과 통합되는 async/non-blocking 피드백이 필요할 수 있어요.

The Provider Abstraction

provider 파라미터로 커스텀 피드백 수집 전략을 지정하세요:

from crewai.flow import Flow, start, human_feedback, HumanFeedbackProvider, HumanFeedbackPending, PendingFeedbackContext

class WebhookProvider(HumanFeedbackProvider):
    """Provider that pauses flow and waits for webhook callback."""

    def __init__(self, webhook_url: str):
        self.webhook_url = webhook_url

    def request_feedback(self, context: PendingFeedbackContext, flow: Flow) -> str:
        # Notify external system (e.g., send Slack message, create ticket)
        self.send_notification(context)

        # Pause execution - framework handles persistence automatically
        raise HumanFeedbackPending(
            context=context,
            callback_info={"webhook_url": f"{self.webhook_url}/{context.flow_id}"}
        )

class ReviewFlow(Flow):
    @start()
    @human_feedback(
        message="Review this content:",
        emit=["approved", "rejected"],
        llm="gpt-4o-mini",
        provider=WebhookProvider("https://myapp.com/api"),
    )
    def generate_content(self):
        return "AI-generated content..."

    @listen("approved")
    def publish(self, result):
        return "Published!"

flow 프레임워크는 HumanFeedbackPending이 raise될 때 상태를 자동으로 영속화해요. 여러분의 프로바이더는 외부 시스템에 알리고 예외를 raise하기만 하면 돼요 — 수동 영속화 호출은 필요 없어요.

Handling Paused Flows (일시 중지된 flow 처리)

async 프로바이더를 사용할 때 kickoff()는 예외를 raise하는 대신 HumanFeedbackPending 객체를 반환해요:

flow = ReviewFlow()
result = flow.kickoff()

if isinstance(result, HumanFeedbackPending):
    # Flow is paused, state is automatically persisted
    print(f"Waiting for feedback at: {result.callback_info['webhook_url']}")
    print(f"Flow ID: {result.context.flow_id}")
else:
    # Normal completion
    print(f"Flow completed: {result}")

Resuming a Paused Flow (일시 중지된 flow 재개)

피드백이 도착하면(예: 웹훅으로) flow를 재개해요:

# Sync handler:
def handle_feedback_webhook(flow_id: str, feedback: str):
    flow = ReviewFlow.from_pending(flow_id)
    result = flow.resume(feedback)
    return result

# Async handler (FastAPI, aiohttp, etc.):
async def handle_feedback_webhook(flow_id: str, feedback: str):
    flow = ReviewFlow.from_pending(flow_id)
    result = await flow.resume_async(feedback)
    return result

Key Types (핵심 타입)

Type Description
HumanFeedbackProvider 커스텀 피드백 프로바이더를 위한 프로토콜
PendingFeedbackContext 일시 중지된 flow 재개에 필요한 모든 정보
HumanFeedbackPending 피드백을 위해 flow가 일시 중지됐을 때 kickoff()가 반환
ConsoleProvider 기본 차단 콘솔 입력 프로바이더

PendingFeedbackContext

컨텍스트는 재개에 필요한 모든 것을 담아요:

@dataclass
class PendingFeedbackContext:
    flow_id: str           # Unique identifier for this flow execution
    flow_class: str        # Fully qualified class name
    method_name: str       # Method that triggered feedback
    method_output: Any     # Output shown to the human
    message: str           # The request message
    emit: list[str] | None # Possible outcomes for routing
    default_outcome: str | None
    metadata: dict         # Custom metadata
    llm: str | None        # LLM for outcome collapsing
    requested_at: datetime

Complete Async Flow Example

from crewai.flow import (
    Flow, start, listen, human_feedback,
    HumanFeedbackProvider, HumanFeedbackPending, PendingFeedbackContext
)

class SlackNotificationProvider(HumanFeedbackProvider):
    """Provider that sends Slack notifications and pauses for async feedback."""

    def __init__(self, channel: str):
        self.channel = channel

    def request_feedback(self, context: PendingFeedbackContext, flow: Flow) -> str:
        # Send Slack notification (implement your own)
        slack_thread_id = self.post_to_slack(
            channel=self.channel,
            message=f"Review needed:\n\n{context.method_output}\n\n{context.message}",
        )

        # Pause execution - framework handles persistence automatically
        raise HumanFeedbackPending(
            context=context,
            callback_info={
                "slack_channel": self.channel,
                "thread_id": slack_thread_id,
            }
        )

class ContentPipeline(Flow):
    @start()
    @human_feedback(
        message="Approve this content for publication?",
        emit=["approved", "rejected"],
        llm="gpt-4o-mini",
        default_outcome="rejected",
        provider=SlackNotificationProvider("#content-reviews"),
    )
    def generate_content(self):
        return "AI-generated blog post content..."

    @listen("approved")
    def publish(self, result):
        print(f"Publishing! Reviewer said: {result.feedback}")
        return {"status": "published"}

    @listen("rejected")
    def archive(self, result):
        print(f"Archived. Reason: {result.feedback}")
        return {"status": "archived"}


# Starting the flow (will pause and wait for Slack response)
def start_content_pipeline():
    flow = ContentPipeline()
    result = flow.kickoff()

    if isinstance(result, HumanFeedbackPending):
        return {"status": "pending", "flow_id": result.context.flow_id}

    return result


# Resuming when Slack webhook fires (sync handler)
def on_slack_feedback(flow_id: str, slack_message: str):
    flow = ContentPipeline.from_pending(flow_id)
    result = flow.resume(slack_message)
    return result


# If your handler is async (FastAPI, aiohttp, Slack Bolt async, etc.)
async def on_slack_feedback_async(flow_id: str, slack_message: str):
    flow = ContentPipeline.from_pending(flow_id)
    result = await flow.resume_async(slack_message)
    return result

async 웹 프레임워크(FastAPI, aiohttp, Slack Bolt async mode)를 사용한다면 flow.resume() 대신 await flow.resume_async()를 사용하세요. 실행 중인 이벤트 루프 안에서 resume()을 호출하면 RuntimeError가 raise돼요.

Async 피드백 모범 사례

  • 반환 타입 확인 — 일시 중지됐을 때 kickoff()는 HumanFeedbackPending을 반환해요 — try/except가 필요 없어요.
  • 올바른 resume 메서드 사용 — sync 코드에서는 resume()을, async 코드에서는 await resume_async()를 사용하세요.
  • callback 정보 저장 — 웹훅 URL, 티켓 ID 등을 저장하려면 callback_info를 사용하세요.
  • 멱등성 구현 — 안전을 위해 resume 핸들러는 멱등(idempotent)이어야 해요.
  • 자동 영속화 — HumanFeedbackPending이 raise되면 상태가 자동으로 저장되며 기본적으로 SQLiteFlowPersistence를 사용해요.
  • 커스텀 영속화 — 필요하다면 from_pending()에 커스텀 영속화 인스턴스를 전달하세요.

Learning from Feedback (피드백에서 학습하기)

learn=True 파라미터는 인간 리뷰어와 메모리 시스템 사이의 피드백 루프를 활성화해요. 활성화하면 시스템이 과거 인간 수정 사항에서 학습해 출력을 점진적으로 개선해요.

How It Works (동작 원리)

  • 피드백 후 — LLM이 출력 + 피드백에서 일반화 가능한 교훈을 추출해 source="hitl"로 메모리에 저장해요. 피드백이 단순 승인(예: "looks good")이라면 아무것도 저장하지 않아요.
  • 다음 검토 전 — 과거 HITL 교훈을 메모리에서 회상해 LLM이 인간이 보기 전에 출력을 개선하는 데 적용해요.

시간이 지나면서, 각 수정이 향후 검토를 알려주기 때문에 인간이 보는 사전 검토된 출력이 점점 더 좋아져요.

Example

class ArticleReviewFlow(Flow):
    @start()
    def generate_article(self):
        return self.crew.kickoff(inputs={"topic": "AI Safety"}).raw

    @human_feedback(
        message="Review this article draft:",
        emit=["approved", "needs_revision"],
        llm="gpt-4o-mini",
        learn=True,  # enable HITL learning
    )
    @listen(or_("generate_article", "needs_revision"))
    def review_article(self):
        return self.last_human_feedback.output if self.last_human_feedback else "article draft"

    @listen("approved")
    def publish(self):
        print(f"Publishing: {self.last_human_feedback.output}")

첫 실행 — 인간이 원본 출력을 보고 "Always include citations for factual claims."라고 말해요. 이 교훈이 추출되어 메모리에 저장돼요. 두 번째 실행 — 시스템이 인용 교훈을 회상해, 인용을 추가하도록 출력을 사전 검토한 뒤 개선된 버전을 보여줘요. 인간의 역할이 "모두 고치기"에서 "시스템이 놓친 것을 잡기"로 바뀌어요.

Configuration (구성)

Parameter Default Description
learn False HITL 학습 활성화
learn_limit 5 사전 검토에 회상할 과거 교훈 최대 수

Key Design Decisions (핵심 설계 결정)

  • 모든 것에 같은 LLM 사용 — 데코레이터의 llm 파라미터가 결과 축약, 교훈 증류, 사전 검토에 모두 공유돼요. 여러 모델을 구성할 필요가 없어요.
  • 구조화된 출력 — 증류와 사전 검토 모두 LLM이 지원할 때 Pydantic 모델로 function calling을 사용하고, 그렇지 않으면 텍스트 파싱으로 폴백해요.
  • 비차단 저장 — 교훈은 백그라운드 스레드에서 실행되는 remember_many()를 통해 저장돼요 — flow가 즉시 계속돼요.
  • 우아한 저하(Graceful degradation) — 증류 중 LLM이 실패하면 저장하지 않아요. 사전 검토 중 실패하면 원본 출력을 보여줘요. 어느 실패도 flow를 막지 않아요.
  • 스코프/카테고리 불필요 — 교훈을 저장할 때 source만 전달돼요. 인코딩 파이프라인이 스코프, 카테고리, 중요도를 자동으로 추론해요.

learn=True는 Flow가 메모리를 사용할 수 있어야 해요. Flow는 기본적으로 자동으로 메모리를 얻지만, _skip_auto_memory로 비활성화했다면 HITL 학습은 조용히 건너뛰어져요.

  • Flows Overview — CrewAI Flows에 대해 배우기
  • Flow State Management — flow에서 상태 관리하기
  • Flow Persistence — flow 상태 영속화
  • Routing with @router — 조건부 라우팅에 대한 더 많은 내용
  • Human Input on Execution — 태스크 레벨 인간 입력
  • Memory — HITL 학습이 사용하는 통합 메모리 시스템

더 알아보기 (Learn more)