agent-augury 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. agent_augury-0.3.0/.env.example +31 -0
  2. agent_augury-0.3.0/.github/workflows/ci.yml +84 -0
  3. agent_augury-0.3.0/.github/workflows/release.yml +50 -0
  4. agent_augury-0.3.0/.gitignore +46 -0
  5. agent_augury-0.3.0/DESIGN.md +382 -0
  6. agent_augury-0.3.0/LICENSE +202 -0
  7. agent_augury-0.3.0/NOTICE +24 -0
  8. agent_augury-0.3.0/PKG-INFO +216 -0
  9. agent_augury-0.3.0/README.md +187 -0
  10. agent_augury-0.3.0/agent-augury-session.yaml +23 -0
  11. agent_augury-0.3.0/examples/benchmark/README.md +103 -0
  12. agent_augury-0.3.0/examples/benchmark/run_l3_passive_awareness.py +204 -0
  13. agent_augury-0.3.0/examples/benchmark/test_l3_passive_awareness.py +57 -0
  14. agent_augury-0.3.0/examples/consensus_demo.py +107 -0
  15. agent_augury-0.3.0/examples/consensus_openai.yaml +39 -0
  16. agent_augury-0.3.0/examples/demo.yaml +43 -0
  17. agent_augury-0.3.0/examples/multi_bot_demo.yaml +70 -0
  18. agent_augury-0.3.0/examples/nous_oauth_session.yaml +21 -0
  19. agent_augury-0.3.0/examples/p1_to_p5_demo.py +361 -0
  20. agent_augury-0.3.0/examples/p1_to_p5_protocol.yaml +177 -0
  21. agent_augury-0.3.0/pyproject.toml +56 -0
  22. agent_augury-0.3.0/src/agent_augury/__init__.py +3 -0
  23. agent_augury-0.3.0/src/agent_augury/agent/__init__.py +1 -0
  24. agent_augury-0.3.0/src/agent_augury/agent/loop.py +241 -0
  25. agent_augury-0.3.0/src/agent_augury/agent/system_prompt.py +86 -0
  26. agent_augury-0.3.0/src/agent_augury/agent/tools.py +203 -0
  27. agent_augury-0.3.0/src/agent_augury/auth/__init__.py +6 -0
  28. agent_augury-0.3.0/src/agent_augury/auth/oauth.py +315 -0
  29. agent_augury-0.3.0/src/agent_augury/auth/token_store.py +125 -0
  30. agent_augury-0.3.0/src/agent_augury/backend/__init__.py +5 -0
  31. agent_augury-0.3.0/src/agent_augury/backend/base.py +59 -0
  32. agent_augury-0.3.0/src/agent_augury/backend/fake.py +28 -0
  33. agent_augury-0.3.0/src/agent_augury/backend/nous_portal.py +30 -0
  34. agent_augury-0.3.0/src/agent_augury/backend/nous_portal_oauth.py +273 -0
  35. agent_augury-0.3.0/src/agent_augury/backend/openai_compat.py +137 -0
  36. agent_augury-0.3.0/src/agent_augury/backends_factory.py +128 -0
  37. agent_augury-0.3.0/src/agent_augury/channel/__init__.py +1 -0
  38. agent_augury-0.3.0/src/agent_augury/channel/discord_bot.py +184 -0
  39. agent_augury-0.3.0/src/agent_augury/channel/discord_mirror.py +76 -0
  40. agent_augury-0.3.0/src/agent_augury/cli.py +353 -0
  41. agent_augury-0.3.0/src/agent_augury/config.py +128 -0
  42. agent_augury-0.3.0/src/agent_augury/model_config.py +81 -0
  43. agent_augury-0.3.0/src/agent_augury/model_listing.py +37 -0
  44. agent_augury-0.3.0/src/agent_augury/protocol/__init__.py +35 -0
  45. agent_augury-0.3.0/src/agent_augury/protocol/approval.py +133 -0
  46. agent_augury-0.3.0/src/agent_augury/protocol/collaboration.py +285 -0
  47. agent_augury-0.3.0/src/agent_augury/protocol/phases.py +73 -0
  48. agent_augury-0.3.0/src/agent_augury/server.py +366 -0
  49. agent_augury-0.3.0/src/agent_augury/session.py +455 -0
  50. agent_augury-0.3.0/src/agent_augury/wizard.py +493 -0
  51. agent_augury-0.3.0/tests/__init__.py +1 -0
  52. agent_augury-0.3.0/tests/conftest.py +34 -0
  53. agent_augury-0.3.0/tests/test_agent_loop.py +352 -0
  54. agent_augury-0.3.0/tests/test_backends.py +158 -0
  55. agent_augury-0.3.0/tests/test_broadcast.py +302 -0
  56. agent_augury-0.3.0/tests/test_cli_output_path.py +63 -0
  57. agent_augury-0.3.0/tests/test_collaboration.py +197 -0
  58. agent_augury-0.3.0/tests/test_discord_bot.py +500 -0
  59. agent_augury-0.3.0/tests/test_integration_openai.py +204 -0
  60. agent_augury-0.3.0/tests/test_mirror.py +171 -0
  61. agent_augury-0.3.0/tests/test_model_config.py +156 -0
  62. agent_augury-0.3.0/tests/test_model_listing.py +545 -0
  63. agent_augury-0.3.0/tests/test_oauth.py +454 -0
  64. agent_augury-0.3.0/tests/test_openai_yaml.py +32 -0
  65. agent_augury-0.3.0/tests/test_parallel.py +290 -0
  66. agent_augury-0.3.0/tests/test_persistence.py +180 -0
  67. agent_augury-0.3.0/tests/test_phases.py +74 -0
  68. agent_augury-0.3.0/tests/test_protocol.py +243 -0
  69. agent_augury-0.3.0/tests/test_regression.py +532 -0
  70. agent_augury-0.3.0/tests/test_server.py +183 -0
  71. agent_augury-0.3.0/tests/test_toy.py +45 -0
  72. agent_augury-0.3.0/tests/test_wiring.py +557 -0
  73. agent_augury-0.3.0/tests/test_wizard.py +732 -0
