Delete checkpoints before removing business fields. Suppress only an
explicit CheckpointNotFoundError; propagate storage failures and
cancellation so the endpoint cannot report false success. Prefer async
checkpoint deletion and fail clearly when cleanup is unsupported.
Classify SSE error events as 401 for authentication, 429 for rate limits,
and 500 for other failures, using the same fixed messages as non-stream
responses. Errors terminate the stream without a success done event or
provider exception text in the response.
Validation: 286 offline tests passed (19 added), including failed-delete
HTTP retry with preserved business/checkpoint state, explicit missing
threads, cancellation, missing cleanup capability, and HTTP SSE errors
before and after text output.
- Validate stage prefixes before forwarding model tokens. Invalid prefixes
may use the existing repair budget; once output starts, validation failure
aborts the turn without appending a regenerated reply. Bound the token
queue and close inner streams when consumption stops.
- Replace delete-time lock eviction with reference-counted session locks
covering holders and waiters. Serialize set/get with chat/stream/delete
and release cancelled waiters without replacing a live lock.
- Store person synonyms under workflow-native driver keys and expose
consistent alias views to single/group reads and graph prompt injection.
- Separate committed field patches from public formUpdate visibility.
Commit callbacks keep repository fields synchronized even when updates
are hidden or the consumer closes after graph completion.
- Disable OpenAI SDK automatic retries. Own bounded retry attempts in the
application, use 1/2/4 second backoff, retry streams only before text is
emitted, enforce attempt timeouts, and close upstream/client resources.
- Translate FastGPT SDK max_retries (total attempts) from the configured
extra-retry count, and verify the real SDK loop uses four attempts.
- Document the per-LLM budget and streaming failure semantics without
claiming the whole multi-call HTTP turn has the same timeout.
Validation: 267 offline tests passed, including actual SDK requests through
MockTransport, stream repair/abort, timeout/close, lock races, aliases and
formUpdate visibility; existing four dependency deprecation warnings.
Workflow reconciliation (see previous commit) showed the 20261022
state variable really stores person identity under driver{i}ID /
driver{i}Phonenumber (x2), while the API get_info group keys
(sfzmhm{i}/sjhm{i}) have zero references in any workflow version -
the legacy group read always returned empty for workflow-produced data.
- registry: +4 keys (driver1/2ID, driver1/2Phonenumber) in
human1/human2 groups, SENSITIVE, documented as synonymous with
sfzmhm{i}/sjhm{i}; 24 -> 28 specs
- driver{i}License deliberately NOT registered: the sandbox reads
state.hphm{i} and only renames the output for confirmation prompts -
it is not a state key, set_info rejects it as unknown (workflow
never consumes it)
- intentional diff documented: acdhuman1/2 group output now includes
the driver keys (legacy FastGPT output did not) - fixes the legacy
'workflow person data unreadable by group' gap; synonym merging
(driver1ID vs sfzmhm1) deferred pending caller confirmation
- tests: registry size 28, driver keys present with sensitive level,
License alias excluded
Verified: 230 unit tests
Reconciled the field registry against workflow/20261022 (synced with
main@c7ca781) and historical versions 20250419-20261021:
- form six keys match the workflow form variable (bNJkeXot) and the
更新表单 whitelist semantics exactly
- acd business keys originate from the 20250419 extraction schema
(dropped by the workflow since 20251015, kept as the API contract)
- person group keys (xm1/sfzmwh1/sjwh1/csbw1...) exist in no workflow
version - they are the API server's own contract
- the form<->business key mapping is migration-new semantics bridging
both sides, fixed by bidirectional tests
Finding: workflow 20261022 state uses driver1ID/driver1License/
driver1Phonenumber naming, which never matched the API get_info group
keys - existing behavior read empty; recorded in the audit backlog to
confirm which naming live callers actually use
- fields_registry: FORM_TO_BUSINESS_KEY bidirectional mapping between
the graph's six form keys (has_injury...) and the acd business keys
(ywrysw...); bjrjs/ywmtc have no form source (set_info only);
business_patch_from_form maps graph diffs to whitelisted business keys
and filters internal/unknown keys
- app_service: chat injection now maps repository business keys back to
form keys; both complete() and stream() write back field diffs
(FormUpdate events captured in stream, closing the skeleton gap);
form keys never leak into the business repository
- docs/assignment6-compat.md: mapping table documented, stream write-back
backlog item resolved
- tests: mapping coverage, write-back mapping end-to-end, reverse
mapping injection (bjrjs/ywmtc excluded), stream FormUpdate write-back
Verified: 205 unit tests
- fields_registry: 24 business keys in acd/human1/human2 groups with
value_type, writable and SensitiveLevel metadata; strict bool/int
parsing (no bool('0') trap); INTERNAL_FIELD_KEYS protection list
- repository: SessionRepository protocol + InMemorySessionRepository
(idempotent delete; PostgreSQL swap-in is assignment 7)
- AccidentAppService: chat/set_info/get_info/delete_session share one
business state - chat injects latest repository fields into the graph
and writes back validated field diffs; set/get/delete make zero LLM
calls and zero helper conversations (includeInputInfo accepted but
unused); unknown get keys keep the '' JSON-empty-string contract,
unknown set keys are rejected by whitelist
- ChatInput.injected_fields neutral contract extension; LangGraphBackend
merges injected fields into graph input (set_info visible next chat)
- endpoints: thin layer - langgraph mode routes business state through
the app service (fastgpt mode keeps the legacy path); delete cleans
repository + graph checkpoint, idempotent
- tests: registry completeness (sfzmwh/sjwh/csbw included), app service
combos with LLM-call spy, HTTP acceptance flow (set -> get -> chat ->
groups -> delete -> no leak, idempotent re-delete)
- docs/assignment6-compat.md: behavior diff list vs FastGPT status quo
and the audit backlog (arbitrary set_info key usage, stream formUpdate
write-back, sensitive-field display boundary)
Verified: 200 unit tests (172 existing + 28 new)
- merge main (b0ff2e0..c7ca781): workflow/20261022 moves dynamic context
out of fixed prompts - collection/extraction prompts carry no inline vars;
reference time and current form travel in the user message <turn_context>
block; photo/confirm data kept at prompt tails
- prompts: all 8 files regenerated from 20261022 (byte-identity tests
point at the new export)
- collect: user message = <turn_context>(本轮参考时间) + <user_input>;
extraction message adds 当前表单; single trusted clock per turn threads
through reply/extraction/repair
- photo: raw input only (20261022 photo nodes use system_text, no wrapper)
- workflow fake llm: unwraps <user_input> before event parsing
- fields: accident_time normalized to extraction format (YYYY年M月D日H点m分,
strip zero padding) - fixes form assertion mismatch with padded output
- evals/run.py: trust_env=False - bypass Windows system proxy (registry
proxy at 192.168.0.4:7897 was intercepting localhost and returning 503)
- verified: 172 unit tests; ALL golden suites pass on real model
(46/46): collection_skip 12/12, photo_skip 4/4, no_false 2/2,
button_guidance 12/12, non_motor_vehicle 14/14, time_confirmation 2/2
- time_context.py: server-side observation clocks for relative-time
evidence (anti client-forgery); resolves X分钟前/小时前 against the
observation timestamp of the turn that produced them, never re-basing
history on the current clock; flags historical references without a
reliable baseline
- state: dialogue_timestamps keyed by dialogue message index, persisted
with checkpoint; welcome seeds index 0
- collect: single clock per turn shared by reply/extraction/repair;
time_evidence injected into runtime context; reply_validator hook
rejects time claims lacking evidence (one shared repair budget);
model still interprets facts and chooses actions
- prompts: collection prompt restructured (shorter, state-bound-to-action
table, gatekeeper principle, time source rules); extraction prompt
aligned; workflow@20261021 json updated to match
- evals: prompt_probes / prompt_compare / time_boundary probe scripts
(supplementary checks, original cases untouched)
- docs: collection-prompt-optimization.md (method + baselines),
time-boundary-fix.md (problem, design, verification)
- gitignore: editor artifact dirs (.agents/.claude/.vscode, skills-lock)
Verified: 171 unit tests pass (16 new time-context tests)
- merge main (fb9271e): collection prompt restructured (state-bound-to-action,
gatekeeper principle); fill_missing eval cases relaxed to semantic regexes
- prompts/collection.md regenerated; byte-identity test updated and passing
- rerun golden evals: collection_skip 11/12, time_confirmation 1/2
(inter-run variance; only single_no_inferred_count fails consistently -
model routes without asking vehicle count after time confirmation)
- plan: assignment status table, five landed architecture decisions
(propose-validate, context_schema DI, verbatim alignment, no LangGraph
Server/SDK with rationale, 2004 semantics), real-model eval results
(collection 12/12; photo_skip 0/4 and no_false 2/8 share the '对'
routing pattern), three open questions with next actions, code map
- 2004 task rewritten as plate-confirm with both paths whitelisted
- graduation requirement reworded: illegal transitions never committed
- README: current status section pointing to plan appendix
Architecture (mirrors workflow/20261016 chatNode structure):
- events.py: client markers parsed to typed ClientEvent (sources
verified against workflow JSON); plain text like '拍好了' is not an event
- transitions.py: LEGAL_TRANSITIONS whitelist drafted from chatNode
prompts and Condition edges (16 entries, each with source_ref);
is_legal_transition rejects unlisted/terminal-violating moves
- graph.py: multi-node graph (collect/single_photo/dual_photo/...
matching workflow chatNodes), route_by_stage conditional edges,
_fake_llm_propose simulates LLM stage-code proposals (assignment 4
replaces with real LLM); code only keeps context bookkeeping
(no_reply_count) and validation
- state.py: expanded domain fields (pending_question, accident/
person fields, clarify/no-reply counts, turn_patch, state_version)
Key finding: 单车拍照#6/双车拍照#7 are browser_addon branches ending
at 0000, so 2005/2015 allow both 300x (direct) and 0000.
Verified: pytest 87 passed (24 transition/event tests newly green).
Known limit: stage proposals come from a fake LLM; per-stage scripts
and real LLM integration are assignments 4-5.
- Updated prompts to enhance clarity and consistency in user interactions, particularly focusing on safety prioritization and accurate information collection.
- Introduced new YAML evaluation cases to test various user input scenarios, ensuring robust handling of short, ambiguous, or irrelevant responses.
- Enhanced guidelines for transitioning to human assistance, emphasizing strict conditions for triggering state changes based on user responses.
- Introduced a comprehensive document detailing the interaction interface design for the intelligent information collection robot.
- Updated version history with new features, including the addition of the clientMode parameter and its impact on output formatting.
- Enhanced clarity in the API request and response structures, including examples for better understanding of the interaction flow.
- Updated ANALYSIS_AUTH_TOKEN and APP_ID in the .env file for enhanced security and configuration.
- Added a comprehensive migration plan document for transitioning the LangGraph backend, detailing the background, scope, goals, and architectural decisions for the project.
- Updated documentation and API endpoint to include a new state code '0005' for handling cases where photo recognition fails consecutively, directing to human assistance.
- Ensured consistency between documentation and code for improved clarity in state management.
- Revised the formUpdate section to clarify the conditions under which structured updates are pushed.
- Added specific examples for accident vehicle count and cause of the incident to enhance understanding.
- Adjusted text_delta examples for improved clarity in response handling.