BondLens is a lightweight agent for Chinese bond market analysis. It focuses on one vertical: turn a natural-language bond question into an auditable run with live/snapshot/static data, deterministic tools, optional LLM narration, and a reviewer-facing Trust Layer.
Not a multi-agent equity research desktop. A claim-level evidence agent for Chinese bonds.
Unlike broad multi-agent equity research platforms, BondLens does not try to be a full investment desk. Its design choice is narrower and more honest: numbers come from code, the model may only narrate over evidence, and every answer can be replayed, judged, and red-teamed.
Numbers are code-calculated.
Narratives are LLM-assisted.
Every output is provenance-tracked.
Non-investment advice. For learning, research, portfolio demonstration, and interview discussion only.
Project page: https://phoenix0531-sudo.github.io/BondLens/
| Run | What you see | Open |
|---|---|---|
| Market overview | Sample yield / volume board + trust-facing pack | demo-market-overview.html |
| Single-bond report | First-bond style report with evidence body | demo-bond-report.html |
| Yield outliers | Cross-section outlier monitor pack | demo-yield-outliers.html |
| LLM final-answer matrix | Recorded CPA path (zh/en × overview/bond) | llm_matrix_cpa_gpt54.md |
Raw JSON siblings live under docs/demo_runs/.
A core design principle of BondLens (shared with platforms such as FinRobot) is the strict separation between deterministic financial computation and LLM-based narration.
| Layer | What produces it | Can invent numbers? |
|---|---|---|
| Yield / volume / percentiles / rankings | Deterministic tools (bond_agent/tools.py) |
No |
| Taxonomy / maturity buckets / peer spread | Rule-based classifiers + pure Python stats | No |
| Data lineage (live / snapshot / static) | Data resolver | No |
| Evidence ledger claims | Built from tool outputs | No |
| Final narrative text | Deterministic report, or LLM only if guardrail + judge pass | Text only; numbers must match evidence |
In short: tools compute, models narrate, trust decides.
| Layer | What it includes |
|---|---|
| Agent core | Single path: Planner → Tools → Evidence → Report (not a multi-role equity desk) |
| Deterministic tools | 7 public operators: search_bonds, describe_market, rank_bonds, detect_yield_outliers, compare_bond_to_market, build_market_monitor, generate_bond_report |
| Trust layer | Numeric + language guardrail · answer judge · Trust score · Evidence Pack · replay store · risk profile |
| Evals | ~110 pytest cases · agent evals 10/10 · red-team 3/3 · Docker /healthz in CI |
| Data | AkShare live → cached snapshot → static Excel, with explicit lineage and maturity coverage board |
| Product surface | Flask + Jinja · zh-default / en switch (query+cookie) · SSE soft-render · CI + GitHub Pages |
| Scale (honest) | ~10k lines of project Python across bond_agent/ + app/tests/evals — focused vertical, not a 100k+ multi-agent platform |
BondLens turns a natural-language bond question into an auditable analysis run:
- Resolve data (live AkShare → cached snapshot → local Excel)
- Plan intent (overview / search / ranking / outliers / monitor / composite / bond report)
- Run deterministic tools
- Build structured evidence (market, peer, monitor, quality, maturity coverage)
- Compose a report with risk notes and mandatory limitations
- Optionally polish with an LLM under numeric + language guardrails
- Score trust, export a Bond Evidence Pack, and store a replay summary
- Answer Snapshot: 3-sentence headline + key metrics; full body collapsed by default
- SSE stream + soft final render: tool-step progress, token preview, final summary card without forced full-page reload; share/full board still via
result_url - Bilingual UI (zh default): query/cookie language memory, explicit zh/en switch, bilingual provenance lines
- Bond type mix + maturity buckets: conservative name-rule taxonomy (no rating inference)
- Peer comparison: same type + maturity bucket spread vs peers
- Cross-section monitor board: high yield / low volume / yield outliers / missing maturity
- Maturity / residual board: live coverage, cashflow teaching duration/DV01, perpetual dual scenarios (first finite leg + theoretical consol)
- Trust score + stress view + audit folds: guardrail / judge / risk / ledger behind details
Data Ops live / snapshot / static sample + lineage + maturity enrichment
Agent Core Planner → Tools → Evidence → Report
Trust Layer Guardrail + Judge + Risk Profile + Trust Score + Replay + Evals
flowchart TD
A[User Question] --> B[Data Source Resolver]
B --> C[Planner multi-intent]
C --> D[Deterministic Tools]
D --> E[Structured Evidence]
E --> F[Report + Limitations]
F --> G{Optional LLM}
G -->|guardrail pass| H[Narrated answer]
G -->|fail or disabled| I[Deterministic answer]
H --> J[Judge + Trust + Pack + Replay]
I --> J
Inspired by deterministic compute, LLM narration research platforms, BondLens specializes the idea for Chinese bonds with claim-level evidence, answer judging, red-team evals, and reviewer-facing evidence packs — not a multi-role equity research desktop.
Captured on the current live agent page (BOND_DATA_MODE=auto, no API key → deterministic final answers).
Narrative: can answer · can go deep · will refuse · can rank · can switch languages · can audit.
How to read the shots:
- Trust is process/evidence confidence, not trade confidence.
- Advisory is a policy block (no LLM), not a disclaimer-only soft refuse.
- Numbers come from tools; if the model fails guardrail, the final answer is deterministic.
- Language is controlled by the single header
中 / ENswitch with query/cookie memory.
Current product shots live in docs/screenshots/current/; the root screenshots folder now only keeps the GitHub social preview asset.
Open a pre-generated Example Run in the browser — no server, no API key:
docs/demo_runs/demo-market-overview.html
Or jump from the Example Runs table above.
pip install -r requirements.txt
./scripts/run_demo.sh
# open http://127.0.0.1:8765/agent
# try: 当前样本收益率分布是什么样?Windows users can run the same deterministic demo with:
scripts\run_demo.batOther packs:
export FLASK_RUN_HOST=127.0.0.1
export PORT=8765
export BOND_DATA_MODE=auto # live first, then snapshot, then static
python app.py
# force live: BOND_DATA_MODE=live
# watch data_source.runtime_mode, maturity board, and trust score when live degradesOptional LLM polish (never required):
export OPENAI_API_KEY=... OPENAI_BASE_URL=http://127.0.0.1:18317/v1 # example: local CPA/OpenAI-compatible gateway
export OPENAI_MODEL=haochi/gpt-5.4
export OPENAI_API_STYLE=chat
export OPENAI_MODEL_FALLBACKS=haochi/gpt-5.4-mini,gongyi/deepseek-v4-flash-search # optional
# Keys stay in process env only. Do not commit secrets.BondLens is Docker-packaged, but the portfolio demo does not require Docker.
The Compose service is intentionally named bondlens, with container name bondlens-demo, image bondlens:local, and host port 8765 mapped to container port 5000.
docker compose up --build
# open http://localhost:8765/agent
# health: http://localhost:8765/healthz- Default UI language: Chinese
- Explicit switch in one place: the page header (
中 / EN) - Persistence:
?lang=zh|enquery wins, thenbondlens_langcookie, elsezh(localStorage mirrors client-side) - Covers templates, intent/tool labels, deterministic report skeleton, advisory refusal, and flash/error copy
- LLM system prompts follow the active language; logs stay developer-facing and are not fully bilingual
| Tool | Inputs | Deterministic outputs |
|---|---|---|
search_bonds |
name / type / maturity / yield filters | match_count, records |
describe_market |
active frame | yield/volume summaries, segments, data quality |
rank_bonds |
by, top_n, order | ranked records |
detect_yield_outliers |
method, threshold | outlier_count, scores |
compare_bond_to_market |
bond / record | percentiles, peer comparison |
build_market_monitor |
top_n | high-yield / low-volume / outliers / missing maturity |
generate_bond_report |
tool outputs + plan | analysis, risk notes, limitations |
Numbers in the final answer must come from these tools (or be rejected by the guardrail).
Each answer includes trust_score (0–100) built from evidence quality, data freshness/degradation,
ledger coverage, guardrail outcome, judge outcome, and a forced non-advisory penalty.
Every run can export a portable Bond Evidence Pack (JSON + static HTML):
- question / intent / tools
- data lineage + maturity coverage
- trust score + adjustments
- guardrail + judge + risk profile
- evidence ledger + final answer
- mandatory limitations
python scripts/generate_demo_packs.pyRuntime packs: .tmp/evidence_packs/
Committed demos: docs/demo_runs/
Live/snapshot feeds have no native maturity field. BondLens enriches matched names from the local security master and exposes:
GET/POST /api/maturity/unmatched?format=csv&data_mode=static
GET/POST /api/maturity/unmatched?format=json&data_mode=static
The UI maturity board links to the same export for unmatched bond names.
当前样本收益率分布是什么样?
搜索23附息国债26并给出收益率分析
打开今日市场监控面板:高收益、低成交与异常
按收益率列出最高的前5只债券
有没有收益率异常的债券?
筛选国债收益率大于 2.5 的债券
今天该不该买债? # advisory policy block; no LLM
POST /api/agent/query
Content-Type: application/json
{
"question": "搜索23附息国债26并给出收益率分析",
"data_mode": "auto"
}Streaming (SSE):
POST /api/agent/stream
Content-Type: application/json
{
"question": "当前债券市场样本概览如何?",
"data_mode": "static"
}Events include status (tool steps), token (partial text), and final (soft-render view + result_url).
Operational endpoints:
GET /healthz
GET /api/agent/schema
GET /replay
GET /packs/<pack_id>.html
GET /packs/<pack_id>.json
GET /api/maturity/unmatched
Deployment notes: docs/deployment.md
Primary: ChinaMoney / AkShare-style spot deal fetch (direct preferred)
Snapshot: .tmp/bond_spot_deal_snapshot.csv
Final fallback: data/testdata.xlsx
- Live fields used include bond name, clean price, yield, BP change, weighted yield, volume, and native residual maturity when present (
termToMaturity) - Residual maturity may still be incomplete → coverage board + trust penalty on weak coverage
- Cashflow duration / DV01 are teaching-level level-coupon estimates, not OAS / full call-tree valuation
- Perpetual-style residuals expose dual scenarios (first finite leg + theoretical perpetual), not a multi-century fake tenor
- No issuer ratings, financial statements, guarantees, or credit events
- Yield is a risk signal, not a trade instruction
Modes:
auto -> live first, cached snapshot second, local fallback third
live -> live source requested; fallback reason shown if it degrades
static -> local Excel only
- Data resolver chooses live / snapshot / static with honest lineage
- Planner classifies multi-intent and selects tools
- Tools run pure Python analytics over the active frame
- Evidence is structured and ledger-backed
- Report is composed from evidence with risks and limitations
- Optional LLM may narrate only after local evidence exists
- Guardrail + judge accept or reject model text
- Trust score + Evidence Pack + replay make the run reviewable without dumping raw JSON
If OPENAI_API_KEY is not set, the project still runs with deterministic fallback output.
Recorded against local CPA/OpenAI-compatible gateway (http://127.0.0.1:18317/v1) with model
haochi/gpt-5.4, BOND_DATA_MODE=static, OPENAI_API_STYLE=chat, and fallback candidates
haochi/gpt-5.4-mini,gongyi/deepseek-v4-flash-search.
Stable first bond for report questions: 06国开24 (bond-name ascending, mergesort).
Full table: docs/demo_runs/llm_matrix_cpa_gpt54.md.
| Scenario | Lang | Threshold | Result | Notes |
|---|---|---|---|---|
| overview | zh | 3/3 final LLM | 3/3 | haochi/gpt-5.4, guardrail passed |
| bond report | zh | 3/3 final LLM | 3/3 | stable first bond 06国开24 |
| overview | en | >=2/3 | 3/3 | stronger than the threshold |
| bond report | en | >=2/3 | 3/3 | stronger than the threshold |
| advisory block | zh/en | never final LLM | 2/2 blocked | deterministic policy refusal, no LLM final |
Honest residuals:
- Provider channel churn can still force deterministic fallback when models vanish.
- Guardrails stay on; unsupported numbers never become final.
- The product still works without an API key through deterministic reports.
- This is evidence of a working path, not a zero-bug claim.
This project started as a 2024 undergraduate thesis: a Flask-based bond data analysis system. The original thesis version is preserved and should not be rewritten:
- Original thesis branch:
undergraduate-thesis-2024 - Current branch:
main
MIT
BondLens is an engineering and research demonstration. It does not provide investment advice, does not claim complete market coverage, and does not replace professional fixed-income research tools.









