Skip to main content

Chain-of-Thought Ledger

The Chain-of-Thought (CoT) Ledger records every reasoning step an agent takes in an immutable, hash-chained format — creating a tamper-evident audit trail that can be replayed, verified, and analyzed months later.

Why Agents Need Reasoning Traces

When AI agents make decisions that affect business operations, you need answers:

  • What did the agent reason about? — Full trace of observations, hypotheses, tool calls, and conclusions
  • Can the trace be trusted? — Cryptographic hash chains prove no steps were inserted, deleted, or modified
  • What went wrong? — Replay a session to understand failures or unexpected decisions
  • Is the agent improving? — Compare reasoning patterns across sessions

How It Works

Agent → log_reasoning_step → CoT Ledger
├── Append to _hatidata_cot table
├── Update per-session hash chain
└── Conditionally dispatch for embedding

Analyst → replay_decision → CoT Ledger
├── Load session trace
└── Verify hash chain integrity

Hash Chaining

Each step's hash is computed from its content and the previous step's hash, forming a cryptographic hash chain. Modifying any step invalidates all subsequent hashes, making tampering detectable.

Append-Only Enforcement

An append-only enforcer intercepts DML statements targeting _hatidata_cot tables:

  • INSERT — Allowed (append new steps)
  • UPDATE — Blocked
  • DELETE — Blocked
  • TRUNCATE — Blocked
  • DROP — Blocked

Enforcement happens at the proxy level, before query execution. Even direct SQL connections cannot tamper with the ledger.

Trace Schema

Each reasoning step is stored with 18 fields:

FieldTypeDescription
trace_idUUIDUnique identifier for this trace entry
org_idVARCHAROrganization identifier
agent_idVARCHARAgent that produced this step
session_idVARCHARGroups steps into a reasoning session
step_indexINTEGEROrdinal position within the session (0-based)
step_typeVARCHAROne of 12 step type variants
contentTEXTThe reasoning content
input_dataJSONInput to this step (tool arguments, observations)
output_dataJSONOutput from this step (tool results, decisions)
confidenceFLOATAgent's self-reported confidence (0.0 to 1.0)
duration_msBIGINTHow long this step took
token_countINTEGERToken usage for LLM-based steps
modelVARCHARModel identifier (e.g., gpt-4o, claude-3.5-sonnet)
metadataJSONArbitrary additional context
prev_hashVARCHARCryptographic hash of the previous step
current_hashVARCHARCryptographic hash of this step
has_embeddingBOOLEANWhether an embedding exists
created_atTIMESTAMPWhen this step was recorded

Step Types

Step TypeDescriptionAlways Embedded?
ObservationAgent observes data or environment stateNo
HypothesisAgent forms a hypothesisNo
ToolCallAgent invokes an external toolNo
ToolResultResult returned from a tool callNo
ReasoningInternal reasoning or analysisNo
DecisionAgent makes a decisionYes
ActionAgent takes an actionYes
ErrorAn error occurred during reasoningYes
CorrectionAgent corrects a previous stepYes
SummaryAgent summarizes findingsNo
PlanStepA step in a multi-step planNo
FinalAnswerThe final output of the reasoning sessionYes

Architecture

Write Path

The write path manages per-session hash chains:

  • Tracks the latest hash per session_id for chain continuity
  • On each log_reasoning_step, computes the new hash, appends the row, and updates the chain head
  • Handles concurrent sessions safely via lock-free operations

Read Path

The read path provides retrieval and verification:

OperationDescription
Replay sessionLoad all steps for a session in order
Get traceLoad a single trace entry by ID
List tracesList recent sessions for an agent
Verify chainVerify hash chain integrity for a session

Embedding Sampling

Not every step needs a vector embedding. The system uses configurable sampling:

  • Sampling rate — Default 10% of steps are embedded
  • Critical step typesDecision, Action, Error, Correction, and FinalAnswer are always embedded
  • Purpose — Embedded steps are findable via semantic search (e.g., "find all decisions about pricing")

MCP Tools

log_reasoning_step

// Input
{
"session_id": "analysis-session-42",
"step_type": "Reasoning",
"content": "The revenue data shows a clear upward trend in Q4, driven primarily by enterprise.",
"input_data": { "query": "SELECT segment, SUM(revenue) FROM orders WHERE quarter = 'Q4' GROUP BY 1" },
"output_data": { "enterprise": 4500000, "mid_market": 2100000, "smb": 890000 },
"confidence": 0.92,
"model": "gpt-4o"
}

// Output
{
"trace_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"step_index": 3,
"current_hash": "a3f2b8c9d4e5f6..."
}

replay_decision

// Input
{ "session_id": "analysis-session-42", "verify_chain": true }

// Output
{
"session_id": "analysis-session-42",
"agent_id": "analyst-agent",
"step_count": 7,
"chain_valid": true,
"steps": [
{ "step_index": 0, "step_type": "Observation", "content": "User requested Q4 revenue analysis..." },
{ "step_index": 1, "step_type": "ToolCall", "content": "Querying orders table..." }
]
}

get_session_history

// Input
{ "agent_id": "analyst-agent", "limit": 20 }

// Output
[{
"session_id": "analysis-session-42",
"step_count": 7,
"first_step_at": "2025-01-15T10:30:00Z",
"last_step_at": "2025-01-15T10:30:15Z",
"chain_valid": true
}]

Usage Example

from hatidata_agent import HatiDataAgent

agent = HatiDataAgent(
host="your-org.proxy.hatidata.com",
agent_id="analyst",
password="hd_live_your_api_key",
)

session_id = "revenue-analysis-q4"

agent.log_reasoning_step(
session_id=session_id,
step_type="Observation",
content="User asked for Q4 revenue breakdown by segment",
)

agent.log_reasoning_step(
session_id=session_id,
step_type="ToolCall",
content="Querying orders table",
input_data={"sql": "SELECT segment, SUM(revenue) FROM orders WHERE quarter='Q4' GROUP BY 1"},
)

agent.log_reasoning_step(
session_id=session_id,
step_type="Reasoning",
content="Enterprise grew 23% QoQ while SMB declined 5%.",
confidence=0.88,
)

agent.log_reasoning_step(
session_id=session_id,
step_type="FinalAnswer",
content="Q4 revenue was $7.49M, up 15% QoQ. Enterprise drove the growth.",
confidence=0.95,
)

# Later: replay and verify
trace = agent.replay_decision(session_id, verify_chain=True)
assert trace["chain_valid"] is True

Configuration

CoT Ledger behavior is configurable per deployment:

SettingDefaultDescription
CoT enabledtrueEnable/disable the CoT ledger
Embedding sample rate10%Fraction of steps to embed (0.0 to 1.0)
Max content length64 KBMaximum content length per step
Retention period90 daysHow long to retain CoT records

Stay in the loop

Product updates, engineering deep-dives, and agent-native insights. No spam.