Java 和 Spring Boot 構建安全、可審計智能體的實踐指南
Agentic AI in Practice: Building Safe, Auditable Agents with Java and Spring Boot
這篇實踐指南説明如何用 Java、Spring Boot 和 Spring AI 構建安全、可審計的智能體,透過工具調用循環處理需要跨系統完成的任務。作者建議先從唯讀工具開始,將權限驗證、硬性限制、步數上限和審批閘口落實在程式碼與服務層,並記錄模型及工具調用以供審計。他亦建議建立至少 20 個真實任務的評測集,並在更改模型版本、提示詞或工具描述時重跑評測。
Introduction
For the last few years, most of us have used large language models the same way: we send a prompt, we get a response, and the conversation ends there. That pattern is useful, but it has a ceiling. The model can explain how to refund a payment, but it cannot check the ledger, create the refund, or notify the customer.
Agentic AI removes that ceiling. An agent is a system where a language model plans a sequence of steps, calls tools to act on the world, observes the results, and continues until a goal is met or a human needs to intervene. The model becomes one component in a loop, not the whole product.
This shift matters most in enterprise software, where work is rarely a single question. A typical operations request touches several systems, follows business rules, and needs an audit trail. In this article I want to cover what agentic AI means in practice, how to build it on the JVM with Spring Boot and Spring AI, and which engineering principles separate a demo from a system you can run in production.
The Core Loop
Every agent, however sophisticated, runs some version of the same loop:
- Perceive: gather the current context, including the user request, conversation history, and relevant data.
- Plan: decide the next action. This may be a direct answer, a tool call, or a request for clarification.
- Act: execute the chosen tool with validated arguments.
- Observe: read the tool result and add it to the context.
- Stop or continue: finish when the goal is satisfied, or loop again.
The important word is observe. A workflow engine follows a predefined graph. An agent chooses its path based on what it has seen so far. That flexibility is the reason agents are powerful, and also the reason they need boundaries.
It helps to distinguish three levels of autonomy:
- Assisted generation: the model drafts text, and a person performs every action.
- Tool-augmented responses: the model calls read-only tools, such as search or lookup, and answers from the results.
- Autonomous execution: the model calls tools that change state, such as creating records or sending messages.
Most production systems should start at level two and move to level three only for narrow, well-tested actions.
Tools Are the Real Interface
In an agent, the tool definitions are the contract between the model and your system. The model never touches your database directly. It sees a name, a description, and a schema, and it proposes arguments. Everything else is your responsibility.
With Spring AI, you can expose a Java method as a tool with an annotation and a clear description. A simplified example for a banking support agent:
@Component
public class AccountTools {
private final AccountService accountService;
public AccountTools(AccountService accountService) {
this.accountService = accountService;
}
@Tool(description = "Returns the current balance and status of a customer account. Read-only.")
public AccountSummary getAccountSummary(
@ToolParam(description = "The internal account identifier") String accountId) {
return accountService.findSummary(accountId);
}
@Tool(description = "Opens a dispute for a specific transaction. Requires a reason code.")
public DisputeReceipt openDispute(
@ToolParam(description = "Transaction identifier") String transactionId,
@ToolParam(description = "One of: DUPLICATE, NOT_RECOGNIZED, WRONG_AMOUNT") String reasonCode) {
return accountService.openDispute(transactionId, DisputeReason.valueOf(reasonCode));
}
}
Notice three things. The read-only tool and the write tool are separate, so it is easy to audit what can change state. The description tells the model when to use the tool and what it does. And the reason code is an enum rather than free text, which reduces the room for malformed or surprising input.
Treat every tool as an API that a hostile or confused caller might use. Validate arguments on the server side, enforce authorization in the service layer, and never rely on the model to respect permissions it was merely told about.
Guardrails Belong in Code
A common mistake is to put safety rules only in the system prompt: "Never refund more than 500 dollars without approval." Prompts are guidance, not enforcement. A determined user, a poorly phrased request, or a model regression can bypass them.
Put hard limits in code:
- Amount and rate limits on financial actions, enforced in the service that performs the operation.
- Allow-lists for which tools an agent can call in a given context, so a support agent cannot access payroll tools.
- Confirmation gates for irreversible actions. The agent can prepare a request, but a human or a separate approval workflow executes it.
- Input and output filters for sensitive data, such as account numbers and personal identifiers, before content reaches the model or leaves the system.
Prompt-level instructions still matter. They shape behavior in the normal case. But the code path should be safe even when the prompt fails.
Observability and Auditability
When an agent makes a decision, someone will eventually ask why. In regulated domains such as fintech, that question comes from risk, compliance, and auditors, not only from engineers.
Every agent run should record:
- the user request and the session identifier,
- each model call with the prompt version and model name,
- each tool call with its arguments and result,
- the final response and whether a human approved any action,
- latency, token usage, and error classification.
In Spring Boot, this usually means structured logs plus a dedicated audit table or event stream. Use correlation identifiers from the first request onward. If you cannot reconstruct a decision from logs, you cannot defend it later.
Observability also helps with cost and reliability. Agents can loop, retry, or call expensive tools repeatedly. Metrics on steps per task, tool failure rates, and tokens per successful outcome will show problems long before users complain.
Designing for Failure
Agents fail in ways that ordinary services do not. A tool may return a valid response that is semantically wrong. A model may call the same tool five times with slightly different arguments. A chain of reasonable decisions may lead to an unreasonable outcome.
Design for these cases explicitly:
- Step limits: stop after a fixed number of tool calls and return a controlled message.
- Idempotency: write tools should accept a client-generated key so retries do not create duplicate records.
- Timeouts on every external call, with clear error messages that the model can interpret.
- Fallback paths to a human queue when confidence is low or the agent is stuck.
- Compensating actions for multi-step writes, so a partial failure can be reversed.
A simple guard in a Spring service might look like this:
public AgentResult run(AgentRequest request) {
int steps = 0;
AgentState state = AgentState.start(request);
while (!state.isDone()) {
if (++steps > MAX_STEPS) {
return AgentResult.escalated("Step limit reached. A specialist will follow up.");
}
state = agentStep(state);
}
return state.result();
}
The code is intentionally boring. The intelligence lives in the model's choices; the reliability lives in the surrounding structure.
Evaluation Is Not Optional
Traditional unit tests check deterministic functions. Agents need evaluation sets: collections of realistic tasks with expected outcomes, run regularly against the current model and prompts.
A useful evaluation suite includes:
- Happy paths: common requests that should succeed end to end.
- Boundary cases: requests near policy limits, ambiguous wording, or missing data.
- Adversarial cases: attempts to access other customers' data, skip approval steps, or override instructions.
- Regression cases: every production incident turned into a test.
Measure task success, not only answer quality. Did the agent create the correct record? Did it ask for approval when required? Did it stay within the step budget? These are engineering metrics, and they should gate releases the same way integration tests do.
Run evaluations whenever you change the model version, system prompt, tool descriptions, or retrieval configuration. Small changes in tool descriptions can alter behavior significantly.
Where Retrieval Fits
Many agents combine tools with retrieval-augmented generation. Policy documents, product manuals, and past case notes live in a vector store, and the agent searches them when it needs context. Retrieval answers "what does our policy say?" while tools answer "what is this customer's balance right now?"
Keep these roles separate. Retrieved text is data, not instructions. If a document contains a sentence such as "ignore previous rules," the agent should treat it as content to summarize, not as a command. Tag retrieved passages clearly in the prompt and enforce tool permissions independently of anything the retrieved text says.
A Practical Architecture
For a Spring Boot system, a reasonable starting architecture looks like this:
- API layer: a REST endpoint that accepts the user request and returns either a final answer or a task identifier for long-running work.
- Agent orchestrator: a service that manages the loop, step limits, and state persistence.
- Model client: Spring AI chat client configured with the approved model and system prompt version.
- Tool registry: annotated tools grouped by domain and permission level.
- Domain services: the existing business logic, with its own authorization and validation.
- Audit store: an append-only table or event topic for every decision and action.
- Human review queue: a workflow for approvals and escalations.
This keeps the agent thin. It coordinates; the domain services remain the source of truth. If you ever need to replace the model provider, the tools and business rules do not change.
Three Principles I Keep Coming Back To
In my work on fintech and digital banking systems, three principles come up again and again.
Scope tools tightly. Every tool an agent can call is a potential failure point and a security surface. Start with the smallest set that solves the business problem. Adding a tool is a design decision, not a configuration change.
Make actions auditable. Log every decision and tool call in a form that a risk or compliance team can read without an engineer translating it. If the explanation only makes sense to the developer who built the system, the system is not ready.
Keep humans in the loop for irreversible actions. Automation should accelerate judgment, not replace accountability. An agent can prepare a wire transfer, draft a dispute letter, or suggest a credit limit change. A person with the right authority should approve the actions that move money or change customer obligations.
What Agentic AI Is Not
It is worth being clear about what agentic AI does not solve. It does not remove the need for good data models, clear business rules, or well-designed APIs. An agent operating on messy systems will produce messy results, only faster. It also does not justify giving a model broad credentials "to see what it can do." Least privilege applies to agents exactly as it applies to services and people.
Finally, agents are not a replacement for deterministic workflow where the process is well understood. If a sequence of steps never changes, a conventional workflow engine or a plain service method is cheaper, faster, and easier to test. Use agents where the path genuinely depends on context: triaging support cases, investigating anomalies, reconciling records across systems, or preparing decisions for specialists.
Getting Started
If you are beginning with agentic AI in a Java codebase, I suggest this sequence:
- Pick one narrow, read-only use case with clear success criteria.
- Expose two or three well-described tools backed by existing services.
- Add structured audit logging from the first prototype.
- Build an evaluation set of at least twenty realistic tasks before tuning prompts.
- Introduce write tools only after the read-only version is reliable, and gate them behind approval.
- Review incidents and add each one to the evaluation suite.
Progress comes from this discipline, not from more clever prompts.
Key Takeaways
- Agentic AI means models that plan, act through tools, observe results, and iterate toward a goal.
- The tools, permissions, and domain services define the real behavior of the system.
- Guardrails must be enforced in code, not only described in prompts.
- Observability and audit trails are requirements in regulated environments.
- Evaluation suites and step limits make agents testable and bounded.
- Keep humans responsible for irreversible or high-impact decisions.
Agentic AI will reward teams that treat it as disciplined software engineering. The model is powerful, but the system around it is what earns trust.
Further Reading
- Spring AI documentation on tool calling and chat clients
- Your organization's model risk and AI governance policies
- Internal post-incident reviews for any agent deployed in production
What agent workflow has worked best for your team? I am especially interested in examples where human approval gates changed the outcome.
來源:DEV Community · dev.to