跳到正文
GitHub·· 7 天前AI 评分62

Crossband 发布:让 Claude、GPT 与本地模型同处一个群聊

shawn-durrani/crossband

AI 导读

Crossband 是一个在本地运行的多模型群聊应用,Claude、GPT、Groq 或 OpenRouter 上的托管开源模型、以及通过 Ollama 或 LM Studio 运行的本地模型都可以加入同一段共享对话记录,彼此能看到对方的发言并表达同意或反对。

正文

Crossband is a group chat with several AI models in it at once. Claude, GPT, a hosted open model through Groq or OpenRouter, or a local one through Ollama or LM Studio: any of them can take a seat. They all read the same transcript, see each other's messages, and can agree or disagree. It runs on your own computer, and from there it can listen and talk, remember what was said last week, and let more than one person join in.

flowchart LR
  subgraph seats["Any model can take a seat"]
    C("Claude")
    G("GPT")
    H("A hosted open model")
    L("A local model")
  end
  T["One shared transcript"]
  tools[("Web search, pages,<br/>GitHub, memory")]
  V("Voice in and out")
  Y(["You"])
  O(["Anyone else<br/>who speaks"])
  C -- "read, reply" --> T
  G --> T
  H --> T
  L --> T
  T -- "calls" --> tools
  T -- "spoken aloud" --> V
  Y -- "types" --> T
  V -- "hears, speaks" --> Y
  V -- "hears" --> O
  classDef node fill:#d4d4d8,stroke:#757575,color:#18181b
  classDef hero fill:#38bdf8,stroke:#0284c7,color:#18181b,stroke-width:2px
  class C,G,H,L,tools,V,Y,O node
  class T hero
  style seats fill:transparent,stroke:#757575,color:#757575

Loading

The name comes from radio. A crossband repeater receives on one band and sends on another. The app does the same between model APIs that each only understand a conversation between two parties.

Why it exists

Every chat app is you and one model. Crossband puts several in the room and lets them talk to each other, which is where the interesting disagreements happen. And because it runs at home, it can do things a hosted chat can't: hear who's speaking, keep a memory across conversations, and call a coding agent into your own repo.

Get it running

You need Python 3.12 or newer, Node 20 or newer, and one model to talk to. That's an Anthropic key, an OpenAI key, or a local model through Ollama or LM Studio, which needs no key at all.

git clone https://github.com/shawn-durrani/crossband.git
cd crossband
./start.sh

Open http://127.0.0.1:8902. The first run takes a few minutes, because start.sh creates the Python environment, installs the dependencies and builds the web app. After that it only redoes a step when something changed, and it won't start a second copy on the same port. If 8902 is taken, set CROSSBAND_PORT.

Add your keys through the setup wizard in the app, or put them in .env. The app checks each key works before it saves it, and never shows a key back to you.

Voice needs one more key. Put your ElevenLabs key in .env as ELEVENLABS_API_KEY and restart the app. Each model then speaks in its own voice. The Models page picks which ElevenLabs model does the speaking, or Automatic, which follows the newest one the app can stream live. Speech goes through the backend, so the key never reaches the browser. If you don't add a key, voice is off and everything else still works.

What you can do

Put several models in one conversation. A provider's API only knows two speakers, so the app shows each model its own past turns as the assistant and everyone else's as the user, each labelled with who said it. The models share a set of tools: web search, fetching a page, viewing a page the way a browser renders it, Reddit and YouTube, GitHub issues, and memory. Every tool result goes into the transcript, where you and every model can see it.

One round looks like this.

%%{init: {"themeVariables": {"actorBkg": "#d4d4d8", "actorBorder": "#757575", "actorTextColor": "#18181b", "noteBkgColor": "#38bdf8", "noteTextColor": "#18181b", "noteBorderColor": "#0284c7", "activationBkgColor": "#38bdf8", "activationBorderColor": "#0284c7"}}}%%
sequenceDiagram
  participant You
  participant App as Crossband
  participant Claude
  participant GPT
  You->>App: a message
  App->>Claude: the transcript, its own turns as assistant,<br/>everyone else's as user
  activate Claude
  Claude-->>App: reply
  deactivate Claude
  App->>GPT: the transcript again, with Claude's reply<br/>as a user turn
  activate GPT
  GPT-->>App: reply, or [pass]
  deactivate GPT
  Note over App: a whole reply of [pass] is removed before anyone sees it

Loading

Let a model stay quiet. If a model's whole reply is [pass], the app removes it before anyone sees or hears it, so a model with nothing to add doesn't have to invent an angle. If you talk over a model halfway through its [pass], that still counts as a pass and leaves nothing behind. A short reply that says the model has nothing to add, that it's staying quiet, or that the message wasn't for it, and then ends in [pass] counts as a pass too. Any other reply that ends in [pass] keeps its words and loses the token. A model you name can't pass. When your message has a question mark in it, the first model to answer is asked once to check it's a question for the models, and a second [pass] stands.

Talk, and hear the answers. Voice works from your phone too, once the app is on your own Tailscale network. Every voice the app has learnt has a place on the Voices page, where you can listen to the stored clips, fix a name, move a recording to the right person, or forget someone.

Have more than one person in the room. Room mode is for when more than one person is talking to the models. When someone speaks, the app compares the voice with the voices it's learnt. That check happens on the computer the app runs on, and follows each voice through the conversation, so a short reply is named from everything that voice has said. If it knows the voice, it puts that person's name on the turn, so the models know who said what. If it doesn't, it asks who's joined. When two people talk over each other, their words are split between them on your computer too. The small model that does the checking is about 38MB, downloaded once, and then works offline. Set CROSSBAND_VOICE_ID_ENABLED=false to turn it off.

