Build a Chat Agent with a Graph¶
Create an agent whose graph is a simple
RegexGuardrail → Generate chain, send a message, and get a
streamed reply.
Prerequisites
- A connected session with
CreateSessionalready called — see Connect and Manage a Session. - A model listed in
models.jsonthat is either alreadyLoadedor at leastLocal. If not, see Use Your Own Local Model.
Steps¶
- In the Project window: right-click → Create → Tryll → Workflow
Asset. Name it
ChatWorkflow. - In the Inspector, expand Graph and add two nodes:
guard— Params typeRegexGuardrail. Leave Triggered Exit empty (END) to drop jailbreaks silently, and set Not Triggered Exit =answer.answer— Params typeGenerate. Default Exit stays empty (END).
Set Start Node = guard and Default Model Name = your model.
3. Attach a TryllAgentComponent to a GameObject and assign
ChatWorkflow to its Workflow Asset field.
4. Wire events and send:
var agentComp = GetComponent<TryllAgentComponent>();
agentComp.OnAnswerText.AddListener(
(text, isDelta, isFinal) => chatText.text += text);
agentComp.OnTurnComplete.AddListener(
(status, _, _) => typingIndicator.SetActive(false));
// When the agent is created, send the first message.
agentComp.OnAgentCreated.AddListener(() => agentComp.SendMessage("Hello!"));
- In the Content Browser, right-click → Tryll → Workflow Asset.
Name it
ChatWorkflowand open it. On the guard node, set Triggered Exit to""(END) and Not Triggered Exit to"answer". The answer node's Default Exit can be left empty (END). - Add a
UTryllAgentComponentto your actor and assignChatWorkflowto its Workflow Asset slot. - In Blueprint:
- Bind On Agent Ready to a handler that calls Send Message.
- Bind On Answer Text to append each chunk to a UI text block.
- Bind On Turn Complete to flip the "typing" indicator off.
#include <tryll/TryllClient.h>
namespace TC = Tryll::Client;
TC::RegexGuardrailParams guardParams;
// guardParams.triggered_exit defaults to "" (END) — drop jailbreaks silently.
guardParams.not_triggered_exit = "answer";
TC::GraphDescription graph;
graph.AddNode("guard", guardParams)
.AddNode("answer", TC::GenerateParams{})
.SetStartNode("guard")
.SetDefaultModelName("My Local Model");
auto agent = client.CreateAgent(graph);
// Register persistent callbacks, then send — fire-and-forget.
agent.SetOnAnswerText([](std::string_view text, bool, bool)
{ std::cout << text << std::flush; });
agent.SetOnTurnComplete([](TC::TurnStatus, std::string_view, std::int32_t)
{ std::cout << "\n"; });
agent.SendText("Hello!");
from tryll_client.graph import GraphDescription, RegexGuardrailParams, GenerateParams
# 1. Build the graph.
guard_params = RegexGuardrailParams(
# triggered_exit defaults to "" (END) — drop jailbreaks silently.
not_triggered_exit = "answer",
)
graph = (
GraphDescription()
.add_node("guard", guard_params)
.add_node("answer", GenerateParams())
.set_start_node("guard")
.set_default_model_name("My Local Model")
)
# 2. Create the agent.
agent = client.create_agent(graph)
# 3. Send a message. send_message blocks and returns the full reply.
reply = agent.send_message("Hello!")
print(reply)
Verify it worked¶
Server-side, at info log level, one turn produces:
[info] Agent 7: turn starting
[info] Node guard: not_triggered
[info] Node answer: default
[info] Agent 7: turn complete (Success, tokens=128)
Client-side, Python returns the full reply string from
send_message, while C++ and Unreal see each streamed chunk arrive
in the SendText callback / OnAnswerText delegate, terminated by
a TurnComplete with status Success.
Common pitfalls¶
- "Model not found" (error
4002) — either the
name is wrong, or the model is not in
models.json. Double-check withclient.list_models(). - "Graph validation failed" (error
3003) — check for a bad
start node name or duplicate node names in the graph. If the message
says
InvalidExitTarget(3008), an exit field on one of the nodes names a node that doesn't exist — fix the string or leave it empty for END. - Silent nothing after
SendMessage. Check that the start node is not an always-ENDbranch (e.g., guardrail that matches everything). The server log will show which route was taken.