속도에 관한 것이 아니다. 서로 다른 관점을 가진 여러 에이전트가 하나의 결정을 놓고 논쟁하고, 그들 중 어느 하나보다 더 나은 결론에 도달하는 것에 관한 것이다. 오케스트레이터, 전문가, 병합을 위한 전체 코드 포함.
하나의 모델에 결정을 평가하도록 요청하면, 하나의 관점, 일반적으로 평균적이고 조심스러운 관점을 제시한다. 동의하고, 순화시키고, 균형을 찾으려는 경향이 있다. 그것이 문제다: 중요한 결정은 하나의 평균적인 관점으로 평가될 수 없으며, 다양한 측면에서 공격받아야 한다.
에이전트의 스웜은 구조적으로 이 문제를 해결한다. 각각 고정된 역할과 편향을 가진 여러 전문가를 생성한다. 하나는 오직 금전만 생각하고, 다른 하나는 기술적 위험만, 세 번째는 사용자만 생각한다. 그들은 하나의 결정을 독립적으로 분석하고, 다른 결론에 도달하며, 그런 다음 해당 결론들의 조정을 강제한다. 여기서 가치는 속도가 아니라, 불일치가 구조에 내장되어 있다는 점이다. 단일 에이전트는 자기 자신과의 집단사고에 빠지기 쉽지만, 역할의 스웜은 그렇지 않다.
이 글에서는 코드와 함께 그러한 스웜을 구축하는 방법을 보여준다. 세 부분을 다룬다: 역할을 할당하는 오케스트레이터, 독립적으로 분석하는 전문가들, 그리고 그들을 하나의 결론으로 조정하는 병합.
아키텍처: 오케스트레이터, 전문가, 병합
분석을 위한 스웜은 세 가지 구성 요소로 이루어진다.
오케스트레이터는 작업을 받아서 어떤 전문가 역할이 필요한지 결정한다. 제품 출시를 평가하는 경우, 투자자, 엔지니어, 제품 전문가, 보안 담당자가 될 수 있다. 오케스트레이터는 스스로 분석하지 않고, 역할을 분배한다.
전문가들은 병렬로 독립적으로 작업한다. 각자는 동일한 결정을 보지만, 자신의 렌즈를 통해서 본다. 결정적으로, 그들은 서로의 결론을 보지 못한다. 그렇지 않으면 동조 현상이 발생하기 때문이다. 독립성이 다양한 관점을 만들어내는 것이다.
병합은 전문가들의 결론을 수집하고 조정한다: 어디에 동의하는지, 어디에서 모순되는지, 모든 각도에 걸친 최종 판결이 무엇인지. 이는 평균을 내는 것이 아니라, 불일치를 신호로 유지하는 종합이다.

