Agent StateState contains the information needed to continue the current run
The model sees a slice of the run state
Memory TypesDifferent retained information supports different decisions
Type
Contains
Example
Working state
Current task progress
A pending tool call
Semantic memory
Stored facts
Preferred report format
Episodic memory
Past events and outcomes
A resolved maintenance case
Procedural memory
Reusable instructions
A validated inspection procedure
Choose what to remember before choosing where to store it
Caveat: Stale MemoryA stored fact can outlive the condition that made it true
A remembered fact outlived the condition
CheckpointA checkpoint saves the state needed to resume a workflow
A checkpoint after each node limits repeated work
Caveat: Resume and Side EffectsResuming a workflow can repeat an external write
Resuming repeated a write
Approval BoundaryPause before a consequential action and resume with a recorded decision
The run waits for a recorded decision
Agent TraceTrace actions and observations instead of relying on the final narrative
The trace shows each node, state update and message
03 · Connecting Agents to Data
§ Text-to-SQL
Data AgentsHere, a data agent is an agent that reads and writes data systems
Reads and writes go through separate tools
Text-to-SQLText-to-SQL translates a natural-language question into a database query
Schema context turns a question into a checkable query
Recap: Factory TablesMachine 1 is P-17; each row in Runs records one production run
Keys and grain decide which joins are valid
Schema ContextExpose names, types, keys, and grain before generating SQL
The prompt carries keys, grain and the metric
Weighted YieldAggregate counts before dividing when run sizes differ
Sum the counts, then divide
Line Yield QueryJoin by the declared key and divide the aggregated counts
SELECT m.line, SUM(r.good) AS good, SUM(r.total) AS total,
ROUND(100.0 * SUM(r.good) / NULLIF(SUM(r.total),0),2) AS yield_pct
FROM Runs r JOIN Machines m ON m.id=r.machine_id
GROUP BY m.line ORDER BY m.line;
The result uses production counts rather than an average of run ratios
Caveat: Join MultiplicationAn extra one-to-many join can inflate both counts and rankings
Three notes triple the production count
Query Validation LayersCheck syntax, access, resources, and semantics separately
A query that runs can still be wrong
Read-Only Query ToolExecute queries under a constrained database identity
Validated SQL → DB role → Budget → Result
Demo: Factory YieldRun the calculation in the local SQLite database
Return total good and total output for each line
Calculate weighted yield as a percentage
Ctrl/⌘ + Enter
Answer: Factory YieldLine A yields 83.75%; line B yields 95%
SELECT m.line, SUM(r.good) AS good, SUM(r.total) AS total,
ROUND(100.0*SUM(r.good)/NULLIF(SUM(r.total),0),2) AS yield_pct
FROM Runs r JOIN Machines m ON m.id=r.machine_id
GROUP BY m.line ORDER BY m.line;
The answer follows the stated metric and joins
Text-to-SQL ExecutionPass schema and metric meaning into the model, then execute the query
const prompt = ChatPromptTemplate.fromMessages([
["system", "Return JSON with sql. Only read Machines and Runs. " + DB_SCHEMA],
["human", "{question}"]
]);
const model = makeModel([JSON.stringify({sql:YIELD_SQL})]);
const proposed = await prompt.pipe(model).pipe(new JsonOutputParser())
.invoke({question:"Yield by line over all runs?"});
const rows = await dbQuery(proposed.sql); // SQLite enforces query-only mode
return {mode:MODEL_MODE, sql:proposed.sql, rows};
Demo: Text-to-SQL ExecutionInspect the proposed SQL and the rows returned by SQLite
Replace SUM(good)/SUM(total) with AVG(1.0*good/total). Compare line A.
Change the proposed query to DELETE FROM Runs. Inspect query-only enforcement.
Run code to inspect its output.
Ready
LangChain.js · JavaScript · Ctrl/Cmd+Enter · Offline replay uses fixed responses; local mode calls your model
Answer: Text-to-SQL ExecutionThe baseline output comes from executing the shown code
{"mode": "Recorded response (no inference)", "sql": "SELECT m.line, ROUND(100.0*SUM(r.good)/SUM(r.total),2) yield_pct FROM Runs r JOIN Machines m ON m.id=r.machine_id GROUP BY m.line ORDER BY m.line", "rows": [{"line": "A", "yield_pct": 83.75}, {"line": "B", "yield_pct": 95}]}
§ Writes and Access
Case study: Repair Request WorkflowA repair task crosses query, permission, and write boundaries
Stage
Contract
Evidence
Find machine
Authorized lookup
Machine ID + current state
Check readings
Freshness + units
Timestamped measurements
Propose repair
Bounded write + review
Target + reason + request ID
Create request
Transaction + idempotency
Stored row + receipt
The final answer follows verified reads and a confirmed write
Transaction BoundaryA transaction groups changes that must succeed or fail together
Both writes happen, or neither
Idempotent RequestA uniqueness constraint can prevent duplicate requests
CREATE TABLE Repairs (
request_id TEXT UNIQUE,
machine_id INTEGER,
reason TEXT,
status TEXT
);
-- The server also verifies that repeated IDs carry the same payload.
Caveat: Timeout After CommitA missing response does not reveal whether the write succeeded
The retry finds the committed request
Demo: Duplicate Repair RequestUse SQLite to observe a unique request key
Insert req_42 twice in one execution using INSERT OR IGNORE
Select Repairs and verify that one row remains
Ctrl/⌘ + Enter
Answer: Duplicate Repair RequestThe unique key leaves one record after two identical inserts
INSERT OR IGNORE INTO Repairs VALUES ('req_42',1,'vibration','open');
INSERT OR IGNORE INTO Repairs VALUES ('req_42',1,'vibration','open');
SELECT * FROM Repairs;
This example demonstrates uniqueness, not the entire write policy
Role-Based Access ControlRBAC grants capabilities to roles assigned to authenticated identities
Identity → Role → Permission → Resource
Caveat: Prompt-Based AccessA prompt cannot replace a database permission check
Only the database permission stops the query
Row Filters and Column MasksFilter records and redact sensitive values at query time
Same table, different visibility per caller
Column Masking ExampleThe same row can expose different values to different roles
machine_id
Role
email visible
1
Operator with approved access
operator1@example.test
1
Analyst
[masked]
Masking changes visibility without changing the source record
Agent EvaluationTest the outcome, the action path, and the database state
Dimension
Check
Task success
Required result verified by evidence
Tool behavior
Correct tool, arguments, and access scope
Recovery
Timeouts and duplicate requests handled
Efficiency
Calls, tokens, and elapsed time
An agent test observes both the final answer and the changed state
Takeaway: Bound actions, recover state, govern data
1Validate tool requests and bound the agent loop
2Keep workflow state outside the model so runs can pause and resume
3Connect agents to data through read-only queries, transactions, and roles
← → / Space · S notes · O overview · F fullscreen · number + Enter