- Add injectSystemPromptOpenAI() for OpenAI messages format (role: system)
- Integrate injection into createOpenAIChatProxy before upstream call
- Update audit logs to track injection status
- Enables brand identity override for both Anthropic and OpenAI endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Anthropic msg_xxx IDs with opaque IDs, strip cache_creation,
service_tier, inference_geo fields. Replace OpenAI chatcmpl-xxx IDs,
strip system_fingerprint. Applied to both streaming and non-streaming.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin can configure modelOverride (actual upstream model) and modelAlias
(name shown to users) per API key. When set, users don't need to specify
the real model — the gateway substitutes it transparently in both requests
and responses (including SSE streams).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When users ask "香港有哪些移民途径" or similar overview questions,
the AI must use exact standard descriptions for each category.
QMAS: "基本门槛条件12项满足6项即可申请" — no mention of 综合计分.
Explicit ❌ prohibition on adding extra scoring criteria in overview.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The QMAS brief description in the category comparison table (Section 10.7)
should only state the 12-item threshold requirement, not the composite score.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both directive chat drawers were rendering AI responses as plain text.
Apply the same ReactMarkdown + remark-gfm treatment used in supervisor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admins can now write natural-language directives that get injected into the
assessment expert's system prompt. Directives are stored in DB, loaded per
execution, and support incremental additions, toggling, and full reset.
Backend:
- New assessment_directives table + ORM entity
- Admin CRUD API at /conversations/admin/assessment-directives
- buildAssessmentExpertPrompt() accepts optional adminDirectives param
- AssessmentExpertService loads active directives from DB before each execution
- Fail-safe: missing repo/tenant context → default prompt (no directives)
Frontend (admin-client):
- New "评估指令" page with table, create/edit modals, toggle switches
- Prompt preview panel showing assembled directive text
- Reset-to-default with confirmation
- React Query hooks for all CRUD operations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously the payment check on invoke_assessment_expert used
`&& this.paymentClient` which silently skipped the entire gate when
PaymentClientService was unavailable (DI failure / optional inject).
Now returns an explicit error when the payment service is unreachable,
preventing unpaid assessments from executing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full abort signal chain: Gateway → ConversationService → Coordinator →
ToolExecutor → BaseSpecialist → Claude API stream. Admin can toggle between
v1 (post-completion re-evaluation) and v2 (interruptible) via REST API.
Changes:
- Gateway: add cancel_stream WebSocket handler + active stream tracking
- Gateway: abort active stream on client disconnect
- ConversationService: accept + forward AbortSignal
- CoordinatorAgentService: link external AbortSignal to internal controller,
thread through tool executor, read assessment mode from Redis feature flag
- BaseSpecialistService: hard abort (throw) instead of soft break,
add abort signal to Promise.race in callClaude(), abort stream on cancel
- ImmigrationToolsService: thread abortSignal to assessment expert
- AdminObservabilityController: GET/PUT feature-flags/assessment-mode
(Redis-backed, defaults to v1)
v1 and v2 coexist — admin controls which mode is active.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When users correct or update personal info after assessment completion,
Coordinator can now re-run run_professional_assessment with forceReassess: true
to bypass the 30-day dedup and produce an updated report.
Changes:
- Add forceReassess boolean param to run_professional_assessment tool definition
- Skip already_assessed check when forceReassess=true in handler
- Add prompt rules for identifying info corrections and triggering re-evaluation
- Document the re-evaluation flow in sections 3.5 and 4.4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two hardening fixes for the professional assessment pipeline:
1. Code-level payment verification before dispatching invoke_assessment_expert
(prevents bypassing the prompt-only gate)
2. Thread onProgress callback through direct tool chain so run_professional_assessment
streams agent_progress events during the 30-45s assessment expert execution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces ad-hoc assessment flow with structured pipeline:
- Code-level payment verification (checks PAID ASSESSMENT order)
- Info completeness validation (age, nationality, education, work exp)
- Assessment expert invocation with result parsing
- Automatic persistence as UserArtifact (assessment_report type)
- 30-day dedup (existing report within 30 days returns cached)
- Frontend rendering for all status codes (completed, payment_required,
info_incomplete, already_assessed, error)
- System prompt updated to mandate new tool for paid assessments
- Post-assessment auto-generation of checklist + timeline
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 4 new tools (generate_document, manage_checklist, create_timeline,
query_user_artifacts) enabling the agent to create and manage persistent
user artifacts. Artifacts are saved to PostgreSQL and support dedup by
title, update-in-place, and cross-session querying. Frontend renders
rich UI cards for each artifact type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: expose circuit breaker status via new AdminObservabilityController
(health, circuit-breakers, redis endpoints). Frontend: new observability
feature in admin-client with auto-refreshing status cards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New RedisClientService: optional ioredis wrapper, gracefully degrades without REDIS_URL
- New RedisModule: global NestJS module providing Redis connectivity
- AgentCheckpoint interface: captures turn, messages, cost, agents, timestamp
- Agent loop saves checkpoint after each tool execution batch (TTL=10min)
- On restart with same conversationId+requestId, loads checkpoint and resumes from saved state
- Checkpoint auto-deleted after load to prevent stale recovery
- Coordinator injects @Optional() RedisClientService, builds save/load callbacks
- Zero impact when Redis is not configured — checkpoint silently skipped
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extend TenantStore with traceId + traceStartTime in AsyncLocalStorage
- Generate traceId (UUID-12) at WebSocket gateway entry point
- Propagate traceId through AgentLoopParams → agentLoop → specialists
- Add [trace:xxx] prefix to all logger calls in agent-loop, coordinator, and specialists
- Replace console.log with NestJS Logger in ConversationGateway
- Include traceId in stream_start event for frontend correlation
- Add traceId to AgentExecutionRecord and BaseStreamEvent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevent AI from revealing underlying model name (Claude/GPT/etc.) under any
circumstance including jailbreak attempts. Two defense layers:
- Prompt: anti-jailbreak rules + "小艾引擎" branding in coordinator system prompt
- Code: sanitizeModelLeaks() regex filter in agent-loop.ts streaming output
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. ClaudeModule missing ConversationORM in TypeOrmModule.forFeature —
ImmigrationToolsService now depends on ConversationORMRepository
(added in query_user_profile), but ClaudeModule only had TokenUsageORM.
Fix: add ConversationORM to ClaudeModule's TypeORM imports.
2. Historical messages show "支付创建失败" for payment QR codes —
toolCall.result is stored as JSON string in DB metadata JSONB.
Live streaming (useChat.ts) parses it correctly, but REST API
load (chatStore.ts → MessageBubble.tsx) does not.
Fix: normalize toolCall.result in ToolCallResult component —
JSON.parse if string, pass through if already object.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Alipay/WeChat adapters now return the source payment URL alongside the
QR base64. The generate_payment tool only returns paymentUrl (short text)
to Claude API — base64 qrCodeUrl is stripped to prevent AI from dumping
raw data:image into text responses. Frontend QRCodeSVG renders from
paymentUrl instead of base64.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
payment-service uses setGlobalPrefix('api/v1'), so all routes are
under /api/v1/orders, /api/v1/payments, etc. PaymentClientService
was calling /orders directly, resulting in 404:
Cannot POST /orders → 创建订单失败
Fixed all 7 endpoint URLs to include the /api/v1 prefix.
Same pattern as file-service fix (FILE_SERVICE_URL).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Problem
User asks "我适合哪种移民方式?" as their first message → AI immediately
mentions 99元 paid assessment. This is aggressive and off-putting for new
users who are just exploring.
## Root Cause
Intent table classified "适合什么" as assessment_request with instruction
to immediately mention 99元. This conflicts with the conversion philosophy
section that says "免费问答建立信任 → 付费评估".
## Fix (3 changes in coordinator-system-prompt.ts)
1. **Intent table**: assessment_request no longer says "immediately mention
99元". Instead references new handling rules below the table.
2. **New "评估请求处理规则" section** (after intent table):
- Early conversation + no user info → exploratory question, NOT
assessment request. Collect info first, give initial direction.
- User shared info + explicitly asks "做个评估" → real assessment
request, mention 99元.
- User shared info but didn't ask → give free initial direction,
don't proactively mention payment.
3. **Assessment suggestion timing** (section 5.6):
- Added 3 prerequisites before mentioning 99元:
a. At least 3 key info items collected
b. Already gave free initial direction (user felt value)
c. Conversation has gone 3-4+ rounds
- Added absolute prohibition: never mention 99元 in first response.
4. **Conversion boundary example**: Changed misleading "我适合走高才通吗
→ 需要评估" to nuanced guidance that distinguishes exploration from
genuine assessment requests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Problem
SDK's Zod validation for `output_config` occasionally fails with:
"Failed to parse structured output: invalid_value at path [intent]"
This crashes the entire response — user sees nothing despite model
generating a valid answer.
## Root Cause
The Anthropic SDK validates streamed structured output against the Zod
schema (CoordinatorResponseSchema) after streaming completes. When the
model returns an intent value not in the z.enum() (rare but happens),
the SDK throws during stream iteration or finalMessage().
## Fix
1. Catch "Failed to parse structured output" errors in both:
- Stream iteration catch block (for-await loop)
- stream.finalMessage() catch block
2. Recover by extracting accumulated text from assistantBlocks
3. Manual JSON.parse (skips Zod validation — intent enum mismatch
doesn't affect user-facing content)
4. Yield parsed.answer + parsed.followUp normally
## Also Included (from previous commit)
- Removed INTENT_MAX_ANSWER_LENGTH hard truncation (弊大于利)
- Only 2000-char safety net remains for extreme edge cases
- followUp: non-question content always appended (prevents content loss)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hard-coded INTENT_MAX_ANSWER_LENGTH limits caused mid-sentence truncation and
content loss. Length control now relies on prompt + schema description + LLM-Judge
(3 layers). Only a 2000-char safety net remains for extreme edge cases.
Also simplified followUp: non-question followUp is now always appended (prevents
model content split from silently dropping text).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
INTENT_MAX_ANSWER_LENGTH was too tight (objection_expression 200 chars truncated
good responses). Bumped all limits ~25-50%. Also fixed followUp filter that silently
dropped content when model split answer across answer+followUp fields — now appends
followUp as continuation when answer ends mid-sentence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P0: Enrich Chapter 10 with detailed policy facts (QMAS scoring, GEP A/B/C
conditions, FAQ quick answers) so Claude can answer common questions directly
without tool calls. Replace absolute rule "never answer from memory" with
3-tier system: Tier 1 (direct from Ch10), Tier 2 (search_knowledge), Tier 3
(invoke_policy_expert).
P1: Context injector now always returns a kb_coverage_hint block — when KB has
results it tells Claude to prefer KB over web_search; when KB has no results
it suggests considering web_search. Web_search tool description updated to
reference the hint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Short follow-up answers like "计算机,信息技术" were being classified as
OFF_TOPIC (0.85) because the InputGate has no conversation context. Now the
gate only runs when there are no previous messages (first message in conversation).
Mid-conversation topic management is handled by the Coordinator prompt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add section 5.6 "隐性转化引导" with trust-first conversion philosophy:
- Free facts vs paid analysis boundary
- "Taste-then-sell" strategy with positive but vague hints
- Assessment suggestion limited to max once per conversation
- Natural urgency only when fact-supported
- Post-assessment → full service transition only when user asks
- Anti-annoyance red line: never make user feel pushed to pay
Recalibrate info exchange (4.3): warm acknowledgment without deep analysis.
Add value framing (4.4) and post-assessment guidance (4.5).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update coordinator system prompt to enforce pricing rules:
- All assessments cost 99 RMB (one-time per user), no free assessments
- Must collect payment before calling assessment expert
- Add fee inquiry intent type to response strategy table
- Update generate_payment tool description with fixed pricing
- Replace "免费初步咨询" with tiered service model
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix auto-scroll by adding missing dependencies (currentConversationId, isStreaming,
completedAgents). Completed agent badges now show for 2.5s then smoothly fade out
instead of accumulating, keeping the status area clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of showing agent status in a separate panel below the chat,
display it inline beneath the typing dots ("...") in the message flow.
The dots remain the primary waiting indicator; agent status appears
below as supplementary context during specialist agent invocations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Haiku sometimes returns JSON wrapped in ```json ... ``` code blocks,
causing JSON.parse to fail. Strip markdown fences before parsing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>