Debugging
Generally the Playground gives a lot of information about what's happening, but when that is insufficient, you have other options.
Logging the raw request and response from LLM calls
You can provide a rawRequestResponseHandler
to the agent to log the raw
request and response from the LLM.
You could use this to log the request and response to a table, or use console logs with Log Streaming to allow debugging and searching through Axiom or another logging service.
const supportAgent = new Agent(components.agent, {
...
rawRequestResponseHandler: async (ctx, { request, response }) => {
console.log("request", request);
console.log("response", response);
},
});
Inspecting the database in the dashboard
You can go to the Data tab in the dashboard and select the agent component above the table list to see the Agent data. The organization of the tables matches the schema. The most useful tables are:
threads
has one row per threadmessages
has a separate row for each CoreMessage - e.g. a user message, assistant tool call, tool result, assistant message, etc. The most important fields areagentName
for which agent it's associated with,status
,order
andstepOrder
which are used to order the messages, andmessage
which is roughly what is passed to the LLM.streamingMessages
has an entry for each streamed message, until it's cleaned up. You can take the ID to look at the associatedstreamDeltas
table.files
captures the files tracked by the Agent from content that was sent in a message that got stored in File Storage.
Troubleshooting
Circular dependencies
Having the return value of workflows depend on other Convex functions can lead
to circular dependencies due to the internal.foo.bar
way of specifying
functions. The way to fix this is to explicitly type the return value of the
workflow. When in doubt, add return types to more handler
functions, like
this:
export const supportAgentWorkflow = workflow.define({
args: { prompt: v.string(), userId: v.string(), threadId: v.string() },
+ handler: async (step, { prompt, userId, threadId }): Promise<string> => {
// ...
},
});
// And regular functions too:
export const myFunction = action({
args: { prompt: v.string() },
+ handler: async (ctx, { prompt }): Promise<string> => {
// ...
},
});