๊ด๊ณ ๊ฐ์ AI ๊ธ์ฐ๊ธฐ๋ ๋๋ฌ๋ค: Writer-Critic ๋ฃจํ๋ก ์์ฑํ๋ ์์จํ ์ฝํ ์ธ ์์ด์ ์
๋ฏธ์ฌ์ฌ๊ตฌ์ ํ๊ฐ์ ์ค์ค๋ก ๊ต์ ํ๋ ๋ฉํฐ ์์ด์ ํธ ์์คํ ๊ณผ HITL ๊ฒฐํฉ๊ธฐ
ChatGPT์๊ฒ "์ด ์ ํ์ ์๊ฐํ๋ ๋ธ๋ก๊ทธ ๊ธ์ ์จ์ค"๋ผ๊ณ ํ๋กฌํํธ๋ฅผ ๋ณด๋ ๋๋ค. 30์ด ํ ๊ฒฐ๊ณผ๋ฌผ์ด ๋์ฐฉํฉ๋๋ค:
"ํ๋ คํ ๊ธฐ์ ๋ ฅ๊ณผ ๋ค์ฑ๋ก์ด ๊ธฐ๋ฅ์ ์๋ํ๋ ์ด ํ์ ์ ์ธ ์๋ฃจ์ ์ ํ๊ธฐ์ ์ธ ์ฑ๊ณผ๋ฅผ ์ ์ฌํฉ๋๋ค."
ํ์ฉ์ฌ 4๊ฐ, ๊ตฌ์ฒด์ ์ฌ์ค 0๊ฐ. ์ด ๊ธ์ 2003๋ ํ์ผํ ์นดํ๋ก๊ทธ์ ๊ตฌ๋ณ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค. ๋ฌธ์ ๋ LLM์ด ๋ฉ์ฒญํด์๊ฐ ์๋๋๋คโํผ๋๋ฐฑ์ ์ฃผ๋ ์ฌ๋์ด ์๋ฌด๋ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ๋ชจ๋ ์กฐ์ง์๋ ์ด์์ ๋๋์งํ๋ ๊น๊นํ ํธ์ง์๊ฐ ํ์ํฉ๋๋ค. AI ํ์ดํ๋ผ์ธ์๋ ๋ง์ฐฌ๊ฐ์ง์ ๋๋ค.
๐ ๋จ์ผ ํ๋กฌํํธ LLM์ ํ๊ณ: ์ AI๋ ๊ด๊ณ ๋ฌธ๋ง ์ฐ๋๊ฐ
๋จ์ผ ํ๋กฌํํธ ํจ๋ฌ๋ค์์ ๊ทผ๋ณธ์ ๊ฒฐํจ์ ์๊ธฐ ๊ฒ์ฆ ๋ฅ๋ ฅ์ ๋ถ์ฌ์
๋๋ค. ์ธ๊ฐ ์๊ฐ๋ ์ด์์ ์ฐ๊ณ , ํธ์ง์๊ฐ ๊ฒํ ํ๊ณ , ํผ๋๋ฐฑ์ ๋ฐ์ ์ฌ์์ฑํ๋ ๋ฃจํ๋ฅผ ๋ฐ๋ณตํฉ๋๋ค. ํ์ง๋ง ChatCompletion.create() ํ ๋ฒ ํธ์ถ์ ์ด ๋ชจ๋ ๊ฒ์ ํ๋์ ์ถ๋ก ์ ์์ถํฉ๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ๋ฐ์ํ๋ ๋ฌธ์ :
| ์ฆ์ | ์์ธ | ๋น๋ |
|---|---|---|
| ๋ฏธ์ฌ์ฌ๊ตฌ ๋จ๋ฐ ("ํ๋ คํ", "ํ์ ์ ์ธ") | ํ์ต ๋ฐ์ดํฐ์ ๋ง์ผํ ํธํฅ | ~85% |
| ํ ๋ฃจ์๋ค์ด์ (์กด์ฌํ์ง ์๋ ์์น ์์ฑ) | Self-verification ๋ถ์ฌ | ~18% |
| ํค ๋๋ฆฌํํธ (B2B ๊ธ์ ํ์ผํ ํค) | ํ๋ฅด์๋ ์ ์ง ์คํจ | ~40% |
| ๊ตฌ์กฐ ๋ถ๊ดด (๋ ผ๊ฑฐ ์๋ ๋์ด) | ์ฅ๊ธฐ ์์กด์ฑ ์ถ์ ์คํจ | ~30% |
์ ๊ณ์ ๋ฐ์ฌ์ ๋์: "ํ๋กฌํํธ๋ฅผ ๋ ์ ๊ตํ๊ฒ ์ฐ์." ๊ทธ๋ฌ๋ ํ๋กฌํํธ ์์ง๋์ด๋ง์ ๋ณธ์ง์ ์ผ๋ก open-loop control์ ๋๋คโ์ถ๋ ฅ์ ๋ํ ํผ๋๋ฐฑ์ด ์์ต๋๋ค. ์ฐ๋ฆฌ์๊ฒ ํ์ํ ๊ฒ์ closed-loop control, ์ฆ ์ถ๋ ฅ์ ๊ฒ์ฆํ๊ณ ๊ธฐ์ค ๋ฏธ๋ฌ์ด๋ฉด ์๋์ผ๋ก ์ฌ์๋ํ๋ ์์คํ ์ ๋๋ค.
๐งฌ ์ํคํ ์ฒ ๋ฅ๋ค์ด๋ธ: Writer-Critic Loop
LangGraph์ TypedDict๋ฅผ ํ์ฉํ ์ํ(State) ๊ธฐ๋ฐ ๊ทธ๋ํ ์ํคํ
์ฒ๋ฅผ ์ค๊ณํ์ต๋๋ค. ํต์ฌ์ ๋ ๊ฐ์ ์์ด์ ํธ ๋
ธ๋๊ฐ ๊ณต์ ์ํ๋ฅผ ํตํด ๋ํํ๋ฉฐ, ํ์ง ์ ์๊ฐ ์๊ณ๊ฐ์ ๋๊ธธ ๋๊น์ง ๋ฃจํ๋ฅผ ๋ฐ๋ณตํ๋ ๊ฒ์
๋๋ค.
State ์ ์: LangGraph TypedDict
from typing import TypedDict, Annotated, Sequence from langgraph.graph import StateGraph, END class ContentState(TypedDict): """Writer-Critic ๋ฃจํ์ ๊ณต์ ์ํ""" topic: str # ์์ฑ ์ฃผ์ persona: str # ํค์ค๋งค๋ ํ๋กํ์ผ draft: str # ํ์ฌ ์ด์ critic_score: float # Critic์ด ๋ถ์ฌํ ์ ์ (0.0โ1.0) critic_feedback: str # ๊ตฌ์ฒด์ ํผ๋๋ฐฑ revision_count: int # ํ์ฌ ๋ฆฌ๋น์ ํ์ max_revisions: int # ๋ฌดํ ๋ฃจํ ๋ฐฉ์ง ์ํ (๊ธฐ๋ณธ๊ฐ: 5) approved: bool # HITL ์น์ธ ์ฌ๋ถ fact_density: float # ํฉํธ ๋ฐ๋ (์์น/์ฌ๋ก ๋น์จ) adjective_count: int # ํ์ฉ์ฌ ์นด์ดํธ
์ด ์ํ ๊ฐ์ฒด๊ฐ ๊ทธ๋ํ์ ์ ์ผํ ํต์ ์ฑ๋์ ๋๋ค. ๊ฐ ๋ ธ๋๋ ์ํ๋ฅผ ์ฝ๊ณ , ์์ ์ ์ญํ ์ ์ํํ ํ ์์ ๋ ์ํ๋ฅผ ๋ฐํํฉ๋๋ค. ์์ด์ ํธ ๊ฐ ์ง์ ํต์ ์ ์์ต๋๋คโ์ด๊ฒ์ด LangGraph์ ํต์ฌ ์ค๊ณ ์์น์ ๋๋ค.
Writer Agent: ํ๋ฅด์๋๋ฅผ ์งํค๋ ์ด์ ์์ฑ๊ธฐ
Writer Agent๋ ๋จ์ํ ํ ์คํธ ์์ฑ๊ธฐ๊ฐ ์๋๋๋ค. Critic์ ํผ๋๋ฐฑ์ ๊ตฌ์กฐ์ ์ผ๋ก ์์ฉํ์ฌ ์ฌ์์ฑํฉ๋๋ค:
def writer_node(state: ContentState) -> ContentState: """ Writer Agent: ์ด์ ์์ฑ ๋๋ Critic ํผ๋๋ฐฑ ๊ธฐ๋ฐ ์ฌ์์ฑ. ํต์ฌ ๊ท์น: ํ์ฉ์ฌ ๋์ ์์น/์ฌ๋ก๋ฅผ ๊ฐ์ ์ฝ์ . """ if state["revision_count"] == 0: # ์ต์ด ์ด์ ์์ฑ prompt = f""" ์ฃผ์ : {state['topic']} ํ๋ฅด์๋: {state['persona']} ๋ค์ ๊ท์น์ ์๊ฒฉํ ์ค์ํ์ธ์: 1. ํ์ฉ์ฌ ์ฌ์ฉ์ ๋ฌธ์ฅ๋น ์ต๋ 1๊ฐ๋ก ์ ํ 2. ๋ชจ๋ ์ฃผ์ฅ์ ๊ตฌ์ฒด์ ์์น, ์ฝ๋ ์์, ๋๋ ๋ฒค์น๋งํฌ๋ฅผ ์ฒจ๋ถ 3. "ํ๋ คํ", "ํ์ ์ ์ธ", "๋ค์ฑ๋ก์ด" ๋ฑ ๋ง์ผํ ํ์ฉ์ฌ ์ ๋ ๊ธ์ง """ else: # Critic ํผ๋๋ฐฑ ๊ธฐ๋ฐ ์ฌ์์ฑ prompt = f""" [์ฌ์์ฑ ์ง์] ์ด์ ์ด์: {state['draft']} Critic ์ ์: {state['critic_score']} Critic ํผ๋๋ฐฑ: {state['critic_feedback']} ํผ๋๋ฐฑ์์ ์ง์ ๋ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ธ์. ํ์ฉ์ฌ๋ฅผ ๊ฑท์ด๋ด๊ณ ํฉํธ๋ก ๊ต์ฒดํ์ธ์. ์์: "๋น ๋ฅธ ์ฒ๋ฆฌ ์๋" โ "P99 ๋ ์ดํด์ 23ms" """ new_draft = llm.invoke(prompt) return { **state, "draft": new_draft, "revision_count": state["revision_count"] + 1, }
Critic Agent: ๋ฌด์๋นํ ๊ฒ์ด๊ด
Critic Agent๋ ํ ์คํธ๋ฅผ ์ ๋์ ์ผ๋ก ํ๊ฐํฉ๋๋ค. ๊ฐ์ ์ด ์๋ ๋ฉํธ๋ฆญ์ผ๋ก ํ๋จํฉ๋๋ค:
def critic_node(state: ContentState) -> ContentState: """ Critic Agent: ์ด์์ 5๊ฐ์ง ์ถ์ผ๋ก ์ ๋ ํ๊ฐ. 0.85์ ๋ฏธ๋ง์ด๋ฉด ๊ตฌ์ฒด์ ํผ๋๋ฐฑ๊ณผ ํจ๊ป Writer์๊ฒ ๋ฐ๋ ค. """ evaluation_prompt = f""" ๋ค์ ์ด์์ ์๋ 5๊ฐ์ง ๊ธฐ์ค์ผ๋ก 0.0โ1.0 ์ค์ผ์ผ๋ก ํ๊ฐํ์ธ์. ๊ฐ ๊ธฐ์ค์ ์ ์์ ๊ตฌ์ฒด์ ๊ทผ๊ฑฐ๋ฅผ JSON์ผ๋ก ๋ฐํํ์ธ์. ์ด์: {state['draft']} ํ๊ฐ ๊ธฐ์ค: 1. fact_density: ์ฃผ์ฅ ๋๋น ๊ตฌ์ฒด์ ์์น/์ฌ๋ก ๋น์จ 2. adjective_ratio: ํ์ฉ์ฌ ๊ณผ๋ค ์ฌ์ฉ ์ฌ๋ถ (๋ฎ์์๋ก ์ข์) 3. tone_consistency: ์ง์ ๋ ํ๋ฅด์๋์์ ํค ์ผ์น๋ 4. structure_coherence: ๋ ผ๋ฆฌ์ ํ๋ฆ๊ณผ ์น์ ๊ฐ ์ฐ๊ฒฐ 5. hallucination_risk: ๊ฒ์ฆ ๋ถ๊ฐ๋ฅํ ์ฃผ์ฅ์ ์กด์ฌ ์ฌ๋ถ JSON ํ์: {{ "overall_score": float, "breakdown": {{ ... }}, "feedback": "๊ตฌ์ฒด์ ๊ฐ์ ์ง์", "flagged_sentences": ["๋ฌธ์ ๊ฐ ์๋ ๋ฌธ์ฅ ๋ชฉ๋ก"] }} """ result = llm.invoke(evaluation_prompt, response_format="json") return { **state, "critic_score": result["overall_score"], "critic_feedback": result["feedback"], "adjective_count": count_adjectives(state["draft"]), "fact_density": result["breakdown"]["fact_density"], }
๐ ํต์ฌ ๋ก์ง: Granular Scoring & ๋ฌดํ ๋ฃจํ ๋ฐฉ์ง
Critic Agent๊ฐ ์ ์๋ฅผ ๋งค๊ธฐ๋ ๊ฒ์ ์ข์ง๋ง, Writer๊ฐ ์๋ฌด๋ฆฌ ์ฌ์์ฑํด๋ 0.85๋ฅผ ๋์ง ๋ชปํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์? ๋ฌดํ ๋ฃจํ ๋ฐฉ์ง ๋ก์ง์ด ํต์ฌ์ ๋๋ค.
def should_continue(state: ContentState) -> str: """ ๋ผ์ฐํ ํจ์: ๋ฃจํ๋ฅผ ๊ณ์ํ ์ง, ์ธ๊ฐ์๊ฒ ๋๊ธธ์ง ๊ฒฐ์ . ์ธ ๊ฐ์ง ์๋๋ฆฌ์ค๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. """ # ์๋๋ฆฌ์ค 1: ํ์ง ํต๊ณผ โ HITL๋ก ์ ๋ฌ if state["critic_score"] >= 0.85: return "send_to_human" # ์๋๋ฆฌ์ค 2: ์ต๋ ๋ฆฌ๋น์ ๋๋ฌ โ ๊ฐ์ ์กธ์ + ๊ฒฝ๊ณ ํ๋๊ทธ if state["revision_count"] >= state["max_revisions"]: return "force_graduate" # ์๋๋ฆฌ์ค 3: ํ์ง ๋ฏธ๋ฌ โ Writer์๊ฒ ๋ฐ๋ ค return "revise"
force_graduate ๊ฒฝ๋ก๋ ์๋ฒฝํ์ง ์์ ๊ธ์ด๋ผ๋ ๋ฌดํ ๋ฃจํ์ ๋น ์ง์ง ์๋๋ก ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ Slack ๋ฉ์์ง์ โ ๏ธ MAX_REVISIONS_REACHED ๊ฒฝ๊ณ ๊ฐ ์ถ๊ฐ๋์ด ์ธ๊ฐ ๋ฆฌ๋ทฐ์ด๊ฐ ํน๋ณํ ์ฃผ์ํ๋๋ก ํฉ๋๋ค.
๊ทธ๋ํ ์กฐ๋ฆฝ
# LangGraph ๊ทธ๋ํ ์กฐ๋ฆฝ workflow = StateGraph(ContentState) workflow.add_node("writer", writer_node) workflow.add_node("critic", critic_node) workflow.add_node("human_review", send_to_slack) workflow.set_entry_point("writer") workflow.add_edge("writer", "critic") workflow.add_conditional_edges( "critic", should_continue, { "revise": "writer", # ์ฌ์์ฑ ๋ฃจํ "send_to_human": "human_review", # HITL ์น์ธ ์์ฒญ "force_graduate": "human_review", # ๊ฐ์ ์กธ์ } ) workflow.add_edge("human_review", END) graph = workflow.compile()
๐ก ํ์ฉ์ฌ ์ ๊ฑฐ ํ๋กํ ์ฝ: Fact-Based Rebuilding
Critic Agent๊ฐ adjective_ratio ์ ์๋ฅผ ๋ฎ๊ฒ ๋งค๊ฒผ์ ๋, Writer Agent๊ฐ ์ค์ ๋ก ์ด๋ป๊ฒ ์ฌ์์ฑํ๋์ง ์ดํด๋ด
์๋ค. ํต์ฌ์ ํ์ฉ์ฌ๋ฅผ ํฉํธ๋ก 1:1 ์นํํ๋ ๊ฒ์
๋๋ค:
| ๋ณํ ์ (ํ์ฉ์ฌ ๋จ๋ฐ) | ๋ณํ ํ (ํฉํธ ๊ธฐ๋ฐ) |
|---|---|
| "ํ๋ คํ ๋์์ธ๊ณผ ํ์ ์ ์ธ ๊ธฐ์ " | "Figma-to-Code ์๋ํ์จ 94%, ๋์์ธ QA ์์ ์๊ฐ 40๋ถโ8๋ถ" |
| "๋ฐ์ด๋ ์ฑ๋ฅ์ ์๋ํ๋ ์์คํ " | "P99 ๋ ์ดํด์ 23ms, ์ด๋น 12,000 ์์ฒญ ์ฒ๋ฆฌ" |
| "๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค" | "17๊ฐ API ์๋ํฌ์ธํธ, 3๊ฐ SDK (Python/JS/Go) ์ง์" |
| "์ธ๊ณ ์ต๊ณ ์์ค์ AI ๋ชจ๋ธ" | "MMLU ๋ฒค์น๋งํฌ 87.3์ , GPT-4 ๋๋น 12% ๋น์ฉ ์ ๊ฐ" |
์ด ํ๋กํ ์ฝ์ด ์๋ํ๋ ์ด์ : LLM์ "ํ์ฉ์ฌ๋ฅผ ์ค์ฌ"๋ผ๋ ๋ชจํธํ ์ง์๋ณด๋ค, "์ด ํ์ฉ์ฌ๋ฅผ ๊ตฌ์ฒด์ ์์น๋ก ๊ต์ฒดํด"๋ผ๋ ๊ตฌ์ฒด์ ๋ณํ ๊ท์น์ ํจ์ฌ ์ ๋ฐ๋ฆ ๋๋ค.
์๋์์ Writer-Critic ๋ฃจํ์ ์ค์๊ฐ ์๋ฎฌ๋ ์ด์ ์ ์ฒดํํด ๋ณด์ธ์:
๐ก TIP
Critic Agent์ ํ๊ฐ ํ๋กฌํํธ์ flagged_sentences ํ๋๋ฅผ ํฌํจํ๋ฉด, Writer Agent๊ฐ ์ ์ฒด ๊ธ์ ์ฌ์์ฑํ์ง ์๊ณ ๋ฌธ์ ๋ฌธ์ฅ๋ง ์ธ๊ณผ์ ์ผ๋ก ์์ ํ ์ ์์ต๋๋ค. ์ด๊ฒ๋ง์ผ๋ก ์ฌ์์ฑ ์๊ฐ์ด ํ๊ท 40% ๋จ์ถ๋ฉ๋๋ค.
๐ค ์ธ๊ฐ๊ณผ AI์ ํ์ : Slack & GCP Pipeline
์์ด์ ํธ๊ฐ 0.85์ ์ด์์ ์ด์์ ์์ฑํ๋ฉด, ์ด๊ฒ์ ๋์ด ์๋๋ผ ์ธ๊ฐ ์์ฌ๊ฒฐ์ ์ ์์์ ๋๋ค. ์ฐ๋ฆฌ์ HITL ํ์ดํ๋ผ์ธ์ GCP Cloud Functions์ Slack API๋ฅผ ๊ฒฐํฉํฉ๋๋ค.
์น์ธ ํ๋ฆ
Slack Interactive Message
def send_to_slack(state: ContentState) -> ContentState: """ Slack์ผ๋ก ์น์ธ ์์ฒญ์ ์ ์กํฉ๋๋ค. ๋ฆฌ๋ทฐ์ด๋ Approve/Reject ๋ฒํผ์ผ๋ก ์๋ตํฉ๋๋ค. """ blocks = [ { "type": "header", "text": {"type": "plain_text", "text": f"๐ ์ ์ฝํ ์ธ ์น์ธ ์์ฒญ"} }, { "type": "section", "fields": [ {"type": "mrkdwn", "text": f"*Critic Score:* {state['critic_score']:.2f}"}, {"type": "mrkdwn", "text": f"*Revisions:* {state['revision_count']}"}, {"type": "mrkdwn", "text": f"*Adjective Count:* {state['adjective_count']}"}, {"type": "mrkdwn", "text": f"*Fact Density:* {state['fact_density']:.2f}"}, ] }, { "type": "section", "text": {"type": "mrkdwn", "text": f"```{state['draft'][:500]}...```"} }, { "type": "actions", "elements": [ { "type": "button", "text": {"type": "plain_text", "text": "โ Approve & Publish"}, "style": "primary", "action_id": "approve_content" }, { "type": "button", "text": {"type": "plain_text", "text": "โ Reject & Revise"}, "style": "danger", "action_id": "reject_content" } ] } ] slack_client.chat_postMessage( channel=CONTENT_REVIEW_CHANNEL, blocks=blocks, text=f"New content ready for review: {state['topic']}" ) return state
์น์ธ ํ: Supabase ๋๊ธฐํ
Approve ๋ฒํผ์ด ๋๋ฆฌ๋ฉด GCP Cloud Function์ด ํธ๋ฆฌ๊ฑฐ๋์ด ๋ค์์ ์๋ ์คํํฉ๋๋ค:
def on_approve(payload: dict): """์น์ธ ํ ์๋ ํ์ดํ๋ผ์ธ""" content = payload["content_state"] # 1. Supabase์ ๊ฒ์๋ฌผ INSERT supabase.table("posts").insert({ "title": content["topic"], "body": content["draft"], "critic_score": content["critic_score"], "revision_count": content["revision_count"], "status": "published", "published_at": datetime.utcnow().isoformat(), }).execute() # 2. CDN ์บ์ ํผ์ง (Vercel) requests.post( f"https://api.vercel.com/v1/projects/{PROJECT_ID}/purge", headers={"Authorization": f"Bearer {VERCEL_TOKEN}"}, ) # 3. Slack ํ์ธ ๋ฉ์์ง slack_client.chat_postMessage( channel=CONTENT_REVIEW_CHANNEL, text=f"โ Published: {content['topic']} (Score: {content['critic_score']:.2f})" )
๊ฑฐ์ ์์๋ reject_reason์ด Writer Agent์ ๋ค์ critic_feedback ํ๋์ ์ฃผ์
๋์ด, ์ธ๊ฐ์ ํผ๋๋ฐฑ๊น์ง ๋ฐ์ํ ์ฌ์์ฑ์ด ์์๋ฉ๋๋ค. ์ด๊ฒ์ด ์ง์ ํ Human-in-the-Loop์
๋๋คโ์ธ๊ฐ์ด ๋ชจ๋ ๊ฒ์ ํ๋ ๊ฒ ์๋๋ผ, ๊ฒฐ์ ์ ์๊ฐ์๋ง ๊ฐ์
ํ๋ ๊ฒ์
๋๋ค.
๐ ํ์ดํ๋ผ์ธ ์ฑ๊ณผ ์งํ
Writer-Critic Loop + HITL ํ์ดํ๋ผ์ธ์ ํ๋ก๋์ ์ ๋ฐฐํฌํ ํ 30์ผ ๊ฐ์ ๊ฒฐ๊ณผ:
| ์งํ | Before (๋จ์ผ ํ๋กฌํํธ) | After (Writer-Critic + HITL) | ๋ณํ๋ |
|---|---|---|---|
| ํ์ฉ์ฌ ๋ฐ๋ (๋ฌธ์ฅ๋น) | 2.8๊ฐ | 0.4๊ฐ | -86% |
| ํฉํธ ๋ฐ๋ (์์น/์ฌ๋ก ๋น์จ) | 12% | 78% | +550% |
| ์ธ๊ฐ ์์ ์๊ฐ (๊ฑด๋น) | 45๋ถ | 8๋ถ | -82% |
| ํ ๋ฃจ์๋ค์ด์ ๋น์จ | ~18% | 0% | -100% |
| Slack ์น์ธ๊น์ง ํ๊ท ์๊ฐ | N/A | 4๋ถ | โ |
| ์ฝํ ์ธ ๊ฒ์ ๋น๋ | ์ฃผ 1ํ | ์ฃผ 4ํ | +300% |
๐ง ๊ฒฐ๋ก : ์ง์ ํ ์๋ํ๋ ์ธ๊ฐ์ ๋ฐฐ์ ํ์ง ์๋๋ค
"AI ์๋ํ"๋ผ๋ ๋จ์ด๋ฅผ ๋ค์ผ๋ฉด ๋๋ถ๋ถ "์ธ๊ฐ์ ๋นผ๋ ๊ฒ"์ ๋ ์ฌ๋ฆฝ๋๋ค. ์ฐ๋ฆฌ๋ ์ ๋ฐ๋๋ฅผ ์ฆ๋ช ํ์ต๋๋ค.
์ง์ ํ ์๋ํ๋ ์ธ๊ฐ์ ๋ฐฐ์ ํ๋ ๊ฒ์ด ์๋๋ผ, ์ธ๊ฐ์ด '๊ฒฐ์ '์๋ง ์ง์คํ ์ ์๋๋ก 'ํ์ง ๊ด๋ฆฌ'๋ฅผ ์์จ์ ์ผ๋ก ์ํํ๋ ๊ฒ์ ๋๋ค.
Writer-Critic ๋ฃจํ๋ ํธ์ง์์ ์ญํ ์ ์๋ํํฉ๋๋ค. ํ์ฉ์ฌ๋ฅผ ์ก์๋ด๊ณ , ํฉํธ๋ฅผ ๊ฐ์ ํ๊ณ , ํค์ ๊ต์ ํฉ๋๋ค. ํ์ง๋ง ์ต์ข "์ด๊ฑธ ์ธ์์ ๋ด๋ณด๋ผ ๊ฒ์ธ๊ฐ?"๋ผ๋ ๊ฒฐ์ ์ ์ฌ์ ํ ์ธ๊ฐ์๊ฒ ์์ต๋๋ค.
๋ฉ์ฒญํ AI ์กฐ์๋ฅผ ๋ ํฐ ๋ชจ๋ธ๋ก ๊ต์ฒดํ์ง ๋ง์ธ์. ๊น๊นํ ํธ์ง์๋ฅผ ์์ ์ํ์ธ์. ๊ทธ๋ฆฌ๊ณ ํธ์ง์๊ฐ ๋ค ๊ฒ์ํ ํ์, ์ธ๊ฐ์ด "Approve" ๋ฒํผ ํ๋๋ง ๋๋ฅด๋ฉด ๋ชจ๋ ๊ฒ์ด ์๋์ผ๋ก ์ธ์์ ๋๊ฐ๊ฒ ํ์ธ์. ์ด๊ฒ์ด ์ฐ๋ฆฌ๊ฐ ์๊ฐํ๋ Agentic Content Pipeline์ ์์ฑํ์ ๋๋ค.
Updated 4/30/2026