1단계: 기본 클라이언트
모델에 대한 간단한 클라이언트로 시작한다. 나는 OpenAI 호환 메시지 형식을 사용하며, 대부분의 제공업체 및 로컬 Ollama에서 작동한다.
1import requests2import json3from concurrent.futures import ThreadPoolExecutor45API = "http://localhost:11434/api/chat" # Ollama, 또는 제공업체 엔드포인트6MODEL = "qwen2.5:32b"78def ask(system, user, temperature=0.7):9 resp = requests.post(API, json={10 "model": MODEL,11 "messages": [12 {"role": "system", "content": system},13 {"role": "user", "content": user},14 ],15 "temperature": temperature,16 "stream": False,17 }, timeout=120)18 resp.raise_for_status()19 return resp.json()["message"]["content"]
2단계: 오케스트레이터가 역할을 할당한다
오케스트레이터는 작업을 받고 어떤 전문가가 필요한지 결정한다. 역할을 미리 하드코딩하지 말고, 모델이 특정 작업에 맞게 선택하도록 하자. 이렇게 하면 스웜이 범용적으로 사용될 수 있다. 파싱을 위해 엄격한 JSON을 요청한다.
1ORCHESTRATOR_SYSTEM = """당신은 분석 스웜의 오케스트레이터입니다.2작업에 대해, 결정에 대해 최대한 다르고 상충되는 관점을 제공할33-5명의 전문가 역할을 정의하십시오. 역할은 서로를 보완하는 것이 아니라4이해관계가 충돌해야 합니다.56각 역할에 대해 다음을 제공하십시오: 이름, 초점(무엇에 집중하는지), 편향(무엇을7선호하고, 무엇을 과대평가하는 경향이 있는지).89JSON 배열로만 응답하고, 설명은 없습니다:10[{"name": "...", "focus": "...", "bias": "..."}, ...]11"""1213def plan_roles(task):14 raw = ask(ORCHESTRATOR_SYSTEM, f"분석할 작업:\n{task}",15 temperature=0.9) # 역할 다양성을 위한 높은 온도16 # 모델이 주변에 텍스트를 추가한 경우 JSON을 잘라냅니다17 start, end = raw.find("["), raw.rfind("]") + 118 return json.loads(raw[start:end])
여기서는 의도적으로 온도를 높게 유지한다: 명확하지 않은 다양한 역할을 원하기 때문이다. 프롬프트에서 "역할은 충돌해야 한다"는 요구사항이 핵심이며, 이것이 없으면 모델은 거의 동일한 세 가지 역할을 제공하고 스웜의 전체적인 요점이 사라진다.
3단계: 전문가들이 병렬로 독립적으로 분석한다
각 전문가는 자신의 역할과 동일한 결정을 받는다. 결정적으로: 그들은 병렬로 실행되며 서로의 결론을 보지 못한다. 여기서 병렬 처리는 속도를 위한 것뿐만 아니라 독립성을 보장하며, 전문가는 물리적으로 다른 사람의 의견에 적응할 수 없다.
1EXPERT_SYSTEM = """당신은 역할: {name}을 가진 전문가입니다.2당신의 초점: {focus}.3당신의 편향: {bias}. 그것과 싸우지 마십시오. 그것이 분석에 대한 당신의 가치입니다.45결정을 당신의 입장에서 엄격하게 분석하십시오. 균형을 잡으려 하지 말고,6다른 관점을 고려하려 하지 마십시오. 다른 전문가들이 그렇게 할 것입니다.7당신의 임무는 당신의 각도를 한계까지 밀어붙이는 것입니다.89다음을 제공하십시오:10- 당신의 입장에서의 판결 (찬성 / 반대 / 조건부)11- 당신의 각도에서 구체적으로 2-3개의 주요 주장12- 당신의 입장에서 가장 잘 보이고 다른 사람들은 놓칠 1가지 위험13짧고 강하게, 군더더기 없이."""1415def run_expert(role, task):16 system = EXPERT_SYSTEM.format(**role)17 opinion = ask(system, f"분석할 결정:\n{task}", temperature=0.7)18 return {"role": role["name"], "opinion": opinion}1920def run_swarm(roles, task):21 # 병렬 실행: 독립성과 속도22 with ThreadPoolExecutor(max_workers=len(roles)) as pool:23 futures = [pool.submit(run_expert, role, task) for role in roles]24 return [f.result() for f in futures]
전문가 프롬프트에 주목하자: 균형을 잡는 것을 명시적으로 금지한다. 이는 직관에 반하지만, 이것이 바로 핵심이다. 각 전문가가 모든 측면을 고려하려고 하면, 다섯 개의 동일한 신중한 의견을 얻게 된다. 각자가 자신의 각도를 한계까지 밀어붙이도록 강제함으로써, 실제 스펙트럼을 얻을 수 있으며, 이후 병합이 이를 조정한다.
4단계: 병합이 결론을 조정한다
이제 몇 가지 날카롭고 일방적인 의견이 있다. 병합은 그것들을 하나의 판결로 수집하지만, 평균을 내지는 않는다. 전문가들이 동의하는 부분(강력한 신호), 모순되는 부분(결정이 필요한 위험 영역), 그리고 무엇이 무엇보다 우선하는지를 찾는다.
1MERGE_SYSTEM = """당신은 분석 스웜의 종합자입니다. 하나의 결정에 대한2서로 다른 편향을 가진 여러 전문가의 의견을 받았습니다.34당신의 임무는 평균을 내는 것이 아닙니다. 당신의 임무는:51. 동의: 서로 다른 입장에도 불구하고 전문가들이 동의한 부분.6 이것이 가장 신뢰할 수 있는 신호이며, 강조하십시오.72. 갈등: 전문가들이 직접적으로 모순되는 부분. 순화시키지 말고,8 갈등을 명시적으로 언급하고 각 측면의 비용이 무엇인지 말하십시오.93. 사각지대: 오직 한 명의 전문가만 언급했지만 중요한 위험.104. 모든 것을 고려한 최종 판결: 찬성 / 반대 / 조건부, 그리고11 어떤 조건에서 변경되는지.1213밀도 있게 작성하십시오. 불일치를 정보로 유지하고 숨기지 마십시오."""1415def merge_opinions(task, opinions):16 block = "\n\n".join(17 f"### 전문가: {o['role']}\n{o['opinion']}" for o in opinions18 )19 user = f"결정:\n{task}\n\n전문가 의견:\n{block}"20 return ask(MERGE_SYSTEM, user, temperature=0.4) # 신중한 종합을 위한 낮은 온도
병합 시 온도를 낮춘다: 전문가들이 다양해야 한다면(높은 T), 종합자는 신중하고 일관되어야 한다(낮은 T). 여기서 핵심 지시는 "평균 내지 말고, 불일치를 정보로 유지하라"는 것이다. 일반적인 병합은 모든 것을 "한편으로는, 다른 한편으로는"이라는 흐릿한 상태로 붕괴시킨다. 좋은 병합은 명확하게 말한다: 여기 모두가 동의하고, 여기에 갈등이 있으며 이것은 이만큼의 비용이 든다.
4.5단계: 가짜 동의에 대한 악마의 대변인
조용한 위험이 하나 있다: 때때로 전문가들이 결정이 좋아서가 아니라, 모두가 관성에 의해 같은 방향을 보고 있기 때문에 동의하는 경우가 있다. 이것은 가짜 동의이며, 확신처럼 보이기 때문에 공개적인 갈등보다 더 위험하다.
이에 맞서 우리는 특별한 에이전트 하나, 즉 악마의 대변인을 추가한다. 그의 유일한 임무는 합의를 공격하는 것이다. 그는 모든 전문가의 의견을 보고, 그들이 모두 동시에 틀릴 수 있는 이유를 찾아야 할 의무가 있다. 스웜이 만장일치로 "찬성"을 외쳤다면, 대변인은 그것이 재앙이 되는 시나리오를 찾는다.
1DEVIL_SYSTEM = """당신은 분석 스웜의 악마의 대변인입니다. 전문가들의2의견을 받았습니다. 당신의 유일한 임무: 그들의 합의를 공격하십시오.34전문가들이 어떤 것에 수렴했다면, 그들이 모두 동시에 틀릴 수 있는5이유를 찾으십시오. 공유된 사각지대를 찾으십시오: 모두가 확인 없이6수용한 가정, 불편하기 때문에 아무도 고려하지 않은 시나리오.78예의 바를 필요 없습니다. 당신의 가치는 집단이 듣고 싶어 하지 않는9말을 한다는 데 있습니다. 다음을 제공하십시오:10- 전문가들의 가장 위험한 공유 가정11- 스웜의 만장일치 의견이 치명적으로 잘못된 것으로 판명되는 시나리오12- 집단이 신중하게 피한 한 가지 질문13합의가 없고 전문가들이 진정으로 의견이 다르다면, 그것을 명확히 말하고14가장 날카롭게 해결되지 않은 갈등을 지적하십시오."""1516def run_devil(task, opinions):17 block = "\n\n".join(18 f"### {o['role']}\n{o['opinion']}" for o in opinions19 )20 user = f"결정:\n{task}\n\n스웜 의견:\n{block}"21 return ask(DEVIL_SYSTEM, user, temperature=0.8)
대변인은 전문가 이후, 병합 이전에 실행되며, 그의 공격은 의견과 함께 종합 과정에 들어간다. 요점은 만장일치 스웜이라도 적어도 하나의 에이전트가 균열을 찾도록 의무화된다는 것이다. 이는 저렴하며(한 번의 호출), 구조적으로 집단사고를 깨뜨린다: 합의는 이제 단순히 발생하는 것이 아니라 공격을 견뎌내야 한다.
4.6단계: 갈등을 날카롭게 하기 위한 토론 라운드
첫 번째 전문가 패스는 독립적이며, 이는 다양성을 위해 올바르다. 그러나 의견이 수집된 후, 한 번의 토론 라운드를 제공할 수 있다: 각 전문가에게 다른 사람들의 의견 요약을 보여주고 이의를 제기하게 한다. 이것은 갈등을 날카롭게 하고, 약한 주장은 사라지고, 강한 주장은 굳건해진다.
1DEBATE_SYSTEM = """당신은 분석 2라운드의 전문가 {name}입니다.2당신의 원래 입장:3{own_opinion}45이제 다른 전문가들의 의견을 봅니다. 압력에 굴복하지 마십시오, 그러나6강력한 주장을 무시하지도 마십시오. 다음을 제공하십시오:7- 다른 사람의 주장이 당신의 입장을 진정으로 타격하는 곳, 정직하게 인정하십시오8- 당신이 선을 지키는 곳과 그들의 반대가 왜 약한지9- 토론 후 판결을 변경했는지 여부와, 변경했다면 어떻게 변경했는지10짧게. 이것은 첫 번째 의견의 반복이 아니라, 상대방에 대한 반응입니다."""1112def debate_round(roles, task, opinions):13 others_map = {}14 for o in opinions:15 others = "\n\n".join(16 f"### {x['role']}\n{x['opinion']}" for x in opinions if x is not o17 )18 others_map[o["role"]] = others1920 def rebut(o):21 system = DEBATE_SYSTEM.format(name=o["role"], own_opinion=o["opinion"])22 user = (f"결정:\n{task}\n\n"23 f"상대방 의견:\n{others_map[o['role']]}")24 return {"role": o["role"], "opinion": ask(system, user, temperature=0.6)}2526 with ThreadPoolExecutor(max_workers=len(opinions)) as pool:27 return list(pool.map(rebut, opinions))
토론 라운드 또한 병렬적이다: 각 전문가는 다른 모든 사람에게 동시에 반응하며, 다시 한 번 실시간 동조는 없다. 토론 후 의견은 일반적으로 더 날카로워진다: 어떤 입장이 공격을 견뎌냈고 어떤 것이 무너졌는지 볼 수 있다. 최종 병합에 들어가는 것은 바로 이렇게 강화된 의견들이다.
5단계: 모든 것을 하나로 모으기
1def analyze(task, debate=True):2 print("오케스트레이터가 역할을 선택하는 중...")3 roles = plan_roles(task)4 for r in roles:5 print(f" - {r['name']}: {r['focus']}")67 print(f"\n{len(roles)}명의 전문가를 병렬로 실행하는 중...")8 opinions = run_swarm(roles, task)9 for o in opinions:10 print(f"\n[{o['role']}]\n{o['opinion']}")1112 # 선택적 토론 라운드: 전문가들이 서로 반박13 if debate:14 print("\n토론 라운드, 전문가들이 서로 반박하는 중...")15 opinions = debate_round(roles, task, opinions)1617 # 악마의 대변인이 스웜의 합의를 공격18 print("\n악마의 대변인이 합의의 균열을 찾는 중...")19 devil = run_devil(task, opinions)20 print(f"\n[악마의 대변인]\n{devil}")2122 # 병합이 결론과 대변인의 공격을 조정23 print("\n병합이 결론을 조정하는 중...")24 opinions_plus = opinions + [{"role": "악마의 대변인", "opinion": devil}]25 verdict = merge_opinions(task, opinions_plus)26 print(f"\n=== 최종 판결 ===\n{verdict}")27 return verdict2829if __name__ == "__main__":30 analyze(31 "무료 티어를 없애고 제품을 14일 무료 체험판이 포함된 "32 "완전 유료화로 전환하려고 합니다. 그래야 할까요?"33 )
이것을 실행하면 전체 파이프라인을 볼 수 있다: 오케스트레이터가 역할을 선택하고, 전문가들이 자신의 각도에서 진실을 자르고, 토론 라운드에서 서로 논쟁하고, 대변인이 그들의 합의를 공격하며, 병합이 공격을 포함한 모든 것을 종합하여 판결을 내린다. 동일한 질문에 대해 단일 에이전트는 모호한 "청중에 따라 다릅니다"라는 답을 주겠지만, 스웜은 갈등이 명시적이고 합의가 스트레스 테스트를 거친 구조화된 분석을 제공한다.
이 스웜이 효과적인 이유
유용한 스웜을 에이전트로 만든 연극과 구분하는 세 가지가 있다.
역할은 보완이 아닌 충돌해야 한다. 전문가가 "마케터, SMM 전문가, 콘텐츠 관리자"라면, 그들의 이해관계가 일치하기 때문에 거의 동일한 답변을 할 것이다. 진정한 가치는 이해관계가 충돌할 때 발생한다: 성장 대 지속 가능성, 속도 대 품질, 지금의 돈 대 나중의 신뢰. 이해관계의 충돌이 결정을 열어젖히는 것이다.
전문가들은 서로를 보지 말아야 한다. 한 전문가가 다른 사람의 의견을 보는 순간, 동조가 시작되고 조정이 일어난다. 독립성은 구현 세부 사항이 아니라 작동 조건이다. 병렬 실행은 이를 무료로 제공한다.
병합은 평균을 내지 않고 갈등을 보존한다. 나쁜 종합은 다섯 개의 날카로운 의견을 하나의 이빨 없는 요약으로 만든다. 좋은 종합은 갈등을 보이게 남겨둔다. 왜냐하면 갈등이 가장 가치 있는 정보이기 때문이다: 그것은 결정이 진정으로 위험한 곳을 보여주며, 모두가 수긍하는 곳이 아니다.
확장할 수 있는 부분
이 뼈대는 명백한 방향으로 확장될 수 있다. 토론 라운드를 추가할 수 있다: 첫 번째 병합 후, 전문가들에게 요약을 보여주고 이의를 제기하게 하여 갈등을 날카롭게 한다. 더 강력한 모델을 사용하여 전문가들 위에 판사를 두어 주장의 무게를 평가하게 할 수 있다. 반복되는 결정 유형에 대해 역할을 영구적으로 만들어 매번 생성하지 않도록 할 수 있다.
그러나 기본 원칙은 유지된다: 다른 렌즈, 독립적인 분석, 불일치를 존중하는 종합. 스웜이 분석에 유용한 이유는 에이전트가 많기 때문이 아니라, 그들이 다르게 보고 서로 공통 분모로 미끄러지는 것을 허용하지 않기 때문이다. 지금 당신이 혼자서 머릿속에서 되뇌이고 있는 결정 하나를 가져와서 그러한 스웜에 돌려보라. 당신이 잡고 있지 않았던 각도들을 보게 될 것이다.





