A

Skill 详情

Agent Roundtable

多智能体圆桌讨论——主题驱动的多轮辩论,含收敛检测与结论生成

来源平台:SkillHub
来源标识:SkillHub/agent-roundtable
源文件:原始说明
AI 平台与模型 高关注 SkillHub 高 风险 下载 195Stars 1 SkillHub
来源平台SkillHub
文档版本1.2.4
热度高关注
排名信号下载 195
概述 安装 文档 下载

快速判断

多智能体圆桌讨论——主题驱动的多轮辩论,含收敛检测与结论生成

最后校验2026-05-27
来源平台SkillHub
安全提示
下载副本ZIP 可用

适合任务

  • 按 SkillHub 收录说明复用成熟任务流程。
  • 通过下载包离线阅读完整 Skill 内容。
  • 结合热度指标优先评估常用 Skill。

输入与输出

输入:任务目标、上下文材料、文件路径、约束条件或需要处理的内容。

输出:按 Skill 说明生成的文档、代码、检查结果、计划、建议或操作步骤。

示例任务

  • 使用 Agent Roundtable 帮我处理当前任务,并说明需要准备哪些输入。
  • 根据 Agent Roundtable 的说明,先列出使用前的安全检查项。

安装方式

  1. 下载本站提供的 Skill ZIP 并解压。
  2. 把解压后的 Skill 目录放入当前 AI 工具支持的 skills 目录。
  3. 如需在线查看原始内容,可打开 GitHub 的 SKILL.md

在线原始地址:skillhub-agent-roundtable/SKILL.md

风险边界

SkillHub 提供了源站安全报告入口,但本站不替代人工审查。使用前仍需检查权限、外部依赖和敏感数据边界。

SKILL.md 文档介绍

Roundtable Discussion Skill

Publishing to skill hubs

When preparing Roundtable for Hermes Skill Hub or OpenClaw/ClawHub, use the release checklist in src/skills/references/skill-hub-publishing.md. Key reminders: keep src/skills/ self-contained and free of private team data, include both Hermes and OpenClaw metadata blocks in SKILL.md, check hermes skills publish --help, clawhub publish --help, and clawhub whoami, and gate real publishing on user confirmation of target account/repo.

Overview

Enable multiple agents to participate in structured, multi-round discussions

around a topic. Each participant is a real sub-agent spawned via

delegate_task — not the main agent role-playing. Each gets its own

conversation context, model call, and toolset.

Core value: Turn "one agent working alone" into "a team having a meeting."

When to Use

  • Tech design review: product, frontend, backend, architect debate approach
  • Competitive analysis: product, marketing, design compare alternatives
  • Bug root cause analysis: backend, ops, test triangulate the issue
  • Product requirements: product, design, dev align on scope
  • Architecture decisions: architect, backend, frontend, devops choose stack

Prerequisites

Enable the roundtable toolset in the profile config:

toolsets:
  - roundtable

Or pass enabled_toolsets: ["roundtable"] when spawning an agent.

Tools

| Tool | Purpose |

|------|---------|

| roundtable_init | Create a discussion with topic + participants (+ optional notifications config) |

| roundtable_speak | Record a participant's speech (auto-triggers notifications if configured) |

| roundtable_read | Read discussion history |

| roundtable_status | Check status + convergence metrics |

| roundtable_summarize | Get structured data for conclusion doc (use compact=true for smaller output) |

| roundtable_end | Conclude or cancel a discussion (triggers concluded notification) |

| roundtable_list | List all discussions |