@@ -0,0 +1,31 @@
1
+ # agent-augury 환경변수 예시 — 이 파일을 .env로 복사하고 값을 채우세요.
2
+ # .env는 gitignore 대상이므로 절대 커밋되지 않습니다.
3
+ # YAML 설정에는 값 대신 "환경변수 이름"만 적습니다 (보안 원칙).
4
+
5
+ # ── OpenAI-compatible 백엔드 (backend.type: openai) ────────────────────────
6
+ # OPENAI_API_KEY=sk-...
7
+
8
+ # ── Nous Portal 백엔드 (backend.type: nous) ────────────────────────────────
9
+ # NOUS_API_KEY=nk-...
10
+
11
+ # ── Nous Portal OAuth (backend.type: nous_oauth) ───────────────────────────
12
+ # 키 불필요 — 디바이스 코드 플로우로 브라우저 인증, ~/.agent-augury/tokens.json 저장
13
+
14
+ # ── Discord 웹훅 미러 (mirror.type: discord_webhook) ───────────────────────
15
+ # AUGURY_MIRROR_URL=https://discord.com/api/webhooks/...
16
+
17
+ # ── Discord 봇 N개 (bots: 섹션, token_env 참조) ────────────────────────────
18
+ # 각 봇은 agent_id에 바인딩됩니다. 채널 ID도 여기로 빼는 것을 권장
19
+ # (examples/multi_bot_demo.yaml의 하드코딩 채널 ID를 대체).
20
+ # BOT_TOKEN_AGENT_1=
21
+ # BOT_TOKEN_AGENT_2=
22
+ # BOT_TOKEN_AGENT_3=
23
+ # BOT_TOKEN_AGENT_4=
24
+ # BOT_CHANNEL_ID_AGENT_1=
25
+ # BOT_CHANNEL_ID_AGENT_2=
26
+ # BOT_CHANNEL_ID_AGENT_3=
27
+ # BOT_CHANNEL_ID_AGENT_4=
28
+
29
+ # ── 테스트 (opt-in, 비용 발생) ─────────────────────────────────────────────
30
+ # AUGURY_RUN_OPENAI_TESTS=1
31
+ # OPENAI_API_KEY=sk-...
@@ -0,0 +1,84 @@
1
+ # agent-augury CI — lint + offline tests + build (Phase 1: 배포 가능 상태)
2
+ #
3
+ # 원칙 (OSS_STRATEGY.md §5 Phase 1):
4
+ # - 오프라인 테스트만 CI에서 실행 (openai 통합은 opt-in — 비용/키 불필요)
5
+ # - ruff check + pytest -q 가 게이트
6
+ # - 태그 푸시 시 PyPI 릴리스 자동화 (별도 워크플로: release.yml)
7
+ #
8
+ # ⚠️ 리스크 기록 (agent-3 교차 검증, 2026-08):
9
+ # `uv pip install --system -e ".[dev]"`는 py-cord(핵심 deps)를 설치한다.
10
+ # py-cord의 Python 3.12 지원 여부가 미확인 → 3.12 매트릭스에서 설치 실패
11
+ # 가능성. 실제 CI 1회 실행 전까지는 3.12를 유지하되, 실패 시:
12
+ # a) python: ["3.11"]만 유지, 또는
13
+ # b) py-cord lazy import 후 extras 분리 (OSS_STRATEGY §2.2, v0.4+)
14
+ #
15
+ # ✅ examples/benchmark/ (agent-3, 2026-09):
16
+ # examples/benchmark/run_l3_passive_awareness.py + test_l3_passive_awareness.py
17
+ # 구현 완료 (L3-only 재정의, 오프라인·결정적). OSS_STRATEGY §10 D1 해소.
18
+ # → 아래 pytest 경로에 examples/benchmark/ 를 포함한다.
19
+ #
20
+ # 참고: agent-augury는 Windows 개발 환경에서 만들어졌으므로 Windows 러너를
21
+ # 매트릭스에 포함한다 (cli.py 경로 처리, wizard TTY 등 플랫폼별 회귀 방지).
22
+
23
+ name: ci
24
+
25
+ on:
26
+ push:
27
+ branches: [main]
28
+ tags: ["v*"]
29
+ pull_request:
30
+ branches: [main]
31
+
32
+ jobs:
33
+ lint:
34
+ name: lint (ruff)
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: astral-sh/setup-uv@v5
39
+ with:
40
+ python-version: "3.11"
41
+ - name: Install dev extras
42
+ run: uv pip install --system -e ".[dev]"
43
+ - name: Ruff check
44
+ run: ruff check src tests examples/benchmark
45
+
46
+ test:
47
+ name: test (${{ matrix.os }} / py${{ matrix.python }})
48
+ runs-on: ${{ matrix.os }}
49
+ strategy:
50
+ fail-fast: false
51
+ matrix:
52
+ os: [ubuntu-latest, windows-latest, macos-latest]
53
+ python: ["3.11", "3.12"] # 3.12는 py-cord 호환성 리스크 — 실패 시 3.11만 유지
54
+ steps:
55
+ - uses: actions/checkout@v4
56
+ - uses: astral-sh/setup-uv@v5
57
+ with:
58
+ python-version: ${{ matrix.python }}
59
+ - name: Install dev extras
60
+ run: uv pip install --system -e ".[dev]"
61
+ - name: Run offline test suite
62
+ run: pytest tests/ examples/benchmark/ -q
63
+ env:
64
+ # openai 통합 테스트는 opt-in — CI에서는 절대 실행하지 않음
65
+ AUGURY_RUN_OPENAI_TESTS: "0"
66
+ # agent-2/agent-3 검증: tests/ 21개 모듈 + examples/benchmark/ 5건 전부 오프라인
67
+ # (test_integration_openai의 openai 3건은 requires_openai 마커로 자동 skip)
68
+
69
+ build:
70
+ name: build sdist+wheel
71
+ runs-on: ubuntu-latest
72
+ needs: [lint, test]
73
+ steps:
74
+ - uses: actions/checkout@v4
75
+ - uses: astral-sh/setup-uv@v5
76
+ with:
77
+ python-version: "3.11"
78
+ - name: Build package
79
+ run: uv build
80
+ - name: Upload artifacts
81
+ uses: actions/upload-artifact@v4
82
+ with:
83
+ name: dist
84
+ path: dist/
@@ -0,0 +1,50 @@
1
+ # agent-augury PyPI 릴리스 — 태그 기반 자동 배포
2
+ #
3
+ # 트리거: `v0.3.0` 같은 태그 푸시
4
+ #
5
+ # ⚠️ 최초 릴리스 절차 (agent-3 권고 반영):
6
+ # Trusted Publishing(OIDC)는 PyPI 프로젝트에 OIDC 설정이 선행돼야 한다.
7
+ # OIDC 미설정 상태에서 태그를 푸시하면 publish 스텝이 실패한다.
8
+ # → **최초 1회는 수동으로**: `uv build` → `twine upload dist/*`
9
+ # (docs/pypi-name-check.md §4 참고), 이후 OIDC(또는 token)를 붙이고
10
+ # 이 워크플로를 활성화한다.
11
+ #
12
+ # 준비물 (OIDC 활성화 후):
13
+ # PyPI → project settings → publishing → "Trusted Publishers"에
14
+ # 이 repo의 환경 연결 (workflow 이름: release.yml)
15
+ #
16
+ # OSS_STRATEGY.md §5 Phase 1의 "uv build + PyPI 릴리스 자동화 (태그 기반)" 항목.
17
+
18
+ name: release
19
+
20
+ on:
21
+ push:
22
+ tags: ["v*"]
23
+
24
+ permissions:
25
+ contents: read
26
+
27
+ jobs:
28
+ release:
29
+ name: publish to PyPI
30
+ runs-on: ubuntu-latest
31
+ permissions:
32
+ id-token: write # Trusted Publishing (PyPI OIDC) 사용 시
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - uses: astral-sh/setup-uv@v5
36
+ with:
37
+ python-version: "3.11"
38
+ - name: Build
39
+ run: uv build
40
+ - name: Publish to PyPI
41
+ # Trusted Publishing (권장, OIDC 설정 후): PYPI_API_TOKEN 불필요
42
+ uses: pypa/gh-action-pypi-publish@release/v1
43
+ with:
44
+ packages-dir: dist/
45
+ # 대안: API token 사용 시 위 스텝을 아래로 교체
46
+ # - name: Publish to PyPI (token)
47
+ # env:
48
+ # TWINE_USERNAME: __token__
49
+ # TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
50
+ # run: uvx twine upload dist/*
@@ -0,0 +1,46 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .env
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .mypy_cache/
13
+ .coverage
14
+ htmlcov/
15
+ .idea/
16
+ .vscode/
17
+ *.log
18
+
19
+ # Root experiment/wizard scratch configs (not part of the runtime)
20
+ /10
21
+ /e2e task
22
+ /L3
23
+ /ㅣ
24
+ /test.yaml
25
+
26
+ # Agent/orchestrator scratch artifacts (debug dumps, handoff notes, queries)
27
+ /_dump_*.py
28
+ /_lifecycle_*.py
29
+ /_query_*.py
30
+ /_run_*.py
31
+ /_discord_done_*.txt
32
+ /query_*.py
33
+ /tmp_query_*.py
34
+ /CLI_CONFIG_*.md
35
+ /N5A_*.md
36
+ /SESSION_FLOW_*.md
37
+ /TEST_STRUCTURE_*.md
38
+ /VERIFICATION_*.md
39
+ /CROSS_CUTTING_*.md
40
+ /L3_*.md
41
+ /FIRST_MULTI_AGENT_E2E.md
42
+ /KANBAN_t_*.md
43
+ /ARCHITECTURE*.md
44
+ /.omc/
45
+ /.worktrees/
46
+ /scripts/
@@ -0,0 +1,382 @@
1
+ # agent-augury — 독립 오픈소스 설계 문서
2
+
3
+ > 상태: v0.3 구현 완료 · 2026-08-27 · 프로젝트명 `agent-augury` (D1 확정)
4
+ > 범위: "패시브 어웨어니스(Passive Awareness) 멀티에이전트" 개념을 담은, Hermes와 독립된 오픈소스 프로젝트
5
+ > 이 문서는 Coral-Protocol [AgentRadio](https://github.com/Coral-Protocol/AgentRadio)의 **개념을 계승**하되 독립 재구현한다. 원본 레포명 `AgentRadio`는 상단의 링크·참고에서만 그대로 쓰고, **이 프로젝트 자기 이름은 `agent-augury`다.**
6
+
7
+ ---
8
+
9
+ ## 1. 배경과 목표
10
+
11
+ ### 1.1 이 프로젝트가 뭘 하는가
12
+
13
+ Coral-Protocol의 [AgentRadio](https://github.com/Coral-Protocol/AgentRadio) (논문: [arXiv:2607.28430](https://arxiv.org/abs/2607.28430))는 "네 명의 코딩 에이전트가 공유 라디오 채널을 쓰면서, 일하는 **동안** 듣는다(패시브 어웨어니스)"는 아이디어를 제안했다. 핵심 통찰은 다음 하나로 요약된다:
14
+
15
+ > 메시지를 듣는 걸 **전경(blocking)** 이 아니라 **배경(background) 태스크**로 돌리면, 에이전트가 일을 멈추지 않고도 동료의 발견을 다음 스텝 경계에서 자연스럽게 흡수한다. 통신이 더 이상 일을 방해하지 않는다.
16
+
17
+ 원본(AgentRadio)은 이 아이디어를 **Claude Code CLI 4개 + Coral 메시지 서버(JAR) + Harbor/Modal 클라우드**로 재현하는 논문 실험 코드다. 즉, 아래 셋에 묶여 있다:
18
+
19
+ 1. **모델 고정** — Claude Code(Anthropic Messages API)만 말함. 다른 모델은 LiteLLM 번역 프록시를 거쳐야 함.
20
+ 2. **채널 고정** — 에이전트 간 통신이 전용 메시지 서버(JAR)로 닫혀 있음.
21
+ 3. **런타임 고정** — Harbor/Modal/Docker 컨테이너 위에서만 돌도록 설계됨.
22
+
23
+ ### 1.2 우리가 만드는 것
24
+
25
+ 이 프로젝트(`agent-augury`)는 **AgentRadio의 "개념(패시브 어웨어니스 + 협업 프로토콜)"을 계승**하되, 모델·채널·런타임 어느 것에도 묶이지 않는 독립 오픈소스로 재구현한다. 협업 프로토콜 전체(P1~P5)는 장기 목표로 계승하고, **구현은 v0.3에서** 다룬다 (§6).
26
+
27
+ | 차원 | 원본 AgentRadio | 이 프로젝트 |
28
+ |------|----------------|-------------|
29
+ | 에이전트 모델 | Claude Code 전용 (프록시 경유 다른 모델) | **모델 무관** (OpenAI-compatible 1순위, Nous Portal 2순위) |
30
+ | 라디오 채널 | 전용 메시지 서버(JAR) | **내부 메시지 서버(경량 in-process)가 SSOT**, 채널은 읽기 전용 미러 |
31
+ | 런타임 | Harbor + Modal + Docker | **로컬 Python 프로세스** (self-host, asyncio) |
32
+ | 에이전트 생성 | 고정 4개 | **사용자가 동적으로 생성/구성** |
33
+ | 배포 | 논문 재현 아티팩트 | **pip 설치 가능한 오픈소스 패키지** |
34
+
35
+ ### 1.3 비목표 (첫 릴리스에서 하지 않음)
36
+
37
+ - 코딩 도구(파일 읽기/쓰기/셸 실행)를 갖춘 완전한 코딩 에이전트 — **1단계는 협업/대화 중심**, 코딩 도구는 이후 플러그인.
38
+ - Harbor/Modal/Docker 연동.
39
+ - coral-server.jar 재사용 — 경량 **in-process** 서버(SQLite 또는 메모리)로 직접 재구현함 (D5, §3.3~§3.4 참고).
40
+ - multi-agent 자동 스케줄링·오케스트레이션 플랫폼(Hermes 칸반류).
41
+
42
+ ---
43
+
44
+ ## 2. 핵심 개념 (이 프로젝트가 계승하는 것)
45
+
46
+ 원본에서 우리가 **가져오는 것**은 코드가 아니라 다음의 "프로토콜"이다.
47
+
48
+ ### 2.1 세 가지 통신 프리미티브
49
+
50
+ | 프리미티브 | 동작 |
51
+ |------------|------|
52
+ | `create_thread(name, participants)` | 이름 있는 대화 스레드를 열고 식별자를 반환 |
53
+ | `send_message(thread, content, mentions)` | 스레드에 메시지를 붙이고 **즉시 반환** (듣는 사람 유무 무관, fire-and-forget) |
54
+
55
+ > (v0.3부터 `wait_for_mention`(L2 foreground blocking)은 완전히 제거되었다. inbox push + step() drain이 유일한 수신 경로이며, 이 프로젝트의 목표인 패시브 어웨어니스(L3)만 제공한다.)
56
+
57
+ ### 2.2 패시브 어웨어니스 (핵심 차별점)
58
+
59
+ - **전경(blocking receive)** = 과거 L2 대조 모드. v0.3에서 `wait_for_mention`과 함께 제거되었다.
60
+ - **배경(passive awareness)** = 수신을 전경에서 기다리지 않는다. 서버가 메시지를 inbox에 push하고, `step()`이 다음 경계에서 자동 흡수 → 에이전트는 계속 일한다. (원본 L3 = 이 프로젝트의 목표, §3.5.2)
61
+
62
+ ### 2.3 5단계 협업 프로토콜 (P1~P5) — 장기 목표
63
+
64
+ 원본의 전체 협업 프로토콜. **v0.1 범위가 아니며, 구현은 v0.2에서 다룬다** (§6 로드맵). 여기서는 계승할 최종 형태를 정의한다.
65
+
66
+ 네 에이전트(수는 사용자 구성)가 고정 프로토콜을 돈다. 에이전트-1이 **어셈블러**가 되어 스레드를 개설하고 페이즈 전이를 게이트한다(모든 에이전트의 명시적 승인을 모아야 다음 페이즈로).
67
+
68
+ 1. **P1 탐색** — 각자 [원본: 백그라운드 워처]를 켜고, 독립적으로 대상(질문/컨텍스트)을 탐색, 하위 질문 초안. (아무것도 안 보냄)
69
+ > "백그라운드 워처"는 원본 L3의 표현이며, **이 구현(v0.1)은 A 모델(워처 없음, push+inbox)** 을 쓴다 (§3.5.2). v0.2에서 P1~P5를 얹을 때 A 모델에 맞게 재구현한다.
70
+ 2. **P2 분할** — 어셈블러가 계획 스레드 생성. 발견 사항 풀링 → 하위 질문 분할 협상 → **전원 승인**까지 수정.
71
+ 3. **P3 실행** — 각자 자기 몫 수행. 발견 즉시 워크로그에 공유(중간 발견/모순/장애/포기된 접근).
72
+ 4. **P4 교차검토** — 각자 결과 스레드에 근거와 함께 방송. 검토자들이 사실 충돌·근거 부족·누락 관찰 지적.
73
+ 5. **P5 제출** — 어셈블러가 승인된 결과로 최종 답을 조립 → 초안 방송 → 최종 승인 → 제출.
74
+
75
+ ### 2.4 접두사 컨벤션 (수신자가 일을 멈추지 않고 분류하도록)
76
+
77
+ - `FYI:` — 답변 불필요, 참고만.
78
+ - `URGENT:` — 수신자 진행 중인 작업에 영향(가정 오류, 요청됐던 것) → 다음 작업 전 처리.
79
+ - 접두사 없음 — 일반 메시지, 자연스러운 휴지기에 답.
80
+
81
+ ---
82
+
83
+ ## 3. 아키텍처
84
+
85
+ ### 3.1 전체 구조
86
+
87
+ ```
88
+ ┌─────────────────────────────────────────────────────┐
89
+ │ 사용자 (CLI / YAML) │
90
+ │ 에이전트 동적 생성 · 채널 선택 · 세션 시작/중단 │
91
+ └──────────────────────┬──────────────────────────────┘
92
+
93
+ ┌──────▼───────┐
94
+ │ Session │ ← 세션 = 에이전트 묶음 + 메시지 서버
95
+ │ (오케스트레이터)│ 생명주기 / (v0.1b~) 게이트
96
+ └──────┬───────┘
97
+ ┌──────────────┼──────────────┐
98
+ │ │ │
99
+ ┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐
100
+ │ Agent #1 │ │ Agent #2 │ │ Agent #N │ ← 독립 루프 (step()이 inbox drain)
101
+ │ (모델 A) │ │ (모델 B) │ │ (모델 C) │
102
+ └──────┬─────┘ └──────┬─────┘ └──────┬─────┘
103
+ │ 세 프리미티브 (공통 결합) │
104
+ └───────────────┬───────────────┘
105
+ ┌──────▼────────┐
106
+ │ 내부 메시지 서버 │ ← SSOT (진실): 스레드/메시지/멘션/대기
107
+ └──────┬────────┘
108
+ │ 읽기 전용 미러 (선택적)
109
+ ┌──────▼───────┐
110
+ │ 채널 어댑터 │ ← v0.1b 이후 사람용 관측창 (미러)
111
+ │ (읽기 전용) │
112
+ └──────────────┘
113
+ ```
114
+
115
+ ### 3.2 핵심 추상화 계층
116
+
117
+ | 계층 | 책임 | 인터페이스 (초안) |
118
+ |------|------|-------------------|
119
+ | **Agent** | 모델에 시스템 프롬프트 주입, 턴 루프, 도구 호출 | `start()`, `send_system()`, `step()`, 도구 바인딩 |
120
+ | **Model Backend** | 특정 모델 API 호출 | `complete(messages, tools)` — OpenAI/Anthropic/기타 어댑터 |
121
+ | **Channel (Mirror)** | 내부 서버 상태를 채널에 읽기 전용 표시 | `mirror_thread()`, `publish_readonly()`, `disconnect()` |
122
+ | **Protocol** | 분할 합의 등 협업 게이트 (v0.1b~, P1~P5는 v0.2) | `run(session)` |
123
+ | **Session** | 에이전트 묶음 + 내부 메시지 서버 + 생명주기 | `start()`, `stop()`, 상태 조회 |
124
+ | **Message Server (SSOT)** | 스레드/메시지/멘션의 진실 공급원 | `create_thread()`, `send_message()` |
125
+
126
+ ### 3.3 채널 설계 결정 — "내부 메시지 서버가 진실(SSOT)이다"
127
+
128
+ **결정:** 메시지의 단일 진실 공급원(SSOT)은 **내부 메시지 서버**다. Discord 등 메시징 채널은 **읽기 전용 미러(선택적 뷰)** 로만 붙는다. 프로토콜은 채널에 의존하지 않는다.
129
+
130
+ **이유 (리뷰 반영, 2026-08-26):**
131
+
132
+ - P1~P5·APPROVE·워크로그 같은 프로토콜 상태를 Discord의 기본 구조 위에 컨벤션으로 인코딩하려면(§3.4) 그 자체가 v0.1의 최대 리스크가 된다. SSOT를 채널에 두는 순간 프로토콜 상태가 외부 서비스 제약에 종속된다.
133
+ - 내부 서버를 SSOT로 두면 스레드/멘션/상태관리를 제어 가능하게 짤 수 있고, 채널은 "지켜보는 뷰"로 격리되어 채널을 늘릴 때도 프로토콜을 건드리지 않는다.
134
+ - 패시브 어웨어니스 검증(L2→L3 통신 모드)은 채널 종류와 무관한 코어 로직이어야 하므로, 코어는 채널에서 분리하는 게 맞다.
135
+
136
+ **대가(단점, 인지하고 수용):**
137
+
138
+ - 내부 서버를 하나 더 구현·운용해야 한다 (단, 원본의 106MB coral-server.jar를 재사용하지 않고 요구에 맞게 경량 재구현).
139
+ - Discord 미러는 "내부 상태 → 채널 표시" 동기화가 추가 작업이며, 사람이 Discord에서 개입해도 에이전트 코어는 기본적으로 그걸 듣지 않는다 (사람 개입 경로는 별도 설계 — v0.1 범위 밖).
140
+
141
+ ### 3.4 내부 메시지 서버 (SSOT) — 스키마와 프리미티브
142
+
143
+ 원본의 세 프리미티브를 **내부 메시지 서버**가 직접 구현한다. 프로토콜 상태는 전부 이 서버 위에 산다.
144
+
145
+ | 프리미티브 | 구현 |
146
+ |------------|------|
147
+ | `create_thread(name, participants)` | 스레드 레코드 생성, 스레드 ID 반환 |
148
+ | `send_message(thread, content, mentions)` | 메시지 추가, **즉시 반환** (fire-and-forget) |
149
+
150
+ #### 3.4.1 최소 스키마
151
+
152
+ ```
153
+ agent { agent_id: str } # "agent-1" …
154
+ thread { thread_id: str, name: str, participants: [agent_id] }
155
+ message { message_id: str, thread_id: str, author: agent_id,
156
+ content: str, mentions: [agent_id], created_at: int }
157
+ inbox { agent_id: str, queue: [message_id] } # 서버가 push, step()이 drain (단일 소비자)
158
+ ```
159
+
160
+ - **멘션 표기**: `mentions[]`에 `agent_id` 문자열을 담는다. 표면 문법(`@agent-2`)은 전적으로 프롬프트/도구가 처리하고, SSOT에는 정규화된 `agent_id`만 남긴다.
161
+ - **브로드캐스트 vs 멘션 전용**: `mentions`가 비어 있으면 브로드캐스트(스레드 `participants` 대상, author 제외 — §3.5.3), 있으면 명시 대상만 수신.
162
+ - 채널(Discord)은 이 서버 상태를 **읽기 전용 미러**로 표시할 뿐, 프로토콜 전이의 판단 근거가 아니다.
163
+
164
+ #### 3.4.2 스냅샷 폭증 방지 (기본값 고정)
165
+
166
+ "전체 스레드 스냅샷"을 그대로 반환하면 컨텍스트가 즉시 폭증한다. v0.1 기본값은 **unread-only**로 고정하고, 방어 옵션을 둔다.
167
+
168
+ - **기본 `unread-only`**: 호출자에게 **읽지 않은 멘션·메시지만** 반환 (호출자별 커서). (v0.3에서 `wait_for_mention`과 커서 기반 수신은 제거되었으며, inbox push + step() drain이 단일 경로다.)
169
+ - 옵션 `last_n`: 최근 N개.
170
+ - 옵션 `summary`: v0.2 이후 (컨텍스트 압축 시).
171
+ - 복구 시점에는 `read_resource` 상당(전체 상태 덤프)을 명시적 도구로만 제공하고, 자동 주입하지 않는다.
172
+
173
+ ### 3.5 에이전트 실행 모델
174
+
175
+ #### 3.5.1 프리미티브 노출 (v0.1a 고정)
176
+
177
+ **결정:** 프리미티브는 에이전트에게 **명시적 도구(tool)** 로 노출한다. (A 모델, §3.5.2)
178
+
179
+ - `create_thread(name, participants)` — **도구**, 에이전트가 호출
180
+ - `send_message(thread, content, mentions)` — **도구**, 에이전트가 호출 (fire-and-forget)
181
+ - `read_resource()` — **도구**, 명시적 전체 상태 덤프 (복구/집계용, 자동 주입 없음)
182
+ - (참고: `wait_for_mention`(L2 foreground blocking)은 v0.3에서 완전히 제거되었다.)
183
+
184
+ L3에서 수신은 "도구 호출"이 아니라 **inbox에 push → step()이 자동 흡수**다. 에이전트는 언제 듣는지를 제어하지 않는다 — 그게 패시브 어웨어니스의 본질이다.
185
+
186
+ #### 3.5.2 수신 모델 (v0.1a 고정: A — send→inbox, step()만 drain)
187
+
188
+ **결정:** v0.1a는 **A 모델**로 고정한다. **L3에는 별도 워처 태스크가 없다.**
189
+
190
+ - **서버**: `send_message`가 대상의 **inbox에 즉시 push**.
191
+ - **step()만 drain**: 각 `step()` 직전에 inbox를 drain해 `[radio]` 블록의 단일 user turn으로 삽입 (§3.6).
192
+ - **패시브 = "전경에서 wait를 부르지 않음"**: 에이전트는 수신을 전경에서 기다리지 않는다. 대신 inbox에 쌓인 메시지가 다음 step()에서 자동 흡수된다. (과거 L2의 전경 blocking receive는 v0.3에서 제거.)
193
+
194
+ > 단일 소비자 원칙: inbox를 소비하는 주체는 **step() 하나**뿐이다. 서버(push)와 step(drain)이 큐를 나눠 쓰므로 레이스가 없다. (B의 워처 태스크, C의 "워처가 받아서 push"는 v0.1에서 쓰지 않는다.)
195
+
196
+ #### 3.5.3 브로드캐스트 fan-out (v0.1a 기본값)
197
+
198
+ - `mentions`가 비어 있으면(브로드캐스트) **해당 스레드의 `participants`에게만 fan-out**하고, **author(송신자)는 제외**한다.
199
+ - `mentions`가 있으면 명시 대상에게만 push. 단, **participants 밖 대상을 가리키는 멘션은 무시(reject)** — 실제 수신은 `participants ∩ mentions`로 한정한다.
200
+ - 모든 push는 FIFO. FYI/URGENT는 프롬프트 정책(모델이 읽고 분류)이며, drain 순서에는 관여하지 않는다.
201
+
202
+ #### 3.5.4 동시성 모델 (v0.1a 기본값 고정 → 병렬 실행으로 전환 완료)
203
+
204
+ **결정:** 단일 asyncio 이벤트 루프 내 에이전트 병렬(asyncio.Task), 프로세스/스레드 격리 없음.
205
+
206
+ - **병렬 실행**: `Session.run()`에서 각 에이전트가 독립 `asyncio.Task`로 돌며, 자기 자신이 finished될 때까지 step을 반복한다. 기존 라운드로빈 `for` 루프를 제거했다.
207
+ - **글로벌 스텝 예산**: 모든 에이전트의 step 합을 `max_steps`로 제한한다. asyncio 단일 루프상에서 +=는 atomic하므로 락 없이 동작한다.
208
+ - **게이트/프로토콜 상태 주입**: `_inject_protocol_gate_state(agent, protocol)` / `agent.gate_open` 갱신을 step 앞에 그대로 둔다. `MessageServer`가 단일 이벤트 루프+협력 스케줄링이라 메시지/게이트 상태 접근은 원자적이므로 race 없다.
209
+ - **도구 로그 실시간 스트리밍**: 병렬화로 도구 호출이 발생 즉시 큐에 밀려 출력된다. `_output_consumer` / `_log_tool_event`(cli.py)는 이미 flush=True이므로 변경 불필요.
210
+ - **서버 상태**: v0.1a는 **메모리**(dict + asyncio 큐). 영속화가 필요해지면 aiosqlite로 전환(단일 writer 태스크가 lock).
211
+ - **inbox**: 에이전트별 `asyncio.Queue`.
212
+ - **프로세스/스레드 격리는 v0.1에서 쓰지 않는다.** 에이전트 = 동일 루프 내 코루틴. (모델 호출은 어댑터가 비동기 HTTP로 띄움.)
213
+
214
+ 이 결정은 v0.1a에서 동시성·잠금 논쟁을 미리 제거하기 위한 것이며, 에이전트 규모가 커지면 프로세스 격리로 재검토한다. 병렬 실행 전환으로 백엔드 LLM 응답을 기다리는 동안 다른 에이전트가 블로킹되지 않으며, 도구 호출 로그가 발생 순서대로 실시간 흘러나온다.
215
+
216
+ ### 3.6 강제 삽입 포맷 (v0.1a 고정)
217
+
218
+ inbox drain 결과를 모델에 어떻게 넣는지 고정한다. (모델별 반응 차이를 줄이기 위해 단순화)
219
+
220
+ - drain한 메시지들을 **한 턴으로 합쳐 하나의 user turn**으로 삽입.
221
+ - `[radio]` 블록으로 감싸고, 각 메시지에 `from=<agent_id>`를 붙인다.
222
+
223
+ ```
224
+ [radio]
225
+ from agent-2: (URGENT) 정답은 42가 아니라 43.
226
+ from agent-3: (FYI) 내 몫은 DB 쪽이야.
227
+ ```
228
+
229
+ - system 턴(시스템 프롬프트 재주입)은 쓰지 않는다 — 오직 단일 user turn.
230
+ - `[radio]` 접두사의 존재가 "이건 동료의 라디오 메시지"임을 모델에게 알린다.
231
+
232
+ ---
233
+
234
+ ## 4. 기술 스택 및 프로젝트 구조 (초안)
235
+
236
+ ### 4.1 스택
237
+
238
+ - **언어/런타임:** Python 3.11+ (asyncio 단일 루프, §3.5.4)
239
+ - **패키징:** `pyproject.toml` (uv 또는 pip)
240
+ - **설정:** YAML + CLI (둘 다 지원)
241
+ - **모델 SDK:** 백엔드별 어댑터. **1순위 OpenAI-compatible, 2순위 Nous Portal.** 최소 의존(표준 라이브러리 + `httpx`/`aiohttp` 정도).
242
+ - **서버 상태:** v0.1a 메모리(dict + asyncio 큐), 필요 시 aiosqlite.
243
+
244
+ ### 4.2 디렉터리 (초안)
245
+
246
+ ```
247
+ agent-augury/
248
+ pyproject.toml
249
+ README.md
250
+ DESIGN.md
251
+ LICENSE # Apache-2.0 (예정)
252
+ src/
253
+ agent_augury/
254
+ cli.py # CLI 진입점 (로그 미러)
255
+ config.py # YAML 로드/검증
256
+ session.py # 세션 = 에이전트 묶음 + 메시지 서버 + 생명주기
257
+ server.py # 내부 메시지 서버 (SSOT, in-process asyncio)
258
+ agent/
259
+ loop.py # 에이전트 step() 루프 + inbox drain (단일 소비자)
260
+ tools.py # create_thread/send_message/read_resource 도구
261
+ system_prompt.py # 모델 무관 통신 규칙 프롬프트 템플릿
262
+ # watcher.py 없음(A 모델) — 필요 시 v0.1+에서 B 모델 도입 시 추가
263
+ backend/
264
+ base.py # ModelBackend 인터페이스
265
+ openai_compat.py # OpenAI 호환 어댑터 (1순위)
266
+ nous_portal.py # Nous Portal 어댑터 (2순위)
267
+ channel/ # (v0.1b 이후) 사람용 관측창 / 미러
268
+ base.py
269
+ protocol/ # (v0.1b~) 분할 합의 등 최소 프로토콜
270
+ phases.py
271
+ approval.py
272
+ examples/
273
+ demo.yaml # 예시 세션 구성
274
+ tests/
275
+ ```
276
+
277
+ ### 4.3 라이선스
278
+
279
+ **결정(예정): Apache-2.0.**
280
+
281
+ 원본이 Apache-2.0이고, 프로토콜/프롬프트 문장을 참고할 가능성이 있으면 Apache-2.0 + 논문·레포 인용이 마찰이 적다. 코드를 전혀 미복제하고 완전 재작성할 경우 MIT도 가능하지만, "원본 개념 계승"이라는 정체성상 Apache-2.0으로 두고 논문([arXiv:2607.28430](https://arxiv.org/abs/2607.28430))과 원본 레포를 명시 인용한다.
282
+
283
+ ---
284
+
285
+ ## 5. 열린 결정 (Open Decisions)
286
+
287
+ 구현 착수 전에 확정할 항목. 사용자가 결정하거나, 구현 중 검증 후 못박는다.
288
+
289
+ | # | 항목 | 기본값(상태) |
290
+ |---|------|-------------|
291
+ | D1 | 프로젝트 이름 | `agent-augury` — **확정 (2026-08-26)** |
292
+ | D2 | 라이선스 | Apache-2.0 (예정) — §4.3 |
293
+ | D3 | Discord 위치 | v0.1a는 CLI 로그 미러만, Discord는 v0.1b 이후 — **확정** |
294
+ | D4 | Nous Portal API 스펙 | 2순위 어댑터라 v0.1a 블로커 아님 — 확인 시점 유동 |
295
+ | D5 | 서버 상태 저장 | v0.1a 메모리, 필요 시 aiosqlite — **확정** |
296
+ | D6 | 배경 워처 | **해당 없음** (A 모델: 워처 없음, §3.5.2) |
297
+
298
+ > 리뷰 반영으로 D1(이름)·D3·D5·D6이 v0.1a 기준으로 확정. D4(Nous Portal API 스펙)는 1순위 OpenAI-compatible 어댑터로 시작하므로 v0.1a 블로커가 아니다.
299
+
300
+ ---
301
+
302
+ ## 6. 구현 로드맵 (첫 릴리스 범위)
303
+
304
+ 로드맵은 **"검증 대상을 최소화"** 하는 원칙으로 재편했다 (리뷰 반영, 2026-08-26).
305
+
306
+ **핵심 판단:** 이 프로젝트의 차별점은 P1~P5 프로토콜이 아니라 **패시브 어웨어니스(L2→L3 통신 모드)** 다. 그러므로 v0.1에서는 통신 메커니즘을 먼저 검증하고, 통과 후에야 최소 프로토콜을 얹는다. P1~P5 전체는 v0.2로 미룬다.
307
+
308
+ ### v0.1a — 통신 메커니즘 검증
309
+
310
+ **목표:** 패시브 어웨어니스가 실제로 되는지, 그것만 단독 검증한다.
311
+
312
+ - 에이전트 **A/B 2명**(모델 무관) + 내부 메시지 서버(SSOT, in-process asyncio)
313
+ - `send_message` = fire-and-forget(도구), 수신은 **inbox push → step() 자동 흡수** (§3.5.2, A 모델)
314
+ - 에이전트가 일하는 동안 동료 멘션을 받아 **다음 step()에 자동 삽입**되는 것 확인 (§3.5.2)
315
+ - **P·APPROVE·워크로그 없음.** 프로토콜은 관여하지 않는다.
316
+ - **관측은 CLI 로그 미러.** Discord는 v0.1b 이후 사람용 관측창으로 미룬다.
317
+
318
+ **통과 기준(자동/스크립트 검증, 주관 판단 배제):** L2 vs L3 대조 토이 시나리오를 **Fake ModelBackend로 tool 시퀀스를 고정**해 숫자로 단언한다. 에이전트는 **A/B 2명**으로 충분하다.
319
+
320
+ ```
321
+ 시나리오: A가 숫자 탐색(search tool 반복) 중, B가 "정답은 42가 아니라 43"이라는
322
+ 정정 멘션을 보낸다.
323
+
324
+ - Fake ModelBackend로 A의 tool 시퀀스를 고정(정정 전 search를 계속 호출하도록).
325
+ - L3(push+inbox): A가 search tool을 정정 전 N회 이상 호출 AND 최종 답이 43.
326
+ (즉, 탐색을 멈추지 않았고 정정을 흡수함)
327
+ - L2(전경 wait): inbox push가 꺼져 있고, wait_for_mention이 전경 tool이라서
328
+ search가 N 미만이거나, 정정 흡수까지 wall-step이 더 큼.
329
+
330
+ 단언(스크립트):
331
+ assert L3.search_count >= N
332
+ assert L3.final_answer == 43
333
+ assert L3.search_count > L2.search_count (또는 L2는 정정 흡수 시점이 더 늦음)
334
+ ```
335
+
336
+ > 논문의 SWE-Atlas(124태스크) 수준은 필요 없다. "L2→L3 통신 모드가 결과를 바꾼다"는 최소 재현이 이 프로젝트의 설득력이며, 그 판단은 "로그 감상"이 아니라 **search 횟수·최종 답·wall-step**이라는 숫자로만 이뤄진다.
337
+
338
+ ### v0.1b — 최소 프로토콜 (분할 합의 1단) — 구현 완료
339
+
340
+ **목표:** v0.1a 위에 "게이트가 있는 협업"이 최소로 도는 것을 확인.
341
+
342
+ - 각자 초안 제시 → 한 스레드에서 분할안 논의 → **전원 APPROVE** → 각자 몫 수행(자유 실행, P4/P5 없음)
343
+ - 내부 서버의 스레드/멘션 위에서 게이트가 도는지 확인
344
+ - **Discord를 "사람용 관측창"으로 도입** (미러, 코어는 Discord 입력 안 들음)
345
+ - **Phase transition hook 도입** — `PhaseManager`로 게이트 OPEN을 명시적 훅으로 노출 (v0.2 P1~P5 확장 대비)
346
+ - **실제 OpenAI-compatible LLM 연동 예시** — `examples/consensus_openai.yaml` (PROPOSE/APPROVE 자율 생성)
347
+
348
+ **통과 기준:** 분할 합의 1단(제안→합의→승인→실행)이 끝까지 돌며 전원 승인 게이트가 동작. — `examples/consensus_demo.py` 검증 완료.
349
+
350
+ ### v0.2 — P1~P5 전체 (구현 완료)
351
+
352
+ 교차검토(P4), 어셈블러 제출(P5) 등 원본의 전체 프로토콜 구현 완료.
353
+
354
+ - 5단계 프로토콜 상태 머신: `CollaborationProtocol` + `PhaseManager`
355
+ - 4개 게이트: P2_SPLIT / P3_EXECUTE / P4_REVIEW / P5_SUBMIT
356
+ - 각 게이트는 전원 승인(APPROVE) 시 자동 개방, 다음 페이즈로 자동 진행
357
+ - P1~P5 E2E 검증: `examples/p1_to_p5_demo.py` — 3개 에이전트, Fake 백엔드
358
+ - 시스템 프롬프트 페이즈 주입: 각 에이전트의 step()마다 현재 페이즈 반영
359
+ - 접두사 컨벤션: `PROPOSE:` / `APPROVE:` / `REJECT:` / `RESULT:` / `FINAL:`
360
+
361
+ ---
362
+
363
+ ### 마일스톤 정리 (가설 검증 순서로 재배치)
364
+
365
+ 핵심 가설(패시브 어웨어니스)에 가깝게 순서를 잡았다. 백엔드·채널 추상화를 M1에 몰아넣지 않는다.
366
+
367
+ | 단계 | 내용 | 통과 기준 |
368
+ |------|------|-----------|
369
+ | M1 | 스캐폴드 + **내부 메시지 서버 primitives + 단위 테스트** (thread/send/wait/inbox) | 서버 primitives 단위 테스트 통과 |
370
+ | M2 | **에이전트 루프**(step/inbox drain, 단일 소비자) | 루프 동작 (Fake ModelBackend 스텁으로) |
371
+ | M3 | **모델 어댑터** (1순위 OpenAI-compatible, 2순위 Nous Portal) | 실제 모델 호출 1회 성공 |
372
+ | M4 | **E2E 데모 + L2 vs L3 대조 검증** (v0.1a) | 위 "v0.1a 통과 기준" 스크립트 통과 |
373
+ | M5 | **(v0.1b)** 분할 합의 1단 + 전원 APPROVE + Discord 관측창 | 위 "v0.1b 통과 기준" — **완료** |
374
+ | M6 | **(v0.2)** P1~P5 전체 프로토콜 + 4개 게이트 + E2E 검증 | 위 "v0.2 구현 완료" — **완료** |
375
+
376
+ ---
377
+
378
+ ## 7. 참고
379
+
380
+ - 원본 저장소: <https://github.com/Coral-Protocol/AgentRadio> (Apache-2.0)
381
+ - 논문: <https://arxiv.org/abs/2607.28430>
382
+ - 상품: <https://coralcode.dev/> (참고용 — 이 프로젝트는 이와 무관한 독립 오픈소스)