Call in a coding agent. You can call Claude Code into the chat as a guest. It joins for one turn, works in its own copy of your repo (a git worktree), and by default it can only read. In run mode it can run the project's own commands, such as the tests or a harness, and report what they printed, without changing a file. If you turn on implement mode, it can branch, run the tests, push, and open a pull request. It can never merge.

Remember across conversations. On its own, Crossband forgets everything when a chat ends. With Membro running beside it, the room remembers from one conversation to the next. Memory, with Membro says how.

See what it costs. Spend is counted in three columns that are never added together: what was metered on an API key, what a subscription covered and would have cost if metered, and unknown. A model with no known price stays unpriced.

Memory, with Membro

Membro is a second app you run on the same computer, and it's the memory. It's optional: everything else works without it, and nothing is remembered.

With Membro running, Crossband hands each chat over when it ends, and Membro distils what was said into facts about you. At the start of every new chat, each model gets a short profile built from those facts, so it already knows what you told it last week, and during a chat a model can ask for more with the memory tools. When you ask about yourself or your past and the facts come up short, the app searches your saved chats too, and a voice seat says a short line while it looks. It does that only when you asked, never for a guest or the TV. The voices the app has learnt live in Membro too, so a person you introduce is remembered in both apps, and forgetting someone in one forgets them in the other. Anything a guest said, or a model read on a web page, waits in Membro's review queue for your approval before it counts. What a guest said in one chat is recalled only in that chat. A fact a model saves itself while a guest is in the room waits for review too, and once you approve it, it stays in that chat.

flowchart LR
  T["One shared transcript"]
  V("Voices the app has learnt")
  M[("Membro")]
  Q["Review queue"]
  Y(["You"])
  T -- "each chat, when it ends" --> M
  M -- "a profile at the start of each chat,<br/>and recall when a model asks" --> T
  V <-- "the same people, in both apps" --> M
  M -- "what a guest said, or a page said" --> Q
  Q -- "approve or dismiss" --> Y
  classDef node fill:#d4d4d8,stroke:#757575,color:#18181b
  classDef hero fill:#38bdf8,stroke:#0284c7,color:#18181b,stroke-width:2px
  class T,V,Q,Y node
  class M hero

Loading

Membro has its own repo, its own port and its own setup. Crossband finds it at CROSSBAND_MEMORY_URL, and the two share the token in MEMORY_AUTH_TOKEN. docs/CONFIG.md lists both.

What it isn't

Crossband is built for one household with one owner. There's one login, not accounts for many people. It isn't a hosted service, and there's no account to create with us. The accounts you may need are with the providers you choose, such as Anthropic, OpenAI or Tavily for search, and a local model needs none. It needs a model to talk to, so bring a key or run a local one. It's maintained by one person and was built for that person's own use first. Issues and pull requests are welcome, and response times vary.

Configuration

config.json holds the defaults that ship with the repo. config.local.json holds your own machine's settings and is gitignored. An environment variable starting CROSSBAND_ overrides both. Every setting is listed in docs/CONFIG.md with its default, and a test fails if one is missing from that list. The descriptions there are written by hand.

The app stopped reading the older MMC_ prefix in v0.3. An MMC_ variable whose CROSSBAND_ name is missing stops the app at startup, with the exact rename printed, so nothing changes without you knowing. See Upgrading a pre-v0.2 install.

Remote access

Out of the box, the app answers only on the computer it runs on. To use it from your phone, put it on your tailnet, which is the private network Tailscale makes between your own devices. Add your computer's tailnet name to CROSSBAND_TRUSTED_HOSTS, restart the app, then run:

tailscale serve --bg https / http://127.0.0.1:8902

That gives the app an HTTPS address that only your devices can reach. It has to be HTTPS, because a browser won't give the microphone to a plain HTTP page. There's no script for this. docs/REMOTE_ACCESS.md writes out every step.

Never open the port to the internet, and never use Tailscale Funnel. The app is built to be reached over your own tailnet and nowhere else, and it stops serving while Funnel has its port on the open internet. Anyone who can reach the port still needs the owner password or a passkey, but that's the second lock, not the first. Once you enrol a passkey, the lock screen asks for it first and keeps the password as the fallback. Read SECURITY.md before you widen anything.

Documentation

docs/README.md lists every document by what you're trying to do. The short version:

Upgrading a pre-v0.2 install

The app was called Sideband while it was being built, and v0.2 renamed the three things that still said so:

  • environment variables, from MMC_ to CROSSBAND_
  • the launchd label, from dev.sideband.server to dev.crossband.server
  • the guest diagnostics MCP server, from sideband-diag to crossband-diag

To move an install across, rename the MMC_ lines in your .env. From v0.3 the app refuses to start until you do, and prints each rename it needs. If you run the app under the supervisor, the launchd job that keeps it up, run bash ops/install-supervisor.sh again. It retires the old launchd label itself.

One old name stays. The source tag the app sends to Membro is multi-model-chat. Membro uses that tag to tell which conversation a memory came from, so renaming it would split every open chat's memory history in two, and you'd gain nothing you can see. The tag only shows up in Membro's admin facts view, like a database table name would.

Licence

MIT.

来源:GitHub · github.com