| roundtable_advance | Manually advance to next round (use when auto-advance doesn't trigger) |

| roundtable_notify | Manually trigger a notification event for a discussion |

Execution Model (Important)

Each participant is a real, independent sub-agent:

1. Coordinator calls delegate_task(goal=..., toolsets=["roundtable"]) for each participant

2. The sub-agent runs its own model inference with its own context window

3. The sub-agent calls roundtable_speak(participant="{profile}", ...) to record its speech

4. The sub-agent returns a summary to the coordinator

5. Coordinator sends notification (optional), then delegates to next participant

This means: 4 participants × 4 rounds = 16 delegate_task calls = 16 independent

model invocations. Expect ~15-20 minutes for a full discussion. Each participant

takes 15-60 seconds per round depending on context size and model speed.

Coordinator Flow

Step 1: Create the Discussion

roundtable_init(
    topic="Database selection: PostgreSQL vs MySQL vs TiDB",
    context="Our e-commerce system needs high-concurrency read/write, 1TB+ data",
    participants=[
        {"profile": "bingge", "role": "Product Director", "perspective": "Focus on UX", "display_name": "Bing"},
        {"profile": "mafei", "role": "Tech Lead", "perspective": "Focus on feasibility", "display_name": "Fei"},
        {"profile": "xiaosu", "role": "Designer", "perspective": "Focus on data display", "display_name": "Su"},
    ],
    created_by="coordinator",  # optional: identifies the discussion creator
    max_rounds=3,
    speech_order="fixed",
    notifications={
        "enabled": True,
        "channels": [
            {"platform": "feishu", "chat_id": "oc_xxx"},
        ],
        "events": ["round_start", "speech", "round_end", "concluded"],
    }
)
→ returns {discussion_id, ...}

Notifications (optional): pushes real-time discussion updates to messaging

channels. See [Notifications](#notifications) section below.

Step 2: Opening Statement (Round 0)

roundtable_speak(
    discussion_id="rt_xxxxxxxx",
    participant="coordinator",
    content="Today we're discussing database selection..."
)

Optionally send opening notification:

send_message(target="feishu:oc_xxx", message="🔔 Roundtable started [rt_xxx]\n📋 Topic: ...\n👥 Participants: ...")

Step 3: Multi-Round Discussion

For each round:

3a. Coordinator summarizes previous round (optional but recommended):

roundtable_speak(
    discussion_id="rt_xxxxxxxx",
    participant="coordinator",
    content="Round N summary: key points, emerging consensus, open questions..."
)

3b. Delegate to each participant SEQUENTIALLY (not parallel):

delegate_task(
    goal="You are {display_name}, {role}. Share your viewpoint on this topic from a {role} perspective, then call roundtable_speak to record your speech.",
    context="""You are participating in a roundtable discussion.

## Discussion Info
- Topic: {topic}
- Context: {context}
- Current Round: Round {N} / {max_rounds}
- Your Role: {role} ({display_name})
- Your Perspective: {perspective}

## Discussion History
{formatted_history}

## Your Task
From your role's perspective, share your viewpoint on this topic.
- Reference others' points if relevant
- Keep it 200-500 words
- Both pros AND cons, with concrete examples

After speaking, call roundtable_speak to record your statement:
roundtable_speak(discussion_id="{id}", participant="{profile}", content="your speech")""",
    toolsets=["roundtable"]
)
# Wait for completion, then send notification, then delegate to participant 2

3c. After each participant, send notification (if configured):

send_message(target="feishu:oc_xxx", message="💬 Round {N} | {role} ({display_name}) spoke:\n{summary}")

3d. After all participants in a round, send round_end notification:

send_message(target="feishu:oc_xxx", message="✅ Round {N} complete\nConsensus: ...\nDisagreements: ...")

Why sequential, not parallel: Participants need to read each other's responses to build on them. Parallel delegation means everyone speaks into a void.

Step 4: Check Convergence

After each round:

roundtable_status(discussion_id="rt_xxxxxxxx")
→ check convergence_score, consensus_points, disagreement_points

Note: Convergence tracking may not work reliably due to the round-tracking bug. The coordinator should manually assess convergence from the discussion content.

Step 5: Generate Conclusion

summary = roundtable_summarize(discussion_id="rt_xxxxxxxx")

⚠️ WARNING: roundtable_summarize returns the ENTIRE discussion as raw JSON — potentially 100KB+. It does NOT generate a summary. The coordinator must:

1. Read the summary data (use read_file with offset/limit for the persisted output)

2. Write the conclusion document themselves based on the discussion content

3. Save to the output_path specified during init (or write manually via write_file)

Important: Write the conclusion doc BEFORE calling roundtable_end — the end call only accepts a brief text string, not a full document.

Step 6: End Discussion

roundtable_end(
    discussion_id="rt_xxxxxxxx",
    conclusion="Brief text summary of the conclusion"
)

Send concluded notification:

send_message(target="feishu:oc_xxx", message="🏁 Discussion ended\nConclusion: ...")

Participant Prompt Template

When delegating to a participant sub-agent, use this template:

You are participating in a roundtable discussion.

## Discussion Info
- Topic: {topic}
- Context: {context}
- Current Round: Round {current_round} / {max_rounds}
- Your Role: {role} ({display_name})
- Your Perspective: {perspective}

## Discussion History
{formatted_history}

## Your Task
From your role's perspective, share your viewpoint on this topic.
- You may reference or respond to other participants' statements
- Keep it concise and powerful, 200-500 words
- If you agree with a point, explicitly state your agreement
- If you disagree, explain why and propose alternatives

After speaking, call roundtable_speak to record your statement.

Notifications

Push real-time discussion updates to messaging channels.

Configuration

roundtable_init(
    topic="...",
    participants=[...],
    notifications={
        "enabled": True,
        "channels": [
            {"platform": "feishu", "chat_id": "oc_xxx"},
        ],
        "events": ["round_start", "speech", "round_end", "concluded"]  # default: all
    }
)

Events

| Event | Trigger | Content |

|-------|---------|---------|

| round_start | First speech in a new round | Round number + previous round summary |

| speech | After each participant speaks | Speaker name/role + content (truncated to 200 chars) |

| round_end | All participants spoke in a round | Key points + convergence score |

| concluded | Discussion ends | Final conclusion + consensus/disagreement points |

send_fn Requirement

The notification system requires a send_fn(platform, chat_id, message) callback

on RoundtableCore. The Hermes adapter (adapters/hermes.py) provides

_hermes_send_fn which calls feishu-send.py via subprocess.

If send_fn is not wired, notifications are silently disabled — the Notifier's

enabled returns False. Verify with: core._send_fn is not None.

Manual fallback: If send_fn is broken or unavailable, the coordinator can

manually push notifications using send_message after each speech:

send_message(target="feishu:oc_xxx", message="💬 Round {N} | Speaker: summary...")

Timing

With automatic notifications (send_fn wired), each speech triggers a subprocess

call to feishu-send.py (~1-2s overhead). With manual send_message, overhead is

similar but requires explicit coordinator action after each speech.

Verifying Notifications (Pitfall)

Feishu API returns messages in chronological order by default (oldest first).

When checking if roundtable notifications arrived in the group chat, you MUST use

sort_type=ByCreateTimeDesc in the API call — otherwise you see old messages and

conclude (incorrectly) that notifications didn't send.

Wrong — default sort returns oldest messages first:

resp = requests.get('https://open.feishu.cn/open-apis/im/v1/messages',
    params={'container_id': 'oc_xxx', 'page_size': 10})

Correct — sort descending to see latest messages:

resp = requests.get('https://open.feishu.cn/open-apis/im/v1/messages',
    params={'container_id': 'oc_xxx', 'page_size': 10, 'sort_type': 'ByCreateTimeDesc'})

Debug pattern: If notifications appear to not fire, add a wrapper send_fn

with logging to confirm the callback is actually invoked:

def debug_send_fn(platform, chat_id, message):
    print(f"[DEBUG SEND] platform={platform}, chat={chat_id}, msg_len={len(message)}")
    original_send(platform, chat_id, message)
    print(f"[DEBUG SEND] OK")

Multiple roundtable.db files: The system may have multiple databases:

  • ~/.roundtable/roundtable.db — main agent discussions
  • ~/.hermes/roundtable.db — hermes tool layer discussions
  • ~/.hermes/profiles/{profile}/home/.roundtable/roundtable.db — sub-agent discussions

Always verify against the correct DB when checking discussion state.

Web Viewer (default ON)

run_demo() has web: bool = True by default (changed from False). This means demo discussions automatically start a web viewer at http://localhost:8199. The viewer uses PM2 to manage an Express subprocess, fcntl for file locking, and nanoid for token generation.

Browser auto-open: Starting a discussion with web=True should automatically open the browser via subprocess.run(["open", web_url]) in the Hermes adapter's _handle_init. This is an adapter-level side-effect, not a core-level one — the core library returns the URL but leaves UX actions to the adapter. The generic adapter intentionally does NOT auto-open (headless environments). If you need to customize browser behavior (e.g., open in a specific browser), modify _handle_init in adapters/hermes.py.

⚠️ Pitfall: Browser opens at END instead of START (2026-05-23) — Boss reported the browser doesn't open when the discussion begins; it only opens after the discussion concludes (or manually). The _handle_init code has the subprocess.run(["open", web_url]) call, but it may not fire reliably in all execution paths. Diagnosis checklist: (1) Verify _handle_init actually reaches the subprocess.run line (add logging) (2) Check if the web_url variable is correctly populated from publisher.start() return (3) Confirm the open command runs in the correct subprocess context (may need shell=False with list args on macOS). See also: Bug task t_xxxxxxxx for the specific fix.

⚠️ Pitfall: WebViewer real-time updates broken on macOS (2026-05-23) — Boss reported that the WebViewer doesn't show new speeches in real-time; the browser must be force-refreshed to see updates. Root cause: The Express server (server.mjs) uses fs.watch(DISCUSSION_PATH, ...) to detect discussion.json changes and broadcast via SSE. But Python's WebPublisher uses atomic write: write .json.tmp → os.rename(). On macOS, fs.watch() on a file loses tracking after rename() replaces the inode — the watcher stays on the old inode, never sees the new file's changes. Fix: Change fs.watch to watch the directory instead of the file, then filter by filename:

// Wrong — breaks on atomic rename:
watch(DISCUSSION_PATH, () => { ... });

// Correct — watches directory, catches rename:
const dir = require("path").dirname(DISCUSSION_PATH);
const filename = require("path").basename(DISCUSSION_PATH);
watch(dir, (eventType, changedFilename) => {
  if (changedFilename !== filename) return;
  // ... debounce + broadcast logic unchanged
});

Alternative: Add server-side polling fallback (setInterval + mtime check) as defense-in-depth. See also: Bug task t_xxxxxxxx for the specific fix.

⚠️ Direct Core API does NOT auto-open browser: When creating discussions via Direct Core API (core.create_discussion()) instead of the roundtable_init tool, the adapter's _handle_init is NOT called, so:

1. The browser does NOT auto-open

2. The WebViewer URL is NOT printed to the user

3. The coordinator MUST manually extract web_url from the response and: (a) share it with the user, and (b) run open "{web_url}" to open the browser.

Pattern: After core.create_discussion() returns, parse web_url from the JSON response, then:

# Share URL with user (via send_message or inline)
# Then open browser
open "http://0.0.0.0:{port}/r/{token}"

Install roundtable in Hermes venv (required for agent to import it):

# hermes venv may lack pip — bootstrap it first
~/.hermes/hermes-agent/venv/bin/python3 -m ensurepip
~/.hermes/hermes-agent/venv/bin/python3 -m pip install /path/to/roundtable

Note: system Python 3.9 on macOS is too old (package requires >=3.10). Always install into the hermes venv (Python 3.11+).

Alternative: uv venv (when hermes venv is missing or broken):

# Create a dedicated venv with Python 3.12 via uv
uv venv /tmp/roundtable-venv --python 3.12
uv pip install -e /path/to/roundtable --python /tmp/roundtable-venv/bin/python3

# Then use this venv for all Direct Core API calls:
/tmp/roundtable-venv/bin/python3 << 'PYEOF'
import json, sys
sys.path.insert(0, "/Users/parsifal/.hermes/hermes-agent")
from tools.roundtable_tools import _get_core
core = _get_core()
# ...
PYEOF

This pattern is useful when:

  • System Python is 3.9 (macOS default) and roundtable requires >=3.10
  • The hermes venv doesn't exist or lacks pip
  • You need a clean, isolated environment for roundtable

Convergence Detection

Each round is evaluated for convergence:

| Metric | Formula | Meaning |

|--------|---------|---------|

| Consensus | Points multiple participants agree on | Alignment |

| Disagreement | Points participants disagree on | Conflict |

| New Point | New topics raised this round | Scope expansion |

| Score | consensus / (consensus + disagreement) | Overall alignment |

Termination conditions:

  • Convergence score > 0.8 → high consensus, wrap up
  • Max rounds reached → prevent infinite discussion
  • Coordinator manually ends → emergency stop
  • All participants vote to end → democratic close

Conclusion Document Format

The format below works for general discussions. For product/design/dev discussions aimed at producing a buildable specification, use the decision-oriented format instead — see src/skills/references/web-viewer-discussion-example.md for the full pattern (MVP scope, tech architecture, acceptance criteria, risk assessment, design deliverables, action items).

# Roundtable Conclusion: [Topic]

## Summary
- Participants: Product(Bing), Design(Su), Dev(Fei)
- Rounds: 3
- Date: 2026-05-20

## Consensus Points
1. [Point 1]
2. [Point 2]

## Disagreement Points
1. [Point 1] - Various perspectives

## Action Items
1. [ ] [Action 1] - Owner: xxx
2. [ ] [Action 2] - Owner: xxx

## Detailed Transcript
### Round 1
- **Product(Bing)**: ...
- **Design(Su)**: ...
- **Dev(Fei)**: ...

### Round 2
...

Data Storage

  • Database: ~/.hermes/roundtable.db (independent from kanban.db)
  • Conclusion docs: Configurable via output_path, defaults to project docs dir
  • ID format: rt_ + 8 hex chars (e.g., rt_a1b2c3d4)

Integration with Kanban

Discussions can be linked to kanban tasks:

# After conclusion, add as task comment
kanban_comment(task_id="t_xxx", body="Roundtable conclusion: {conclusion_path}")

Direct Core API Fallback (Last Resort Only)

> ⚠️ IMPORTANT: The standard flow is ALWAYS roundtable_init + delegate_task(toolsets=["roundtable"]). Do NOT default to Direct Core API. If sub-agents fail to call roundtable_speak, treat it as a bug to investigate and fix — not a workaround to codify. The user explicitly rejected the workaround-first mindset: "不是本质问题吗?一直workaround?"

>

> Before using Direct Core API, exhaust these options first:

> 1. Check if the sub-agent's enabled_toolsets actually includes "roundtable"

> 2. Check if the sub-agent's profile has the roundtable toolset registered

> 3. Try with a different profile to isolate the issue

> 4. Report the bug as a kanban task for investigation (bug tracking task: t_xxxxxxxx)

>

> Only use Direct Core API when: (a) the bug is confirmed and (b) you need the discussion to proceed NOW while the fix is in progress.

Known Issue: Sub-agents spawned via delegate_task with toolsets=["roundtable"] sometimes fail to invoke roundtable_speak — the sub-agent outputs speech text but tool_trace is empty. This is a bug, not a design limitation. Root cause is under investigation.

Direct Core API (emergency bypass only): Bypass the Hermes tool layer and call RoundtableCore directly:

~/.hermes/hermes-agent/venv/bin/python3 << 'PYEOF'
import json, sys
sys.path.insert(0, "/Users/parsifal/.hermes/hermes-agent")
from tools.roundtable_tools import _get_core

core = _get_core()

# Record a speech
result = core.speak(
    discussion_id="rt_xxxxxxxx",
    participant="bingge",           # must match participants list exactly
    content='Speech content here'   # use single quotes to avoid heredoc issues
)
print(json.dumps(result, ensure_ascii=False))

# Read discussion history
history = core.read(discussion_id="rt_xxxxxxxx")
print(json.dumps(history, ensure_ascii=False, indent=2))

# End discussion
core.end_discussion(discussion_id="rt_xxxxxxxx", conclusion="Brief conclusion text")
PYEOF

Key gotchas:

  • NEVER import RoundtableCore directly — always use _get_core() from tools.roundtable_tools. Direct import bypasses send_fn wiring, so notifications silently fail. Boss explicitly corrected this (2026-05-23): "你这压根不是用我们开发的圆桌技能做啊".
  • Use single quotes for content strings containing double quotes (heredoc escaping)
  • participant must exactly match the profile name in participants list
  • coordinator is always allowed (bypasses participant check)
  • Each core.speak() returns round_complete and next_speaker — use to track progress
  • core._send_fn must be non-None for notifications to fire (wired by _hermes_send_fn)
  • Cross-process singleton: _get_core() returns a singleton, BUT each Python invocation is a fresh process, so the singleton only lives for that one script execution. The _publishers dict (WebPublisher) is in-memory and lost between invocations. This is handled by the _update_web_discussion_json fallback — see "Cross-Process WebPublisher Data Sync" section.

Hybrid Workflow (temporary measure only): If roundtable_speak is confirmed broken in sub-agents, use delegate_task for reasoning and Direct Core API for recording — but treat this as a temporary workaround while the bug is being fixed, not a permanent pattern.

# Step 1: Delegate to sub-agent for thought generation
result = delegate_task(
    goal="You are the Product Director. Share your viewpoint on '{topic}' from a product perspective...",
    context="...",
    toolsets=["roundtable"]  # sub-agent will fail to call roundtable_speak — that's expected
)

# Step 2: Extract speech text from sub-agent's summary
speech_text = extract_from_summary(result)  # parse the sub-agent's output

# Step 3: Record via Direct Core API (100% reliable)
terminal(f"""
~/.hermes/hermes-agent/venv/bin/python3 << 'PYEOF'
import json, sys
sys.path.insert(0, "/Users/parsifal/.hermes/hermes-agent")
from tools.roundtable_tools import _get_core
core = _get_core()
result = core.speak(discussion_id="{discussion_id}", participant="{profile}", content='''{speech_text}''')
print(json.dumps(result, ensure_ascii=False))
PYEOF
""")

Key insight: The sub-agent's reasoning is valuable — don't waste it. Just don't rely on it to persist the data.

When to use Direct Core API: Any time reliability matters — demos, verification meetings, production discussions. The direct API is faster (no sub-agent overhead) and 100% reliable.

24. Quick verification meetings — When Boss says "组织一次会议看效果", use the lightweight pattern: coordinator speaks for all participants directly via roundtable_speak (no delegate_task). Runs in ~2min vs ~15-20min. Always include notifications config even for demos — the goal is to verify the full pipeline including group chat sync. See src/skills/references/quick-verification-example.md for a working example.

25. delegate_task sub-agents can fail to call roundtable_speak — Observed in 2026-05-23 session (6/6 failures). Sub-agents produce speech text in their response but tool_trace is empty. DO NOT assume this is permanent — it's a bug under investigation (t_xxxxxxxx). Before falling back to Direct Core API, check: (1) sub-agent's enabled_toolsets includes "roundtable", (2) the profile has roundtable tools registered, (3) try a different profile. If confirmed broken, use Direct Core API as temporary bypass and file/update the bug task.

26. Direct Core API is an emergency bypass, NOT the default — Use roundtable_init + delegate_task as the primary flow. Direct Core API (core.speak()) exists for when the standard flow is confirmed broken AND you need the discussion to proceed now. Always file a bug task when you're forced to use it.

27. web and web_port params on roundtable_init — The core supports web=True to auto-start a WebPublisher HTTP server. The Hermes tool schema now includes these params. Default web=True in _handle_init so discussions always open WebViewer. Port auto-increments if 8199 is busy (check web_url in init response for actual port).

28. Adapter handles UX side-effects, core returns data — When adding features that interact with the user's environment (browser, notifications, file watchers), implement them at the adapter level (adapters/hermes.py), not in core.py. The core library should be stateless and headless — it returns data (URLs, IDs, status) and the adapter decides what to do with it. Example: browser auto-open lives in _handle_init (adapter), not in create_discussion (core). The generic adapter may choose different UX behavior or none at all.

> Notifications implementation detail: See src/skills/references/notifications-implementation.md for architecture, send_fn wiring, and execution flow diagrams. See src/skills/references/notification-debugging.md for verification pitfalls and debugging checklist.

1. At least 2 participants required — A discussion needs multiple viewpoints

2. Participant must be registered — Only profiles listed in participants can speak. Exception: participant="coordinator" is always allowed (bypasses the participant check). This was a bug fix — previously, roundtable_speak(participant="coordinator") would fail with "not an active member". Coordinator's speech is recorded in round 0 and does NOT affect round advancement logic.

3. Round 0 is opening — Coordinator speaks first, then round 1 begins

4. Auto-conclude on max_rounds — Discussion ends automatically when max rounds exceeded

5. Independent database — roundtable.db is separate from kanban.db; don't mix paths

6. No LLM in summarizeroundtable_summarize returns raw JSON data (potentially 100KB+), NOT a summary. The coordinator must manually write the conclusion document. Use read_file with offset/limit to process the persisted output.

7. connect() expects Path, not str — Use connect(Path("~/.hermes/roundtable.db")), not connect("/Users/...")

8. delegate_task needs roundtable toolset — Sub-agents spawned via delegate_task must have enabled_toolsets=['roundtable'] to use roundtable tools

9. Round tracking is buggy — All speeches may end up as Round 0 regardless of actual round. The round field in speak responses may not reflect reality. Workaround: track rounds manually in the coordinator's logic.

10. No roundtable_advance tool — There is no tool to explicitly advance rounds. Rounds are supposed to auto-advance when all participants have spoken, but due to the round-tracking bug this may not work. The coordinator should track round progress manually.

11. Sequential delegation, not parallel — Delegate to participants one at a time so they can read each other's responses. Parallel delegation means everyone speaks without context.

12. Coordinator summarizes between rounds — After each round of participant speeches, the coordinator should call roundtable_speak(participant="coordinator") to summarize before delegating the next round. This gives participants context for their next response.

13. summarize output is persisted as file — When roundtable_summarize returns >8K chars, the full output is saved to a temp file. Use read_file(path, offset, limit) to process it in chunks.

14. Hermes adapter send_fn must be wired — The adapters/hermes.py creates RoundtableCore() without passing a send_fn, so notifications configured in roundtable_init will silently not send. Fix: implement _hermes_send_fn(platform, chat_id, message) that calls feishu-send.py, then pass it to RoundtableCore(send_fn=_hermes_send_fn). Default profile should be "default" (not hardcoded to any specific profile). Without this fix, notifications are metadata only.

15. Participants are real sub-agents, not role-play — Each participant is a separate delegate_task() call that spawns an independent sub-agent with its own context window, model inference, and tool access. This is why a 4-round, 4-participant discussion takes 15-20 minutes (16+ independent model calls). The coordinator drives rounds sequentially so participants can read each other's responses.

16. Generic adapter lacks notificationsadapters/generic.py does not support notifications config or send_fn. Non-Hermes users must manually handle event dispatch. If releasing as standalone library, the generic adapter needs send_fn parameter support and advance() + notify() methods.

14. send_fn must be wired in adapter — The Notifier requires a send_fn callback to actually send messages. In adapters/hermes.py, _get_core() must pass send_fn=_hermes_send_fn to RoundtableCore(). Without this, notifications config is silently ignored (Notifier.enabled returns False). If notifications aren't firing, check that _hermes_send_fn is implemented and passed to the core. The send_fn uses subprocess.run(["python3", script, profile, chat_id, message]) to call feishu-send.py.

15. HERMES_PROFILE env var — The send_fn reads os.environ.get("HERMES_PROFILE", "default") to determine which profile's Bot sends the notification. Default is "default" (coordinator). This is set automatically by the Hermes runtime — don't hardcode it.

16. Real sub-agents, not role-playing — Each participant in a roundtable discussion is a separate delegate_task call spawning an independent sub-agent with its own context window and model call. This is why 4 participants × 4 rounds = 16 delegate_task calls and ~15-20 minutes of runtime. If it were just the main agent role-playing, it would take seconds.

17. Notifications don't block — All notification failures are caught and logged. A broken send_fn never breaks the discussion flow. This is by design.

18. Sub-agent context window — Each participant sub-agent only sees what the coordinator passes in context. If you omit discussion history, the participant speaks "into a void". Always include formatted_history from roundtable_read.

19. Write conclusion doc BEFORE roundtable_endroundtable_end only accepts a brief conclusion text string (1-2 sentences). The full conclusion document (with action items, tables, detailed transcript) must be written as a separate file via write_file. Do this BEFORE calling roundtable_end so you can still use roundtable_summarize if needed.

20. Round numbering mismatch — The roundtable_summarize output labels speeches as "Round 0", "Round 1", "Round 2" based on the DB's internal round counter. The coordinator's mental model (Round 1 = first participant round) may differ from the DB's (Round 0 = opening, Round 1 = first participant round). Always check the actual round field in the data rather than assuming.

21. History reading cost grows linearlyroundtable_read returns ALL speeches. In Round 3 of a 3-participant discussion, the first participant's delegate_task took 165s because the context included 9 full speeches. For longer discussions (4+ rounds, 4+ participants), consider summarizing previous rounds in the coordinator's roundtable_speak between rounds to keep context manageable.

22. No convergence metrics in practiceroundtable_summarize returns final_convergence_score: null and empty consensus_points/disagreement_points. The convergence detection is not implemented in the core engine. The coordinator MUST manually assess convergence from discussion content — don't rely on the status/summarize tools for this.

23. Coordinator MUST pass notifications config — Even though send_fn is wired in the adapter, notifications is an OPTIONAL parameter on roundtable_init. If you don't pass it, no events fire and no messages reach the group chat — silently. This is the #1 cause of "why didn't my discussion sync to the group?" Always include notifications when the discussion should be visible to the team. For OPC team, the standard config is: notifications={"enabled": True, "channels": [{"platform": "feishu", "chat_id": "oc_your_company_group_id"}], "events": ["round_start", "speech", "round_end", "concluded"]}. Without this explicit config, discussions run in "silent mode" — useful for testing, but almost never what you want in production.

Cross-Process WebPublisher Data Sync (Critical Pitfall)

Problem: Hermes tool calls (roundtable_init, roundtable_speak, roundtable_end) each run in a separate Python process. The _publishers dict in RoundtableCore is in-memory — it's populated during create_discussion() in process A, but speak() runs in process B where _publishers is empty. Result: publisher.on_speech() is never called, discussion.json stays at "speeches": [], and the WebViewer shows nothing.

Fix (applied 2026-05-23): Two cross-process fallback methods in core.py:

  • _update_web_discussion_json() — called in speak() when publisher not in memory; reads/writes discussion.json directly with fcntl locking
  • _conclude_web_discussion() — called in end_discussion() when publisher not in memory; sets status="concluded" and writes conclusion

These methods find the JSON file at /tmp/roundtable_web/{discussion_id}/discussion.json and update it with the same atomic write pattern (flock → write .tmp → fsync → rename) that WebPublisher uses.

Also fixed: publisher.conclude() was called without the conclusion string argument — fixed to pass disc_after.conclusion or "".

Verification: Tested with fully independent processes (create in process 1, 6 speeches each in separate processes, end in process 8). All 6 speeches appeared in WebViewer, conclusion displayed correctly.

Impact: Without this fix, the WebViewer is essentially non-functional in real Hermes usage since every tool call is a fresh process.

Test Results

See src/skills/references/test-results-2026-05-20.md for the first functional test results, including bugs found and product acceptance report.

Open-Source Release

See src/skills/references/open-source-readiness.md for the pre-release checklist (LICENSE, cleanup, adapter gaps, test isolation).

Working Examples

  • src/skills/references/opc-experience-discussion-example.md — 4-round, 4-participant discussion with timing data and workflow
  • src/skills/references/notifications-example.md — roundtable with real-time push notifications to Feishu
  • src/skills/references/release-planning-discussion.md — 3-round product/design/dev discussion for open-source release planning
  • src/skills/references/ai-relay-open-source-discussion.md — 3-round discussion with hybrid workflow (delegate_task + Direct Core API), notifications, and conclusion doc → 5/29 release plan
  • src/skills/references/web-viewer-discussion-example.md — Decision-oriented conclusion doc pattern: MVP scope, tech architecture, acceptance criteria, risk assessment, design deliverables. Use this format when the discussion goal is to produce a buildable specification.
  • src/skills/references/post-discussion-kanban-dispatch.md — After discussion concludes, create kanban tasks grouped by owner, subscribe notifications, and dispatch to team via Feishu groups.

Open-Source Release

See src/skills/references/open-source-readiness-checklist.md for the pre-release audit: missing LICENSE, Hermes-specific files to separate, build-backend fix, .gitignore, internal docs to remove, generic adapter gaps, and target package structure.

Test line

建议反馈