Bambon Negotiator
Bambon (Bayesian Adaptive Mixed-type Bilateral ODRL-policy Negotiator) is a learning-based negotiator for negotiating constraints over one or multiple actions (e.g., use, read, share, transfer) expressed in an ODRL-style policy format.
Bambon observes opponent offers, updates Bayesian beliefs about opponent preferences (numeric / enum / set issues), and generates counter-offers until:
- agreement is reached (
odrl:Agreement), or - the negotiation becomes infeasible (
NegotiationInfeasible)
Features
- ✅ Negotiates constraints per action (single-action or multi-action offers)
- ✅ Supports mixed-type issues:
- numeric constraints
- categorical/enum constraints
- set-valued constraints
- ✅ Bayesian belief models to learn opponent preferences over time
- ✅ Hard policy enforcement (non-negotiable constraints)
- ✅ Context similarity & severity scoring
- ✅ Hopelessness detection (fails fast if negotiation diverges)
Repository Structure
src/bambon/
negotiator.py Bambon: observe / counter-offer / agreement logic
distributions/
belief.py BeliefRegistry, one belief model per action and issue
distributions.py numeric Beta, categorical Dirichlet, set Beta-Bernoulli
models/
models.py core dataclasses and NegotiationInfeasible
expressions.py expression helpers for constraints
utils/
helpers.py constraint parsing and operator helpers
context.py similarity and severity scoring
utility.py the ODRL utility model
baselines/veto.py veto-and-repair shim, usable around any strategy
negmas_negotiator.py ODRLOutcomeSpace and the NegMAS adapter
tests/ unit tests, and the scoring code the benchmark shares
data/50scenarios/ 50 hand-authored scenarios across 10 sectors
data/generated/ the five generated corpora the tournament runs on
data/generator/ the seeded generator and its feasibility oracle
examples/ edc-dataspace-demo: the end-to-end EDC connector demo
paper/ the ODRL 2026 paper and its sweeps
paper_www2027/ the WWW 2027 paper
odrl-tournament/ the benchmark harness, as a submodule
archive/ superseded runs and old baseline code; safe to delete
Offer Format (ODRL-style)
Bambon expects offers in an ODRL-like JSON format:
{
"@id": "urn:uid:example",
"@type": "odrl:Offer",
"permission": [
{
"action": "use",
"constraint": [
{
"leftOperand": "odrl:purpose",
"operator": "odrl:eq",
"rightOperand": "research"
}
]
}
]
}
Constraints are normalized internally (e.g., odrl: prefixes stripped during parsing).
Quickstart
1) Create a negotiator
from negotiator import Bambon
neg = Bambon(
name="Provider",
init_offer=init_offer, # required
hard_policy=hard_policy # optional
)
Each action in init_offer["permission"] gets its own:
- belief registry
- negotiation state
2) Observe an opponent offer and generate a response
signals, response_offer = neg.observe(opponent_offer)
print("Signals:", signals)
print("Response:", response_offer)
signalsisNonewhile negotiatingsignalsbecomes non-null when an action is accepted and agreement is formedresponse_offerwill be either:- a counter-offer (
odrl:Offer), or - an agreement (
odrl:Agreement)
- a counter-offer (
Running
Run tests (recommended)
pytest -q
or:
python -m unittest -v
Example Minimal Negotiation Loop
from negotiator import Bambon
provider = Bambon(name="Provider", init_offer=provider_init, hard_policy=provider_hard)
consumer = Bambon(name="Consumer", init_offer=consumer_init)
offer = consumer_init
for round_i in range(50):
sig_p, offer = provider.observe(offer)
if offer.get("@type") == "odrl:Agreement":
print("Agreement reached (provider -> consumer)")
break
sig_c, offer = consumer.observe(offer)
if offer.get("@type") == "odrl:Agreement":
print("Agreement reached (consumer -> provider)")
break
else:
print("No agreement reached within max rounds")
Key Negotiation Parameters
Bambon behavior is governed by tunable controls, including:
lr— learning rate for belief updatesinertia— resistance to changing past proposalstau_in,tau_out— thresholds controlling adaptation vs acceptancemax_set_k— maximum size for proposed setsjaccard_snap_threshold— snaps set proposals when overlap is highno_agreement_until— prevents early agreement before minimum roundsmax_rounds— maximum rounds before infeasibilityhopeless_window,hopeless_patience— detects non-converging negotiation
Negotiation terminates with NegotiationInfeasible when:
- maximum number of rounds is exceeded, or
- persistent low context and high severity indicate divergence
This enables early stopping in non-converging negotiations.
Negotiation Semantics
Bambon operates under incomplete and asymmetric information, modeling opponent preferences through Bayesian belief updates.
Each issue is internally represented in a computational domain (numeric, categorical, or set-based), enabling:
- probabilistic acceptance decisions using Monte Carlo sampling
- operator-aware constraint compatibility checks
- adaptive proposal generation based on learned beliefs
Acceptance is not deterministic:
- numeric issues use credible intervals and probabilistic consistency
- categorical issues use Dirichlet-based argmax likelihood
- set issues use Beta-Bernoulli inclusion probabilities
Global negotiation signals:
- context score (overall similarity of offers)
- severity (degree of disagreement)
These signals influence acceptance thresholds dynamically.
Multi-Action Negotiation
Each action (e.g., use, read, share) is negotiated independently:
- separate belief models per action
- separate convergence tracking
- combined into a single ODRL offer
An agreement is reached when all issues for at least one action are accepted.
Output Types
Bambon produces ODRL-like structures:
- Counter-offer:
@type = "odrl:Offer" - Agreement:
@type = "odrl:Agreement"with constraints filled using accepted values
Acknowledgments
This work has been supported by the Horizon Europe research and innovation programme under the project CLIMRES (Grant Agreement No. 101147777).
The content of this paper reflects only the authors’ views, and the European Commission is not responsible for any use that may be made of the information it contains.
Experiments (ODRL 2026 Paper)
This repository accompanies the paper:
“Constrained Adaptive Negotiation Agent of ODRL Offers Under Incomplete and Asymmetric Information”
2nd ODRL and Beyond: Practical Applications and Challenges for Policy-Based Access and Usage Control
May 10–11, 2026, Dubrovnik, Croatia
Running Experiments
To reproduce the experimental results:
- Run negotiation simulations:
python test_negotiator.py
- Then open and execute the notebook:
results_analysis.ipynbThe notebook performs the analysis and visualization of the negotiation outcomes.
Experiments (WWW 2027 Paper)
What the benchmark does
odrl-tournament/ plays the negotiation strategies published in the literature
against Bambon on ODRL documents, and scores every encounter on the question the
usual benchmarks cannot ask: may each party lawfully perform what it just
signed?
Each scenario supplies four ODRL documents: both parties' opening offers and both
parties' hard policies. The negotiable space is built from the two opening offers
alone, one issue per (rule type, action, leftOperand) triple, so nothing is
taken from either hard policy. Each agent's utility function is built from its
own aspiration and its own hard policy, and within a round an agent sees only the
incoming offer. Information is asymmetric and incomplete on both sides.
The published agents are given the hard policy the way the literature gives a reservation value: a scalar floor, deliberately not a gate. Were it a gate they would be compliant by construction, since that gate is the mechanism under test, and the comparison would be circular. This is what the benchmark measures: a scalar floor cannot express a per-issue constraint, so an outcome that breaches one hard bound while scoring well on the other fifteen clears the floor and gets signed.
Eight NegMAS strategies run unmodified: Boulware, Conceder and Linear
(Faratin, Sierra & Jennings 1998), NaiveTFT, CAB, WAB, MiCRO (de Jonge,
IJCAI 2022) and Tough. NiceTFT and Hybrid are excluded because they
enumerate the outcome space to invert their utility function, and an ODRL
scenario here has between 10^11 and 10^16 outcomes, so they are killed before
their first bid. That exclusion is a finding, not a convenience.
Metrics
| column | definition |
|---|---|
agree |
fraction of encounters ending in a signed contract |
compl.|agr |
of the contracts it signed, the fraction lawful for it |
compl. all |
of all encounters, the fraction not ending in a breach |
lawful |
agree x compl.|agr: contracts both reached and lawful |
Nash|agr |
product of the two parties' utilities, agreements only |
Nash |
the same, scoring a failed encounter 0 |
fairness |
1 - |u_provider - u_consumer| on signed contracts |
Jain |
Jain index of the two utilities on signed contracts |
u |
own utility, 0 for a failed encounter |
u^c |
own utility, also 0 for an unlawful agreement |
turns |
individual agent moves used before termination |
Three conventions worth stating, because each is a choice:
- Lawful deals closed is the headline, not compliance. Compliance alone is
trivially maximised by never agreeing (
Toughscores 0.716 on it). The product rewards only contracts that were both reached and lawful. - The Nash product is unconditional. A failed encounter scores 0 rather than being dropped, because conditioning on agreement flatters an arm that closes only the easy ones.
- The unit of replication is the scenario, not the encounter. Metrics are averaged within a scenario first, and significance is a 20,000-sample paired sign-flip permutation test over scenarios.
Compliance is judged by one predicate, count_hard_violations, shared with the
agent's own test suite rather than reimplemented, so a guard can never be looser
than the metric that grades it.
The corpora
gap is how far apart the two parties open, ZOPA the width of the lawful
window as a share of that gap, and split how far each party must concede to
reach the middle of that window.
| corpus | scenarios | gap | ZOPA/gap | split | what it isolates |
|---|---|---|---|---|---|
far_wide |
120 | 0.80 | 0.39 | 50/50 | the baseline geometry |
far_narrow |
120 | 0.80 | 0.11 | 50/50 | a 3.6x narrower target |
close_narrow |
120 | 0.20 | 0.11 | 50/50 | the same target, openings 4x closer |
far_narrow_asym |
120 | 0.80 | 0.11 | 20/80 | one party concedes 4x as far |
full_infeasible |
60 | 0.80 | empty | - | no lawful contract exists |
Every scenario ships a witness contract that a feasibility oracle proves lawful for both parties, and any scenario the oracle cannot certify is discarded rather than emitted. An agent that fails to close a lawful contract cannot blame the corpus.
Running the tournament
cd odrl-tournament
pip install -r requirements.txt # negmas 0.16, pandas, numpy, pyyaml, matplotlib
python -m odrl_tournament metrics # what is measured
python -m odrl_tournament geometry far_wide far_narrow_asym # corpus geometry
python -m odrl_tournament run --config configs/quick.yaml # smoke test, ~1 min
The full run, one corpus at a time:
for c in far_wide far_narrow close_narrow far_narrow_asym full_infeasible; do
python -m odrl_tournament run --config configs/www2027.yaml --corpus "$c"
done
python -m odrl_tournament report --results 'results/*.csv' --figures figures/
python -m odrl_tournament asymmetry --results results/far_narrow_asym.csv
Each feasible corpus is 120 scenarios x 81 ordered pairings = 9,720 encounters and takes roughly 100 CPU-minutes. Shard it by scenario range and concatenate:
for i in 0 1 2 3; do
python -m odrl_tournament run --config configs/www2027.yaml --corpus far_wide \
--lo $((i*30)) --hi $(((i+1)*30)) --out results/far_wide_$i.csv &
done; wait
Progress is checkpointed per scenario, so an interrupted run costs at most the
scenario in flight. Everything is configured from one YAML file
(configs/www2027.yaml is the published configuration); see
odrl-tournament/README.md for every option, how to
build new corpora with your own geometry, and how to add an agent.
The five published result sets ship inside the submodule, so the tables and figures rebuild without rerunning anything:
python -m odrl_tournament report --results 'results/published/*.csv.gz' \
--figures figures/
Headline result
Across 43,740 encounters on five corpora against eight published strategies, in both roles:
| Bambon | published agents | |
|---|---|---|
| compliance among signed contracts | 1.000 | 0.000 - 0.118 |
lawful deals closed (far_wide) |
0.349 | 0.221 best |
| unlawful contracts signed where none is lawful | 0 of 4,860 | up to 0.860 |
The negative result carries equal weight. Set the scalar reservation faithfully, at the worst deal lawful on every issue at once, and the negotiation becomes rationally infeasible in 83-98% of scenarios: the two floors demand more utility than the domain contains. Set it low enough to permit agreement and the agent signs contracts it cannot perform. There is no scalar in between, which is a statement about the representation rather than about any particular agent.
Metadata
Release files for bambon 2.0.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| bambon-2.0.5.tar.gz | 102.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bambon-2.0.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 211.6 kB
Release files / bambon-2.0.5.tar.gz
| Download URL | bambon-2.0.5.tar.gz |
|---|---|
| Size | 102.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83d19400375957a7db5c6e9772cb1987d9830ae51126934ed6f2e37717c2cf43
|
|
BLAKE2b-256 checksum How to use checksums |
ef9e62795210875e552b4cdf8f25988272ebafa19c016b60c15ec6a929cec4e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|
Release files / bambon-2.0.5-py3-none-any.whl
| Download URL | bambon-2.0.5-py3-none-any.whl |
|---|---|
| Size | 109.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
864c6bd806d429c5c82ca4411cf126b35aba0f8d9d66a2def53f28ab1524834d
|
|
BLAKE2b-256 checksum How to use checksums |
61454cc7e9c1dd7f2ebf2bbcb8744e6f73ba8135841ffc9f96279e57c7e4500f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|
Security for bambon 2.0.5
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.