feat(proto): add RunAgent, CancelAnswerJob, Agent* messages
Extend AnswerJobState with RUNNING/CANCELLED/EXPIRED (values 5-7, backward compatible). Add AgentRequest/AgentResponse/AgentToolLoopOptions/ AgentResponseContract/AgentUsage/AgentOutputFormat and Cancel* messages. Existing RPCs unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
9c923913f6
commit
00e8f9b190
101
ai.proto
101
ai.proto
@ -46,12 +46,18 @@ message GetAnswerJobRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AnswerJobState — состояние отложенной генерации ответа.
|
// AnswerJobState — состояние отложенной генерации ответа.
|
||||||
|
// Значения 0-4 исторические (GenerateAnswer deferred). Значения 5-7 добавлены для
|
||||||
|
// durable RunAgent jobs; старые клиенты их не создают и потому не увидят. RUNNING
|
||||||
|
// для старого клиента семантически эквивалентен PENDING (job ещё не терминальна).
|
||||||
enum AnswerJobState {
|
enum AnswerJobState {
|
||||||
ANSWER_JOB_STATE_UNSPECIFIED = 0;
|
ANSWER_JOB_STATE_UNSPECIFIED = 0;
|
||||||
ANSWER_JOB_STATE_PENDING = 1; // ещё выполняется (evolver работает / модель дописывает)
|
ANSWER_JOB_STATE_PENDING = 1; // ещё не взята в работу / выполняется (evolver работает, модель дописывает)
|
||||||
ANSWER_JOB_STATE_DONE = 2; // готово, content заполнен
|
ANSWER_JOB_STATE_DONE = 2; // готово, content заполнен
|
||||||
ANSWER_JOB_STATE_FAILED = 3; // ошибка, error заполнен
|
ANSWER_JOB_STATE_FAILED = 3; // ошибка, error заполнен
|
||||||
ANSWER_JOB_STATE_NOT_FOUND = 4; // неизвестный/протухший job_id (напр. после рестарта core)
|
ANSWER_JOB_STATE_NOT_FOUND = 4; // неизвестный/протухший job_id (напр. после рестарта core)
|
||||||
|
ANSWER_JOB_STATE_RUNNING = 5; // взята воркером (lease активен), tool loop исполняется
|
||||||
|
ANSWER_JOB_STATE_CANCELLED = 6; // отменена через CancelAnswerJob
|
||||||
|
ANSWER_JOB_STATE_EXPIRED = 7; // истёк expiration до достижения терминального состояния
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnswerJobResponse — результат опроса отложенной генерации.
|
// AnswerJobResponse — результат опроса отложенной генерации.
|
||||||
@ -117,8 +123,101 @@ message HealthCheckResponse {
|
|||||||
string version = 2;
|
string version = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// RunAgent: надёжный агентный вызов (server-side tool filtering, deferred jobs).
|
||||||
|
// В отличие от GenerateAnswer, НЕ подмешивает Telegram/Steam-персонализацию и не
|
||||||
|
// требует telegram_user_id/steam_user_id. Вызывающая сторона полностью владеет
|
||||||
|
// массивом messages (включая system). Требует service-to-service авторизации
|
||||||
|
// (Bearer service-token в gRPC metadata) — agent_id/tool_profile сами по себе не
|
||||||
|
// являются авторизацией.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// AgentToolLoopOptions задаёт лимиты tool loop. Значения клиента могут только
|
||||||
|
// сузить серверные потолки (max_tool_iterations/max_total_tool_calls ≤ server max).
|
||||||
|
message AgentToolLoopOptions {
|
||||||
|
bool tools_enabled = 1; // false — модель отвечает без tools
|
||||||
|
uint32 max_tool_iterations = 2; // потолок round-trip'ов модель↔tools (0 = серверный default)
|
||||||
|
uint32 max_total_tool_calls = 3; // потолок РЕАЛЬНЫХ tool call'ов, не итераций (0 = серверный default)
|
||||||
|
uint32 tool_timeout_seconds = 4; // таймаут одного tool call (0 = серверный default)
|
||||||
|
uint32 total_timeout_seconds = 5; // wall-clock таймаут всего вызова (0 = серверный default), независим от budget
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentOutputFormat — требуемый формат финального ответа.
|
||||||
|
enum AgentOutputFormat {
|
||||||
|
AGENT_OUTPUT_FORMAT_UNSPECIFIED = 0; // трактуется как TEXT
|
||||||
|
AGENT_OUTPUT_FORMAT_TEXT = 1; // произвольный текст
|
||||||
|
AGENT_OUTPUT_FORMAT_JSON = 2; // должен быть валидный JSON (без schema-проверки)
|
||||||
|
AGENT_OUTPUT_FORMAT_JSON_SCHEMA = 3; // валидный JSON, проходящий json_schema
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentResponseContract описывает требования к структурированному ответу.
|
||||||
|
message AgentResponseContract {
|
||||||
|
AgentOutputFormat format = 1;
|
||||||
|
string json_schema = 2; // JSON Schema (draft-07), обязателен при format=JSON_SCHEMA
|
||||||
|
bool repair_invalid_output = 3; // при провале валидации — один repair-запрос модели (без новых tools)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentRequest — запрос агентного вызова.
|
||||||
|
message AgentRequest {
|
||||||
|
repeated StatelessMessage messages = 1; // полный контекст, включая system
|
||||||
|
string model_name = 2; // модель (по умолчанию deepseek-v4-flash)
|
||||||
|
bool enable_reasoning = 3; // reasoning не попадает в обычный content
|
||||||
|
|
||||||
|
string agent_id = 4; // идентификатор логического агента (для логов/метрик, НЕ авторизация)
|
||||||
|
string tool_profile = 5; // серверный профиль tools (НЕ авторизация; проверяется против credential)
|
||||||
|
repeated string requested_tools = 6; // сужение профиля: effective = profile.allowed ∩ requested; пусто = все allowed
|
||||||
|
|
||||||
|
AgentToolLoopOptions tool_loop = 7;
|
||||||
|
AgentResponseContract response_contract = 8;
|
||||||
|
|
||||||
|
string request_id = 9; // сквозной идентификатор для трассировки
|
||||||
|
string idempotency_key = 10; // повторный RunAgent с тем же ключом не создаёт дубликат job
|
||||||
|
bool supports_deferred = 11; // клиент умеет status=PENDING + GetAnswerJob для тяжёлых операций
|
||||||
|
|
||||||
|
map<string, string> metadata = 12; // произвольные метки (не логируются как секреты)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentUsage — счётчики использования за вызов.
|
||||||
|
message AgentUsage {
|
||||||
|
uint32 tool_iterations = 1; // сколько round-trip'ов модель↔tools произошло
|
||||||
|
uint32 total_tool_calls = 2; // сколько РЕАЛЬНЫХ tool call'ов исполнено (3 calls в одной итерации = 3)
|
||||||
|
repeated string tools_used = 3;
|
||||||
|
uint64 input_tokens = 4;
|
||||||
|
uint64 output_tokens = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentResponse — результат агентного вызова.
|
||||||
|
message AgentResponse {
|
||||||
|
AnswerStatus status = 1; // COMPLETE — content финальный; PENDING — тянуть через GetAnswerJob(job_id)
|
||||||
|
string content = 2;
|
||||||
|
string error = 3;
|
||||||
|
double duration_ms = 4;
|
||||||
|
string job_id = 5; // при status=PENDING
|
||||||
|
AgentUsage usage = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelAnswerJobRequest — запрос отмены отложенной job (идемпотентный).
|
||||||
|
message CancelAnswerJobRequest {
|
||||||
|
string job_id = 1;
|
||||||
|
string reason = 2; // причина отмены (сохраняется), опционально
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelAnswerJobResponse — результат отмены.
|
||||||
|
message CancelAnswerJobResponse {
|
||||||
|
AnswerJobState state = 1; // состояние job после запроса (CANCELLED, либо терминальное, если уже завершилась)
|
||||||
|
bool cancelled = 2; // true — отмена применена этим вызовом; false — job уже была терминальна/отменена
|
||||||
|
string error = 3;
|
||||||
|
}
|
||||||
|
|
||||||
// AIService - основной сервис для работы с AI
|
// AIService - основной сервис для работы с AI
|
||||||
service AIService {
|
service AIService {
|
||||||
|
// RunAgent — надёжный агентный вызов с серверной фильтрацией tools и deferred
|
||||||
|
// jobs. Требует service-to-service авторизации.
|
||||||
|
rpc RunAgent(AgentRequest) returns (AgentResponse);
|
||||||
|
|
||||||
|
// CancelAnswerJob отменяет отложенную job (идемпотентно). Требует авторизации.
|
||||||
|
rpc CancelAnswerJob(CancelAnswerJobRequest) returns (CancelAnswerJobResponse);
|
||||||
|
|
||||||
// GenerateAnswer генерирует ответ на запрос
|
// GenerateAnswer генерирует ответ на запрос
|
||||||
rpc GenerateAnswer(AIRequest) returns (AIResponse);
|
rpc GenerateAnswer(AIRequest) returns (AIResponse);
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.6.1
|
// - protoc-gen-go-grpc v1.6.1
|
||||||
// - protoc v7.34.0
|
// - protoc v7.34.0
|
||||||
// source: proto/l-ai-core-proto/ai.proto
|
// source: ai.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
@ -19,6 +19,8 @@ import (
|
|||||||
const _ = grpc.SupportPackageIsVersion9
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
AIService_RunAgent_FullMethodName = "/ai.AIService/RunAgent"
|
||||||
|
AIService_CancelAnswerJob_FullMethodName = "/ai.AIService/CancelAnswerJob"
|
||||||
AIService_GenerateAnswer_FullMethodName = "/ai.AIService/GenerateAnswer"
|
AIService_GenerateAnswer_FullMethodName = "/ai.AIService/GenerateAnswer"
|
||||||
AIService_GenerateStateless_FullMethodName = "/ai.AIService/GenerateStateless"
|
AIService_GenerateStateless_FullMethodName = "/ai.AIService/GenerateStateless"
|
||||||
AIService_HealthCheck_FullMethodName = "/ai.AIService/HealthCheck"
|
AIService_HealthCheck_FullMethodName = "/ai.AIService/HealthCheck"
|
||||||
@ -33,6 +35,11 @@ const (
|
|||||||
//
|
//
|
||||||
// AIService - основной сервис для работы с AI
|
// AIService - основной сервис для работы с AI
|
||||||
type AIServiceClient interface {
|
type AIServiceClient interface {
|
||||||
|
// RunAgent — надёжный агентный вызов с серверной фильтрацией tools и deferred
|
||||||
|
// jobs. Требует service-to-service авторизации.
|
||||||
|
RunAgent(ctx context.Context, in *AgentRequest, opts ...grpc.CallOption) (*AgentResponse, error)
|
||||||
|
// CancelAnswerJob отменяет отложенную job (идемпотентно). Требует авторизации.
|
||||||
|
CancelAnswerJob(ctx context.Context, in *CancelAnswerJobRequest, opts ...grpc.CallOption) (*CancelAnswerJobResponse, error)
|
||||||
// GenerateAnswer генерирует ответ на запрос
|
// GenerateAnswer генерирует ответ на запрос
|
||||||
GenerateAnswer(ctx context.Context, in *AIRequest, opts ...grpc.CallOption) (*AIResponse, error)
|
GenerateAnswer(ctx context.Context, in *AIRequest, opts ...grpc.CallOption) (*AIResponse, error)
|
||||||
// GenerateStateless генерирует ответ из массива messages (system+history+user)
|
// GenerateStateless генерирует ответ из массива messages (system+history+user)
|
||||||
@ -56,6 +63,26 @@ func NewAIServiceClient(cc grpc.ClientConnInterface) AIServiceClient {
|
|||||||
return &aIServiceClient{cc}
|
return &aIServiceClient{cc}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *aIServiceClient) RunAgent(ctx context.Context, in *AgentRequest, opts ...grpc.CallOption) (*AgentResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(AgentResponse)
|
||||||
|
err := c.cc.Invoke(ctx, AIService_RunAgent_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aIServiceClient) CancelAnswerJob(ctx context.Context, in *CancelAnswerJobRequest, opts ...grpc.CallOption) (*CancelAnswerJobResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(CancelAnswerJobResponse)
|
||||||
|
err := c.cc.Invoke(ctx, AIService_CancelAnswerJob_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *aIServiceClient) GenerateAnswer(ctx context.Context, in *AIRequest, opts ...grpc.CallOption) (*AIResponse, error) {
|
func (c *aIServiceClient) GenerateAnswer(ctx context.Context, in *AIRequest, opts ...grpc.CallOption) (*AIResponse, error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
out := new(AIResponse)
|
out := new(AIResponse)
|
||||||
@ -122,6 +149,11 @@ func (c *aIServiceClient) GetAnswerJob(ctx context.Context, in *GetAnswerJobRequ
|
|||||||
//
|
//
|
||||||
// AIService - основной сервис для работы с AI
|
// AIService - основной сервис для работы с AI
|
||||||
type AIServiceServer interface {
|
type AIServiceServer interface {
|
||||||
|
// RunAgent — надёжный агентный вызов с серверной фильтрацией tools и deferred
|
||||||
|
// jobs. Требует service-to-service авторизации.
|
||||||
|
RunAgent(context.Context, *AgentRequest) (*AgentResponse, error)
|
||||||
|
// CancelAnswerJob отменяет отложенную job (идемпотентно). Требует авторизации.
|
||||||
|
CancelAnswerJob(context.Context, *CancelAnswerJobRequest) (*CancelAnswerJobResponse, error)
|
||||||
// GenerateAnswer генерирует ответ на запрос
|
// GenerateAnswer генерирует ответ на запрос
|
||||||
GenerateAnswer(context.Context, *AIRequest) (*AIResponse, error)
|
GenerateAnswer(context.Context, *AIRequest) (*AIResponse, error)
|
||||||
// GenerateStateless генерирует ответ из массива messages (system+history+user)
|
// GenerateStateless генерирует ответ из массива messages (system+history+user)
|
||||||
@ -145,6 +177,12 @@ type AIServiceServer interface {
|
|||||||
// pointer dereference when methods are called.
|
// pointer dereference when methods are called.
|
||||||
type UnimplementedAIServiceServer struct{}
|
type UnimplementedAIServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedAIServiceServer) RunAgent(context.Context, *AgentRequest) (*AgentResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method RunAgent not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedAIServiceServer) CancelAnswerJob(context.Context, *CancelAnswerJobRequest) (*CancelAnswerJobResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method CancelAnswerJob not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedAIServiceServer) GenerateAnswer(context.Context, *AIRequest) (*AIResponse, error) {
|
func (UnimplementedAIServiceServer) GenerateAnswer(context.Context, *AIRequest) (*AIResponse, error) {
|
||||||
return nil, status.Error(codes.Unimplemented, "method GenerateAnswer not implemented")
|
return nil, status.Error(codes.Unimplemented, "method GenerateAnswer not implemented")
|
||||||
}
|
}
|
||||||
@ -184,6 +222,42 @@ func RegisterAIServiceServer(s grpc.ServiceRegistrar, srv AIServiceServer) {
|
|||||||
s.RegisterService(&AIService_ServiceDesc, srv)
|
s.RegisterService(&AIService_ServiceDesc, srv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _AIService_RunAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(AgentRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(AIServiceServer).RunAgent(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: AIService_RunAgent_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(AIServiceServer).RunAgent(ctx, req.(*AgentRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _AIService_CancelAnswerJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CancelAnswerJobRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(AIServiceServer).CancelAnswerJob(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: AIService_CancelAnswerJob_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(AIServiceServer).CancelAnswerJob(ctx, req.(*CancelAnswerJobRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _AIService_GenerateAnswer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _AIService_GenerateAnswer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(AIRequest)
|
in := new(AIRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
@ -299,6 +373,14 @@ var AIService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
ServiceName: "ai.AIService",
|
ServiceName: "ai.AIService",
|
||||||
HandlerType: (*AIServiceServer)(nil),
|
HandlerType: (*AIServiceServer)(nil),
|
||||||
Methods: []grpc.MethodDesc{
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "RunAgent",
|
||||||
|
Handler: _AIService_RunAgent_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CancelAnswerJob",
|
||||||
|
Handler: _AIService_CancelAnswerJob_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "GenerateAnswer",
|
MethodName: "GenerateAnswer",
|
||||||
Handler: _AIService_GenerateAnswer_Handler,
|
Handler: _AIService_GenerateAnswer_Handler,
|
||||||
@ -325,5 +407,5 @@ var AIService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "proto/l-ai-core-proto/ai.proto",
|
Metadata: "ai.proto",
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user