When Does a Central Bank Sound "Resolute", and When "Cautious"?
A central bank seems to say the same two things at every meeting: a rate decision and a few paragraphs of reasoning. But the tone of those paragraphs shifts from meeting to meeting — sometimes "a resolute stance," sometimes "a cautious approach," sometimes "risks will be closely monitored." Journalists, economists, and market participants all read and interpret that tone. Nobody measures it systematically.
So we took 14 meetings — 28 statements — from Turkey's central bank policy committee's 2025-2026 cycle and ran them through HuMetric end-to-end, in real production. This post is the developer log of that pilot: how we connected, who wrote the pack, how the signals went in, what came out — all with real requests and real JSON.
We didn't write the pack. An agent did
Defining seven metrics (belirsizlik [uncertainty], temkinlilik
[caution], kararlilik [resolve], sikilastirma_egilimi [tightening
bias], risk_vurgusu [risk emphasis], dis_etken_atfi [external
attribution], somutluk [concreteness]), each with its own prompt, a
direction (higher_is_better / lower_is_better), and band thresholds
— we didn't do that by hand. We gave HuMetric's Pack Wizard a single
natural-language request, and Gentic (a research layer the agent can
call on its own judgment) handled the rest.
First, a session on the site side:
curl -s -X POST https://gethumetric.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"...","password":"..."}'
# → {"token": "...", "user": {...}}
Then we kicked off the wizard session with one sentence:
curl -s -X POST https://gethumetric.com/api/wizard/sessions \
-H "Authorization: Bearer $SITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "A pack that measures uncertainty/caution/resolve/tightening bias/risk emphasis/external attribution/concreteness from central bank meeting statements; entity = one policy meeting",
"entity_type_hint": "ppk_toplantisi",
"lang": "en"
}'
# → {"session_id": "b9257ec7-9629-4387-b551-8cefd936e2c8"}
The session runs asynchronously in the background, so we polled for the result (a live stream is also available, but polling is simpler in curl):
curl -s https://gethumetric.com/api/wizard/sessions/$SID \
-H "Authorization: Bearer $SITE_TOKEN"
# status: queued → running → completed
After 3 turns (turn_count: 3) the session hit completed. Along the
way, Gentic called a web-research tool once on its own judgment
(gentic_tools: {"search_web": {"count": 1, "cost_cents": 5}}) — without
us triggering anything, it researched something like "how is uncertainty
and tightening language expressed in central bank statements." It never
asked a clarifying question (pending_question: null), and handed us a
publish-ready pack with a self-review score attached (confidence: 0.85):
A slice of the resulting pack (the full file is at the end of this post):
entity_type: ppk_toplantisi
metrics:
- key: sikilastirma_egilimi
label: Tightening Bias
type: float
direction: higher_is_better
prompt: >
Score the hawkish/tightening bias in this meeting statement from
-1 to 1. Weigh the frequency and strength of terms like
"tightening," "tight monetary policy," "rate hike" ...
bands:
- {max: -0.33, level: critical}
- {max: 0.33, level: warn}
- {max: 1, level: good}
So what did Gentic actually do? It's not a black box — the wizard's review step shows exactly which tool the agent called, what it found, and how it critiqued its own output:
Gentic has more than one tool available — a memory/context tool, a wiki
lookup tool, and a web-research tool (search_web). This session called
only search_web (category: research), which returned 5 results —
titles like "Central Bank Policy Committee Decisions," "Analysis of
Central Bank Policy Statements," "Experts Assess the Rate Decision,"
each with an openable source link. That tool choice was the agent's own
— we didn't decide which tool was needed.
Below that is a list of the agent critiquing the pack it just wrote
— for example: "band thresholds (−0.33 / 0.33) aren't grounded in
sample data, may not reflect the actual distribution" (flagged
medium). That's not a rubber stamp; it's a self-aware assessment — and
the finding is genuinely accurate: we gave it one sentence, no sample
data. The model is visible too: deepseek:deepseek-chat — whatever
provider the tenant has bound, the wizard's agent runs on it.
Connecting to production — two ways
We ran this pilot straight from the API, not the dashboard — which is why most of this post shows the actual requests rather than screenshots.
Way 1 — plain curl. Log in for a token, then point the tenant's
LLM provider at DeepSeek (bring-your-own-key — BYOK):
curl -s -X POST https://api.gethumetric.com/v1/login \
-H "Content-Type: application/json" \
-d '{"email":"...","password":"..."}'
# → {"dashboard_token": "...", "tenant_id": 2, ...}
curl -s -X PUT https://api.gethumetric.com/v1/tenant/keys \
-H "Authorization: Bearer $DASH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"deepseek_key":"sk-...","llm_provider":"deepseek"}'
# → {"has_deepseek_key": true, "llm_provider": "deepseek", ...}
From here, the API key that actual requests carry is generated separately (different from the login token — this is the long-lived key the signal/entity endpoints need):
curl -s -X POST https://api.gethumetric.com/v1/tenant/rotate-api-key \
-H "Authorization: Bearer $DASH_TOKEN"
# → {"api_key": "hm_live_...", "api_key_prefix": "hm_live", ...}
Way 2 — MCP. We also wanted a chat assistant (Claude Code included, or any MCP-speaking client) to do the same work with direct tool calls. Setup is one command:
claude mcp add humetric --scope local \
--env HUMETRIC_MCP_API_KEY=hm_live_... \
--env HUMETRIC_BASE_URL=https://api.gethumetric.com \
-- uvx --from git+https://github.com/bestekarx/humetric.git humetric-mcp
Once connected, the assistant sees tools for sending a signal
(humetric_ingest_signal), reading an entity's metrics
(humetric_get_entity_metrics), asking why a value is what it is
(humetric_explain_metric), reading change over time
(humetric_metric_history), and ranking entities by a metric
(humetric_query_entities) — no more copy-pasting between the dashboard
and the chat window.
A real operational note: the MCP client only reads its connection environment variables at session start. So changing the API key or the base URL doesn't reach an already-running session — it needs a fresh one. We actually hit this: after switching to production, we noticed for a while that we were still talking to the old (local) connection, because the MCP subprocess still had the old settings in memory. Small but real — the kind of thing anyone who's changed a config and asked "why am I still seeing the old one" will recognize.
One meeting, end to end
A meeting is created as an entity (with the fields the pack requires:
toplanti_id, tarih, karar_no, metin), then the decision text goes
in as a signal:
curl -s -X POST https://api.gethumetric.com/v1/entities \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{
"id": "ppk-2025-15",
"entity_type": "ppk_toplantisi",
"fields": {
"toplanti_id": "2025-15", "tarih": "6 Mart 2025",
"karar_no": "2025-15", "metin": "..."
}
}'
curl -s -X POST https://api.gethumetric.com/v1/signals \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{
"entity_id": "ppk-2025-15",
"entity_type": "ppk_toplantisi",
"text": "<full decision text>",
"external_id": "2025-15-karar",
"occurred_at": "2025-03-06T00:00:00Z"
}'
# → 202 {"signal_id": "450c7428-..."}
occurred_at matters here: it's the meeting's real date, not the
moment the signal was sent. HuMetric's confidence decay (computed at
read time, exponential with age) is anchored to this date — essential
for backfilling 14 historical meetings without the send order affecting
the outcome.
Once processed, the result can be read at three depths. The value:
curl -s https://api.gethumetric.com/v1/entities/ppk-2025-15/metrics \
-H "Authorization: Bearer $API_KEY"
{
"entity_id": "ppk-2025-15",
"metrics": [
{"metric_key": "kararlilik", "value": 0.8, "confidence": 0.85, "source_count": 2},
{"metric_key": "sikilastirma_egilimi", "value": 0.61, "confidence": 0.75, "source_count": 2}
]
}
The answer to "why is this the value" lives at /explain — the exact
sentence:
curl -s https://api.gethumetric.com/v1/entities/ppk-2025-15/metrics/kararlilik/explain \
-H "Authorization: Bearer $API_KEY"
{
"metric_key": "kararlilik", "value": 0.8, "confidence": 0.85,
"extracted": [{
"value": 0.8,
"reasoning": "The text uses strong resolve language: 'decisive stance,' 'with resolve,' 'will be maintained.'",
"source_span": "The decisive monetary policy stance is reinforcing the disinflation process through domestic demand rebalancing, real appreciation of the Turkish lira, and improvement in inflation expectations."
}]
}
That source_span isn't a summary the model made up — it's a
verbatim substring of the signal text. The engine verifies it; if it
doesn't match, the metric isn't dropped, it's held for human review. So
the answer to "why this score" is always a sentence you can copy and
paste.
And /history for change over time — every point anchored to the real
meeting date:
curl -s https://api.gethumetric.com/v1/entities/ppk-2025-15/metrics/sikilastirma_egilimi/history \
-H "Authorization: Bearer $API_KEY"
The actual finding: tone hardens as the rate falls
After processing all 14 meetings, the table boils down to one sentence: over 18 months the policy rate fell from 47.5% to 37% (with a tightening detour against market volatility along the way), and the tightening emphasis in the committee's language never eased.
At the first meeting (Jan 23, 2025, 47.5%) sikilastirma_egilimi was
+0.65; at the last (Jul 23, 2026, 37%) it was +0.80. There are
dips in between (down to +0.45 at one meeting) — not a straight line,
but the direction holds: even while cutting, the committee never drops
its "will tighten if needed" language. Over the same period,
dis_etken_atfi climbs noticeably going into 2026 (0.60 to 0.90) — the
minutes attribute more and more to global developments. somutluk was
already high from the start (0.85-0.90 throughout): the statements have
been dense with figures and dates all along, not something that builds
up over the period.
The evidence is now one link away
The full metric profile for the June 19, 2025 meeting — all 7 metrics, each with its evidence sentence, no account needed:
gethumetric.com/share/type/…/e/ppk-2025-37
Appendix: the full pack
entity_type: ppk_toplantisi
label: TCMB PPK Toplantı Metni Ton Analizi Paketi
version: 1
required_fields:
- key: toplanti_id
type: str
label: Toplantı Kimliği
- key: tarih
type: str
label: Toplantı Tarihi
- key: karar_no
type: str
label: Karar Numarası
- key: metin
type: str
label: PPK Metni
metrics:
- key: belirsizlik
label: Belirsizlik Vurgusu
type: float
prompt: 'PPK toplantı metnindeki belirsizlik vurgusunu -1 ile 1 arasında puanla. Metinde ''belirsizlik'', ''öngörülemez'', ''belirsiz'', ''net değil'', ''önemli ölçüde belirsiz'', ''riskler'', ''gelişmeler yakından izlenecek'' gibi ifadelerin sıklığını ve gücünü değerlendir. -1: metin hiç belirsizlik ifadesi içermiyor, kesin ve net bir dil kullanılıyor. 0: metin orta düzeyde belirsizlik ifadesi içeriyor, bazı koşullu ifadeler var. 1: metin yoğun belirsizlik vurgusu yapıyor, geleceğe dair çok sayıda koşullu ve belirsiz ifade içeriyor.'
direction: lower_is_better
unit: ''
bands:
- max: -0.33
level: good
- max: 0.33
level: warn
- max: 1
level: critical
default_confidence: 0.8
sensitive: false
- key: temkinlilik
label: Temkinlilik Dili
type: float
prompt: 'PPK toplantı metnindeki temkinlilik/ihtiyatlılık dilini -1 ile 1 arasında puanla. Metinde ''temkinli'', ''ihtiyatlı'', ''dikkatli'', ''tedbirli'', ''yakından izlenecek'', ''gerekirse'', ''ihtiyaç duyulması halinde'' gibi ihtiyatlı ifadelerin sıklığını ve gücünü değerlendir. -1: metin hiç temkinli ifade içermiyor, doğrudan ve iddialı bir dil kullanılıyor. 0: metin orta düzeyde temkinli ifade içeriyor. 1: metin aşırı temkinli, neredeyse her cümlede ihtiyat ve koşul ifadesi var.'
direction: lower_is_better
unit: ''
bands:
- max: -0.33
level: good
- max: 0.33
level: warn
- max: 1
level: critical
default_confidence: 0.8
sensitive: false
- key: kararlilik
label: Kararlılık Dili
type: float
prompt: 'PPK toplantı metnindeki kararlılık vurgusunu -1 ile 1 arasında puanla. Metinde ''kararlılıkla'', ''kararlı'', ''kesin'', ''azimle'', ''sürdürülecek'', ''devam edilecek'', ''taahhüt'', ''kararlı adımlar'' gibi kararlılık ifadelerinin sıklığını ve gücünü değerlendir. -1: metin hiç kararlılık ifadesi içermiyor, tereddütlü ve zayıf bir dil kullanılıyor. 0: metin orta düzeyde kararlılık ifadesi içeriyor. 1: metin güçlü kararlılık vurgusu yapıyor, net taahhütler ve kesin ifadeler içeriyor.'
direction: higher_is_better
unit: ''
bands:
- max: -0.33
level: critical
- max: 0.33
level: warn
- max: 1
level: good
default_confidence: 0.8
sensitive: false
- key: sikilastirma_egilimi
label: Sıkılaştırma Eğilimi
type: float
prompt: 'PPK toplantı metnindeki parasal sıkılaştırma (şahin) eğilimini -1 ile 1 arasında puanla. Metinde ''sıkılaştırma'', ''sıkı para politikası'', ''faiz artırımı'', ''sıkılaştırıcı'', ''ek sıkılaştırma'', ''şahin'', ''dezenflasyon'' gibi sıkılaştırma yönlü ifadelerin sıklığını ve gücünü değerlendir. -1: metin genişleme/gevşeme yönlü ifadeler içeriyor, sıkılaştırmadan hiç bahsedilmiyor. 0: metin nötr, sıkılaştırma veya gevşeme yönünde net sinyal vermiyor. 1: metin güçlü sıkılaştırma eğilimi gösteriyor, ek sıkılaştırma sinyalleri veriyor.'
direction: higher_is_better
unit: ''
bands:
- max: -0.33
level: critical
- max: 0.33
level: warn
- max: 1
level: good
default_confidence: 0.8
sensitive: false
- key: risk_vurgusu
label: Risk Vurgusu
type: float
prompt: 'PPK toplantı metnindeki risk vurgusunu -1 ile 1 arasında puanla. Metinde ''risk'', ''riskler'', ''aşağı yönlü risk'', ''yukarı yönlü risk'', ''tehdit'', ''olumsuz gelişme'', ''bozulma'' gibi risk ifadelerinin sıklığını ve gücünü değerlendir. -1: metin hiç risk ifadesi içermiyor, olumlu ve güvenli bir tablo çiziyor. 0: metin orta düzeyde risk ifadesi içeriyor, dengeli bir risk değerlendirmesi yapıyor. 1: metin yoğun risk vurgusu yapıyor, çok sayıda risk ve tehdit ifadesi içeriyor.'
direction: lower_is_better
unit: ''
bands:
- max: -0.33
level: good
- max: 0.33
level: warn
- max: 1
level: critical
default_confidence: 0.8
sensitive: false
- key: dis_etken_atfi
label: Dış Etken Atfı
type: float
prompt: 'PPK toplantı metnindeki dış etkenlere atıf yapma eğilimini -1 ile 1 arasında puanla. Metinde ''küresel'', ''uluslararası'', ''jeopolitik'', ''küresel risk iştahı'', ''enerji fiyatları'', ''küresel finansal koşullar'', ''dış talep'', ''küresel belirsizlik'' gibi dış faktörlere yapılan atıfların sıklığını ve gücünü değerlendir. -1: metin hiç dış etken atfı içermiyor, tüm gelişmeleri iç dinamiklerle açıklıyor. 0: metin orta düzeyde dış etken atfı içeriyor, iç ve dış faktörleri dengeli ele alıyor. 1: metin gelişmeleri ağırlıklı olarak dış etkenlere bağlıyor, dış faktörlere yoğun atıf yapıyor.'
direction: lower_is_better
unit: ''
bands:
- max: -0.33
level: good
- max: 0.33
level: warn
- max: 1
level: critical
default_confidence: 0.8
sensitive: false
- key: somutluk
label: Somutluk Düzeyi
type: float
prompt: 'PPK toplantı metninin somutluk düzeyini -1 ile 1 arasında puanla. Metinde somut rakamlar, yüzde değerleri, tarihler, spesifik politika araçları, net hedefler ve ölçülebilir ifadelerin varlığını değerlendir. -1: metin tamamen soyut ve genel ifadelerden oluşuyor, hiç somut rakam veya spesifik politika detayı içermiyor. 0: metin orta düzeyde somut, bazı rakam ve spesifik ifadeler içeriyor. 1: metin yüksek somutlukta, çok sayıda spesifik rakam, yüzde, tarih ve net politika taahhüdü içeriyor.'
direction: higher_is_better
unit: ''
bands:
- max: -0.33
level: critical
- max: 0.33
level: warn
- max: 1
level: good
default_confidence: 0.8
sensitive: false
display:
title_field: karar_no
subtitle_field: tarih
primary_metrics:
- sikilastirma_egilimi
- kararlilik
- belirsizlik
groups:
- label: Ton ve Yön
metrics:
- kararlilik
- temkinlilik
- sikilastirma_egilimi
- label: Risk ve Belirsizlik
metrics:
- belirsizlik
- risk_vurgusu
- dis_etken_atfi
- label: İletişim Kalitesi
metrics:
- somutluk
The pack keeps its Turkish prompts and labels on purpose: the source material — Turkish central bank statements — is Turkish, and metric keys are the tenant's data, not our interface text. The engine doesn't care what language a key is in either.