前言:封闭告一段落,开始系统复盘:一个生产 Agent 到底怎么跑起来。踩坑、经验和落地,尽量用几篇文章讲清,这篇文章是系列第6篇:Agent 挂掉常常不是模型傻,而是身份传丢了、会话只放在内存里(重启或换机器就没了)、用户点了取消但模型和工具请求还在跑。希望这篇文章对你有所帮助,有所借鉴。
线上生产常叠在一起的几件事:
- 用户连点发送,后台起了两个任务;
- 点了取消,模型和工具还在跑、调用费还在产生;
- 换手机打开,刚才的会话找不着;
- 工具居然拿模型参数里塞的 userId 当登录身份来用。
这篇只讲怎么把这四件事收住:
- 谁在聊:租户(一家公司 / 一个客户在系统里的隔离边界)、用户从登录态来,写进数据库里的会话和 Run(用户点一次发送,后台为这句话开的一次任务);
- 用谁的权限调工具:整条链路带上同一份请求上下文,不要使用模型参数里的 userId;
- 同一次点击不要起两个任务:双击发送、网络重试,要回到同一条 Run;
- 取消怎样才算真的停:不只关掉推流,还要打断正在跑的模型和工具请求。
继续用案例 OpsPilot(企业运维助手,虚构案例),技术栈仍是 TypeScript;Go / Java / Node 都能做,关键是边界清楚:不要让前端直连模型 Key。
会话服务到底管什么
会话服务至少做这五件事:
- 会话与消息写进数据库(创建、追加、分页拉取)
- 一次 Run 的生命周期(排队、执行、等人确认、完成、失败、取消)
- 把登录态变成显式上下文,交给编排层和工具层(不要使用模型参数里的 userId)
- 同一次发送不要起两个 Run(防双击、防网络重试)
- 取消要能打断正在跑的模型和工具请求
即使用 LangGraph 这类框架跑 Agent 循环,租户、登录态、取消、跟前端的接口,仍归会话服务。框架只负责把图跑到哪一步记下来。这次任务进行到哪、能不能取消,以会话服务里的 Run 为准,不要两套状态各说各的。
请求上下文:整条链路只信这一份
请求进会话服务时,先根据登录态拼出一份 RequestContext 对象数据。后面编排、工具调用都只认这份对象(需要时在链路里传它的引用),不要再从零散的 Header 里抠身份,更不要使用模型参数里写的「我是谁」:
type RequestContext = { // 入口鉴权后的请求上下文
requestId: string // 本请求 id,日志与排障用
traceId: string // 全链路追踪 id,会话 / 工具 / 模型共用
tenantId: string // 租户;查库、检索过滤以此为准
userId: string // 当前用户;工具鉴权读这里
roles: string[] // 角色,用于权限与可见范围
scopes: string[] // 允许调用的工具或资源范围
authHeader?: string // 仅服务端持有,不进 Prompt、不进日志明文
locale?: string // 可选:语言 / 地区
deadlineAt?: number // 这次 Run 的截止时间,到点应取消
}
这份对象怎么用,就记三句:
- 给模型拼 Prompt、做知识检索过滤时,用里面的 tenantId / roles,决定「能看见哪些数据」。
- 调业务接口时,用服务端自己保存的访问凭证(一串证明「你是谁、有没有权限」的密钥,常放在 Authorization 头里)去调;需要「以当前用户身份去调」时,也由服务端换成对的凭证,不要让模型在参数里填这串密钥。
- 打日志时,访问凭证只留 hash 或后几位,完整内容不要写进日志。
时序可以看成:
sequenceDiagram
participant C as 前端
participant S as 会话服务
participant R as Agent Runtime
participant T as 工具
C->>S: POST /chat
S->>R: 读历史、开始 Run
R-->>S: 进度事件
S-->>C: SSE 推进度
R->>T: 带 RequestContext 调工具
T-->>R: 工具结果
C->>S: POST /cancel
S->>R: 取消 Run(写库 + abort)
再记住三件和 ID、取消、历史有关的事:
- 一个会话用固定的 conversationId;用户每点一次发送,新建一个 runId。
- 取消必须带上这个 runId,说清楚停的是哪一次任务,不要只丢一句「停一下」。
- 聊天列表里给用户看的历史消息,和这次 Run 跑模型、调工具时的中间过程分开存。
鉴权、多租户与凭证
身份校验要分三层做,不能只在门口验一次:
常见事故是:入口验过登录,工具里却拿模型参数里的 userId 来鉴权。模型一旦被诱导改掉这个字段,就会越权。工具参数里的用户身份不要使用,只认 RequestContext。
查库必带租户
租户 id、用户 id 要写进表,并且每次查询都带上,不能只靠前端「别请求别人的会话 id」这种自觉:
CREATE TABLE conversations (
id UUID PRIMARY KEY, -- 会话 id
tenant_id TEXT NOT NULL, -- 租户;查询必带
user_id TEXT NOT NULL, -- 所属用户;查询必带
title TEXT NOT NULL, -- 会话标题,如「排障 - payment-api」
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 按租户 + 用户列会话列表时用
CREATE INDEX conversations_tenant_user_idx
ON conversations (tenant_id, user_id, created_at DESC);
查会话时不要提供「只按 conversationId 查」的函数。别人若猜到或泄露了会话 id,就能串到别的对话。正确写法是三个条件一起匹配:
- conversationId
- 租户id
- 用户id
type ConversationScope = {
tenantId: string
userId: string
}
async function getConversation(
db: Database,
scope: ConversationScope,
conversationId: string
) {
// id + 租户 + 用户一起匹配,缺一不可
return db.oneOrNone(
`SELECT * FROM conversations
WHERE id = $1 AND tenant_id = $2 AND user_id = $3`,
[conversationId, scope.tenantId, scope.userId]
)
}
不止聊天表,下面这些地方也要用同一套隔离,否则会从别的口子漏数据:
- 知识检索(向量 / 全文):在服务端查询条件里就带上租户,不要查完再滤
- 长期记忆的读写 key
- 工具去调业务接口时带上的身份信息
- 链路追踪记录、运营后台谁能看哪些租户
租户条件要写在数据库 / 检索的查询里,不要先不加隔离把相关数据全查出来,再在应用代码里把别人的数据扔掉。那样一来,没权限的内容可能已经进了排序、日志或模型上下文。
管理员要跨租户排查问题时,走单独的权限和接口,强制填写原因并记谁查了什么;不要复用普通聊天接口。用户注销或租户停用后,会话、记忆、上传文件和检索索引都要能删干净,并且删没删得掉可以核对。
各种密钥不要进 Prompt
密钥、访问凭证只放在服务端,不要写进发给模型的 Prompt,也不要写进会存进消息历史的正文里:
再说一次:模型工具参数里的 userId、tenantId、roles 不要使用;工具应忽略这些字段,只认 RequestContext。
起 Run:同一次点击只要一个任务
用户点发送时,请求体里至少带三样:会话 id、用户原话、一个请求键(由客户端在用户点发送时生成,浏览器或 App 都行;同一句发送、连点、自动重试都带同一个值。服务端不要每次请求自己新造一个,否则对不上)。
短时间内同一个请求键,必须回到同一条 Run。否则双击发送或网络抖一下,后台会起两个循环,模型和工具各跑两遍,又贵又危险。
type StartChatInput = {
conversationId: string
idempotencyKey: string // 请求键;短时间内相同则返回同一条 Run
message: string // 用户本轮输入
}
Agent 可能跑很久,不要让「接收发送请求」的那次 HTTP 一直卡着把模型、工具全跑完。建议拆成两步:
- 先登记这次任务:往数据库插入一条 Run。表上对「同一个租户 + 同一个请求键」加唯一限制。插成功就是新任务;插失败说明这个键已经用过,直接返回已有的 runId,不要再开一条。
- 再交给后台去跑:把 runId 丢进队列(或同类后台任务),由别的进程慢慢调模型、调工具。接收请求的这边只负责打开 SSE,把进度推给前端。
CREATE TABLE runs (
id UUID PRIMARY KEY, -- Run id
tenant_id TEXT NOT NULL, -- 租户;与请求键一起做唯一约束
conversation_id UUID NOT NULL, -- 落在哪个会话
idempotency_key TEXT NOT NULL, -- 客户端带来的请求键;短时间内同键同 Run
status TEXT NOT NULL, -- queued / running / interrupted / …
cancel_requested BOOLEAN NOT NULL DEFAULT FALSE, -- 用户是否已点取消;后台每步要看
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (tenant_id, idempotency_key) -- 同一租户下,同键只能有一条 Run
);
// 步骤 1:登记 Run(同租户同请求键插第二次会失败,应改成查出已有 runId 返回)
// 步骤 2:通知后台去跑;本函数很快返回,不在这里跑完整 Agent
async function startRun(input: StartChatInput, ctx: RequestContext) {
const run = await db.insertRun({
tenantId: ctx.tenantId,
conversationId: input.conversationId,
idempotencyKey: input.idempotencyKey,
status: 'queued' // 已登记,等后台来执行
})
await queue.publish('agent.runs', { runId: run.id, tenantId: ctx.tenantId })
return run
}
超时怎么分层(建议):
- 模型调用超时:会话服务把这次 Run 标成失败或部分完成,用 SSE 推一条错误事件给前端。
- 工具调用超时:由真正跑工具的那一层决定是重试还是把失败写回对话让模型换办法。会话服务不要自己偷偷无限重试,这样调用费容易失控。
取消要真能停下模型和工具请求
SSE / HTTP 连接断了,只表示前端暂时听不到进度,不等于用户点了取消,也不等于后台已经停。
用户要点取消,前端应打明确的取消接口(不要指望「关掉推流」就够了)。服务端做两件事:
- 把「用户要取消」写进数据库(cancel_requested = true),后台跑模型 / 工具的每一步都能读到;
- 找到正在跑这次 Run 的后台任务,调用中断(常见是 abort()),打断已经发出去的模型请求和工具 HTTP。
async function cancelRun(runId: string, ctx: RequestContext) {
// 1) 取消意图写进数据库,换机器 / 下一步循环也能看见
await db.markCancelRequested(runId, ctx.tenantId)
// 2) 打断本机正在跑的任务(若任务在别的机器,靠第 1 步的库字段也能停)
const worker = workers.get(runId)
worker?.abort()
}
后台每一步都要同时看:库里的 cancel_requested,以及本次调用的中断信号(如 AbortSignal)。前端取消路径建议固定为 POST /runs/:id/cancel,否则容易出现「页面已停,模型调用费还在产生」。
任务可以交给后台队列去跑,但这次任务进行到哪、进度怎么推、谁有权限,仍以会话服务里的 Run 为准。不要另外再建一张任务表、却不带租户字段,否则隔离和取消都会对不上。
对外接口与排查
对外最少准备这几个接口(路径名可按你们规范改,意思别丢):
- 拉历史:GET /conversations/:id/messages
- 发起一次 Run:POST /conversations/:id/runs,进度用 SSE 推
- 人确认后恢复:POST /runs/:id/resume
- 取消:POST /runs/:id/cancel
Run 状态建议固定用这几个:queued(已登记排队)、running(执行中)、interrupted(暂停等人)、completed(正常结束)、failed(失败)、cancelled(已取消)。前端按这套状态和 SSE 业务事件更新页面即可,不要去解析半截内部日志来猜进度。
出问题先查(也是最常见的踩坑):
- 换手机 / 换浏览器后续不上刚才的对话:会话、Run、取消意图是不是只放在内存里,没有写进数据库(一扩容或一重启就没了)。
- 工具返回 401,或工具越权用了别人的身份:RequestContext 有没有传到工具;访问凭证是不是过期了;有没有误用模型参数里的 userId / tenantId(身份只认登录态拼出来的上下文)。
- 用户点了取消,调用费还在产生:是不是只关掉了 SSE;取消有没有写进数据库,模型和工具的 HTTP 有没有被真正打断。
- 能看到别人租户或别人的会话:查询是不是只按 conversationId;有没有同时带上 tenantId / userId。
- 连点发送起了两个任务:起 Run 时有没有用同一个请求键占位;同键是否只对应一条 Run。
小结
会话服务要立住,核心就三块:身份从哪来、一次发送怎么起停、数据怎么隔开。
- 身份只认登录态拼出的 RequestContext。 网关 / 会话服务验登录后整条链路带着这份对象;工具里不要使用模型参数里的 userId / tenantId,并再按业务规则验一遍「能不能做这件事」。密钥和访问凭证只放服务端,别进 Prompt、别进聊天记录。
- 一次点击对应一条 Run;取消要真能停。 客户端带上同一个请求键,短时间内同键只起一条任务,先写进数据库再丢给后台跑。用户点取消:取消意图写进数据库,并打断正在跑的模型和工具请求。关掉 SSE 只是前端听不见进度,不等于任务已停。
- 租户(和用户)写进查询条件,不是事后过滤。 查会话、检索、记忆读写都带上隔离字段;不要先把相关数据全查出来,再在应用代码里扔掉别人的。另建后台任务表时也要带租户,状态仍以会话服务里的 Run 为准。
这篇文章是根据落地实践写的,希望对你有所帮助、有所借鉴,有任何问题和建议,欢迎一起在评论区沟通交流。
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
Preface: Close the door and start the system reset: how does a producer Agent actually get up? Stepping, experience and landing, to the extent possible, in several articles, is the sixth in the series: Agent hangs up is often not a stupid model, but a lost identity, a session is simply in memory (restarting or changing the machine is gone), the user points the cancellation but the model and tool requests are still running. I hope this article will be helpful and useful.
There are a couple of things that are often stacked together:
- User connection, with two missions in the background;
- Cancelled, models and tools are running and call fees are generated;
- Switching on the phone.
- The tool actually uses the userId plugged in the model parameter as the login identity.
This article only says how to put these four things together:
- Who's talking about the tenant (a company/a customer in the system's quarantine boundary), the user from login, the session in the database and Run (a user point sent, a back-office assignment for this);
- Whose permission modulation tool is used: the same request for context along the entire chain, not using userId in model parameters;
- Do not start two jobs with the same click: double-click sending, network retry, returning to the same Run;
- How does the cancellation really stop: not only does it turn off the push, but also interrupts the running model and tool requests.
With the continued use of OpsPilot (enterprise transport assistant, fictional case), the technology warehouse remains TypeScript; Go / Java / Node can do it. The key is clear borders: do not let the front-end straight-link model be Key.
What exactly does the session service care about?
The session service does at least five things:
- Sessions and messages are written into the database (create, add, drag)
- One Run life cycle (queuing, execution, identification, completion, failure, cancellation)
- Translating login into visible context, to layout and tool layer (do not use userId in model parameters)
- No two Runs for the same dispatch.
- Cancel requests to interrupt running models and tools.
Runs the Agent cycle using a frame like LangGraph, and the tenant, login, cancels, and follows the front-end interface is still part of the session service. The framework is only responsible for the steps taken to write down the maps. Where this mission is going, can it be cancelled, based on Run from the session service, not in two separate sets.
Request context: This is the only part of the whole chain.
When requesting a session service, spell a ReQuestContext object data based on the login. tool call recognizes this object (whose reference is posted in the chain when needed) and does not use the name "Who I am" written in the model parameters:
Type RequestContext = {/ / / import clearance request context
/ This request id, log and disabled
trackId: string / / full chain tracking id, session / tool / model sharing
TenantId: string / / Tenant; Quest Filter
userId: string// current user; tool recognition here
Roles for permission and Visibility
scopes: string[ ] / range of tools or resources allowed for call
AuthHeader? : string / / Only held by the service, not in Prompt, not in log
Locale? :string / / Optional: Language / Area
The deadline for this Run should be cancelled.
}
How does this object work?
- When filtrating the model Prompt, filtrate the knowledge, use the tenantId / loles inside to determine what data to see.
- When moving business interfaces, switch them with access vouchers (a set of keys to prove who you are and whether you have permission, which are often placed in the head of the Association); and when "modified as current user" is required, the document is also changed from one service to another, so that the model does not fill the key in its parameters.
- When you play the log, only the Hash or the last few of the access vouchers are left, and the complete contents are not entered in the log.
Time series can be seen as:
sequenceDiagram
Party C as Frontend
Party S as Session Service
participant R as Agent Runtime
Participant T as a tool
C->>S: POST /chat
S->R: Read history, start Run
R->S: Progress event
S->C: SSE propulsion
R->T: RequestContext
T->R: Tool results
C->>S: POST /cancel
S->R: Cancel Run
And remember three things about ID, cancellation, history:
- A session is used as a fixed conversion Id; the user sends it every minute and creates a new run Id.
- The cancellation must be accompanied by this Runid, so let's be clear as to which mission to stop, not just a "stop."
- A chat list of historical messages for users, separated from the intermediate process of this Run run model, modulation tool.
Authority, multi-tenant and certificate
The identification check is to be done on three levels, not just at the door:
The common occurrence is that the login is checked at the entrance, and the tool uses the userId in the model parameters for authentication. Once the model is induced to change this field, it will be overstepped. The user identity in the tool parameter is not used, only the RequestContext is recognized.
Chaco must take the tenant.
Tenants id, users id have to write in the form, and every query has to be carried on, not by the self-consciousness of the front-end id:
CREATE TABLE conversations (
id UUID PRIMERY KEY, -- id session
Tenant id TEXT NOT NULL, -- Tenant; required for queries
user id TEXT NOT NULL -- subscriber; Query must contain
Title TEXT NOT NULL, -- session title, e.g., "Destruction - Payment-api"
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- by Tenant + User Session List
CREATE INDEX conversations_tenant_user_idx
ON conversations (tenant_id, user_id, created_at DESC);
Do not provide a function "only by reference Id" when searching for a session. If someone had guessed or leaked a conversation id, they could have had another conversation. The correct writing is a combination of three conditions:
- conversationId
- Tenant id
- User id
type ConversationScope = {
tenantId: string
userId: string
}
async function getConversation(
db: Database,
scope: ConversationScope,
conversationId: string
) {
/ id + Tenant + User Matches, Not Without
return db.oneOrNone(
`SELECT * FROM conversations
WHERE id = $1 AND tenant_id = $2 AND user_id = $3`,
[conversationId, scope.tenantId, scope.userId]
)
}
These are not just chat tables, but the following places are also isolated, or the data are leaked from other mouths:
- Knowledge retrieval ( vector / full text): take the tenant in service-end search terms and do not check and filter
- long-term memory reading and writing, Key.
- I.D. on the tool to fix the business interface.
- Link tracking, who can see the tenants?
Tenant conditions are to be written in database / search search, not to identify all data without segregation, and then to throw someone else's data away in the application code. In that case, the content without permission may have been sorted, log or model context.
The administrator is required to cross-check the tenant's screen, take a separate permission and interface, make the reasons available and record what; and do not return to the regular chat interface. After the user cancels or the tenant closes down, the session, memory, uploading of documents and indexing of the search are removed and can be verified.
Do Not Enter Keys
Keys, access vouchers are only on the service side, not in Prompt, which is sent to the model, nor in the text that will be stored in the message history:
Repeat: use not field in model tool parameters for userId, TenantId, rolls; tool should ignore these fields and only recognize RequestContext.
Run: Just one job for the same click
When a user point is sent, the request contains at least three types: session id, user original, one request key (generated by the client when sent by the user point, browser or App; all send, link, automatic retry with the same value. Do not ask for a new one at a time, or it will not match).
The same request key must be returned to the same Run in a short time. Otherwise, double-clicking or web shaking will result in two cycles of models and tools running twice, which are expensive and dangerous.
type StartChatInput = {
conversationId: string
ideapointyKey: string/ / Request key; return the same Run within a short time
message: string/ / user current round input
}
Agent may be running for a long time, so don't let the HTTP be stuck running all the models and tools. It is suggested to split into two steps:
- Registration of the mission first: insert a Run to the database. The table places a single limit on "Same Tenant + Same Request Key". Success is a new task; failure means that this key has been used and returns directly to the existing RunId and does not open another one.
- Backstage to run: throw RunId in the queue (or similar backstage tasks) with other processes slowly adjusting models and tools. This side of the request only opens the SSE and pushes the progress to the front.
CREATE TABLE runs (
id UUID PRIMARY KEY, -- Run id
tenant id TEXT NOT NULL, -- tenant; only constraint with requested key
conversion id UUID NOT NULL, -- in which session
input key TEXT NOT NULL, -- requested keys from client; same keys as Run
status TEXT NOT NULL, -- queued / running / interrupted / …
cancel requested BOOLEAN NOT NULL DEFAULT FALSE, -- users are cancelled at point; backstage steps are checked
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (tenant id, ideatency key) -- the same tenant can only have one Run
);
Step 1: Register Run (with the tenant 's second request key will fail and should be changed to identify the existing runId return)
Step 2: Notify backstage to run; this function returns very quickly and does not run fully here
async function startRun(input: StartChatInput, ctx: RequestContext) {
const run = await db.insertRun({
tenantId: ctx.tenantId,
conversationId: input.conversationId,
idempotencyKey: input.idempotencyKey,
Status: 'queued ' // Registered, awaiting backstage execution
})
await queue.publish('agent.runs', { runId: run.id, tenantId: ctx.tenantId })
return run
}
timeout how to layer (recommendation):
- Model calls timeout: The session service marked this Run as a failure or partial completion, pushing an error event to the front.
- tool callZ103Z: The level of the real running tool decides whether to write back the failure and replace the model. The session service does not sneak into limitless retry itself, so that the call fee is easily out of control.
Cancel the model and tool requests.
The SSE / HTTP connection is broken, which means only that the front end is temporarily unable to hear progress, which does not mean that the user has called off or that the backstage has stopped.
The user point is cancelled and the front end should be clearly de-interfaced (do not expect "to turn off the flow" enough). The service does two things:
- Write "User wants to cancel" into the database (cancerl requested = true) and read it every step of the backstage run model / tool;
- Finds the runback of Run, calls out (often abort()) and interrupts model requests and tools already sent out.
async function cancelRun(runId: string, ctx: RequestContext) {
/ 1) Unintention to enter the database, change the machine and see it in the / next step cycle
await db.markCancelRequested(runId, ctx.tenantId)
/ 2) Interrupting the running mission of this machine (if the mission is in another machine, it can stop by the 1st-step library of field)
const worker = workers.get(runId)
worker?.abort()
}
At the same time, every step in the backstage is to look at: cancer requested in the library, and the cut-off signal (e.g. AbortSignal). The front-end cancellation path proposal is fixed to POST /runs /:id/cancel, otherwise "page stopped and model call fees are still generated".
The task can be given to the backstage line to run, but where it goes, how it moves, who has permission, and Run in the chat service. Do not create another calendar without the tenant field, otherwise isolation and cancellation would be out of line.
External interface and routing
A minimum number of interfaces will be prepared from the outside:
- History: GET/conversions/:id/messages
- Launch Run: POST / protocols/:id/runs, progress with SSE push
- Recovery after identification: POST/runs/:id/resume
- Cancel: POST / Runs /:id/cancel
Run Status recommends that these are fixed: queued (registered queuing), running (ongoing), interrupted (suspension, etc.), completed (normal end), failed (failed), caancelled (cancelled). The front-end updates the page with this status and SSE business events, and does not decipher the internal log to guess progress.
Question first (also the most common pedestal):
- Change of cell phone / Change of browser does not follow up on the conversation just now: does the conversation, Run, cancellation intent only exist in the memory and not in the database (one enlargement or reboot does not exist).
- The tool returns 401, or the tool oversteps the authority to use another person ' s identity: whether RequestContext has reached the tool; whether the access certificate has expired; whether it has misused the userId / tenantId in the model parameter (identity is only context as spelled out in the login).
- The user called off and the call fee is still running: is it just the SSE? The HTTP that's not written in the database, models and tools is not really interrupted.
- To see someone else's tenant or other person's session: to ask if it's only by conversion Id; whether it's accompanied by tenant Id/user Id.
- Two tasks were sent: did Run take place with the same request key from the start; or did the same key correspond to only one Run.
Summary
The session service has three pieces: where the identity comes from, where the transmission stops, how the data is separated.
- Identities are only spelled in state requestContext. Gateway / session service login with the object; not use userId / tenantId in the model parameter, and check "can we do this" again according to business rules. Keys and access vouchers are only for the service, not for Prompt, not for chat records.
- One click for Run; cancel if it really stops. The client carries the same request key, and the same key takes up only one task in a short period of time, writing in the database and then throwing it backstage. User point cancelled: the intention to enter the database cancelled and the running model and tool requests interrupted. Turning off the SSE just doesn't hear progress at the front end, doesn't mean the mission has stopped.
- Tenants (and users) write in search conditions, not ex post filters. field is separated from conversation, retrieval, memory reading and writing; the relevant data should not be traced and then thrown out of the application code. Another back-office schedule will also be created with the tenant and will remain in Run status, which is in the session service.
This article is based on the practice of landings and wishes to be useful, useful and informative, with any questions and suggestions, and would welcome a joint exchange of views in the comment area.
Auto-fetched and translated by AI Atlas. All rights belong to the original author.