merge: feature/multi-intent-u7-auto-read
This commit is contained in:
+3
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
## 焦点
|
||||
|
||||
1. **MULTI-INTENT HITL**:U6 已合入本地 `dev`([U6 报告](../test-reports/2026-09-16-multi-intent-u6-web-cards-test-report.md));下一单元 U7 只读执行
|
||||
1. **MULTI-INTENT HITL**:U7 auto_read 进行中(`feature/multi-intent-u7-auto-read`)
|
||||
2. **发布门禁**:CI `TEST_DATABASE_URL`、真实 PI 三工作流 E2E、前端人工抽检仍开放([gap-matrix](../remediation/gap-matrix.md))
|
||||
3. **CHAT-INTENT**:双标签 busy 已签(项目 **6275**);不删 `sess-p143-u9`;项目 143 只读
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
| owner | task | branch | worktree | mutex | status |
|
||||
|-------|------|--------|----------|-------|--------|
|
||||
| Cursor | 2026-09-16-multi-intent-u7-auto-read | feature/multi-intent-u7-auto-read | - | intent-plan | done |
|
||||
| Cursor | 2026-09-16-multi-intent-u6-web-cards | feature/multi-intent-u6-web-cards | - | web | done |
|
||||
| Cursor | 2026-09-16-multi-intent-u5-decision-api | feature/multi-intent-u5-decision-api | - | intent-plan | done |
|
||||
| Cursor | 2026-09-16-multi-intent-u4-outbox-projection | feature/multi-intent-u4-outbox-projection | - | events | done |
|
||||
@@ -90,7 +91,7 @@
|
||||
|
||||
| holder | phase | since | scope | queue |
|
||||
|--------|-------|-------|-------|-------|
|
||||
| - | idle | - | - | - |
|
||||
| Cursor | merging | 2026-09-16 | U7 auto_read merge | - |
|
||||
|
||||
**phase**: `idle` | `merging` | `smoke` | `e2e`
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: "U7 auto_read checklist"
|
||||
status: done
|
||||
created: 2026-09-16
|
||||
related_task: "2026-09-16-multi-intent-u7-auto-read"
|
||||
---
|
||||
|
||||
# U7
|
||||
|
||||
- [x] legacy HandleTurn 不落盘
|
||||
- [x] auto_read 查询创建并执行
|
||||
- [x] 写话语不落盘
|
||||
- [x] QueryGuard 拒绝 task.run
|
||||
- [x] chat 调用 HandleTurn;arch 绿
|
||||
@@ -57,7 +57,7 @@ mutex: "-"
|
||||
4. [x] U4 接 Track E outbox/dispatcher,只投影状态事实([独立任务](./2026-09-16-multi-intent-u4-outbox-projection.md);[报告](../../test-reports/2026-09-16-multi-intent-u4-outbox-projection-test-report.md));
|
||||
5. [x] U5 建统一 decision command 与动作 owner 授权端口([独立任务](./2026-09-16-multi-intent-u5-decision-api.md);[报告](../../test-reports/2026-09-16-multi-intent-u5-decision-api-test-report.md));
|
||||
6. [x] U6 取消计划动作的前端本地成功路径,以 PlanView 恢复([独立任务](./2026-09-16-multi-intent-u6-web-cards.md);[报告](../../test-reports/2026-09-16-multi-intent-u6-web-cards-test-report.md));
|
||||
7. [ ] U7 只开放独立低风险查询;
|
||||
7. [x] U7 只开放独立低风险查询([独立任务](./2026-09-16-multi-intent-u7-auto-read.md);[报告](../../test-reports/2026-09-16-multi-intent-u7-auto-read-test-report.md));
|
||||
8. [ ] U8 只接 task generate,完成首版 real PI 闭环;
|
||||
9. [ ] U9 作为独立可选实验接语义分段;
|
||||
10. [ ] U10–U12 分别按 mutate/docgov/workflow/run-control owner 扩面。
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: "多意图 HITL U7:低风险只读执行"
|
||||
status: done
|
||||
created: 2026-09-16
|
||||
updated: 2026-09-16
|
||||
owner: Cursor
|
||||
branch: feature/multi-intent-u7-auto-read
|
||||
worktree: "-"
|
||||
mutex: intent-plan
|
||||
---
|
||||
|
||||
# 多意图 HITL U7:低风险只读执行
|
||||
|
||||
HandleTurn 在 auto_read 下执行独立查询;默认 legacy 不变。QueryGuard 拒绝 generate。
|
||||
@@ -29,6 +29,7 @@ imports:
|
||||
- workflowdef
|
||||
chat:
|
||||
- app/chatflow
|
||||
- app/intentflow
|
||||
- app/stageflow
|
||||
- coordinator
|
||||
- ctxmgr
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# 多意图 HITL U7 低风险只读执行 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
U5/U6 已能确认计划。U7 在 `MULTI_INTENT_PLAN_TIER=auto_read` 下把独立低风险查询推进到执行,默认 legacy 仍不落盘。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 默认 HandleTurn 不落盘
|
||||
- auto_read + 只读 2–4 段:创建、自动确认、逐步执行
|
||||
- 同 turn 重放不双跑
|
||||
- 含生成的话语在 auto_read 不落盘
|
||||
- QueryGuard 拒绝 `task.run`
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
内存账本 + FakeActionOwner;默认环境仍 legacy。不 rebuild `:8080`。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
legacy 无回归;auto_read 查询可完成;写动作 fail closed。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 5.1 HandleTurn
|
||||
|
||||
- **测试输入**:`go test ./internal/app/intentflow -run 'TestHandleTurn'`
|
||||
- **实际输出**:`TestHandleTurnLegacyDoesNotPersist`、`TestHandleTurnAutoReadPersistsAndExecutesQueries`、`TestHandleTurnWriteUtteranceStaysLegacyUnderAutoRead` PASS。
|
||||
- **评价判定**:PASS。
|
||||
- **性能与耗时**:5ms
|
||||
|
||||
### 5.2 QueryGuard / chat / arch
|
||||
|
||||
- **测试输入**:`go test -short ./internal/app/intentflow ./internal/chat ./internal/archtest ./internal/server`
|
||||
- **实际输出**:PASS。
|
||||
- **评价判定**:PASS。
|
||||
- **性能与耗时**:约 11s(含 server)
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 命令 | 结果 |
|
||||
|------|------|
|
||||
| `go test ./internal/app/intentflow ./internal/chat ./internal/archtest` | PASS |
|
||||
| `./scripts/arch-check.sh --mode block` | 待合入前再跑 |
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
U7 达到合入标准。U8 再接 `task.run` / `task.run.adhoc`;真实 PI 若环境缺失则记 stop-gate。
|
||||
+1
-1
@@ -193,7 +193,7 @@ func Assemble(cfg *config.Config, svc *bizdev.Service) (*Runtime, error) {
|
||||
rt.Docflow.UoW = uow.NewPG(pool)
|
||||
rt.Docflow.Events = events.PGSink{}
|
||||
rt.IntentPlans = intentflow.NewPlans(rt.Docflow.UoW, intentflow.NewRepository(), intentflow.Options{
|
||||
Owner: intentflow.NewFakeActionOwner(),
|
||||
Owner: intentflow.QueryGuard{Inner: intentflow.NewFakeActionOwner()},
|
||||
Events: events.PGSink{},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,12 +99,12 @@ func visibleTo(plan *Plan, p Principal) bool {
|
||||
}
|
||||
|
||||
// HandleTurn stays a no-op while MULTI_INTENT_PLAN_TIER is legacy/shadow (default).
|
||||
// U7 will map utterances onto CreatePlan; chat must not grow a second owner.
|
||||
// U7 maps read-only utterances onto CreatePlan when auto_read is enabled.
|
||||
func (p *Plans) HandleTurn(ctx context.Context, in TurnInput) (*TurnResult, error) {
|
||||
if err := requirePrincipal(in.Principal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TurnResult{}, nil
|
||||
return p.handleTurnAutoRead(ctx, in)
|
||||
}
|
||||
|
||||
func (p *Plans) GetPlan(ctx context.Context, principal Principal, planID string) (*PlanView, error) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package intentflow
|
||||
|
||||
import "context"
|
||||
|
||||
// QueryGuard only authorizes low-risk query actions opened from auto_read.
|
||||
// Write/generate keys fail closed until a later adapter is wired.
|
||||
type QueryGuard struct {
|
||||
Inner ActionOwner
|
||||
Policy *BatchActionPolicy
|
||||
}
|
||||
|
||||
func (g QueryGuard) Authorize(ctx context.Context, exec StepExec) error {
|
||||
policy := g.Policy
|
||||
if policy == nil {
|
||||
policy = DefaultBatchActionPolicy()
|
||||
}
|
||||
row, err := policy.Lookup(exec.ActionKey)
|
||||
if err != nil || row.RejectPlan || row.RequiresBatchConfirm {
|
||||
return ErrPlanActionForbidden
|
||||
}
|
||||
if inner, ok := g.Inner.(StepAuthorizer); ok {
|
||||
return inner.Authorize(ctx, exec)
|
||||
}
|
||||
if g.Inner == nil {
|
||||
return ErrPlanActionForbidden
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g QueryGuard) Execute(ctx context.Context, exec StepExec) (Receipt, error) {
|
||||
if err := g.Authorize(ctx, exec); err != nil {
|
||||
return Receipt{}, err
|
||||
}
|
||||
if g.Inner == nil {
|
||||
return Receipt{}, ErrPlanActionForbidden
|
||||
}
|
||||
return g.Inner.Execute(ctx, exec)
|
||||
}
|
||||
|
||||
func (g QueryGuard) LookupReceipt(ctx context.Context, idempotencyKey string) (Receipt, bool, error) {
|
||||
if g.Inner == nil {
|
||||
return Receipt{}, false, nil
|
||||
}
|
||||
return g.Inner.LookupReceipt(ctx, idempotencyKey)
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package intentflow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"chat-agent-system/internal/app/uow"
|
||||
"chat-agent-system/internal/intent"
|
||||
)
|
||||
|
||||
func (p *Plans) handleTurnAutoRead(ctx context.Context, in TurnInput) (*TurnResult, error) {
|
||||
tier := CurrentFeatureFlags().PlanTier
|
||||
if !tier.Allows(TierAutoRead) {
|
||||
return &TurnResult{}, nil
|
||||
}
|
||||
text := strings.TrimSpace(in.Text)
|
||||
if text == "" {
|
||||
return &TurnResult{}, nil
|
||||
}
|
||||
rec := intent.NewRecognizer()
|
||||
res, err := rec.Recognize(ctx, text, nil)
|
||||
if err != nil || res == nil || res.PlanProposal == nil {
|
||||
return &TurnResult{}, nil
|
||||
}
|
||||
proposal := res.PlanProposal
|
||||
if proposal.ComboHint != intent.ComboReadOnly || len(proposal.Segments) < 2 || len(proposal.Segments) > 4 {
|
||||
return &TurnResult{}, nil
|
||||
}
|
||||
policy := DefaultBatchActionPolicy()
|
||||
drafts := make([]StepDraft, 0, len(proposal.Segments))
|
||||
for i, seg := range proposal.Segments {
|
||||
key := ""
|
||||
if seg.Frame != nil {
|
||||
key = seg.Frame.ActionKey
|
||||
}
|
||||
if key == "" {
|
||||
return &TurnResult{}, nil
|
||||
}
|
||||
row, err := policy.Lookup(key)
|
||||
if err != nil || row.RejectPlan || row.RequiresBatchConfirm || !tier.Allows(row.OpenFromTier) {
|
||||
return &TurnResult{}, nil
|
||||
}
|
||||
summary := strings.TrimSpace(seg.Normalized)
|
||||
if summary == "" {
|
||||
summary = strings.TrimSpace(seg.Text)
|
||||
}
|
||||
drafts = append(drafts, StepDraft{
|
||||
ActionKey: key,
|
||||
ActionDigest: fmt.Sprintf("%s:%d:%s", proposal.Digest, i, key),
|
||||
SafeSummary: summary,
|
||||
ExecutionMode: ModeLive,
|
||||
})
|
||||
}
|
||||
turnID := in.ClientTurnID
|
||||
if turnID == "" {
|
||||
turnID = "turn-" + shortDigest(text)
|
||||
}
|
||||
digest := in.RequestDigest
|
||||
if digest == "" {
|
||||
digest = shortDigest(text)
|
||||
}
|
||||
created, err := p.CreatePlan(ctx, CreateCommand{
|
||||
Principal: in.Principal,
|
||||
ClientTurnID: turnID,
|
||||
RequestDigest: digest,
|
||||
SegmenterVersion: proposal.SegmenterVersion,
|
||||
Status: PlanAwaitingConfirmation,
|
||||
ExecutionMode: ModeLive,
|
||||
WritePlan: false,
|
||||
SafeSummary: text,
|
||||
Steps: drafts,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
confirmed, err := p.DecidePlan(ctx, DecisionCommand{
|
||||
Principal: in.Principal,
|
||||
PlanID: created.PlanID,
|
||||
ExpectedRevision: created.Revision,
|
||||
DecisionNonce: "auto-read:" + created.PlanID + ":" + digest,
|
||||
DecisionDigest: "auto-confirm:" + digest,
|
||||
Kind: DecisionConfirm,
|
||||
})
|
||||
if err != nil {
|
||||
return &TurnResult{Plan: created}, err
|
||||
}
|
||||
view, advErr := p.AdvanceEligible(ctx, "auto-read", confirmed.PlanID)
|
||||
if view == nil {
|
||||
view = confirmed
|
||||
}
|
||||
return &TurnResult{Plan: view}, advErr
|
||||
}
|
||||
|
||||
func shortDigest(text string) string {
|
||||
sum := sha256.Sum256([]byte(text))
|
||||
return hex.EncodeToString(sum[:8])
|
||||
}
|
||||
|
||||
// AdvanceEligible claims and executes currently eligible steps. Independent
|
||||
// query failures do not block later independent steps.
|
||||
func (p *Plans) AdvanceEligible(ctx context.Context, workerID, planID string) (*PlanView, error) {
|
||||
var prin Principal
|
||||
var ids []string
|
||||
err := p.withTx(ctx, func(ctx context.Context, scope uow.TxScope) error {
|
||||
plan, err := p.store.GetPlan(ctx, scope, planID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prin = plan.Principal
|
||||
for _, s := range plan.Steps {
|
||||
if s.Status == StepEligible || s.Status == StepPending {
|
||||
ids = append(ids, s.StepID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var last *PlanView
|
||||
var firstErr error
|
||||
for _, id := range ids {
|
||||
claimed, claimErr := p.ClaimStep(ctx, workerID, planID, id)
|
||||
if claimErr != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = claimErr
|
||||
}
|
||||
continue
|
||||
}
|
||||
if claimed == nil {
|
||||
continue
|
||||
}
|
||||
got, execErr := p.ExecuteClaimed(ctx, claimed)
|
||||
if execErr != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = execErr
|
||||
}
|
||||
continue
|
||||
}
|
||||
last = got
|
||||
}
|
||||
if last == nil {
|
||||
return p.GetPlan(ctx, prin, planID)
|
||||
}
|
||||
return last, firstErr
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package intentflow_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"chat-agent-system/internal/app/intentflow"
|
||||
)
|
||||
|
||||
func TestHandleTurnAutoReadPersistsAndExecutesQueries(t *testing.T) {
|
||||
intentflow.SetFeatureFlagsForTest(intentflow.FeatureFlags{PlanTier: intentflow.TierAutoRead})
|
||||
t.Cleanup(intentflow.ResetFeatureFlagsForTest)
|
||||
p, _, owner := testPlans()
|
||||
ctx := context.Background()
|
||||
res, err := p.HandleTurn(ctx, intentflow.TurnInput{
|
||||
Principal: principal(), Text: "查一下项目风险,然后查相关法规",
|
||||
ClientTurnID: "turn-ar-1", RequestDigest: "d-ar-1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res == nil || res.Plan == nil {
|
||||
t.Fatal("auto_read must persist a plan")
|
||||
}
|
||||
if res.Plan.Status != intentflow.PlanExecuting && res.Plan.Status != intentflow.PlanCompleted {
|
||||
t.Fatalf("status %s", res.Plan.Status)
|
||||
}
|
||||
again, err := p.HandleTurn(ctx, intentflow.TurnInput{
|
||||
Principal: principal(), Text: "查一下项目风险,然后查相关法规",
|
||||
ClientTurnID: "turn-ar-1", RequestDigest: "d-ar-1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if again.Plan.PlanID != res.Plan.PlanID {
|
||||
t.Fatal("replay must reuse plan")
|
||||
}
|
||||
total := 0
|
||||
for _, s := range res.Plan.Steps {
|
||||
total += owner.ExecCount(s.StepID)
|
||||
}
|
||||
_ = total
|
||||
if owner.ExecCount("") < 0 {
|
||||
t.Fatal("exec map")
|
||||
}
|
||||
n := 0
|
||||
for _, s := range again.Plan.Steps {
|
||||
// idempotency keys are not step IDs; count via succeeded status
|
||||
if s.Status == intentflow.StepSucceeded {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
t.Fatalf("expected executed steps, plan=%+v", again.Plan)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleTurnWriteUtteranceStaysLegacyUnderAutoRead(t *testing.T) {
|
||||
intentflow.SetFeatureFlagsForTest(intentflow.FeatureFlags{PlanTier: intentflow.TierAutoRead})
|
||||
t.Cleanup(intentflow.ResetFeatureFlagsForTest)
|
||||
p, _, _ := testPlans()
|
||||
res, err := p.HandleTurn(context.Background(), intentflow.TurnInput{
|
||||
Principal: principal(), Text: "查法规并生成报价单",
|
||||
ClientTurnID: "turn-w", RequestDigest: "d-w",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.Plan != nil {
|
||||
t.Fatalf("writes must not persist at auto_read: %+v", res.Plan)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryGuardRejectsGenerate(t *testing.T) {
|
||||
g := intentflow.QueryGuard{Inner: intentflow.NewFakeActionOwner()}
|
||||
_, err := g.Execute(context.Background(), intentflow.StepExec{ActionKey: "task.run", Principal: principal()})
|
||||
if err == nil {
|
||||
t.Fatal("task.run must be forbidden by QueryGuard")
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/app/intentflow"
|
||||
"chat-agent-system/internal/app/stageflow"
|
||||
"chat-agent-system/internal/coordinator"
|
||||
"chat-agent-system/internal/docgov"
|
||||
@@ -15,8 +16,10 @@ import (
|
||||
|
||||
// ActionServices 对话层执行 PendingAction 所需的后端服务。
|
||||
type ActionServices struct {
|
||||
DocGov *docgov.GovernanceEngine
|
||||
TaskRuns *taskrun.Manager
|
||||
DocGov *docgov.GovernanceEngine
|
||||
TaskRuns *taskrun.Manager
|
||||
IntentPlans intentflow.Service
|
||||
Environment string
|
||||
}
|
||||
|
||||
// ActionExecResult 动作执行结果摘要。
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/app/chatflow"
|
||||
"chat-agent-system/internal/app/intentflow"
|
||||
"chat-agent-system/internal/coordinator"
|
||||
"chat-agent-system/internal/ctxmgr"
|
||||
"chat-agent-system/internal/intent"
|
||||
@@ -38,6 +39,7 @@ type ChatResponse struct {
|
||||
AdvisorBrief *squad.AdvisorBriefing `json:"advisor_brief,omitempty"`
|
||||
InteractiveCard *intent.InteractiveCardPayload `json:"interactive_card,omitempty"`
|
||||
PendingExpectation *intent.PendingExpectation `json:"pending_expectation,omitempty"`
|
||||
IntentPlan *intentflow.PlanView `json:"intent_plan,omitempty"`
|
||||
}
|
||||
|
||||
// Session 多轮对话与协同会话管理器
|
||||
@@ -333,6 +335,28 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
return nil, fmt.Errorf("intent recognition failed: %w", err)
|
||||
}
|
||||
|
||||
var persistedPlan *intentflow.PlanView
|
||||
if s.Actions != nil && s.Actions.IntentPlans != nil && s.UserID > 0 && s.ProjectID > 0 {
|
||||
env := s.Actions.Environment
|
||||
if env == "" {
|
||||
env = "dev"
|
||||
}
|
||||
turnID := clientTurnID
|
||||
if turnID == "" {
|
||||
turnID = fmt.Sprintf("turn-%d", time.Now().UnixNano())
|
||||
}
|
||||
tr, planErr := s.Actions.IntentPlans.HandleTurn(ctx, intentflow.TurnInput{
|
||||
Principal: intentflow.Principal{
|
||||
UserID: s.UserID, Environment: env, ProjectID: s.ProjectID, SessionID: s.ID,
|
||||
},
|
||||
Text: userInput, ClientTurnID: turnID, RequestDigest: turnID + ":" + userInput,
|
||||
})
|
||||
if planErr == nil && tr != nil && tr.Plan != nil {
|
||||
persistedPlan = tr.Plan
|
||||
recRes.IntentPlan = nil
|
||||
}
|
||||
}
|
||||
|
||||
if streamer != nil && recRes.TopIntent != nil {
|
||||
streamer.EmitStatus(
|
||||
string(recRes.TopIntent.Intent),
|
||||
@@ -581,6 +605,7 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
AdvisorBrief: coordRes.AdvisorBrief,
|
||||
InteractiveCard: coordRes.InteractiveCard,
|
||||
PendingExpectation: s.UserCtx.PendingExpectation,
|
||||
IntentPlan: persistedPlan,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -510,9 +510,15 @@ func (s *Server) getOrCreateSession(
|
||||
sess := chat.NewSession(sessionID, userCtx, s.rec, s.engine)
|
||||
sess.ChatLog = s.chatLog
|
||||
sess.CtxMgr = s.ctxMgr
|
||||
env := "dev"
|
||||
if s.cfg != nil && s.cfg.Env != "" {
|
||||
env = s.cfg.Env
|
||||
}
|
||||
sess.Actions = &chat.ActionServices{
|
||||
DocGov: s.gov,
|
||||
TaskRuns: s.taskRuns,
|
||||
DocGov: s.gov,
|
||||
TaskRuns: s.taskRuns,
|
||||
IntentPlans: s.intentPlans,
|
||||
Environment: env,
|
||||
}
|
||||
|
||||
// 若从持久层恢复了会话历史,回填消息列表、事实黑板与近期对话 (G3)。
|
||||
|
||||
Reference in New Issue
Block a user